Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions tests/routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2111,15 +2111,32 @@ describe("pagesRouter - hyphenated param names", () => {
});

describe("pagesRouter - dotted/colon param names (Next.js parity)", () => {
it("discovers dynamic segments with dots and colons", async () => {
it("discovers dynamic segments with dots", async () => {
await withTempDir("vinext-pages-dotted-param-", async (tmpDir) => {
const pagesDir = path.join(tmpDir, "pages");
await mkdir(path.join(pagesDir, "products"), { recursive: true });
await mkdir(path.join(pagesDir, "repos"), { recursive: true });
await writeFile(
path.join(pagesDir, "products", "[variant.id].tsx"),
"export default function Page() { return null; }",
);

invalidateRouteCache(pagesDir);
const routes = await pagesRouter(pagesDir);
const patterns = routes.map((r) => r.pattern);

expect(patterns).toContain("/products/:variant.id");
invalidateRouteCache(pagesDir);
});
});

// `:` is a reserved character in Windows filenames — it delimits an NTFS
// alternate data stream, so writing `[repo:name].tsx` there silently produces
// a `[repo` file instead. The scenario is unreachable on Windows, so gate it
// to POSIX and keep the assertion unconditional.
it.runIf(process.platform !== "win32")("discovers dynamic segments with colons", async () => {
await withTempDir("vinext-pages-colon-param-", async (tmpDir) => {
const pagesDir = path.join(tmpDir, "pages");
await mkdir(path.join(pagesDir, "repos"), { recursive: true });
await writeFile(
path.join(pagesDir, "repos", "[repo:name].tsx"),
"export default function Page() { return null; }",
Expand All @@ -2129,7 +2146,6 @@ describe("pagesRouter - dotted/colon param names (Next.js parity)", () => {
const routes = await pagesRouter(pagesDir);
const patterns = routes.map((r) => r.pattern);

expect(patterns).toContain("/products/:variant.id");
expect(patterns).toContain("/repos/:repo:name");
invalidateRouteCache(pagesDir);
});
Expand All @@ -2155,18 +2171,17 @@ describe("pagesRouter - dotted/colon param names (Next.js parity)", () => {
});
});

it("skips routes whose param names end in + or * (would collide with internal modifiers)", async () => {
// The + and * cases are split because `*` is a reserved character in Windows
// filenames — a literal `[id*].tsx` file can't exist there, so that scenario
// is gated to POSIX (where it's reachable).
it("skips routes whose param names end in + (would collide with internal modifiers)", async () => {
await withTempDir("vinext-pages-skip-plus-", async (tmpDir) => {
const pagesDir = path.join(tmpDir, "pages");
await mkdir(pagesDir, { recursive: true });
await writeFile(
path.join(pagesDir, "[id+].tsx"),
"export default function Page() { return null; }",
);
await writeFile(
path.join(pagesDir, "[id*].tsx"),
"export default function Page() { return null; }",
);

invalidateRouteCache(pagesDir);
const routes = await pagesRouter(pagesDir);
Expand All @@ -2176,4 +2191,25 @@ describe("pagesRouter - dotted/colon param names (Next.js parity)", () => {
invalidateRouteCache(pagesDir);
});
});

it.runIf(process.platform !== "win32")(
"skips routes whose param names end in * (would collide with internal modifiers)",
async () => {
await withTempDir("vinext-pages-skip-star-", async (tmpDir) => {
const pagesDir = path.join(tmpDir, "pages");
await mkdir(pagesDir, { recursive: true });
await writeFile(
path.join(pagesDir, "[id*].tsx"),
"export default function Page() { return null; }",
);

invalidateRouteCache(pagesDir);
const routes = await pagesRouter(pagesDir);

// Skipped entirely to avoid ambiguity with internal :name+ / :name* modifiers
expect(routes).toHaveLength(0);
invalidateRouteCache(pagesDir);
});
},
);
});
Loading