130 lines
5.0 KiB
TypeScript
130 lines
5.0 KiB
TypeScript
import { expect, test } from "bun:test";
|
|
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { compileWireFile } from "../../compiler/src/index.ts";
|
|
import { uiComponentNames, uiComponentsDir, uiCss } from "../src/index.ts";
|
|
|
|
test("bundled UI assets are discoverable and readable", () => {
|
|
expect(uiComponentNames()).toContain("Button");
|
|
expect(uiComponentNames()).toContain("FAQAccordion");
|
|
expect(uiComponentNames()).toContain("AnnouncementBar");
|
|
expect(uiComponentNames()).toContain("BackToTop");
|
|
expect(readFileSync(join(uiComponentsDir(), "Button.wrn"), "utf8")).toContain("component Button");
|
|
expect(uiCss()).toContain("--wire-");
|
|
});
|
|
|
|
test("generated component reference documents every bundled component and its props", () => {
|
|
const reference = JSON.parse(
|
|
readFileSync(join(uiComponentsDir(), "..", "component-reference.json"), "utf8"),
|
|
) as { count: number; components: Array<{ name: string; props: Array<{ name: string }> }> };
|
|
expect(reference.count).toBe(uiComponentNames().length);
|
|
expect(reference.count).toBeGreaterThanOrEqual(891);
|
|
expect(reference.components.map((component) => component.name)).toEqual(
|
|
expect.arrayContaining(uiComponentNames()),
|
|
);
|
|
expect(reference.components.find((component) => component.name === "Button")?.props).toEqual(
|
|
expect.arrayContaining([expect.objectContaining({ name: "label" })]),
|
|
);
|
|
});
|
|
|
|
test("PDF minimum-release and essential build-first components are bundled", () => {
|
|
const required = [
|
|
"Typography",
|
|
"Form",
|
|
"FormLabel",
|
|
"FormHelpText",
|
|
"FormError",
|
|
"LoadingButton",
|
|
"Combobox",
|
|
"MultiSelect",
|
|
"TimePicker",
|
|
"DateTimePicker",
|
|
"RecurringSchedulePicker",
|
|
"QuietHoursPicker",
|
|
"ConfirmationDialog",
|
|
"DataTable",
|
|
"FilterBar",
|
|
"MegaMenu",
|
|
"DesktopNavigation",
|
|
"ProductsMegaMenu",
|
|
"MobileNavigation",
|
|
"MarketingPageShell",
|
|
"ProductPageShell",
|
|
"LegalPageShell",
|
|
"ProductCard",
|
|
"MetricGrid",
|
|
"FAQ",
|
|
"PricingComparisonTable",
|
|
"SDKTabs",
|
|
"LegalTableOfContents",
|
|
"CookiePreferencesDialog",
|
|
];
|
|
|
|
expect(uiComponentNames()).toEqual(expect.arrayContaining(required));
|
|
});
|
|
|
|
test("component-system CSS includes responsive, theme-token, focus, and reduced-motion rules", () => {
|
|
const css = uiCss();
|
|
expect(css).toContain("@media (max-width: 768px)");
|
|
expect(css).toContain("@media (max-width: 480px)");
|
|
expect(css).toContain("@media (prefers-reduced-motion: reduce)");
|
|
expect(css).toContain(":focus-visible");
|
|
expect(css).toContain("var(--wire-color-surface)");
|
|
expect(css).toContain("min-height: 44px");
|
|
expect(css).toContain("--wire-motion-base");
|
|
expect(css).toContain("--wire-ease-emphasized");
|
|
expect(css).toContain("@keyframes wire-component-enter");
|
|
expect(css).toContain("@keyframes wire-dialog-enter");
|
|
expect(css).toContain("@media (hover: hover) and (pointer: fine)");
|
|
expect(css).toContain("--color-violet-600: var(--wire-color-primary)");
|
|
expect(css).toContain("--color-blue-600: var(--wire-color-info)");
|
|
expect(css).toContain("--color-red-600: var(--wire-color-danger)");
|
|
expect(css).toMatch(/:root\s*\{[^}]*--color-violet-600: var\(--wire-color-primary\)/s);
|
|
expect(css).toContain(".wire-bg-primary");
|
|
expect(css).toContain(".wire-text-muted");
|
|
expect(css).toContain(".wire-visually-hidden");
|
|
expect(css).toContain(".wire-catalog-card--channel::before");
|
|
expect(css).toContain(".wire-catalog-card--integration::before");
|
|
});
|
|
|
|
test("page shells leave background ownership to the page", () => {
|
|
const css = uiCss();
|
|
const shellRule = css.match(/\.wire-page-shell\s*\{([^}]*)\}/)?.[1] ?? "";
|
|
expect(shellRule).not.toMatch(/background(?:-color)?\s*:/);
|
|
});
|
|
|
|
test("component boundaries have no default margins and expose class customization", () => {
|
|
for (const name of uiComponentNames()) {
|
|
const source = readFileSync(join(uiComponentsDir(), `${name}.wrn`), "utf8");
|
|
const root = source.match(/view\s*\{\s*<[A-Za-z][^>]*>/)?.[0] ?? "";
|
|
const classes = root.match(/\bclass="([^"]*)"/)?.[1]?.split(/\s+/) ?? [];
|
|
expect(classes.filter((token) => /^-?m[trblxy]?-/.test(token))).toEqual([]);
|
|
expect(source).toMatch(/^\s*(?:class|className)\s*(?::[^=\r\n]+)?=/m);
|
|
expect(root).toMatch(/\{class(?:Name)?\}/);
|
|
}
|
|
});
|
|
|
|
test("layout boundaries do not add default padding", () => {
|
|
for (const name of [
|
|
"Container",
|
|
"CTASection",
|
|
"EmptyState",
|
|
"ErrorState",
|
|
"Hero",
|
|
"LegalDocumentLayout",
|
|
"TimelineItem",
|
|
]) {
|
|
const source = readFileSync(join(uiComponentsDir(), `${name}.wrn`), "utf8");
|
|
const root = source.match(/view\s*\{\s*<[A-Za-z][^>]*>/)?.[0] ?? "";
|
|
const classes = root.match(/\bclass="([^"]*)"/)?.[1]?.split(/\s+/) ?? [];
|
|
expect(classes.filter((token) => /^p[trblxy]?-/.test(token))).toEqual([]);
|
|
}
|
|
});
|
|
|
|
test("every bundled UI component compiles", () => {
|
|
for (const name of uiComponentNames()) {
|
|
const path = join(uiComponentsDir(), `${name}.wrn`);
|
|
expect(() => compileWireFile(readFileSync(path, "utf8"), path)).not.toThrow();
|
|
}
|
|
});
|