Files
bun.sh/test/bake/framework-router.test.ts

136 lines
5.2 KiB
TypeScript

import { frameworkRouterInternals } from "bun:internal-for-testing";
import { describe, expect, test } from "bun:test";
import { tempDirWithFiles } from "harness";
import path from "path";
const { parseRoutePattern, FrameworkRouter } = frameworkRouterInternals;
const testRoutePattern = (style: string) => {
// The 'expected' is a one-off string serialization that is only used for testing.
// Params are serialized as ":param", catch all as ":*param", and optional catch all as ":*?param".
const fn = (pattern: string, expected: string, kind: "page" | "layout" | "extra" = "page") => {
test(`[${style}] pass: ${JSON.stringify(pattern)}`, () => {
const result = parseRoutePattern(style, pattern);
if (result === null) {
throw new Error("Parser said this file is not a route");
}
expect(result.kind, "expected route kind to match").toBe(kind);
expect(result.pattern, "expected route pattern to match").toBe(expected);
});
};
fn.fails = (pattern: string, msg: string) => {
test(`[${style}] error: ${JSON.stringify(pattern)}`, () => {
expect(() => parseRoutePattern(style, pattern)).toThrow(msg);
});
};
fn.isNull = (pattern: string) => {
test(`[${style}] ignore: ${JSON.stringify(pattern)}`, () => {
expect(parseRoutePattern(style, pattern)).toBeNull();
});
};
return fn;
};
describe("pattern parse", () => {
const testPages = testRoutePattern("nextjs-pages");
testPages("/index.tsx", "", "page");
testPages("/_layout.tsx", "", "layout");
testPages("/subdir/index.tsx", "/subdir", "page");
testPages("/subdir/_layout.tsx", "/subdir", "layout");
testPages("/subdir/[page].tsx", "/subdir/:page", "page");
testPages("/[user]/posts.tsx", "/:user/posts", "page");
testPages("/[user]/_layout.tsx", "/:user", "layout");
testPages("/subdir/[page]/[other].tsx", "/subdir/:page/:other", "page");
testPages("/[page]/[other]/index.js", "/:page/:other", "page");
testPages("/[...data].js", "/:*data", "page");
testPages("/[[...data]].js", "/:*?data", "page");
testPages("/[...data]/index.tsx", "/:*data", "page");
testPages("/[[...data]]/index.jsx", "/:*?data", "page");
testPages("/hello/[...data]/index.tsx", "/hello/:*data", "page");
testPages("/hello/[[...data]]/index.jsx", "/hello/:*?data", "page");
testPages("/[...data]/_layout.tsx", "/:*data", "layout");
testPages("/[[...data]]/_layout.jsx", "/:*?data", "layout");
testPages("/hello/[...data]/_layout.tsx", "/hello/:*data", "layout");
testPages("/hello/[[...data]]/_layout.jsx", "/hello/:*?data", "layout");
// Parenthesis is the error location (column:length)
testPages.fails("/subdir/[", 'Missing "]" to match this route parameter (8:1)');
testPages.fails("/subdir/[a", 'Missing "]" to match this route parameter (8:2)');
testPages.fails("/subdir/[page.tsx", 'Missing "]" to match this route parameter (8:9)');
testPages.fails("/subdir/[]/hello", "Parameter needs a name (8:2)");
testPages.fails("/subdir/[.hello]-hello.tsx", 'Parameter name cannot start with "." (use "..." for catch-all) (8:8)');
testPages.fails(
"/subdir/[..hello]-hello.tsx",
'Parameter name cannot start with "." (use "..." for catch-all) (8:9)',
);
testPages.fails("/subdir/[...hello]-hello.tsx", "Parameters must take up the entire file name (8:10)");
testPages.fails("/subdir/[...hello]/bar.tsx", "Catch-all parameter must be at the end of a route (8:10)");
testPages.fails(
"/hello/[[optional_param]]/_layout.tsx",
'Optional parameters can only be catch-all (change to "[[...optional_param]]" or remove extra brackets) (7:18)',
);
const testApp = testRoutePattern("nextjs-app-ui");
testApp("/page.tsx", "", "page");
testApp("/layout.tsx", "", "layout");
testApp("/route/[param]/page.tsx", "/route/:param", "page");
testApp("/route/(group)/page.tsx", "/route/(group)", "page");
testApp("/route/[param]/not-found.tsx", "/route/:param", "extra");
testApp.isNull("/route/_layout.tsx");
});
test("discovers from filesystem paths", () => {
const dir = tempDirWithFiles("fsr", {
"hello.tsx": "1",
"meow/_layout.tsx": "1",
"meow/bark/[param]/hello.tsx": "1",
"[world].tsx": "1",
});
const router = new FrameworkRouter({ root: dir, style: "nextjs-pages" });
expect(router.toJSON()).toEqual({
part: "/",
page: null,
layout: null,
children: [
{
part: "/:world",
page: path.join(dir, "[world].tsx"),
layout: null,
children: [],
},
{
part: "/meow",
page: null,
layout: path.join(dir, "meow/_layout.tsx"),
children: [
{
part: "/bark",
page: null,
layout: null,
children: [
{
part: "/:param",
page: null,
layout: null,
children: [
{
part: "/hello",
page: path.join(dir, "meow/bark/[param]/hello.tsx"),
layout: null,
children: [],
},
],
},
],
},
],
},
{
part: "/hello",
page: path.join(dir, "hello.tsx"),
layout: null,
children: [],
},
],
});
});