Testing architecture — story-driven, local-first, deployed-target bounded

CareerVector's testing strategy is a stack, not a single tool. Each lane exists because a kind of mistake would otherwise reach production. The common rule across every lane: stay inside the Cloudflare Workers quota policy. See cf-workers-quota-policy.md.

Story catalog traceability

wiki/content/stories/USER-STORIES.md is the canonical story catalog. Every story has an ID P<persona>-G<goal>-S<n> and a severity. The story-spec helper in ui/e2e/helpers/stories/story.ts accepts that ID, so each spec file in ui/e2e/stories/ claims one or more stories it covers. The graph projection links spec → story → behaviour → idea, so a missing spec for a critical story shows up as a coverage hole rather than as silence.

Local e2e against wrangler dev

ui/playwright.config.ts runs Playwright against the local wrangler dev server by default. The CF quota cost is zero — every request is served by the local Miniflare runtime — so the suite can be as exhaustive as needed without bargaining with the account-wide budget.

ui/scripts/link-shared-state.mjs is the small helper that points sibling Miniflare instances at the same D1 state file, so a spec that spans two browsers (one editor, one viewer) sees the same workspace without going through a deployed relay.

A11y as assertion

ui/e2e/helpers/stories/a11y.ts lets a story spec assert a11y as a test outcome instead of an after-the-fact audit. A regression that breaks the dashboard's screen-reader path fails the spec for the story it broke, not "a separate a11y job that nobody reads".

Adversarial probes

ui/e2e/helpers/adversarial/probes.ts is the catalogue of hostile inputs and destructive sequences referenced from the "Adversarial" severity tier in the story catalog. Probes include: malformed JSON from a provider, provider rate-limit storms, surprise WS disconnects mid-write, oversized pastes, concurrent writes from two actors on the same field, and so on.

Adversarial probes live in helpers so they can be composed into any spec that needs them — they are not a separate run lane.

Known-bugs suite

ui/playwright.known-bugs.config.ts is a quarantine config for tests that currently fail because of a real product bug that is documented but not yet fixed. The suite runs but does not gate merges — it gates the inverse: when a known-bugs spec starts passing, the developer who fixed the bug moves the spec back into the main suite. This stops the well-known anti- pattern of "skipped, then forgotten."

Live journey opt-in

ui/e2e/helpers/live-capture.ts enables a small, named set of specs to run against deployed targets for trust signal — never on push, only via explicit dispatch or a tightly bounded schedule. The opt-in is the only sanctioned path from local e2e to deployed-target traffic and exists because some bugs (provider quirks, CF edge oddities) cannot be reproduced under Miniflare.

The opt-in is the explicit exception to "never run e2e against deployed workers"; the policy in cf-workers-quota-policy.md still caps the volume.

Durability contracts

ui/e2e/helpers/persistence/*.ts provides lifecycle helpers and the D1 corruption probe so persistence tests can assert what is durable rather than just "did the request return 200". The contract: every canonical write lands in D1 before any broadcast; a reload after a write must see the write; a recovered D1 must reject torn data. These are specs that protect the assumption every other spec implicitly leans on.

Shared wrangler / Miniflare state

dev-shared-wrangler-state and shared-miniflare-state are two names for the same setup: the dev helpers under ui/scripts/ plus ui/svelte.config.js and ui/vite.config.ts align the dev server, the relay, and Playwright on one Miniflare backing store. Without that, a spec that starts the editor and then opens a viewer in a second context would see two unrelated D1 databases and never converge.

Source: wiki/content/canon/qa-testing-architecture.md