import { expect, test } from "bun:test"; import { diagnose, formatDiagnostic, parse } from "../src/index.ts"; test("parses explicit rendering modes and the never hydration alias", () => { const ast = parse(`page Marketing { render = "static" hydrate = "never" view {

Fast

} }`); expect(ast.renderMode).toBe("static"); expect(ast.hydrate).toBe("none"); expect(() => parse('page Invalid { render = "sometimes" view {

No

} }')).toThrow( "Unknown render mode", ); }); test("parses a declarative cache policy", () => { const ast = parse(`page Dashboard { cache { strategy = "stale-while-revalidate" ttl = "5m" tags = ["users", "dashboard"] vary = ["tenant", "language"] } view {

Dashboard

} }`); expect(ast.cache).toEqual({ strategy: "stale-while-revalidate", ttl: "5m", tags: '["users", "dashboard"]', vary: '["tenant", "language"]', }); }); test("parses native array and object literals in state and component props", () => { const ast = parse(` component Navigation { state items = [{"label":"Home","href":"/"}] state options = {"dense":true,"nested":{"label":"A {brace}"}} view { } } `); expect(ast.states[0]?.expr).toBe('[{"label":"Home","href":"/"}]'); expect(ast.states[1]?.expr).toBe('{"dense":true,"nested":{"label":"A {brace}"}}'); const elements = ast.view.filter((node) => node.type === "element"); const navbar = elements[0]; const sidebar = elements[1]; expect(navbar?.type).toBe("element"); expect(sidebar?.type).toBe("element"); if (navbar?.type !== "element" || sidebar?.type !== "element") return; expect(navbar.attrs.find((attr) => attr.name === "items")?.value).toBe("{items}"); expect(sidebar.attrs.find((attr) => attr.name === "items")?.value).toBe( '{[{"label":"Cases","href":"/cases"}]}', ); expect(sidebar.attrs.find((attr) => attr.name === "options")?.value).toBe('{{"dense":true}}'); }); test("parses formatted multiline arrays and objects in state", () => { const ast = parse(`component Navigation { state items = [ { "label": "Home", "href": "/" }, { "label": "Services", "children": [ { "label": "Reports", "href": "/reports" } ] } ] state options = { "dense": true, "columns": 4 } view { } }`); expect(ast.states[0]?.expr).toContain('"Services"'); expect(ast.states[0]?.expr).toContain('"Reports"'); expect(ast.states[1]?.expr).toContain('"columns": 4'); }); test("parses WRN 0.3 execution, data, security, and reactivity blocks", () => { const ast = parse(`page Dashboard { runtime = "universal" hydrate = "visible" state count = 1 computed { doubled = count * 2 } effect { console.log(doubled) } security { auth = "required" csrf = "true" } load server { return { count: 1 } } load client { return { refreshed: true } } action save(input) { return input } view { } }`); expect(ast.runtime).toBe("universal"); expect(ast.hydrate).toBe("visible"); expect(ast.computed).toEqual([{ name: "doubled", expr: "count * 2" }]); expect(ast.effects).toHaveLength(1); expect(ast.security).toEqual({ auth: "required", csrf: "true" }); expect(ast.loads.map((load) => load.mode)).toEqual(["server", "client"]); expect(ast.actions).toEqual([expect.objectContaining({ name: "save", args: ["input"] })]); }); test("diagnoses server-only interactive roots and accessibility issues", () => { const diagnostics = diagnose( `component AvatarButton { runtime = "server" state open = false view { } }`, { file: "AvatarButton.wrn", accessibility: true }, ); expect(diagnostics.map((diagnostic) => diagnostic.code)).toContain( "WRN-RUNTIME-SERVER-INTERACTIVE", ); expect(diagnostics.map((diagnostic) => diagnostic.code)).toContain("WRN-A11Y-001"); }); test("formats parser diagnostics with stable codes and source locations", () => { const source = `page Broken { runtime = "quantum"\n view {
} }`; const [diagnostic] = diagnose(source, { file: "Broken.wrn" }); expect(diagnostic?.code).toBe("WRN-RUNTIME-TARGET"); expect(formatDiagnostic(source, diagnostic!)).toContain("Broken.wrn"); }); test("parses keyed each blocks without changing legacy loop syntax", () => { const keyed = parse(`component Rows { props { rows = [] } view { {#each rows as row, index key row.id}

{index}: {row.name}

{/each} } }`); const loop = keyed.view.find((node) => node.type === "each"); expect(loop).toEqual( expect.objectContaining({ type: "each", list: "rows", item: "row", index: "index", key: "row.id", }), ); const legacy = parse(`component Rows { props { rows = [] } view { {#each rows as row}

{row.name}

{/each} } }`); expect(legacy.view.find((node) => node.type === "each")).toEqual( expect.objectContaining({ type: "each", key: undefined }), ); }); test("parses public component event declarations inside props", () => { const ast = parse(`component SearchBox { props { value = "" @event search = function @event clear = function } view { } }`); expect(ast.props.map((prop) => prop.name)).toEqual(["value"]); expect(ast.events).toEqual([{ name: "search" }, { name: "clear" }]); }); test("parses edge and worker execution targets", () => { for (const runtime of ["edge", "worker", "service-worker"] as const) { expect(parse(`page Runtime { runtime = "${runtime}" view {

Hi

} }`).runtime).toBe( runtime, ); } });