Files
2026-05-27 23:29:53 +01:00

6.3 KiB

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

// Navigation
function navigateToStory(page: Page, meta: StoryMetadata): Promise<void>
function navigateToStoryWithUI(page: Page, meta: StoryMetadata): Promise<void>
function switchToStory(page: Page, meta: StoryMetadata): Promise<void>
function reloadStory(page: Page): Promise<void>

// Waiting
function waitForStoryLoaded(page: Page, options?: WaitOptions): Promise<void>
function waitForStorybookIframe(page: Page, options?: WaitOptions): Promise<FrameLocator>
function waitForStoryInteractive(page: Page, options?: WaitOptions): Promise<void>

// Iframe access
function getStorybookFrame(page: Page): FrameLocator
function getStoryCanvas(frame: FrameLocator): Locator

// Args
function getStoryArgs(page: Page): Promise<Record<string, unknown>>
function updateStoryArgs<T = Record<string, unknown>>(page: Page, args: Partial<T>): Promise<void>

// Interactions
function clickWithFallback(locator: Locator): Promise<void>
function getClickableElement(frame: FrameLocator, selector: string): Locator

// Forms
function getFormFromStory(frame: FrameLocator, selector?: string): Locator

// Port registry (new)
const STORYBOOK_PORTS: Record<string, number>
function storyUrl(pkg: string, storyId: string, opts?: UrlOptions): string

// Story index (new)
function loadStoryIndex(baseUrl: string): Promise<StoryIndex>
function defineStoryId(kind: string, name: string): string

// Assertions (new)
function expectNoConsoleErrors(page: Page, opts?: { allow?: RegExp[] }): Promise<void>
function expectNoFailedRequests(page: Page): Promise<void>
function expectAllFontsLoaded(page: Page): Promise<void>
function auditAccessibility(page: Page, opts?: AxeOptions): Promise<AxeResult>

// 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

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.