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;'); expect(code).toContain('"/about": Record;'); 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; }; 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(); });