# OpenSpec Change: enterprise-browser-storybook-test-utils - **Change ID**: enterprise-browser-storybook-test-utils - **Package**: `@suaveplan/storybook-test-utils` - **Category**: browser - **Target path**: `browser/dev/storybook-test-utils` - **Status**: IN PROGRESS - **Date**: 2026-04-24 - **Reference spec**: `/mnt/s/web/suaveplan/enterprise/storybook-test-utils.md` --- ## Summary Deliver `@suaveplan/storybook-test-utils` on the genesis branch at enterprise quality. The package provides Playwright helpers for driving Storybook preview UIs across every Suaveplan React package. Genesis: splits the single `storybook-helpers.ts` into six focused modules, ships a `STORYBOOK_PORTS` registry, adds typed story-index discovery, console/network/accessibility assertion helpers, a reusable Playwright `test` fixture, moves `@playwright/test` to `peerDependencies`, writes the currently-zero test coverage (≥95%), and authors all missing `.md` docs. --- ## Current-state audit (genesis) Fresh build — does not exist on genesis yet. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/logger` (`workspace:^`) | Structured logging for helper diagnostics | | `tslib` (`catalog:build`) | Runtime helpers | `peerDependencies`: `@playwright/test` (used in public API types — must not be a dev-only dep). Dev: `vitest`, `@vitest/coverage-istanbul`, `@playwright/test`, `@suaveplan/testing`. No React runtime dep — this package is a Playwright helper library. --- ## Preserved public API surface ```ts // Navigation function navigateToStory(page: Page, meta: StoryMetadata): Promise function navigateToStoryWithUI(page: Page, meta: StoryMetadata): Promise function switchToStory(page: Page, meta: StoryMetadata): Promise function reloadStory(page: Page): Promise // Waiting function waitForStoryLoaded(page: Page, options?: WaitOptions): Promise function waitForStorybookIframe(page: Page, options?: WaitOptions): Promise function waitForStoryInteractive(page: Page, options?: WaitOptions): Promise // Iframe access function getStorybookFrame(page: Page): FrameLocator function getStoryCanvas(frame: FrameLocator): Locator // Args function getStoryArgs(page: Page): Promise> function updateStoryArgs>(page: Page, args: Partial): Promise // Interactions function clickWithFallback(locator: Locator): Promise function getClickableElement(frame: FrameLocator, selector: string): Locator // Forms function getFormFromStory(frame: FrameLocator, selector?: string): Locator // Port registry (new) const STORYBOOK_PORTS: Record function storyUrl(pkg: string, storyId: string, opts?: UrlOptions): string // Story index (new) function loadStoryIndex(baseUrl: string): Promise function defineStoryId(kind: string, name: string): string // Assertions (new) function expectNoConsoleErrors(page: Page, opts?: { allow?: RegExp[] }): Promise function expectNoFailedRequests(page: Page): Promise function expectAllFontsLoaded(page: Page): Promise function auditAccessibility(page: Page, opts?: AxeOptions): Promise // Fixtures (new) const test: typeof baseTest // extended with storybook fixture ``` Subpath exports: `"."`, `"./navigation"`, `"./waiting"`, `"./iframe"`, `"./args"`, `"./interactions"`, `"./forms"`, `"./ports"`, `"./story-index"`, `"./assertions"`, `"./fixtures"`. --- ## New additions for genesis ### 1. Split helper modules `storybook-helpers.ts` (monolith) → six focused modules under `src/`: `navigation`, `waiting`, `iframe`, `args`, `interactions`, `forms`, each with its own `.md`. ### 2. `STORYBOOK_PORTS` registry `src/ports/registry.ts` — canonical `STORYBOOK_PORTS` map (package name → port number); `storyUrl(pkg, storyId)` helper replaces hard-coded `localhost:6001` across all consumer E2E suites. ### 3. Typed story-index discovery `src/story-index/index.ts` — `loadStoryIndex(baseUrl)` fetches and parses Storybook `index.json`; `defineStoryId(kind, name)` generates a typed story ID. ### 4. Console/network/a11y assertion helpers `src/assertions/console.ts` — `expectNoConsoleErrors(page, opts)`. `src/assertions/network.ts` — `expectNoFailedRequests(page)`, `expectAllFontsLoaded(page)`. `src/assertions/a11y.ts` — `auditAccessibility(page, opts)` running axe-core in the preview iframe; matches the CLAUDE.md web-app verification policy. ### 5. Reusable Playwright fixture `src/fixtures/playwright.ts` — `test.extend` with a `storybook` fixture pre-configured for the port registry. ### 6. Tests (currently zero) Write `src/**/*.test.ts` driving a local HTTP server with stub Storybook HTML; coverage ≥ 95%. ### 7. `@playwright/test` moved to `peerDependencies` Removes the type-leak where public API signatures returned `Page`/`Locator`/`FrameLocator` but the package was declared dev-only. --- ## File layout ```text browser/dev/storybook-test-utils/ package.json tsconfig.json vitest.config.ts src/ index.ts navigation/ navigation.ts navigation.test.ts navigation.md index.ts waiting/ waiting.ts waiting.test.ts waiting.md index.ts iframe/ iframe.ts iframe.test.ts iframe.md index.ts args/ args.ts args.test.ts args.md index.ts interactions/ interactions.ts interactions.test.ts interactions.md index.ts forms/ forms.ts forms.test.ts forms.md index.ts ports/ registry.ts registry.test.ts registry.md index.ts story-index/ index-loader.ts index-loader.test.ts index-loader.md index.ts assertions/ console.ts console.test.ts console.md network.ts network.test.ts network.md a11y.ts a11y.test.ts a11y.md index.ts fixtures/ playwright.ts playwright.md index.ts types/ types.ts types.md tests/ integration.test.ts ``` --- ## Impact **Downstream**: every `@suaveplan/*` package with Storybook stories and a `e2e/*.spec.ts` file. **Breaking changes**: `@playwright/test` becomes `peerDependency`; consumers must have it in their own `devDependencies` (they typically already do). **Prerequisites**: None — no runtime workspace deps beyond `tslib`.