import { expect, test } from "bun:test"; import { readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { compileWireFile } from "../../../packages/compiler/src/index.ts"; const root = join(import.meta.dir, ".."); const pagesDir = join(root, "app", "pages"); const detailPagesDir = join(pagesDir, "components"); const reference = JSON.parse( readFileSync(join(root, "..", "..", "packages", "ui", "component-reference.json"), "utf8"), ) as { count: number; components: Array<{ name: string; mount: string; category: string; events: string[] }>; }; const slugOf = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); test("showcase configures local Lucide icon generation", () => { const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { devDependencies: Record; }; const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8"); expect(packageJson.devDependencies["@iconify/tailwind4"]).toBeTruthy(); expect(packageJson.devDependencies["@iconify-json/lucide"]).toBeTruthy(); expect(styles).toContain('@plugin "@iconify/tailwind4";'); expect(styles).toContain(".playground-preview-source > .wire-action:not(.w-full)"); expect(styles).toContain("width: fit-content;"); }); test("showcase uses a searchable three-column documentation shell", () => { const layout = readFileSync(join(root, "app", "layouts", "showcase.wrn"), "utf8"); const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8"); const runtime = readFileSync(join(root, "public", "playground.js"), "utf8"); expect(layout).toContain("data-docs-directory"); expect(layout).toContain("data-docs-search"); expect(layout.match(/data-docs-component-link/g)).toHaveLength(reference.count); expect(styles).toContain("grid-template-columns: 17rem minmax(0, 1fr)"); expect(runtime).toContain("data-docs-menu-toggle"); expect(runtime).toContain('event.key.toLowerCase() === "k"'); }); test("component sidebar shows delivery status and refreshes its active link after navigation", () => { const layout = readFileSync(join(root, "app", "layouts", "showcase.wrn"), "utf8"); const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8"); const runtime = readFileSync(join(root, "public", "playground.js"), "utf8"); const completeComponents = [ "Accordion", "Alert", "Avatar", "Avatar Group", "Badge", "Blockquote", "Button", "Button Group", "Card", "Carousel", "Chat Bubble", "Collapse", "Checkbox", "Date Picker", "Device Frame", "File Upload Progress", "Color Picker", "File Input", "Input", "Input Group", "Radio", "Range Slider", "Select", "Switch", "Textarea", "Time Picker", "Advanced Select", "Combo Box", "Input Number", "Pin Input", "Strong Password", "Toggle Count", "Toggle Password", ]; expect(layout.match(/data-component-status="complete"/g)).toHaveLength(completeComponents.length); expect(layout.match(/data-component-status="pending"/g)).toHaveLength( reference.count - completeComponents.length, ); for (const component of completeComponents) { expect(layout).toContain( `${component}Complete`, ); } expect(styles).toContain('[data-component-status="working"]'); expect(runtime).toContain('window.addEventListener("wrnexus:navigated", updateActiveNavigation)'); expect(runtime).toContain('link.removeAttribute("aria-current")'); }); test("accordion examples do not include the generic composable slot placeholder", () => { const page = readFileSync(join(detailPagesDir, "accordion.wrn"), "utf8"); expect(page).not.toContain("Composable content area."); }); test("showcase SSR document layout maps persisted design cookies to html attributes", () => { const source = readFileSync(join(root, "app", "layouts", "document.wrn"), "utf8"); expect(source).toContain(""); expect(source).toContain('
'); for (const setting of ["style", "palette", "mode", "font", "scale"]) { expect(source).toContain(`data-ui-${setting}`); expect(source).toContain(`cookies['wrn-ui-${setting}']`); } }); test("advanced select demos use validation schemas and a real remote API", () => { const page = readFileSync( join(root, "app", "pages", "components", "advanced-select.wrn"), "utf8", ); expect(page).toContain('data-schema="advanced-select-validation"'); expect(page).toContain('data-schema="advanced-select-dynamic-validation"'); expect(readFileSync(join(root, "app", "api", "teams.ts"), "utf8")).toContain("export const GET"); }); test("category pages preview every UI component exactly once", () => { const categoryPages = readdirSync(pagesDir) .filter((file) => file !== "index.wrn" && file.endsWith(".wrn")) .map((file) => readFileSync(join(pagesDir, file), "utf8")) .join("\n"); const mounts = [...categoryPages.matchAll(/
match[1], ); expect(mounts).toHaveLength(reference.count); expect(new Set(mounts).size).toBe(reference.count); expect(mounts.sort()).toEqual(reference.components.map((component) => component.mount).sort()); }); test("every component has a detail page with its expected live use cases", () => { const files = readdirSync(detailPagesDir).filter((entry) => entry.endsWith(".wrn")); expect(files).toHaveLength(reference.count); const detailMounts = files.flatMap((file) => { const source = readFileSync(join(detailPagesDir, file), "utf8"); return [...source.matchAll(/
match[1]); }); // Every detail page has one playground plus three standard demos. Interactive // components add specialized capability demos beyond that shared baseline. expect(detailMounts).toHaveLength(reference.count * 4 + 249); for (const component of reference.components) { const expectedMounts = component.mount === "Accordion" ? 10 : component.mount === "Alert" ? 16 : component.mount === "Avatar" ? 18 : component.mount === "AvatarGroup" ? 6 : component.mount === "Badge" ? 16 : component.mount === "Button" ? 9 : component.mount === "ButtonGroup" ? 8 : component.mount === "Card" ? 24 : component.mount === "Carousel" ? 14 : component.mount === "ChatBubble" ? 5 : component.mount === "Collapse" ? 3 : component.mount === "FileUploadProgress" ? 7 : [ "Checkbox", "ColorPicker", "FileInput", "Input", "InputGroup", "Radio", "RangeSlider", "Select", "Switch", "Textarea", "TimePicker", ].includes(component.mount) ? component.mount === "Radio" || component.mount === "Checkbox" ? 12 : 10 : component.mount === "AdvancedSelect" ? 42 : component.mount === "ComboBox" ? 22 : component.mount === "InputNumber" ? 13 : component.mount === "PinInput" ? 19 : component.mount === "StrongPassword" ? 7 : component.mount === "ToggleCount" ? 5 : component.mount === "TogglePassword" ? 11 : 4; expect(detailMounts.filter((mount) => mount === component.mount)).toHaveLength(expectedMounts); } const uploadProgressPage = readFileSync(join(detailPagesDir, "file-upload-progress.wrn"), "utf8"); for (const fileName of [ "design-system.zip", "product-images.tar", "launch-video.mp4", "quarterly-report.pdf", "customer-export.csv", "brand-assets.fig", ]) { expect(uploadProgressPage).toContain(fileName); } expect(uploadProgressPage).toContain('status="complete"'); expect(uploadProgressPage).toContain('status="error"'); expect(uploadProgressPage).toContain('status="cancelled"'); }, 15_000); test("every detail page has a type-aware realtime playground", () => { const files = readdirSync(detailPagesDir).filter((entry) => entry.endsWith(".wrn")); for (const file of files) { const source = readFileSync(join(detailPagesDir, file), "utf8"); expect(source).toContain("data-playground-preview"); expect(source).toContain("data-playground-form"); expect(source).toContain("data-playground-code"); expect(source).toContain("data-playground-copy"); expect(source).toContain('href="#playground"'); } const runtime = readFileSync(join(root, "public", "playground.js"), "utf8"); expect(runtime).toContain("fetch(url"); expect(runtime).toContain("HTMLSelectElement"); expect(runtime).toContain("navigator.clipboard.writeText"); expect(runtime).toContain("updateCode(playground)"); expect(runtime).toContain("__wrnexusHydrateScopes"); expect(runtime).toContain("data-playground-json"); expect(runtime).toContain('createPolicy("wrnexus-playground"'); expect(runtime).toContain("preview.replaceChildren"); expect(runtime).not.toContain("preview.innerHTML"); }); test("detail pages show component syntax beside each live preview", () => { const buttonPage = readFileSync(join(detailPagesDir, "button.wrn"), "utf8"); expect(buttonPage).toContain('name="pg_label" type="text"'); expect(buttonPage).toContain('name="pg_variant"'); expect(buttonPage).toContain(''); expect(buttonPage).not.toContain(''); expect(buttonPage).toContain('name="pg_color"'); expect(buttonPage).toContain(''); expect(buttonPage).toContain('name="pg_size"'); expect(buttonPage).toContain(''); expect(buttonPage.match(/class="demo-workbench"/g)).toHaveLength(8); expect(buttonPage.match(/<Button/g)).toHaveLength(9); expect(buttonPage).not.toContain("<div data-component="Button""); expect(buttonPage).not.toContain('
'); }); test("every detail page lists all public events immediately before props", () => { for (const component of reference.components) { const source = readFileSync(join(detailPagesDir, `${slugOf(component.name)}.wrn`), "utf8"); const eventsIndex = source.indexOf('
Events'); if (component.events.length === 0) { expect(source.slice(eventsIndex, propsIndex)).toContain("No public component events."); } else { for (const event of component.events) { expect(source.slice(eventsIndex, propsIndex)).toContain(`@${event}`); } } } }); test("button documents click, focus, and blur events", () => { const source = readFileSync(join(detailPagesDir, "button.wrn"), "utf8"); for (const event of ["click", "focus", "blur"]) { expect(source).toContain(`@${event}`); } }); test("button group documents every requested layout and size", () => { const source = readFileSync(join(detailPagesDir, "button-group.wrn"), "utf8"); for (const heading of [ "Example", "Small", "Medium", "Large", "Responsive stack", "Vertical stack", "Button toolbar", ]) { expect(source).toContain(`

${heading}

`); } expect(source.match(/class="demo-workbench"/g)).toHaveLength(7); expect(source).toContain('Basic usage'); expect(source).toContain('Sizes'); expect(source).toContain('responsive="true"'); expect(source).toContain('orientation="vertical"'); expect(source).toContain('toolbar="true"'); expect(source).toContain('ariaLabel="Text formatting"'); for (const event of ["click", "select", "change"]) { expect(source).toContain(`@${event}`); } }); test("radio documents single, grouped, card, list, aligned, and validation layouts", () => { const source = readFileSync(join(detailPagesDir, "radio.wrn"), "utf8"); for (const heading of [ "Default radio", "Disabled", "Inline radio group", "Vertical radio group", "Radios with descriptions", "Radio cards", "Vertical radio cards", "Right-aligned radio", "Radio list group", "Horizontal radio list group", "Validation states", ]) { expect(source).toContain(`

${heading}

`); } }); test("checkbox documents mixed, grouped, card, list, aligned, and validation layouts", () => { const source = readFileSync(join(detailPagesDir, "checkbox.wrn"), "utf8"); for (const heading of [ "Default checkbox", "Indeterminate", "Disabled", "Checkbox group options", "Checkboxes with descriptions", "Checkbox cards", "Vertical checkbox cards", "Right-aligned checkbox", "Checkbox list group", "Horizontal checkbox list group", "Validation states", ]) { expect(source).toContain(`

${heading}

`); } }); test("card documents content, media, animation, layout, alert, and panel capabilities", () => { const source = readFileSync(join(detailPagesDir, "card.wrn"), "utf8"); const headings = [ "Default card", "Body", "Simple card", "Header and footer", "Footer only", "Small", "Default", "Large", "Navigation", "Navigation with select on mobile", "Top image", "Bottom image", "Image overlays", "Image scaling animation on hover", "Transition on hover", "Horizontal", "Card group", "Top bordered card", "Card actions", "Card with alert", "Centered body content", "Empty state", "Scrollable body", ]; for (const heading of headings) { expect(source).toContain(`

${heading}

`); } expect(source.match(/class="demo-workbench"/g)).toHaveLength(23); for (const section of [ "Basic usage", "Content", "Structure", "Sizes", "Navigation", "Images", "Animations", "Card layout", "Panels", ]) { expect(source).toContain(`${section}`); } expect(source).toContain('imagePosition="overlay"'); expect(source).toContain('hover="image"'); expect(source).toContain('hover="raise"'); expect(source).toContain('layout="horizontal"'); expect(source).toContain('mobileNavigation="true"'); expect(source).toContain('scrollable="true"'); for (const event of ["click", "action", "navigate", "dismiss", "load", "error"]) { expect(source).toContain(`@${event}`); } }); test("advanced select and combobox document their complete event contract", () => { for (const slug of ["advanced-select", "combo-box"]) { const source = readFileSync(join(detailPagesDir, `${slug}.wrn`), "utf8"); for (const event of ["search", "select", "change", "clear", "open", "close", "load", "error"]) { expect(source).toContain(`@${event}`); } expect(source).toContain('id="events"'); expect(source).toContain("event.detail"); } }); test("pin input documents verification events", () => { const source = readFileSync(join(detailPagesDir, "pin-input.wrn"), "utf8"); for (const event of ["input", "change", "complete", "paste", "clear", "error"]) { expect(source).toContain(`@${event}`); } expect(source).toContain('id="events"'); expect(source).toContain("event.detail"); expect(source).toContain("Different lengths"); expect(source).toContain('length="3"'); expect(source).toContain('length="5"'); expect(source).toContain('length="7"'); }); test("toggle password documents visibility states, validation, and declared events", () => { const source = readFileSync(join(detailPagesDir, "toggle-password.wrn"), "utf8"); for (const event of ["input", "change", "toggle"]) { expect(source).toContain(`@${event}`); } expect(source).toContain("Checkbox-controlled toggle"); expect(source).toContain("Synchronized password fields"); expect(source).toContain("Without visibility toggle"); expect(source).toContain("fields='[\n {"); expect(source).toContain('"label": "New password"'); expect(source).toContain('"name": "current-password"'); expect(source).not.toContain("fields='[..]'"); expect(source).toContain('data-schema="toggle-password-validation"'); }); test("strong password documents live scoring, requirements, popover, and custom characters", () => { const source = readFileSync(join(detailPagesDir, "strong-password.wrn"), "utf8"); expect(source).toContain("Live strength meter"); expect(source).toContain("Requirements and hint text"); expect(source).toContain("Requirements in a popover"); expect(source).toContain("Custom special characters"); expect(source).toContain('presentation="popover"'); expect(source).toContain('specialCharactersSet="@#_-."'); for (const event of ["input", "change", "strength"]) { expect(source).toContain(`@${event}`); } }); test("complex component props are shown as complete readable multiline values", () => { for (const slug of ["advanced-select", "combo-box", "select", "toggle-password"]) { const source = readFileSync(join(detailPagesDir, `${slug}.wrn`), "utf8"); expect(source).not.toMatch(/\b(?:options|groups|items|fields)='\[\.\.\.\]'/); } const advancedSelect = readFileSync(join(detailPagesDir, "advanced-select.wrn"), "utf8"); expect(advancedSelect).toContain("options='[\n {"); expect(advancedSelect).toContain('"value": "design"'); }); test("code examples preserve literal object braces after client hydration", () => { const source = readFileSync(join(detailPagesDir, "toggle-password.wrn"), "utf8"); expect(source).toContain("{"); expect(source).toContain("}"); expect(source).not.toContain('fields=\'[\n {\n "label"'); }); test("every generated showcase page compiles", () => { const paths = [ ...readdirSync(pagesDir) .filter((entry) => entry.endsWith(".wrn")) .map((entry) => join(pagesDir, entry)), ...readdirSync(detailPagesDir) .filter((entry) => entry.endsWith(".wrn")) .map((entry) => join(detailPagesDir, entry)), ]; for (const path of paths) { expect(() => compileWireFile(readFileSync(path, "utf8"), path)).not.toThrow(); } });