import { test, expect } from "bun:test"; import { readFileSync } from "node:fs"; import { Window } from "happy-dom"; import { CORE_COMPONENT_CONTRACTS, CORE_DESIGN_SYSTEMS, compileDesignSystem, validateDesignSystem, type DesignSystem, } from "../../design-system/src/index.ts"; import { renderComponent, mountHtml } from "../../test/src/index.ts"; import { renderDocument, renderDocumentStream } from "../../ssr/src/index.ts"; import { resolveThemeConfig, renderThemeCss } from "../../styles/src/theme.ts"; import { designFixtureBody, PREVIEW_CSS } from "./design-system-fixture.ts"; const render = (name: keyof typeof CORE_COMPONENT_CONTRACTS, props: Record = {}) => renderComponent(readFileSync(CORE_COMPONENT_CONTRACTS[name].source, "utf8"), props); const selector = (name: string, part: string) => `[data-wrn-owner="${name}"][data-wrn-part="${part}"]`; test("every advertised contract part and axis has an additive source hook", () => { for (const [name, contract] of Object.entries(CORE_COMPONENT_CONTRACTS)) { const source = readFileSync(contract.source, "utf8"); expect(source).toContain(`data-wrn-component="${name}"`); for (const part of Object.keys(contract.parts)) expect(source).toContain(`data-wrn-part="${part}"`); for (const axis of Object.keys(contract.axes)) expect(source).toContain(`data-wrn-axis-${axis}="{${axis}}"`); } }); test("application theme tokens and controlClass token overrides retain precedence over recipes", async () => { const theme = resolveThemeConfig({ designSystem: { system: CORE_DESIGN_SYSTEMS.soft }, default: "light", themes: { light: { "radius-sm": "5px" } }, }); const body = (await render("Button", { label: "App token" })) + (await render("Button", { label: "Local token", controlClass: "local-geometry" })); const window = new Window(); try { window.document.write( renderDocument({ meta: {}, body, extraHead: ``, componentRecipeCss: theme.recipeCss, }), ); const buttons = window.document.querySelectorAll("button.wrn-btn"); expect(window.getComputedStyle(buttons[0]!).borderRadius).toBe("5px"); expect(window.getComputedStyle(buttons[1]!).borderRadius).toBe("11px"); expect(buttons[1]!.classList.contains("local-geometry")).toBe(true); } finally { await window.happyDOM.close(); } }); test("the identical composed page consumes all three systems in light and dark", async () => { const body = await designFixtureBody(); for (const [name, system] of Object.entries(CORE_DESIGN_SYSTEMS)) for (const mode of ["light", "dark"]) { const theme = resolveThemeConfig({ designSystem: { system }, default: mode }); const html = renderDocument({ meta: {}, body, htmlAttrs: ` data-theme="${mode}"`, extraHead: ``, componentRecipeCss: theme.recipeCss, styleNonce: "recipe-test", }); const window = new Window(); try { window.document.write(html); const style = (component: string, part: string) => window.getComputedStyle(window.document.querySelector(selector(component, part))!); expect(style("Button", "control").borderRadius).toBe(name === "soft" ? "16px" : "0px"); expect(style("Input", "control").borderRadius).toBe(name === "soft" ? "16px" : "0px"); expect(style("Card", "panel").borderRadius).toBe(name === "soft" ? "24px" : "0px"); expect(window.document.querySelector("input#display-name")).not.toBeNull(); expect(html.lastIndexOf("data-wrn-design-recipes")).toBeGreaterThan( html.lastIndexOf('data-wrnexus-style-kind="component"'), ); expect(html).toContain('data-wrn-design-recipes nonce="recipe-test"'); expect(style("Button", "control").backgroundImage).toBe("none"); } finally { await window.happyDOM.close(); } } }); test("explicit pill, size, color and Card geometry props survive recipes", async () => { for (const system of Object.values(CORE_DESIGN_SYSTEMS)) { const body = (await render("Button", { label: "Pill", pill: true, size: "sm", color: "danger" })) + (await render("Card", { title: "Card", radius: "lg", shadow: "none" })); const theme = resolveThemeConfig({ designSystem: { system } }); const window = new Window(); try { window.document.write( renderDocument({ meta: {}, body, extraHead: ``, componentRecipeCss: theme.recipeCss, }), ); const button = window.document.querySelector(selector("Button", "control"))!; expect(window.getComputedStyle(button).borderRadius).toBe("999px"); expect(button.getAttribute("data-wrn-axis-size")).toBe("sm"); expect( window.getComputedStyle(window.document.querySelector(selector("Card", "panel"))!) .borderRadius, ).toBe("16px"); expect( window.getComputedStyle(window.document.querySelector(selector("Card", "panel"))!) .boxShadow, ).toBe("none"); } finally { await window.happyDOM.close(); } } }); test("typography and Container recipes follow the selected system without changing layout props", async () => { for (const [name, system] of Object.entries(CORE_DESIGN_SYSTEMS)) { const theme = resolveThemeConfig({ designSystem: { system }, default: "light" }); const body = (await render("Container", { columns: 1, maxWidth: "lg" })) + (await render("PageHeader", { title: "A long heading that must retain its typography across layout widths", })); const window = new Window(); try { window.document.write( renderDocument({ meta: {}, body, extraHead: ``, componentRecipeCss: theme.recipeCss, }), ); const container = window.document.querySelector(selector("Container", "root"))!; expect(container.getAttribute("data-columns")).toBe("1"); expect(container.getAttribute("data-max-width")).toBe("lg"); expect(window.getComputedStyle(container).getPropertyValue("padding-inline")).toBe( name === "soft" ? "1.5rem" : "1rem", ); const heading = window.document.querySelector(selector("PageHeader", "title"))!; expect(window.getComputedStyle(heading).fontFamily).toContain( name === "soft" ? "Georgia" : name === "angular" ? "Times New Roman" : "IBM Plex Mono", ); } finally { await window.happyDOM.close(); } } }); test("matching compound rules override base and native disabled state rules", async () => { const system: DesignSystem = { schemaVersion: 1, id: "rules", name: "Rules", version: "1", tokens: {}, recipes: { Button: { contractVersion: 1, base: { control: { borderWidth: "1px" } }, variants: [ { when: { variant: "outline", size: "sm" }, parts: { control: { borderWidth: "3px" } } }, ], states: [{ state: "disabled", parts: { control: { opacity: 0.4, borderWidth: "5px" } } }], }, }, }; const window = new Window(); try { const body = await render("Button", { label: "Unavailable", variant: "outline", size: "sm", disabled: true, }); window.document.write( renderDocument({ meta: {}, body, componentRecipeCss: compileDesignSystem(system).recipeCss }), ); const button = window.document.querySelector(selector("Button", "control"))!; expect(window.getComputedStyle(button).borderWidth).toBe("5px"); expect(window.getComputedStyle(button).opacity).toBe("0.4"); expect(button.hasAttribute("disabled")).toBe(true); } finally { await window.happyDOM.close(); } }); test("recipes do not leak parent variant conditions into nested instances", async () => { const system: DesignSystem = { schemaVersion: 1, id: "nested", name: "Nested", version: "1", tokens: {}, recipes: { Button: { contractVersion: 1, variants: [{ when: { variant: "outline" }, parts: { control: { borderWidth: "5px" } } }], }, }, }; const outer = await render("Button", { label: "Outer", variant: "outline" }); const inner = await render("Button", { label: "Inner", variant: "ghost" }); const window = new Window(); try { window.document.write( `
${outer}${inner}
`, ); const controls = window.document.querySelectorAll("button.wrn-btn"); expect(window.getComputedStyle(controls[0]!).borderWidth).toBe("5px"); expect(window.getComputedStyle(controls[1]!).borderWidth).not.toBe("5px"); } finally { await window.happyDOM.close(); } }); test("nested design islands isolate recipes and inline modal panels regardless of stylesheet order", async () => { const outer = await render("Button", { label: "Outer" }), inner = await render("Button", { label: "Inner" }); const modal = await render("Modal", { title: "Nested dialog", defaultOpen: true }); const soft = compileDesignSystem(CORE_DESIGN_SYSTEMS.soft, { scope: "outer", mode: "light" }).css; const square = compileDesignSystem(CORE_DESIGN_SYSTEMS.square, { scope: "inner", mode: "light", }).css; for (const css of [soft + square, square + soft]) { const window = new Window(); try { window.document.write( `
${outer}
${inner}${modal}
`, ); const buttons = window.document.querySelectorAll("button.wrn-btn"); expect(window.getComputedStyle(buttons[0]!).borderRadius).toBe("16px"); expect(window.getComputedStyle(buttons[1]!).borderRadius).toBe("0px"); expect( window.getComputedStyle(window.document.querySelector(selector("Modal", "panel"))!) .borderRadius, ).toBe("0px"); } finally { await window.happyDOM.close(); } } }); test("all systems retain hydrated switches and modal opening/dismissal", async () => { for (const system of Object.values(CORE_DESIGN_SYSTEMS)) { const theme = resolveThemeConfig({ designSystem: { system } }); const source = (await render("Switch", { id: "switch", name: "switch", label: "Email updates" })) + (await render("Modal", { title: "Review changes", triggerLabel: "Open dialog" })); const dom = mountHtml(`${source}`); const toggle = dom.querySelector('input[role="switch"]') as HTMLInputElement; toggle.click(); expect(toggle.checked).toBe(true); const trigger = dom.querySelector( '[data-wrn-owner="Modal"][data-wrn-part="trigger"]', ) as HTMLButtonElement; trigger.click(); expect(trigger.getAttribute("aria-expanded")).toBe("true"); const close = dom.querySelector( '[data-wrn-owner="Modal"][data-wrn-part="close"]', ) as HTMLButtonElement; close.click(); expect(trigger.getAttribute("aria-expanded")).toBe("false"); } }); test("custom document shells and streamed documents preserve the nonce-protected late recipe block", async () => { const css = compileDesignSystem(CORE_DESIGN_SYSTEMS.soft).recipeCss; const body = await render("Input", { label: "Name" }); const html = renderDocument({ meta: {}, body, componentRecipeCss: css, styleNonce: "test-nonce", documentTemplate: `${body}`, }); expect(html.indexOf("data-wrn-design-recipes")).toBeGreaterThan( html.indexOf("data-wrnexus-style-kind"), ); const streamed = await new Response( renderDocumentStream({ meta: {}, body, componentRecipeCss: css, styleNonce: "test-nonce" }), ).text(); expect(streamed).toContain('data-wrn-design-recipes nonce="test-nonce"'); expect(validateDesignSystem(CORE_DESIGN_SYSTEMS.soft)).toEqual([]); });