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
Frontend boundariescorepack pnpm web:lint
Boundary regression controlscorepack pnpm web:lint:test
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.
  • For a bounded API request and retained Outreach revision, run apps/api/test/api-client-timeout.test.ts and the Outreach revision form and hook tests. Use synthetic stalled header, success-body and error-body streams; also exercise missing or mismatched publication identity, a matching historical draft, and a server error after a committed revision using apps/api/test/outreach-postcommit-failure.test.ts. Verify an unconfirmed write retains both submitted and later text, settles pending state, requires deliberate review and never automatically resubmits. Explicit 4xx conflicts retain their conflict handling.
  • Type-level tests prove the public inferred shapes of read hooks and contracts.
  • The boundary lint proves the documented context/view dependency direction, Operations read-kernel access, browser-capability ports, and context-owned API/query/store usage from TypeScript syntax, resolved symbols and capability provenance. Weak/missing generated declarations and casts cannot authorize direct route/view API or QueryClient access. The TypeScript CI web job blocks on it; see automated boundary checks.
  • Boundary regressions allow canonical DTO/schema/constant imports without exemptions while rejecting independent API-client, Query/Zustand, browser-port, dependency and barrel violations. Real generated-route controls require actionable boundary diagnostics independently of compiler diagnostics. Capability/dependency exemptions still need reasons and stale-entry controls. Canonical-import changes retain the full web suite: inspect Jobs paging, saved-view inputs, optimistic rollback, SSE, event/stage parity and list/detail route composition coverage.
  • Playwright uses a real API plus seeded SQLite fixtures to prove route and realtime behavior without a live worker or model.
  • shipped-fit-lifecycle.spec.ts reads canonical final-fit metadata through the real review-queue API. It distinguishes post-voice gate findings from audits recorded after acceptance, retains the original gate and provenance coverage, and keeps historical judge-claimed coverage visibly separate from grounded coverage. Its fixture restores the original metadata after each case.
  • Resume review uses complementary session, browser and persistence checks. useApplyReviewSession.test.ts owns tenant/job/material fences, independent comment/revision ordering, overlapping saves, stale cache publications, normalized acknowledgements and approval preparation. artifact-comparison.spec.ts drives the real Plate editor and Apply session through multiline paste, autosave/reload, delayed acknowledgements, conflicts, failed render and recovery before dry-run authorization. Its save/render/model HTTP responses are controlled, and its attachments retain the submitted documents and observed transition order. apps/api/test/resume-review-drafts.test.ts, resume-templates.test.ts, apply-audit.test.ts and the application-feedback suites own canonical SQLite, accepted-artifact bytes, revision conflicts and approval/audit preservation. Browser response controls do not establish those persistence guarantees or live provider behavior.
  • Storybook proves state/variant rendering and shared accessibility behavior.

Visual And Base UI Contracts ​

The frontend retains base-rhea component ancestry with the Swiss timetable visual contract in DESIGN.md: Helvetica Neue/Helvetica/Arial product type, JetBrains Mono technical text, square geometry, neutral chart ramps, monochrome primary/focus tokens, and red/amber state accents. Domain status uses an icon/dot plus text rather than a colored capsule, and filled score/count markers pair explicit foreground and background tokens. 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.