Files
cloudflare__vinext/tests/root-params.test.ts
T
Nathan Nguyen 76c5d448fc fix: enforce strict parameter isolation for root-params in prerendering and SSR (#1324)
* fix: support root-params dynamic getters during static prerendering and SSR

* fix: enforce strict parameter isolation for root params in prerendering and SSR

* fix: correct parameter mapping in multi-source resolver and clean up handleSsr fallback

* fix: use dedicated standalone ALS for root-params outside of unified request context

* fix: thread root params through generated app page dispatch

Normal App Router page requests computed root params in the typed handler, but the generated RSC entry did not forward them into page dispatch. SSR then received an empty root params scope for requests that should expose root params.

Destructure and forward rootParams in the generated entry, assert that template wiring, reuse the shared root-param picker, and align the scope helper API with the existing runWith overload pattern.
2026-05-20 10:53:25 +01:00

86 lines
2.8 KiB
TypeScript

import { describe, it, expect } from "vite-plus/test";
import { getRootParam, runWithRootParamsScope } from "../packages/vinext/src/shims/root-params.js";
import {
runWithRequestContext,
createRequestContext,
} from "../packages/vinext/src/shims/unified-request-context.js";
import { runWithNavigationContext } from "../packages/vinext/src/shims/navigation-state.js";
import {
getNavigationContext,
setNavigationContext,
} from "../packages/vinext/src/shims/navigation.js";
describe("next/root-params shim", () => {
it("resolves to undefined when called outside of root params scope", async () => {
const val = await getRootParam("lang");
expect(val).toBeUndefined();
});
it("resolves to the correct param within runWithRootParamsScope", async () => {
const result = await runWithRootParamsScope({ lang: "en", locale: "en-US" }, async () => {
const langVal = await getRootParam("lang");
const localeVal = await getRootParam("locale");
const missingVal = await getRootParam("missing");
return { langVal, localeVal, missingVal };
});
expect(result).toEqual({
langVal: "en",
localeVal: "en-US",
missingVal: undefined,
});
});
it("supports nested scopes overriding outer scopes", async () => {
const result = await runWithRootParamsScope({ lang: "en" }, async () => {
const outer = await getRootParam("lang");
const inner = await runWithRootParamsScope({ lang: "es" }, async () => getRootParam("lang"));
const outerAfter = await getRootParam("lang");
return { outer, inner, outerAfter };
});
expect(result).toEqual({
outer: "en",
inner: "es",
outerAfter: "en",
});
});
it("integrates correctly with unified request context", async () => {
const ctx = createRequestContext({ rootParams: { lang: "fr" } });
const result = await runWithRequestContext(ctx, async () => {
const langVal = await getRootParam("lang");
// Nested overriding scope inside unified context
const nestedVal = await runWithRootParamsScope({ lang: "de" }, async () =>
getRootParam("lang"),
);
const langValAfter = await getRootParam("lang");
return { langVal, nestedVal, langValAfter };
});
expect(result).toEqual({
langVal: "fr",
nestedVal: "de",
langValAfter: "fr",
});
});
it("proves sibling standalone state survives runWithRootParamsScope", async () => {
await runWithNavigationContext(async () => {
setNavigationContext({
pathname: "/blog/en",
searchParams: new URLSearchParams(),
params: { lang: "en" },
});
await runWithRootParamsScope({ lang: "en" }, async () => {
expect(getNavigationContext()?.pathname).toBe("/blog/en");
await expect(getRootParam("lang")).resolves.toBe("en");
});
});
});
});