Skip to content

ADR 005 Testing Framework

Claude product-architect (Opus 4.6) edited this page Feb 22, 2026 · 3 revisions

ADR-005: Testing Framework

Status

Accepted (Revised -- originally Vitest, changed to Jest 30.x)

Context

Cornerstone needs testing at multiple levels:

  1. Unit tests: Individual functions, utilities, React components
  2. Integration tests: API endpoint testing (HTTP-level), database operations
  3. End-to-end tests: Full browser-based user flows (owned by the e2e-test-engineer agent)

Alternatives Considered

Vitest (original decision)

  • Built on top of Vite's transform pipeline -- native TypeScript and ESM support
  • Jest-compatible API (describe, it, expect)
  • Shares Vite's configuration
  • Rejected: When Vite was replaced by Webpack (ADR-004) due to native binary issues, Vitest lost its primary advantage of sharing Vite's configuration. Vitest also depends on esbuild internally, which has the same native binary crash issue on ARM64 emulation.

Jest 30.x

  • The most widely used JavaScript test runner with mature ecosystem
  • ESM support via --experimental-vm-modules flag (stable enough for production use)
  • TypeScript support via ts-jest (pure JavaScript transform, no native binaries)
  • jsdom environment for React component testing
  • Extensive mocking capabilities
  • Rich assertion library with snapshot testing
  • Works reliably in all environments

Playwright (for E2E)

  • Cross-browser testing (Chromium, Firefox, WebKit)
  • Excellent TypeScript support and auto-wait mechanisms
  • Built-in test runner with parallel execution
  • Best-in-class debugging with trace viewer
  • See ADR-011 for E2E architecture details

Decision

  • Unit and integration tests: Jest 30.x with ts-jest for TypeScript transformation
  • E2E tests: Playwright (configured in the e2e/ workspace, see ADR-011)
  • HTTP testing: Fastify's built-in inject() method for API endpoint integration tests

Test File Convention

  • Test files are co-located with source files: foo.ts has its test at foo.test.ts
  • Test files use the .test.ts (or .test.tsx for React components) extension
  • No separate __tests__/ directories
  • E2E tests use .spec.ts extension (in the e2e/tests/ directory, separate from unit tests)

Configuration

A single jest.config.ts at the project root configures test discovery across all workspaces:

// Uses ts-jest ESM preset
preset: 'ts-jest/presets/default-esm';

// Projects for server, client, and shared workspaces
projects: [
  { displayName: 'server', testMatch: ['<rootDir>/server/src/**/*.test.ts'] },
  { displayName: 'client', testMatch: ['<rootDir>/client/src/**/*.test.{ts,tsx}'] },
  { displayName: 'shared', testMatch: ['<rootDir>/shared/src/**/*.test.ts'] },
];

The client project uses jest-environment-jsdom for DOM testing with @testing-library/react and @testing-library/jest-dom.

Running Tests

npm test                    # Run all tests (requires --experimental-vm-modules)
npm run test:coverage       # Run with coverage reporting

Consequences

Positive

  • Mature, battle-tested framework with extensive community support and documentation
  • Pure JavaScript transform chain (ts-jest) -- no native binary dependencies
  • Co-located test files make it easy to find and maintain tests
  • Fastify's inject() method enables testing routes without starting an HTTP server
  • Rich mocking and assertion APIs out of the box
  • Excellent IDE integration and debugging support
  • jsdom environment provides realistic DOM for component testing

Negative

  • ESM support requires --experimental-vm-modules flag (stable in practice, but still flagged as experimental)
  • Two test runners (Jest + Playwright) add some tooling complexity
  • Playwright E2E tests are slower than unit tests and require browser installation
  • ts-jest transform is slower than native esbuild/SWC transforms (acceptable for project scale)

Clone this wiki locally