Testing Guide
Running Days uses Vitest for unit tests and Playwright for end-to-end tests.
Test Structure
text
packages/
βββ business-logic/
β βββ src/
β βββ streak-calculator.ts
β βββ streak-calculator.test.ts β Co-located
βββ database/
β βββ src/
β βββ encryption.test.ts
βββ utils/
βββ src/
βββ date-utils.test.ts
apps/
βββ api/
β βββ tests/
β βββ auth.test.ts
β βββ cache.test.ts
βββ web/
βββ e2e/
βββ auth.spec.ts
βββ dashboard.spec.tsRunning Tests
All Tests
bash
pnpm testSpecific Package
bash
pnpm --filter @running-days/business-logic test
pnpm --filter @running-days/database testWatch Mode
bash
pnpm --filter @running-days/business-logic test:watchWith Coverage
bash
pnpm test:coverageWriting Unit Tests
Basic Test
typescript
import { describe, it, expect } from 'vitest';
import { calculateStreaks } from './streak-calculator.js';
describe('calculateStreaks', () => {
it('should return 0 for empty input', () => {
const result = calculateStreaks([]);
expect(result.current).toBe(0);
});
});Testing with Mocked Time
typescript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
describe('time-sensitive tests', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-01-15T12:00:00Z'));
});
afterEach(() => {
vi.useRealTimers();
});
it('should calculate based on current date', () => {
// Test runs as if it's Jan 15, 2024
});
});Testing Async Code
typescript
it('should fetch data', async () => {
const result = await fetchWorkouts();
expect(result).toHaveLength(5);
});E2E Tests with Playwright
Running E2E Tests
bash
# All E2E tests
pnpm --filter @running-days/web test:e2e
# Specific test file
pnpm --filter @running-days/web test:e2e auth.spec.ts
# With UI mode
pnpm --filter @running-days/web test:e2e --uiWriting E2E Tests
typescript
import { test, expect } from '@playwright/test';
test('user can view dashboard', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});Authentication in E2E
Use the mock API client for authenticated tests:
typescript
test('authenticated dashboard shows workouts', async ({ page }) => {
// Mock auth is handled by test fixtures
await page.goto('/dashboard');
await expect(page.getByTestId('workout-list')).toBeVisible();
});Test Best Practices
Do
- Test behavior, not implementation
- Use descriptive test names
- Keep tests focused and isolated
- Test edge cases and error paths
- Use realistic test data
Donβt
- Donβt test framework code
- Donβt mock everything
- Donβt write flaky tests
- Donβt skip failing tests permanently
Coverage Requirements
Aim for these coverage levels:
| Package | Target |
|---|---|
| business-logic | 90% |
| database | 80% |
| api | 75% |
| web | 70% (E2E covers UI) |
CI Integration
Tests run automatically on:
- Pull request creation
- Push to main branch
- Release tagging
See .github/workflows/ci.yml for configuration.