ADR-041: Vitest for Frontend Testing
Status
Implemented
Date
2025-01-16 (Retrospective)
Decision Makers
- Frontend Team - Testing framework selection
- QA Team - Testing strategy
Layer
Testing
Related ADRs
- ADR-034: Vite Build System
Supersedes
- Jest (migrated to Vitest)
Depends On
- ADR-034: Vite Build System
Context
Frontend testing requires a modern, fast framework:
- Speed: Tests should run quickly
- Vite Integration: Share Vite configuration
- ESM Native: Modern module support
- React Testing Library: Component testing
- Coverage: Code coverage reporting
Requirements:
- Jest-compatible API (easy migration)
- Hot module replacement for watch mode
- TypeScript native support
- Coverage target: 80%
- React Testing Library integration
Decision
We adopt Vitest as the frontend testing framework:
Key Design Decisions
- Vitest: Vite-native testing framework
- Jest API: Compatible for easy migration
- happy-dom: Lightweight DOM environment
- React Testing Library: Component testing
- c8 Coverage: V8-based coverage
Configuration
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'happy-dom',
setupFiles: ['./src/test/setup.ts'],
include: ['src/**/*.{test,spec}.{ts,tsx}'],
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: ['node_modules/', 'src/test/'],
thresholds: {
lines: 80,
branches: 80,
functions: 80,
statements: 80,
},
},
},
});
Test Pattern
// Component test
import { render, screen, userEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { RequirementForm } from './RequirementForm';
describe('RequirementForm', () => {
it('validates required fields', async () => {
const onSubmit = vi.fn();
render(<RequirementForm onSubmit={onSubmit} />);
await userEvent.click(screen.getByRole('button', { name: 'Submit' }));
expect(screen.getByText('Title is required')).toBeInTheDocument();
expect(onSubmit).not.toHaveBeenCalled();
});
it('submits valid form data', async () => {
const onSubmit = vi.fn();
render(<RequirementForm onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText('Title'), 'New Requirement');
await userEvent.click(screen.getByRole('button', { name: 'Submit' }));
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ title: 'New Requirement' })
);
});
});
Test Scripts
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:ui": "vitest --ui"
}
}
Consequences
Positive
- Speed: 10x faster than Jest
- Vite Parity: Same config for build and test
- HMR Tests: Instant test re-runs
- ESM Native: No transpilation needed
- Jest Compatible: Easy migration path
Negative
- Newer Ecosystem: Fewer plugins than Jest
- API Differences: Some Jest APIs not available
- Browser Testing: Requires @vitest/browser
- Learning Curve: New tool for team
Neutral
- Coverage Tools: c8 vs Istanbul
- Snapshot Testing: Supported but different format
Implementation Status
- Core implementation complete
- Tests written and passing
- Documentation updated
- Migration/upgrade path defined
- Monitoring/observability in place
Implementation Details
- Config:
frontend/vitest.config.ts - Setup:
frontend/src/test/setup.ts - Mocks:
frontend/src/test/mocks/ - Utils:
frontend/src/test/utils/
LLM Council Review
Review Date: 2025-01-16 Confidence Level: High (100%) Verdict: STRONG APPROVAL
Quality Metrics
- Consensus Strength Score (CSS): 0.95
- Deliberation Depth Index (DDI): 0.85
Council Feedback Summary
Unanimous consensus to approve. Vitest is the industry-standard choice for Vite-based applications. The 10x speed improvement and shared config eliminate double-compilation costs.
Key Concerns Identified:
- Test Isolation: Vitest uses worker threads (not processes like Jest); global state can leak
- Snapshot Churn: happy-dom renders HTML differently; existing snapshots will fail on format
- Chart Testing: happy-dom lacks some SVG/Canvas implementations for visualization libraries
Required Modifications:
- Add Strict Cleanup:
test: {
restoreMocks: true,
clearMocks: true,
mockReset: true,
pool: 'threads',
isolate: true,
} - Plan Snapshot Migration: Expect one-time mass update (
vitest -u) - JSDOM Fallback: For chart tests, use
// @vitest-environment jsdom - Setup File: Add
setupFiles: ['./src/test/setup.ts']for global cleanup - Coverage Exclusions: Exclude mock files and type definitions
Modifications Applied
- Documented strict cleanup configuration
- Added snapshot migration plan
- Documented jsdom fallback for visualizations
- Added setup file requirement
Council Ranking
- All models reached strong consensus
- gpt-5.2: Best Response (isolation)
- gemini-3-pro: Strong (migration)
References
ADR-041 | Testing Layer | Implemented