Skip to content

Frontend QA

Frontend QA is layered because type correctness, accessible components, browser behavior, and visual consistency catch different failures.

Command Map

LayerCommand
TypeScriptcorepack pnpm web:check
Unit, hook, component, a11ycorepack pnpm --filter @jobctrl/web test
Type-level contractscorepack pnpm --filter @jobctrl/web test-d
Production bundlecorepack pnpm web:build
End-to-endcorepack pnpm --filter @jobctrl/web e2e -- tests/<flow>.spec.ts
Storybook bundle and runnercorepack pnpm web:storybook:build then corepack pnpm web:storybook:test

What Each Layer Proves

  • Colocated Vitest/RTL/MSW tests prove selectors, query keys, hooks, mutations, forms, components, rollback, and critical/serious axe checks.
  • Type-level tests prove the public inferred shapes of read hooks and contracts.
  • Playwright uses a real API plus seeded SQLite fixtures to prove route and realtime behavior without a live worker or model.
  • Storybook proves state/variant rendering and shared accessibility behavior.

Rhea And Base UI Contracts

The current frontend preset is base-rhea: Geist product type, JetBrains Mono for technical text, the 10px semantic radius scale, capped 24px cards, neutral chart ramps, violet primary/focus tokens, and domain status rendered as an icon/dot plus text rather than a colored capsule. Contrast calculations, focused primitive behavior, and the rendered token-foundation browser gate own those outcomes; route code should consume semantic tokens instead of introducing local substitutes.

Interactive behavior belongs to wrappers under apps/web/src/shared/ui/. base-ui-migration-boundary.test.ts must find no direct @radix-ui/* imports and no raw native <select> elements. Focus containment and return, Escape/outside dismissal, portal stacking, accessible naming, and controlled state must be proven through the wrapper's focused tests and a real route—not only by inspecting its classes.

Use this focused starting point:

bash
corepack pnpm --filter @jobctrl/web exec vitest run \
  src/styles/token-contrast.test.ts \
  src/shared/ui/base-ui-migration-boundary.test.ts \
  src/shared/ui/button.test.tsx \
  src/shared/ui/input.test.tsx \
  src/shared/ui/select.test.tsx \
  src/shared/ui/tabs.test.tsx

Token And Primitive Changes

Token changes need light/dark contrast and density proof. Shared primitive changes need scoped primitive tests, Storybook, boundary scans, and a generated CSS check. Use the exact commands in the token foundation and shared primitive sections.

Route-Level Changes

Run the relevant flow spec plus the seeded route visual QA when a change affects layout, overlays, theme, density, forms, filters, focus, or destructive controls. Review the path manually using the Browser Smoke guide.

Parity And Accessibility

Two parity tests are non-negotiable runtime backstops:

  • every domain event type has a real invalidation handler;
  • every stage-state kind has a non-default badge rendering.

The Base UI migration boundary is the third structural backstop. For Pipelines, lifecycle events must invalidate the operations query while bounded polling covers worker heartbeat/task-queue telemetry that has no domain event. Use the production-shaped seed to keep three source families distinct from Enrichment pass and Preparation fanout, and verify ETA, freshness, capacity, queue, active inventory, privacy masking, and unavailable reasons without invented certainty.

Critical and serious axe violations fail the component/Storybook bar. Contrast is checked from resolved tokens because jsdom cannot evaluate rendered colors. Any future Storybook a11y escape hatch needs a matching backlog entry.

Test Data Boundary

Frontend QA uses synthetic stories, seeded databases, stub dispatchers, and disposable browser fixtures. It does not need real profile data, real contacts, mailbox access, model spend, or application submission.