Files
2026-07-22 17:29:08 +05:30

80 lines
3.0 KiB
TypeScript

import { test, expect } from "bun:test";
import {
compileRoutePattern,
fileToRoute,
generateRoutesFile,
matchRoute,
sortRoutes,
type Route,
} from "../src/index.ts";
function route(raw: string): Route {
return { raw, file: raw, ...compileRoutePattern(raw) };
}
test("generateRoutesFile emits a Routes map with param types", () => {
const code = generateRoutesFile([
route("/"),
route("/about"),
route("/users/[id]"),
route("/docs/[[...slug]]"),
]);
expect(code).toContain('"/": Record<string, never>;');
expect(code).toContain('"/about": Record<string, never>;');
expect(code).toContain('"/users/[id]": { "id": string };');
expect(code).toContain('"/docs/[[...slug]]": { "slug"?: string | readonly string[] };');
expect(code).toContain("export function href");
});
test("generated href() fills required, optional and catch-all params", async () => {
const code = generateRoutesFile([
route("/"),
route("/users/[id]"),
route("/org/[org]/team/[team]"),
route("/blog/[[page]]"),
route("/docs/[...slug]"),
]);
const { tmpdir } = await import("node:os");
const { writeFileSync, mkdirSync } = await import("node:fs");
const { join } = await import("node:path");
const { pathToFileURL } = await import("node:url");
const dir = join(tmpdir(), "wrnexus-routes-test");
mkdirSync(dir, { recursive: true });
const file = join(dir, `r${Date.now()}.ts`);
writeFileSync(file, code);
const mod = (await import(pathToFileURL(file).href)) as {
href: (p: string, params?: Record<string, string | string[]>) => string;
};
expect(mod.href("/")).toBe("/");
expect(mod.href("/users/[id]", { id: "42" })).toBe("/users/42");
expect(mod.href("/org/[org]/team/[team]", { org: "acme", team: "core" })).toBe(
"/org/acme/team/core",
);
expect(mod.href("/users/[id]", { id: "a b" })).toBe("/users/a%20b");
expect(mod.href("/blog/[[page]]")).toBe("/blog");
expect(mod.href("/docs/[...slug]", { slug: ["guide", "start here"] })).toBe(
"/docs/guide/start%20here",
);
});
test("matches optional and catch-all routes", () => {
expect(matchRoute([route("/blog/[[page]]")], "/blog")?.params).toEqual({});
expect(matchRoute([route("/blog/[[page]]")], "/blog/2")?.params).toEqual({ page: "2" });
expect(matchRoute([route("/docs/[...slug]")], "/docs/a/b")?.params).toEqual({ slug: "a/b" });
expect(matchRoute([route("/docs/[[...slug]]")], "/docs")?.params).toEqual({});
});
test("static routes sort ahead of dynamic and catch-all routes", () => {
const sorted = sortRoutes([route("/docs/[...slug]"), route("/docs/[id]"), route("/docs/new")]);
expect(sorted.map((item) => item.raw)).toEqual(["/docs/new", "/docs/[id]", "/docs/[...slug]"]);
});
test("route groups and page/index filenames do not affect URLs", () => {
expect(fileToRoute("(marketing)/pricing/page.wrn")).toBe("/pricing");
expect(fileToRoute("(dashboard)/index.wrn")).toBe("/");
});
test("malformed encoded route params return no match instead of throwing", () => {
expect(matchRoute([route("/users/[id]")], "/users/%E0%A4%A")).toBeNull();
});