feat: sync showcase with WRNexusJS 0.8.7

This commit is contained in:
2026-08-10 14:00:37 +05:30
parent 8ba1f67f98
commit 186f97de37
172 changed files with 38454 additions and 24441 deletions
+290 -423
View File
@@ -2,6 +2,7 @@ import { expect, test } from "bun:test";
import { readFileSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { compileWireFile } from "@wrnexus/compiler";
import { parse as parseWireSyntax } from "@wrnexus/syntax";
const root = join(import.meta.dir, "..");
const pagesDir = join(root, "app", "pages");
@@ -10,9 +11,48 @@ const reference = JSON.parse(
readFileSync(join(root, "node_modules", "@wrnexus", "ui", "component-reference.json"), "utf8"),
) as {
count: number;
components: Array<{ name: string; mount: string; category: string; events: string[] }>;
components: Array<{
name: string;
mount: string;
category: string;
props: Array<{ name: string }>;
slots: string[];
events: string[];
}>;
};
const slugOf = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
const manifest = JSON.parse(readFileSync(join(root, "showcase-manifest.json"), "utf8")) as {
componentCount: number;
categoryCount: number;
totalDemoCount: number;
components: Array<{
name: string;
mount: string;
slug: string;
category: string;
demoCount: number;
propCount: number;
slots: string[];
events: string[];
profiled: boolean;
}>;
};
const detailSource = (slug: string) => readFileSync(join(detailPagesDir, `${slug}.wrn`), "utf8");
const interactiveOverlayNames = [
"ContextMenu",
"Drawer",
"Dropdown",
"Modal",
"Popover",
"Tooltip",
];
function mountCount(source: string, mount: string): number {
const tag = new RegExp(`<${mount}(?:\\s|>|/)`, "g");
const legacy = new RegExp(`<div\\s+data-component="${mount}"`, "g");
return (source.match(tag)?.length ?? 0) + (source.match(legacy)?.length ?? 0);
}
test("showcase configures local Lucide icon generation", () => {
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as {
@@ -24,10 +64,28 @@ test("showcase configures local Lucide icon generation", () => {
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;");
expect(styles).toContain('[data-ui-component="MetricGrid"]');
expect(styles).toContain("width: 100%;");
});
test("showcase uses a searchable three-column documentation shell", () => {
test("showcase manifest matches the generated UI reference", () => {
expect(manifest.componentCount).toBe(reference.count);
expect(manifest.components).toHaveLength(reference.count);
expect(manifest.totalDemoCount).toBeGreaterThanOrEqual(reference.count * 3);
expect(new Set(manifest.components.map((component) => component.slug)).size).toBe(
reference.count,
);
for (const component of reference.components) {
const documented = manifest.components.find((entry) => entry.name === component.name);
expect(documented).toBeTruthy();
expect(documented?.mount).toBe(component.mount);
expect(documented?.events).toEqual(component.events);
expect(documented?.slots).toEqual(component.slots);
}
});
test("showcase uses a searchable 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");
@@ -40,66 +98,6 @@ test("showcase uses a searchable three-column documentation shell", () => {
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(
`<span data-docs-component-name>${component}</span><small data-component-status="complete">Complete</small>`,
);
}
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("<html");
@@ -111,380 +109,247 @@ test("showcase SSR document layout maps persisted design cookies to html attribu
}
});
test("advanced select demos use validation schemas and a real remote API", () => {
const page = readFileSync(
join(root, "app", "pages", "components", "advanced-select.wrn"),
"utf8",
test("category pages preview every UI component exactly once", () => {
const categoryPages = readdirSync(pagesDir)
.filter((file) => manifest.components.some((component) => `${component.category}.wrn` === file))
.map((file) => readFileSync(join(pagesDir, file), "utf8"))
.join("\n");
const invalidPreviews = reference.components.flatMap((component) => {
const count = mountCount(categoryPages, component.mount);
return count === 1
? []
: [{ name: component.name, mount: component.mount, category: component.category, count }];
});
expect(invalidPreviews).toEqual([]);
});
test("every component has a detail page, playground, and manifest-driven demos", () => {
const files = readdirSync(detailPagesDir).filter((entry) => entry.endsWith(".wrn"));
expect(files).toHaveLength(reference.count);
for (const component of manifest.components) {
const source = detailSource(component.slug);
expect(source).toContain("data-playground-preview");
expect(source).toContain("data-playground-form");
expect(source).toContain("data-playground-code");
expect(source).toContain('href="#playground"');
expect(source.match(/class="demo-workbench"/g)).toHaveLength(component.demoCount);
expect(mountCount(source, component.mount)).toBeGreaterThanOrEqual(component.demoCount + 1);
}
}, 20_000);
test("playgrounds preserve typed values, safe DOM replacement, themes, and event output", () => {
const generator = readFileSync(join(root, "scripts", "generate-showcase.mjs"), "utf8");
const runtime = readFileSync(join(root, "public", "playground.js"), "utf8");
expect(generator).toContain("JSON.parse(");
expect(generator).toContain("Number(");
expect(generator).toContain("data-playground-json");
expect(generator).toContain("prop.name}='{${playgroundStateName(prop)}}'");
expect(generator).toContain('data-slot="${escapeAttribute(slot)}"');
const generatorWithoutDataSlots = generator.replaceAll(
'data-slot="${escapeAttribute(slot)}"',
"",
);
expect(generatorWithoutDataSlots).not.toContain('slot="${escapeAttribute(slot)}"');
expect(runtime).toContain("fetch(url");
expect(runtime).toContain("navigator.clipboard.writeText");
expect(runtime).toContain("preview.replaceChildren");
expect(runtime).not.toContain("preview.innerHTML");
expect(runtime).toContain("__wrnexusHydrateScopes");
expect(runtime).toContain("wireTheme?.bind?.(document)");
expect(runtime).toContain("bindPlaygroundEvents");
expect(runtime).toContain('attributes.push(`${name}="${field.checked}"`)');
});
test("playground enum controls use public profiles instead of inferred implementation comparisons", () => {
const generator = readFileSync(join(root, "scripts", "generate-showcase.mjs"), "utf8");
expect(generator).toContain("profileFor(component.name)?.options?.[prop.name]");
expect(generator).not.toContain("source.matchAll(pattern)");
const pageHeader = manifest.components.find((component) => component.name === "PageHeader");
if (pageHeader) {
const source = detailSource(pageHeader.slug);
expect(source).toContain('name="pg_icon" type="text"');
expect(source).toMatch(/<option value="default"(?: selected)?>default<\/option>/);
expect(source).toMatch(/<option value="solid"(?: selected)?>solid<\/option>/);
}
});
test("every declared public event is documented and visible in the playground", () => {
for (const component of manifest.components.filter((entry) => entry.events.length > 0)) {
const source = detailSource(component.slug);
expect(source).toContain('id="events"');
expect(source).toContain('href="#events"');
expect(source).toContain("data-playground-event-log");
for (const event of component.events) {
expect(source).toContain(`<code>@${event}</code>`);
expect(source).toContain(`@${event}='console.log(payload)'`);
}
}
});
test("new and recently repaired components have production use-case profiles", () => {
const expectedProfiles = [
"PublicPageShell",
"AnnouncementBar",
"Breadcrumb",
"PageHeader",
"Hero",
"HeroActions",
"SplitHero",
"Section",
"SectionHeader",
"MarketingSectionHeader",
"TextLink",
"FeatureGrid",
"FeatureCard",
"FeatureIconCard",
"MetricGrid",
"MetricCard",
"StatsBar",
"CTASection",
"BackToTop",
"Footer",
"ContextMenu",
"Drawer",
"Dropdown",
"Modal",
"Popover",
"Tooltip",
];
for (const name of expectedProfiles) {
const component = manifest.components.find((entry) => entry.name === name);
if (!component) continue;
expect(component.profiled).toBe(true);
const source = detailSource(component.slug);
expect(source.match(/class="demo-workbench"/g)?.length ?? 0).toBeGreaterThanOrEqual(3);
}
});
test("grid, hero, shell, and overlay detail pages use responsive showcase stages", () => {
const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8");
for (const name of [
"MetricGrid",
"FeatureGrid",
"StatsBar",
"Hero",
"SplitHero",
"AnnouncementBar",
"CTASection",
"Footer",
]) {
expect(styles).toContain(`[data-ui-component="${name}"]`);
}
const metricGrid = manifest.components.find((component) => component.name === "MetricGrid");
if (metricGrid) {
const source = detailSource(metricGrid.slug);
expect(source).toContain('columns="3"');
const metricGridReference = reference.components.find(
(component) => component.name === "MetricGrid",
);
if (metricGridReference?.props.some((prop) => prop.name === "maxWidth")) {
expect(source).toContain('maxWidth="full"');
}
expect(source).toContain("items='[");
}
});
test("overlay previews start closed and expose usable click, hover, or context triggers", () => {
const generator = readFileSync(join(root, "scripts", "generate-showcase.mjs"), "utf8");
const profiles = readFileSync(join(root, "scripts", "showcase-profiles.mjs"), "utf8");
const styles = readFileSync(join(root, "app", "styles", "global.css"), "utf8");
expect(generator).toContain("interactiveOverlayComponents");
expect(generator).toContain("overlayVisibilityProps.has(name)");
expect(generator).toContain(
'data-interactive-preview="${isInteractiveOverlay(component) ? "true" : "false"}"',
);
expect(styles).toContain('.catalog-card[data-interactive-preview="true"]');
expect(styles).toContain('[data-ui-component="Drawer"]');
expect(styles).toContain('[data-ui-component="Modal"]');
const overlaysPage = readFileSync(join(pagesDir, "overlays.wrn"), "utf8");
for (const name of interactiveOverlayNames) {
const component = manifest.components.find((entry) => entry.name === name);
if (!component) continue;
const detail = detailSource(component.slug);
expect(detail).toContain("demo-interaction-hint");
expect(detail).not.toMatch(new RegExp(`<${component.mount}[^>]*(?:open|defaultOpen)="true"`));
expect(overlaysPage).not.toMatch(
new RegExp(`<${component.mount}[^>]*(?:open|defaultOpen)="true"`),
);
}
expect(profiles).toContain('trigger: "contextmenu"');
expect(profiles).toContain('trigger: "click"');
expect(profiles).toContain('trigger: "hover"');
expect(profiles).toContain('trigger: "both"');
expect(profiles).toContain('triggerLabel: "Open drawer"');
expect(profiles).toContain('triggerLabel: "Open modal"');
expect(profiles).toContain('triggerLabel: "View details"');
});
test("advanced select demos use validation schemas and a real remote API", () => {
const component = manifest.components.find((entry) => entry.name === "AdvancedSelect");
if (!component) return;
const page = detailSource(component.slug);
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(/<div data-component="([^"]+)"/g)].map(
(match) => 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(/<div data-component="([^"]+)"/g)].map((match) => 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('<option value="destructive">destructive</option>');
expect(buttonPage).not.toContain('<option value="success">success</option></select></label>');
expect(buttonPage).toContain('name="pg_color"');
expect(buttonPage).toContain('<option value="success">success</option>');
expect(buttonPage).toContain('name="pg_size"');
expect(buttonPage).toContain('<option value="icon-lg">icon-lg</option>');
expect(buttonPage.match(/class="demo-workbench"/g)).toHaveLength(8);
expect(buttonPage.match(/&lt;Button/g)).toHaveLength(9);
expect(buttonPage).not.toContain("&lt;div data-component=&quot;Button&quot;");
expect(buttonPage).not.toContain('<details class="demo-code">');
});
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('<section id="events"');
const propsIndex = source.indexOf('<section id="api"');
expect(eventsIndex).toBeGreaterThan(-1);
expect(propsIndex).toBeGreaterThan(eventsIndex);
expect(source.slice(eventsIndex, propsIndex)).toContain("Component events");
expect(source).toContain('<a href="#events">Events</a>');
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(`<code>@${event}</code>`);
}
}
}
});
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(`<code>@${event}</code>`);
}
});
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(`<h2>${heading}</h2>`);
}
expect(source.match(/class="demo-workbench"/g)).toHaveLength(7);
expect(source).toContain('<span class="detail-toc-group">Basic usage</span>');
expect(source).toContain('<span class="detail-toc-group">Sizes</span>');
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(`<code>@${event}</code>`);
}
});
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(`<h2>${heading}</h2>`);
}
});
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(`<h2>${heading}</h2>`);
}
});
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",
test("specialized form component examples remain complete", () => {
const checks: Array<[string, string[]]> = [
["PinInput", ["Different lengths", 'length="3"', 'length="5"', 'length="7"']],
[
"TogglePassword",
[
"Checkbox-controlled toggle",
"Synchronized password fields",
'data-schema="toggle-password-validation"',
],
],
[
"StrongPassword",
["Live strength meter", "Requirements in a popover", 'specialCharactersSet="@#_-."'],
],
];
for (const heading of headings) {
expect(source).toContain(`<h2>${heading}</h2>`);
}
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(`<span class="detail-toc-group">${section}</span>`);
}
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(`<code>@${event}</code>`);
for (const [name, needles] of checks) {
const component = manifest.components.find((entry) => entry.name === name);
if (!component) continue;
const source = detailSource(component.slug);
for (const needle of needles) expect(source).toContain(needle);
}
});
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(`<code>@${event}</code>`);
}
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(`<code>@${event}</code>`);
}
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(`<code>@${event}</code>`);
}
expect(source).toContain("Checkbox-controlled toggle");
expect(source).toContain("Synchronized password fields");
expect(source).toContain("Without visibility toggle");
expect(source).toContain("fields='[\n &#123;");
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(`<code>@${event}</code>`);
}
});
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");
test("complex component props are complete readable multiline values", () => {
for (const name of ["AdvancedSelect", "ComboBox", "Select", "TogglePassword"]) {
const component = manifest.components.find((entry) => entry.name === name);
if (!component) continue;
const source = detailSource(component.slug);
expect(source).not.toMatch(/\b(?:options|groups|items|fields)='\[\.\.\.\]'/);
}
const advancedSelect = readFileSync(join(detailPagesDir, "advanced-select.wrn"), "utf8");
expect(advancedSelect).toContain("options='[\n &#123;");
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("&#123;");
expect(source).toContain("&#125;");
expect(source).not.toContain('fields=\'[\n {\n "label"');
test("generated playground boolean states avoid multiline-sensitive ternaries", () => {
const generator = readFileSync(join(root, "scripts", "generate-showcase.mjs"), "utf8");
expect(generator).toContain('?? "${initial}") === "true"');
expect(generator).not.toContain("=== null ? ${initial} :");
for (const component of manifest.components) {
const source = detailSource(component.slug);
expect(source).not.toMatch(/state\s+playground_[A-Za-z0-9_$]+\s*=.*===\s*null\s*\?/);
}
});
test("every generated showcase page compiles", () => {
test("every generated showcase page compiles and is accepted by the route syntax parser", () => {
const paths = [
...readdirSync(pagesDir)
.filter((entry) => entry.endsWith(".wrn"))
@@ -495,6 +360,8 @@ test("every generated showcase page compiles", () => {
];
for (const path of paths) {
expect(() => compileWireFile(readFileSync(path, "utf8"), path)).not.toThrow();
const source = readFileSync(path, "utf8");
expect(() => parseWireSyntax(source)).not.toThrow();
expect(() => compileWireFile(source, path)).not.toThrow();
}
});