Skip to content

Testing strategy

Testing protects, in order: tenant isolation, data integrity, the contract, the experience (TEST_STRATEGY).

Layer Tooling Shape today
Unit (web/domain/mobile) Vitest / Jest ~250 tests: pure helpers, components over a stubbed transport
Integration (api/db/worker) Vitest + real Postgres ~215 tests incl. the negative-auth registry and fault-injection atomicity
End-to-end Playwright against a built app 35 journeys incl. axe scans on every major surface
Visual/primitives Storybook (+a11y addon) Primitives and pattern stories

House rules that bite:

  • E2E serves the last build — rebuild before running after web changes.
  • Contract-changing PRs run turbo run test --force (cache-masking incident).
  • Axe assertions wait for toasts to detach and entry animations to finish — both produce false contrast failures mid-transition.
  • Keyboard journeys are asserted with real focus, not simulated class checks.

Two checks run inside pnpm lint and fail the build rather than warning:

  • Research-id uniqueness. docs/product/RESEARCH_BACKLOG.md is the canonical register of deferred scope, and every R-NN is referenced from PR descriptions and the execution plan. If you add an entry, give it a new id — defining an existing one twice forks the register, and the check will tell you which id and which lines. When renumbering, update references by meaning: R-5 is a substring of PR-5, so a global search-and-replace will corrupt history. Run the gate’s own fixture tests with pnpm check:research-ids.
  • Design-token contrast. The twelve Badge tone pairs are measured against WCAG AA in packages/design-tokens. If you change a colour token, that test tells you whether the pair still clears 4.5:1. Choosing the colour is a design decision; clearing AA is arithmetic.