From 13ec810cb992632f7fa12be90d9b11f391f00dda Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 7 Nov 2023 13:48:02 +0000 Subject: [PATCH] fix: [app dir bootstrapping 8] `useParamsWithFallback` hook and add tests (#12041) * fix: first solution using RouterContext * fix: second solution by importing router from next/compat/router * fix return type --- .../lib/hooks/useParamsWithFallback.test.ts | 63 +++++++++++++++++++ packages/lib/hooks/useParamsWithFallback.ts | 15 +++-- vitest.workspace.ts | 9 ++- 3 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 packages/lib/hooks/useParamsWithFallback.test.ts diff --git a/packages/lib/hooks/useParamsWithFallback.test.ts b/packages/lib/hooks/useParamsWithFallback.test.ts new file mode 100644 index 0000000000..0a8ff38dd7 --- /dev/null +++ b/packages/lib/hooks/useParamsWithFallback.test.ts @@ -0,0 +1,63 @@ +import { renderHook } from "@testing-library/react-hooks"; +import { vi } from "vitest"; +import { describe, expect, it } from "vitest"; + +import { useParamsWithFallback } from "./useParamsWithFallback"; + +describe("useParamsWithFallback hook", () => { + it("should return router.query when param is null", () => { + vi.mock("next/navigation", () => ({ + useParams: vi.fn().mockReturnValue(null), + })); + + vi.mock("next/compat/router", () => ({ + useRouter: vi.fn().mockReturnValue({ query: { id: 1 } }), + })); + + const { result } = renderHook(() => useParamsWithFallback()); + + expect(result.current).toEqual({ id: 1 }); + }); + + it("should return router.query when param is undefined", () => { + vi.mock("next/navigation", () => ({ + useParams: vi.fn().mockReturnValue(undefined), + })); + + vi.mock("next/compat/router", () => ({ + useRouter: vi.fn().mockReturnValue({ query: { id: 1 } }), + })); + + const { result } = renderHook(() => useParamsWithFallback()); + + expect(result.current).toEqual({ id: 1 }); + }); + + it("should return useParams() if it exists", () => { + vi.mock("next/navigation", () => ({ + useParams: vi.fn().mockReturnValue({ id: 1 }), + })); + + vi.mock("next/compat/router", () => ({ + useRouter: vi.fn().mockReturnValue(null), + })); + + const { result } = renderHook(() => useParamsWithFallback()); + + expect(result.current).toEqual({ id: 1 }); + }); + + it("should return useParams() if it exists", () => { + vi.mock("next/navigation", () => ({ + useParams: vi.fn().mockReturnValue({ id: 1 }), + })); + + vi.mock("next/compat/router", () => ({ + useRouter: vi.fn().mockReturnValue({ query: { id: 2 } }), + })); + + const { result } = renderHook(() => useParamsWithFallback()); + + expect(result.current).toEqual({ id: 1 }); + }); +}); diff --git a/packages/lib/hooks/useParamsWithFallback.ts b/packages/lib/hooks/useParamsWithFallback.ts index 53dd1bb498..8e5e4eb5f3 100644 --- a/packages/lib/hooks/useParamsWithFallback.ts +++ b/packages/lib/hooks/useParamsWithFallback.ts @@ -1,13 +1,18 @@ "use client"; +import { useRouter as useCompatRouter } from "next/compat/router"; import { useParams } from "next/navigation"; -import { useRouter } from "next/router"; +import type { ParsedUrlQuery } from "querystring"; + +interface Params { + [key: string]: string | string[]; +} /** * This hook is a workaround until pages are migrated to app directory. */ -export function useParamsWithFallback() { - const router = useRouter(); - const params = useParams(); - return params || router.query; +export function useParamsWithFallback(): Params | ParsedUrlQuery { + const params = useParams(); // always `null` in pages router + const router = useCompatRouter(); // always `null` in app router + return params ?? router?.query ?? {}; } diff --git a/vitest.workspace.ts b/vitest.workspace.ts index 20d12799fb..1b59d9ba80 100644 --- a/vitest.workspace.ts +++ b/vitest.workspace.ts @@ -36,7 +36,7 @@ const workspaces = packagedEmbedTestsOnly test: { include: ["packages/**/*.{test,spec}.{ts,js}", "apps/**/*.{test,spec}.{ts,js}"], // TODO: Ignore the api until tests are fixed - exclude: ["**/node_modules/**/*", "packages/embeds/**/*"], + exclude: ["**/node_modules/**/*", "packages/embeds/**/*", "packages/lib/hooks/**/*"], setupFiles: ["setupVitest.ts"], }, }, @@ -67,6 +67,13 @@ const workspaces = packagedEmbedTestsOnly setupFiles: ["packages/app-store/test-setup.ts"], }, }, + { + test: { + name: "@calcom/packages/lib/hooks", + include: ["packages/lib/hooks/**/*.{test,spec}.{ts,js}"], + environment: "jsdom", + }, + }, ]; export default defineWorkspace(workspaces);