fix: inject grouped route codecs
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describe, expect, expectTypeOf, it } from "vitest";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
createMemoryRouter,
|
||||
matchRoutes,
|
||||
@@ -18,7 +19,10 @@ import {
|
||||
} from "../../src/presentation/routes/app-router.tsx";
|
||||
import { createTestApplication } from "../helpers/create-test-application.ts";
|
||||
import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
import { ROUTE_RUNTIME } from "../../src/features/installed-feature-runtimes.tsx";
|
||||
import {
|
||||
ROUTE_CODECS,
|
||||
ROUTE_RUNTIME,
|
||||
} from "../../src/features/installed-feature-runtimes.tsx";
|
||||
import { LocaleProvider } from "../../src/presentation/i18n/index.ts";
|
||||
import { SessionProvider } from "../../src/presentation/providers/session-provider.tsx";
|
||||
import { ThemeProvider } from "../../src/presentation/providers/theme-provider.tsx";
|
||||
@@ -29,6 +33,72 @@ function StudioFallbackFixture() {
|
||||
return <h1>{String(input.params["*"])}</h1>;
|
||||
}
|
||||
|
||||
const reviewFixtureCodecs = Object.freeze({
|
||||
ReviewFixtureParams: z
|
||||
.object({ reviewId: z.string().min(1) })
|
||||
.strict(),
|
||||
ReviewFixtureSearch: z
|
||||
.object({
|
||||
filter: z.preprocess(
|
||||
(value) => (Array.isArray(value) ? value[0] : value),
|
||||
z.string().trim().min(1).optional(),
|
||||
),
|
||||
})
|
||||
.strip(),
|
||||
});
|
||||
|
||||
const groupedFixtureCodecs = Object.freeze({
|
||||
none: ROUTE_CODECS.none,
|
||||
NotFoundSplat: ROUTE_CODECS.NotFoundSplat,
|
||||
});
|
||||
|
||||
const reviewFixtureRegistry = Object.freeze({
|
||||
REVIEW_FIXTURE: Object.freeze({
|
||||
...ROUTE_REGISTRY.APP_HOME,
|
||||
routeId: "REVIEW_FIXTURE",
|
||||
path: "/review/:reviewId",
|
||||
paramsSchema: "ReviewFixtureParams",
|
||||
searchSchema: "ReviewFixtureSearch",
|
||||
}),
|
||||
});
|
||||
|
||||
function ReviewRouteFixture() {
|
||||
const input = useRouteInput<"REVIEW_FIXTURE">();
|
||||
expectTypeOf(input.routeId).toEqualTypeOf<"REVIEW_FIXTURE">();
|
||||
return (
|
||||
<section>
|
||||
<h1>{input.routeId}</h1>
|
||||
<p data-testid="review-param">{String(input.params.reviewId)}</p>
|
||||
<p data-testid="review-search">{JSON.stringify(input.search)}</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const reviewFixtureRuntime = Object.freeze({
|
||||
REVIEW_FIXTURE: Object.freeze({
|
||||
moduleId: "review-fixture",
|
||||
Component: ReviewRouteFixture,
|
||||
}),
|
||||
});
|
||||
|
||||
function compileTimeGroupedRouteContract() {
|
||||
// @ts-expect-error A grouped route composition must provide its codec registry.
|
||||
createGroupedRouteObjects(
|
||||
reviewFixtureRegistry,
|
||||
reviewFixtureRuntime,
|
||||
{ PUBLIC: <Outlet />, STUDIO: <Outlet /> },
|
||||
"type-test-build",
|
||||
);
|
||||
return createGroupedRouteObjects(
|
||||
reviewFixtureRegistry,
|
||||
reviewFixtureRuntime,
|
||||
{ PUBLIC: <Outlet />, STUDIO: <Outlet /> },
|
||||
"type-test-build",
|
||||
reviewFixtureCodecs,
|
||||
);
|
||||
}
|
||||
void compileTimeGroupedRouteContract;
|
||||
|
||||
function renderRouter() {
|
||||
return render(
|
||||
<ApplicationProvider
|
||||
@@ -42,6 +112,51 @@ function renderRouter() {
|
||||
}
|
||||
|
||||
describe("generic application router", () => {
|
||||
it("renders and canonicalizes an isolated non-installed route codec contract", async () => {
|
||||
expect(ROUTE_REGISTRY).not.toHaveProperty("REVIEW_FIXTURE");
|
||||
expect(ROUTE_CODECS).not.toHaveProperty("ReviewFixtureParams");
|
||||
expect(ROUTE_CODECS).not.toHaveProperty("ReviewFixtureSearch");
|
||||
|
||||
const routes = createGroupedRouteObjects(
|
||||
reviewFixtureRegistry,
|
||||
reviewFixtureRuntime,
|
||||
{ PUBLIC: <Outlet />, STUDIO: <Outlet /> },
|
||||
"test-build",
|
||||
reviewFixtureCodecs,
|
||||
);
|
||||
const router = createMemoryRouter(routes, {
|
||||
initialEntries: [
|
||||
"/review/non-empty?filter=%20first%20&filter=second&unknown=drop",
|
||||
],
|
||||
});
|
||||
|
||||
render(
|
||||
<ApplicationProvider application={createTestApplication()}>
|
||||
<LocaleProvider>
|
||||
<ThemeProvider>
|
||||
<SessionProvider>
|
||||
<RouterProvider router={router} />
|
||||
</SessionProvider>
|
||||
</ThemeProvider>
|
||||
</LocaleProvider>
|
||||
</ApplicationProvider>,
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByRole("heading", { name: "REVIEW_FIXTURE" }),
|
||||
).toBeVisible();
|
||||
expect(screen.getByTestId("review-param")).toHaveTextContent("non-empty");
|
||||
expect(screen.getByTestId("review-search")).toHaveTextContent(
|
||||
'{"filter":"first"}',
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(router.state.location).toMatchObject({
|
||||
pathname: "/review/non-empty",
|
||||
search: "?filter=first",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("assembles generic Public and Studio parents with Studio catch-all precedence", () => {
|
||||
const registry = {
|
||||
APP_HOME: ROUTE_REGISTRY.APP_HOME,
|
||||
@@ -66,6 +181,7 @@ describe("generic application router", () => {
|
||||
STUDIO: <div data-layout="studio" />,
|
||||
},
|
||||
"test-build",
|
||||
groupedFixtureCodecs,
|
||||
);
|
||||
|
||||
expect(routes.map((route) => route.id)).toEqual([
|
||||
@@ -87,6 +203,7 @@ describe("generic application router", () => {
|
||||
STUDIO: <div data-layout="studio" />,
|
||||
},
|
||||
"test-build",
|
||||
groupedFixtureCodecs,
|
||||
);
|
||||
expect(installedRoutes[1]?.children?.at(-1)?.id).toBe("NOT_FOUND");
|
||||
});
|
||||
@@ -101,6 +218,7 @@ describe("generic application router", () => {
|
||||
STUDIO: <div data-layout="studio" />,
|
||||
},
|
||||
"test-build",
|
||||
groupedFixtureCodecs,
|
||||
),
|
||||
).toThrow("Missing route runtime: APP_HOME");
|
||||
});
|
||||
@@ -130,6 +248,7 @@ describe("generic application router", () => {
|
||||
),
|
||||
},
|
||||
"test-build",
|
||||
groupedFixtureCodecs,
|
||||
);
|
||||
const router = createMemoryRouter(routes, {
|
||||
initialEntries: ["/studio/unknown/path"],
|
||||
@@ -175,6 +294,7 @@ describe("generic application router", () => {
|
||||
STUDIO: <Outlet />,
|
||||
},
|
||||
"test-build",
|
||||
groupedFixtureCodecs,
|
||||
);
|
||||
const router = createMemoryRouter(routes, {
|
||||
initialEntries: ["/private-fixture"],
|
||||
|
||||
Reference in New Issue
Block a user