Files
cloudflare__vinext/tests/app-page-segment-state.test.ts
T
Nathan Nguyen d85791c2fb feat(app-router): promote segment reset semantics (#1187)
* feat(app-router): promote segment reset semantics

Implements #726-CORE-11/12 from #726.

Add typed App Router segment state-key derivation, wire template/loading/error/access boundaries to semantic reset keys, and preserve pathname fallback semantics for legacy boundary callers.

Cover dynamic params, route groups, catch-all keys, template reset ownership, and boundary reset behavior.

* fix(app-router): treat empty boundary reset keys as absent

Empty segment identities can be produced for root, route-group-only, and default slot paths. Normalize empty reset keys to null so pathname fallback continues to clear caught boundary state.

* fix(app-router): scope route reset keys to branches

Use typed segment state identities and full visible route-state paths for route-wide wrappers so unrelated branches do not collide on the same leaf value.

Add hostile unit and browser tests for dynamic and static branch collisions.
2026-05-13 10:02:02 +01:00

82 lines
2.5 KiB
TypeScript

import { describe, expect, it } from "vite-plus/test";
import {
resolveAppPageLeafSegmentStateKey,
resolveAppPageRouteStateKey,
resolveAppPageSegmentStateKey,
} from "../packages/vinext/src/server/app-page-segment-state.js";
describe("app page segment state keys", () => {
// Mirrors Next.js createRouterCacheKey(..., true): the React state key is
// the active segment identity without search params, so search-only changes
// do not reset templates or boundaries.
it("resolves dynamic params into segment state keys without search params", () => {
expect(
resolveAppPageSegmentStateKey(["dashboard", "[team]", "settings"], 1, {
team: "alpha",
}),
).toBe("team|alpha|d");
});
it("skips route groups when selecting the state key below a tree position", () => {
expect(
resolveAppPageSegmentStateKey(["(marketing)", "blog", "[slug]"], 0, {
slug: "launch",
}),
).toBe("blog");
expect(
resolveAppPageSegmentStateKey(["(marketing)", "blog", "[slug]"], 1, {
slug: "launch",
}),
).toBe("blog");
expect(
resolveAppPageSegmentStateKey(["(marketing)", "blog", "[slug]"], 2, {
slug: "launch",
}),
).toBe("slug|launch|d");
});
it("keeps the leaf segment helper scoped to the active local segment", () => {
expect(
resolveAppPageLeafSegmentStateKey(["(marketing)", "blog", "[slug]"], {
slug: "launch",
}),
).toBe("slug|launch|d");
expect(resolveAppPageLeafSegmentStateKey(["(marketing)"], {})).toBe("");
});
it("uses the full visible segment-state path for route-wide reset keys", () => {
expect(
resolveAppPageRouteStateKey(["(marketing)", "blog", "[slug]"], {
slug: "launch",
}),
).toBe(JSON.stringify(["blog", "slug|launch|d"]));
expect(
resolveAppPageRouteStateKey(["posts", "[id]"], {
id: "123",
}),
).not.toBe(
resolveAppPageRouteStateKey(["photos", "[id]"], {
id: "123",
}),
);
expect(resolveAppPageRouteStateKey(["account", "settings"], {})).not.toBe(
resolveAppPageRouteStateKey(["admin", "settings"], {}),
);
});
it("keeps catch-all segment keys canonical", () => {
expect(
resolveAppPageSegmentStateKey(["docs", "[...parts]"], 1, {
parts: ["guides", "routing"],
}),
).toBe("parts|guides/routing|c");
expect(
resolveAppPageSegmentStateKey(["docs", "[[...parts]]"], 1, {
parts: [],
}),
).toBe("parts||oc");
});
});