Skip to content

Testing Strategy

Layered verification: focused unit/route tests first, browser smoke when a change touches user workflows.

Suites and commands

| Suite | Runner | Command | |---|---|---| | Unit + API route tests | Vitest (src/__tests__) | npm run test:run | | Single file | Vitest | npm run test:run -- src/__tests__/api/example.test.ts | | Component tests | Testing Library + JSDOM | npm run test:run (same suite) | | Coverage | V8 | npm run test:coverage | | Browser E2E | Playwright (e2e/, ~30 specs) | npm run e2e | | Launch workflows / visuals | Playwright | npm run e2e:launch-workflows, npm run e2e:launch-visuals | | Public smoke (prod server) | Playwright | npm run e2e:smoke | | Accessibility | axe-core | npm run e2e:axe, npm run e2e:axe:rooms | | AI evals (deterministic) | Vitest | npm run ai:evals | | Typecheck | tsc + route typegen | npm run typecheck | | Lint + zero-warning budget | ESLint | npm run lint, npm run lint:warning-budget |

vitest.config.ts sets low global coverage floors (large route surface) with 80%-statement scoped gates on lib/auth, lib/billing, lib/cases, lib/ai, proxy.ts, and a 55% gate on lib/redis. Ratchet upward, never down.

Route tests (required per protected route)

Call handlers directly with NextRequest; mock auth + Prisma via src/__tests__/helpers/mocks.ts. Cover: unauthenticated → 401; wrong role → 403; happy path; invalid input; cross-firm access (must miss).

Component tests

Assert user-visible behavior with semantic queries (getByRole), not implementation details. Upload components additionally cover: bad MIME, oversize file, presigned-URL failure, storage PUT failure, success.

E2E

Use Playwright for auth cookies, file selection, redirects, responsive layout, and cross-route navigation. Launch smoke covers auth/onboarding, case CRUD, tasks, document upload/share, reports export, billing shell, portal entry, integration health previews, and the mobile viewport matrix.

Gates and evidence

Source: docs/dev/testing.md