import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; const root = process.cwd(); const componentsDir = join(root, "src/lib/ui/components"); const storiesDir = join(root, "src/lib/ui/stories"); const requiredStoryFiles = [ "Badge.stories.svelte", "Button.stories.svelte", "DashboardFrame.stories.svelte", "DashboardHeader.stories.svelte", "DashboardOnePager.stories.svelte", "FooterCell.stories.svelte", "FooterStatusCell.stories.svelte", "GridFrame.stories.svelte", "IconButton.stories.svelte", "IconGlyph.stories.svelte", "ModuleCard.stories.svelte", "Panel.stories.svelte", "ProgressMeter.stories.svelte", "Separator.stories.svelte", "ServiceGroupPanel.stories.svelte", "ServicePanel.stories.svelte", "ServiceRow.stories.svelte", "Sparkline.stories.svelte", "StatusBadge.stories.svelte", "StatusStrip.stories.svelte", "SystemState.stories.svelte", "TelemetryCard.stories.svelte", "TelemetryGrid.stories.svelte", "TelemetryStrip.stories.svelte", "WeatherModule.stories.svelte", ] as const; const forbiddenStoryContent = [ "dimension lab", "dimensionlab", "vince", "homepage", ] as const; describe("Storybook inventory", () => { test("exposes scripts for local and static Storybook review", () => { const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { scripts?: Record; }; expect(packageJson.scripts?.storybook).toBeTypeOf("string"); expect(packageJson.scripts?.storybook).toContain("storybook dev"); expect(packageJson.scripts?.["build-storybook"]).toBe("storybook build"); }); test("has a story for every reusable dashboard UI component", () => { for (const filename of requiredStoryFiles) { expect(existsSync(join(storiesDir, filename)), `${filename} is missing`).toBe(true); } }); test("keeps component and story files paired as the UI inventory changes", () => { const componentStoryFiles = readdirSync(componentsDir) .filter((filename) => filename.endsWith(".svelte")) .map((filename) => filename.replace(".svelte", ".stories.svelte")); for (const filename of componentStoryFiles) { expect(existsSync(join(storiesDir, filename)), `${filename} is missing`).toBe(true); } }); test("keeps Storybook fixtures generic and content-free", () => { const storyText = readdirSync(storiesDir) .filter((filename) => filename.endsWith(".svelte") || filename.endsWith(".ts")) .map((filename) => readFileSync(join(storiesDir, filename), "utf8").toLowerCase()) .join("\n"); for (const forbidden of forbiddenStoryContent) { expect(storyText).not.toContain(forbidden); } }); test("does not add deferred form/navigation primitives", () => { for (const component of ["Input", "ToggleGroup", "ScrollArea"]) { expect(existsSync(join(root, `src/lib/ui/components/${component}.svelte`))).toBe(false); expect(existsSync(join(storiesDir, `${component}.stories.svelte`))).toBe(false); } }); });