This commit captures work from multiple sessions building the statistics system and frontend component library. Backend - Phase 3.5: Statistics System - Box score statistics with materialized views - Play stat calculator for real-time updates - Stat view refresher service - Alembic migration for materialized views - Test coverage: 41 new tests (all passing) Frontend - Phase F1: Foundation - Composables: useGameState, useGameActions, useWebSocket - Type definitions and interfaces - Store setup with Pinia Frontend - Phase F2: Game Display - ScoreBoard, GameBoard, CurrentSituation, PlayByPlay components - Demo page at /demo Frontend - Phase F3: Decision Inputs - DefensiveSetup, OffensiveApproach, StolenBaseInputs components - DecisionPanel orchestration - Demo page at /demo-decisions - Test coverage: 213 tests passing Frontend - Phase F4: Dice & Manual Outcome - DiceRoller component - ManualOutcomeEntry with validation - PlayResult display - GameplayPanel orchestration - Demo page at /demo-gameplay - Test coverage: 119 tests passing Frontend - Phase F5: Substitutions - PinchHitterSelector, DefensiveReplacementSelector, PitchingChangeSelector - SubstitutionPanel with tab navigation - Demo page at /demo-substitutions - Test coverage: 114 tests passing Documentation: - PHASE_3_5_HANDOFF.md - Statistics system handoff - PHASE_F2_COMPLETE.md - Game display completion - Frontend phase planning docs - NEXT_SESSION.md updated for Phase F6 Configuration: - Package updates (Nuxt 4 fixes) - Tailwind config enhancements - Game store updates Test Status: - Backend: 731/731 passing (100%) - Frontend: 446/446 passing (100%) - Total: 1,177 tests passing Next Phase: F6 - Integration (wire all components into game page) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
735 lines
16 KiB
Markdown
735 lines
16 KiB
Markdown
# Frontend Testing Strategy
|
|
|
|
**Project**: Paper Dynasty Real-Time Game Engine - SBA Frontend
|
|
**Framework**: Vue 3 + Nuxt 3 + TypeScript
|
|
**Testing Philosophy**: Test early, test often - unit tests in every phase
|
|
**Created**: 2025-01-10
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This document outlines our comprehensive testing strategy for the SBA frontend. **Unlike the original plan to defer testing to Phase F9, we now write unit tests alongside feature development in each phase** (F1-F8), with E2E and performance testing in Phase F9.
|
|
|
|
---
|
|
|
|
## Testing Philosophy
|
|
|
|
### Core Principles
|
|
|
|
1. **Test-Driven Quality**: Write tests as you build features, not after
|
|
2. **Fast Feedback**: Unit tests run in milliseconds, catch bugs immediately
|
|
3. **Confidence**: High test coverage allows refactoring without fear
|
|
4. **Documentation**: Tests serve as executable documentation
|
|
5. **Regression Prevention**: Tests prevent reintroducing fixed bugs
|
|
|
|
### Why Test in Each Phase?
|
|
|
|
✅ **Immediate Feedback**: Catch bugs before they compound
|
|
✅ **Better Design**: Writing testable code forces better architecture
|
|
✅ **Faster Development**: Less debugging time overall
|
|
✅ **Prevent Regressions**: Safe refactoring as project grows
|
|
✅ **Easier Onboarding**: Tests document expected behavior
|
|
|
|
❌ **Without Tests**: Tech debt accumulates, bugs multiply, refactoring becomes risky
|
|
|
|
---
|
|
|
|
## Testing Pyramid
|
|
|
|
```
|
|
/\
|
|
/E2\ E2E Tests (Phase F9)
|
|
/____\ - Full user flows
|
|
/ \ - Cross-browser testing
|
|
/ INTEG \ - Real WebSocket connections
|
|
/ RATION \
|
|
/____________\
|
|
/ \ Integration Tests (Light, as needed)
|
|
/ UNIT \ - API mocking
|
|
/ TESTS \ - Store + composable integration
|
|
/____________________\
|
|
80% of tests Unit Tests (Every Phase)
|
|
- Composables, stores, utils
|
|
- Fast, isolated, deterministic
|
|
```
|
|
|
|
**Target Distribution**:
|
|
- **80%**: Unit tests (fast, isolated, many)
|
|
- **15%**: Integration tests (medium speed, some mocking)
|
|
- **5%**: E2E tests (slow, expensive, critical paths only)
|
|
|
|
---
|
|
|
|
## Testing Tools
|
|
|
|
### Unit Testing
|
|
|
|
**Vitest** - Modern, fast test runner
|
|
- Native ESM support
|
|
- Compatible with Vue and Nuxt
|
|
- Watch mode with instant feedback
|
|
- Built-in coverage reporting
|
|
|
|
**@vue/test-utils** - Official Vue testing library
|
|
- Mount Vue components
|
|
- Simulate user interactions
|
|
- Test props, emits, slots
|
|
|
|
**happy-dom** - Lightweight DOM implementation
|
|
- Faster than jsdom
|
|
- Sufficient for most unit tests
|
|
- Integrates with Vitest
|
|
|
|
### E2E Testing (Phase F9)
|
|
|
|
**Playwright** - Modern E2E testing
|
|
- Cross-browser (Chromium, Firefox, WebKit)
|
|
- Mobile emulation
|
|
- Network interception
|
|
- Video/screenshot capture
|
|
|
|
---
|
|
|
|
## What to Test (By Layer)
|
|
|
|
### Composables (Critical - 90%+ coverage)
|
|
|
|
**Why**: Composables contain business logic and are highly reusable.
|
|
|
|
**Test**:
|
|
- ✅ Return values and reactive refs
|
|
- ✅ Side effects (API calls, WebSocket emits)
|
|
- ✅ Error handling and edge cases
|
|
- ✅ Lifecycle hooks (onMounted, onUnmounted)
|
|
- ✅ Integration with other composables/stores
|
|
|
|
**Example**: `useWebSocket.spec.ts`
|
|
```typescript
|
|
describe('useWebSocket', () => {
|
|
it('connects with JWT auth on mount', () => { ... })
|
|
it('reconnects with exponential backoff', () => { ... })
|
|
it('emits correct events', () => { ... })
|
|
it('handles connection errors gracefully', () => { ... })
|
|
})
|
|
```
|
|
|
|
### Stores (Critical - 90%+ coverage)
|
|
|
|
**Why**: Stores hold application state and business logic.
|
|
|
|
**Test**:
|
|
- ✅ State initialization
|
|
- ✅ Actions modify state correctly
|
|
- ✅ Computed properties calculate correctly
|
|
- ✅ Persistence (localStorage integration)
|
|
- ✅ Multiple action calls (state transitions)
|
|
|
|
**Example**: `auth.spec.ts`
|
|
```typescript
|
|
describe('useAuthStore', () => {
|
|
it('logs in user with Discord OAuth', () => { ... })
|
|
it('persists token to localStorage', () => { ... })
|
|
it('refreshes expired tokens', () => { ... })
|
|
it('logs out and clears state', () => { ... })
|
|
})
|
|
```
|
|
|
|
### Components (Important - 70%+ coverage)
|
|
|
|
**Why**: Components are the user interface layer.
|
|
|
|
**Test**:
|
|
- ✅ Props render correctly
|
|
- ✅ User interactions trigger emits
|
|
- ✅ Conditional rendering (v-if, v-show)
|
|
- ✅ Computed properties and methods
|
|
- ⚠️ **Not**: Visual appearance (use Storybook for that)
|
|
|
|
**Example**: `ScoreBoard.spec.ts`
|
|
```typescript
|
|
describe('ScoreBoard', () => {
|
|
it('displays correct score and inning', () => { ... })
|
|
it('highlights current inning', () => { ... })
|
|
it('shows correct inning half (Top/Bottom)', () => { ... })
|
|
})
|
|
```
|
|
|
|
### Utilities (Critical - 100% coverage)
|
|
|
|
**Why**: Pure functions are easiest to test and should be thoroughly covered.
|
|
|
|
**Test**:
|
|
- ✅ Input/output correctness
|
|
- ✅ Edge cases (null, undefined, empty)
|
|
- ✅ Error handling
|
|
- ✅ Type safety
|
|
|
|
**Example**: `formatters.spec.ts`
|
|
```typescript
|
|
describe('formatInning', () => {
|
|
it('formats top of 1st inning', () => { ... })
|
|
it('formats bottom of 9th inning', () => { ... })
|
|
it('handles extra innings', () => { ... })
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Patterns
|
|
|
|
### Mocking Strategy
|
|
|
|
**Mock External Dependencies**:
|
|
```typescript
|
|
// Mock Socket.io
|
|
vi.mock('socket.io-client', () => ({
|
|
io: vi.fn(() => ({
|
|
on: vi.fn(),
|
|
emit: vi.fn(),
|
|
connect: vi.fn(),
|
|
disconnect: vi.fn(),
|
|
}))
|
|
}))
|
|
|
|
// Mock Nuxt composables
|
|
vi.mock('#app', () => ({
|
|
useRuntimeConfig: vi.fn(() => ({
|
|
public: {
|
|
apiUrl: 'http://localhost:8000',
|
|
wsUrl: 'http://localhost:8000'
|
|
}
|
|
}))
|
|
}))
|
|
```
|
|
|
|
**Don't Mock What You Own**:
|
|
- ❌ Don't mock stores in composable tests (test integration)
|
|
- ❌ Don't mock child components (use shallow mount if needed)
|
|
- ✅ Mock external APIs, WebSocket, localStorage
|
|
|
|
### Test Structure (AAA Pattern)
|
|
|
|
```typescript
|
|
describe('Feature', () => {
|
|
it('does something specific', () => {
|
|
// Arrange: Set up test data and mocks
|
|
const store = useAuthStore()
|
|
const mockToken = 'jwt-token-123'
|
|
|
|
// Act: Perform the action being tested
|
|
store.setToken(mockToken)
|
|
|
|
// Assert: Verify the outcome
|
|
expect(store.token).toBe(mockToken)
|
|
expect(localStorage.setItem).toHaveBeenCalledWith('token', mockToken)
|
|
})
|
|
})
|
|
```
|
|
|
|
### Testing Async Code
|
|
|
|
```typescript
|
|
it('fetches user data on login', async () => {
|
|
const store = useAuthStore()
|
|
|
|
// Mock API response
|
|
vi.mocked($fetch).mockResolvedValue({
|
|
id: '123',
|
|
username: 'testuser'
|
|
})
|
|
|
|
// Act
|
|
await store.login('code', 'state')
|
|
|
|
// Assert
|
|
expect(store.currentUser).toEqual({
|
|
id: '123',
|
|
username: 'testuser'
|
|
})
|
|
})
|
|
```
|
|
|
|
### Testing Composables
|
|
|
|
```typescript
|
|
import { mount } from '@vue/test-utils'
|
|
import { defineComponent } from 'vue'
|
|
|
|
it('useGameActions emits correct events', () => {
|
|
const mockSocket = {
|
|
emit: vi.fn(),
|
|
connected: true
|
|
}
|
|
|
|
// Create wrapper component to test composable
|
|
const TestComponent = defineComponent({
|
|
setup() {
|
|
const actions = useGameActions('game-123')
|
|
return { actions }
|
|
},
|
|
template: '<div></div>'
|
|
})
|
|
|
|
const wrapper = mount(TestComponent, {
|
|
global: {
|
|
provide: {
|
|
socket: mockSocket
|
|
}
|
|
}
|
|
})
|
|
|
|
// Act
|
|
wrapper.vm.actions.rollDice()
|
|
|
|
// Assert
|
|
expect(mockSocket.emit).toHaveBeenCalledWith('roll_dice', {
|
|
game_id: 'game-123'
|
|
})
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Phase-by-Phase Testing Plan
|
|
|
|
### Phase F1: Core Infrastructure (20+ tests)
|
|
|
|
**Focus**: Composables and stores (most critical)
|
|
|
|
**Tests**:
|
|
- `useWebSocket.spec.ts` (8+ tests)
|
|
- Connection/disconnection
|
|
- Exponential backoff
|
|
- JWT authentication
|
|
- Event handler registration
|
|
|
|
- `useGameActions.spec.ts` (6+ tests)
|
|
- Validation logic
|
|
- Emit parameter construction
|
|
- Error handling
|
|
|
|
- `auth.spec.ts` (3+ tests)
|
|
- Login/logout flow
|
|
- Token management
|
|
- localStorage persistence
|
|
|
|
- `game.spec.ts` (2+ tests)
|
|
- State updates
|
|
- Play history management
|
|
|
|
- `ui.spec.ts` (2+ tests)
|
|
- Toast lifecycle
|
|
- Modal stack
|
|
|
|
**Estimated Time**: 3-4 hours
|
|
|
|
---
|
|
|
|
### Phase F2: Game State Display (10+ tests)
|
|
|
|
**Focus**: Display components and state sync
|
|
|
|
**Tests**:
|
|
- `ScoreBoard.spec.ts` (2+ tests)
|
|
- `GameBoard.spec.ts` (2+ tests)
|
|
- `PlayByPlay.spec.ts` (2+ tests)
|
|
- `CurrentBatter.spec.ts` (2+ tests)
|
|
- State synchronization logic (2+ tests)
|
|
|
|
**Estimated Time**: 2-3 hours
|
|
|
|
---
|
|
|
|
### Phase F3: Decision Input Workflow (8+ tests)
|
|
|
|
**Focus**: Decision components and validation
|
|
|
|
**Tests**:
|
|
- `DefensiveSetup.spec.ts` (2+ tests)
|
|
- `OffensiveApproach.spec.ts` (2+ tests)
|
|
- `StolenBaseInputs.spec.ts` (2+ tests)
|
|
- Decision validation logic (2+ tests)
|
|
|
|
**Estimated Time**: 2 hours
|
|
|
|
---
|
|
|
|
### Phase F4: Manual Outcome Workflow (8+ tests)
|
|
|
|
**Focus**: Dice roll and outcome validation
|
|
|
|
**Tests**:
|
|
- `DiceRollButton.spec.ts` (2+ tests)
|
|
- `OutcomeInputForm.spec.ts` (3+ tests)
|
|
- Outcome validation logic (3+ tests)
|
|
|
|
**Estimated Time**: 2 hours
|
|
|
|
---
|
|
|
|
### Phase F5: Substitutions (8+ tests)
|
|
|
|
**Focus**: Substitution logic and components
|
|
|
|
**Tests**:
|
|
- `SubstitutionModal.spec.ts` (2+ tests)
|
|
- `PinchHitterSelect.spec.ts` (2+ tests)
|
|
- Substitution validation (4+ tests)
|
|
|
|
**Estimated Time**: 2 hours
|
|
|
|
---
|
|
|
|
### Phase F6: Game Management (10+ tests)
|
|
|
|
**Focus**: Game API and management features
|
|
|
|
**Tests**:
|
|
- `useGameApi.spec.ts` (4+ tests)
|
|
- `GameCreationForm.spec.ts` (2+ tests)
|
|
- `GamesList.spec.ts` (2+ tests)
|
|
- `GameFilters.spec.ts` (2+ tests)
|
|
|
|
**Estimated Time**: 2-3 hours
|
|
|
|
---
|
|
|
|
### Phase F7: Spectator Mode (5+ tests)
|
|
|
|
**Focus**: Spectator permissions and UI
|
|
|
|
**Tests**:
|
|
- Spectator role validation (2+ tests)
|
|
- `SpectatorGameView.spec.ts` (2+ tests)
|
|
- Spectator WebSocket events (1+ test)
|
|
|
|
**Estimated Time**: 1-2 hours
|
|
|
|
---
|
|
|
|
### Phase F8: Polish & Animation (5+ tests)
|
|
|
|
**Focus**: Animation triggers and timing
|
|
|
|
**Tests**:
|
|
- Animation trigger logic (3+ tests)
|
|
- Toast timing and auto-dismiss (2+ tests)
|
|
|
|
**Estimated Time**: 1-2 hours
|
|
|
|
---
|
|
|
|
### Phase F9: E2E & Performance (10+ tests)
|
|
|
|
**Focus**: End-to-end user flows and performance
|
|
|
|
**E2E Tests**:
|
|
- `auth-flow.e2e.ts` - Complete auth workflow
|
|
- `game-creation.e2e.ts` - Create and join game
|
|
- `gameplay-flow.e2e.ts` - Full game from start to finish
|
|
- `substitution-flow.e2e.ts` - Player substitutions
|
|
- `spectator-flow.e2e.ts` - Spectator mode
|
|
- `mobile-responsive.e2e.ts` - Mobile UX
|
|
- `websocket-recovery.e2e.ts` - Connection recovery
|
|
|
|
**Performance Tests**:
|
|
- Lighthouse audits (automated)
|
|
- Bundle size monitoring
|
|
- Memory leak detection
|
|
|
|
**Estimated Time**: 1 week
|
|
|
|
---
|
|
|
|
## Running Tests
|
|
|
|
### Development Workflow
|
|
|
|
```bash
|
|
# Run all tests (once)
|
|
npm run test
|
|
|
|
# Watch mode (re-run on file changes)
|
|
npm run test:watch
|
|
|
|
# Run specific test file
|
|
npm run test useWebSocket.spec.ts
|
|
|
|
# Run tests matching pattern
|
|
npm run test --grep "WebSocket"
|
|
|
|
# Coverage report
|
|
npm run test:coverage
|
|
|
|
# UI mode (interactive)
|
|
npm run test:ui
|
|
```
|
|
|
|
### CI/CD Pipeline
|
|
|
|
```yaml
|
|
# .github/workflows/test.yml
|
|
name: Test Suite
|
|
on: [push, pull_request]
|
|
|
|
jobs:
|
|
unit-tests:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-node@v3
|
|
with:
|
|
node-version: '20'
|
|
- run: npm ci
|
|
- run: npm run test:coverage
|
|
- uses: codecov/codecov-action@v3
|
|
with:
|
|
files: ./coverage/coverage-final.json
|
|
|
|
e2e-tests:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: actions/setup-node@v3
|
|
- run: npm ci
|
|
- run: npx playwright install
|
|
- run: npm run test:e2e
|
|
```
|
|
|
|
---
|
|
|
|
## Coverage Goals
|
|
|
|
### Minimum Coverage Targets
|
|
|
|
| Layer | Target | Priority |
|
|
|-------|--------|----------|
|
|
| Composables | 90% | Critical |
|
|
| Stores | 90% | Critical |
|
|
| Utilities | 100% | Critical |
|
|
| Components | 70% | Important |
|
|
| Overall | 80% | Required |
|
|
|
|
### Coverage Exemptions
|
|
|
|
**Don't need 100% coverage for**:
|
|
- UI-only components (visual polish)
|
|
- Third-party library wrappers
|
|
- Configuration files
|
|
- Type definitions
|
|
|
|
**Do need high coverage for**:
|
|
- Business logic (composables, stores)
|
|
- Validation and error handling
|
|
- WebSocket communication
|
|
- Authentication and security
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### DO ✅
|
|
|
|
- Write tests alongside feature code
|
|
- Test behavior, not implementation
|
|
- Use descriptive test names
|
|
- Keep tests independent (no shared state)
|
|
- Mock external dependencies
|
|
- Test edge cases and errors
|
|
- Run tests before committing
|
|
|
|
### DON'T ❌
|
|
|
|
- Skip tests to "save time" (you'll pay later)
|
|
- Test implementation details
|
|
- Create test dependencies (test A needs test B)
|
|
- Mock everything (test real integration)
|
|
- Write slow unit tests (use E2E instead)
|
|
- Ignore failing tests
|
|
- Commit code that breaks tests
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Tests Run Slowly
|
|
|
|
**Problem**: Unit tests take > 5 seconds
|
|
**Solution**:
|
|
- Check for unmocked API calls
|
|
- Use happy-dom instead of jsdom
|
|
- Avoid unnecessary component mounts
|
|
- Run tests in parallel
|
|
|
|
### Flaky Tests
|
|
|
|
**Problem**: Tests pass sometimes, fail other times
|
|
**Solution**:
|
|
- Fix race conditions (use `waitFor`)
|
|
- Clear mocks between tests
|
|
- Avoid relying on timing
|
|
- Check for shared state
|
|
|
|
### Coverage Not Updating
|
|
|
|
**Problem**: Coverage report doesn't reflect new tests
|
|
**Solution**:
|
|
- Delete `coverage/` directory
|
|
- Re-run `npm run test:coverage`
|
|
- Check if files are ignored in config
|
|
|
|
---
|
|
|
|
## Example Test Suites
|
|
|
|
### Composable Test Template
|
|
|
|
```typescript
|
|
// tests/unit/composables/useExample.spec.ts
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
import { useExample } from '~/composables/useExample'
|
|
|
|
describe('useExample', () => {
|
|
beforeEach(() => {
|
|
// Setup: Create fresh instances, mock dependencies
|
|
})
|
|
|
|
afterEach(() => {
|
|
// Cleanup: Clear mocks, restore state
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('initialization', () => {
|
|
it('initializes with default state', () => {
|
|
// Test default values
|
|
})
|
|
})
|
|
|
|
describe('actions', () => {
|
|
it('performs action successfully', async () => {
|
|
// Test happy path
|
|
})
|
|
|
|
it('handles errors gracefully', async () => {
|
|
// Test error case
|
|
})
|
|
})
|
|
|
|
describe('edge cases', () => {
|
|
it('handles null input', () => {
|
|
// Test edge case
|
|
})
|
|
})
|
|
})
|
|
```
|
|
|
|
### Store Test Template
|
|
|
|
```typescript
|
|
// tests/unit/store/example.spec.ts
|
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
import { useExampleStore } from '~/store/example'
|
|
|
|
describe('useExampleStore', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
})
|
|
|
|
it('initializes with default state', () => {
|
|
const store = useExampleStore()
|
|
expect(store.someState).toBe(null)
|
|
})
|
|
|
|
it('updates state via action', () => {
|
|
const store = useExampleStore()
|
|
store.someAction('value')
|
|
expect(store.someState).toBe('value')
|
|
})
|
|
|
|
it('computes derived state correctly', () => {
|
|
const store = useExampleStore()
|
|
store.someState = 'test'
|
|
expect(store.computedValue).toBe('TEST')
|
|
})
|
|
})
|
|
```
|
|
|
|
### Component Test Template
|
|
|
|
```typescript
|
|
// tests/unit/components/Example.spec.ts
|
|
import { describe, it, expect } from 'vitest'
|
|
import { mount } from '@vue/test-utils'
|
|
import ExampleComponent from '~/components/Example.vue'
|
|
|
|
describe('ExampleComponent', () => {
|
|
it('renders props correctly', () => {
|
|
const wrapper = mount(ExampleComponent, {
|
|
props: {
|
|
title: 'Test Title'
|
|
}
|
|
})
|
|
|
|
expect(wrapper.text()).toContain('Test Title')
|
|
})
|
|
|
|
it('emits event on button click', async () => {
|
|
const wrapper = mount(ExampleComponent)
|
|
|
|
await wrapper.find('button').trigger('click')
|
|
|
|
expect(wrapper.emitted('click')).toBeTruthy()
|
|
expect(wrapper.emitted('click')?.[0]).toEqual(['expected-value'])
|
|
})
|
|
|
|
it('conditionally renders based on prop', () => {
|
|
const wrapper = mount(ExampleComponent, {
|
|
props: {
|
|
show: false
|
|
}
|
|
})
|
|
|
|
expect(wrapper.find('.conditional-element').exists()).toBe(false)
|
|
})
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
**Phase Completion Criteria**:
|
|
- ✅ All new code has corresponding tests
|
|
- ✅ All tests passing (no skipped tests)
|
|
- ✅ Coverage meets phase target (> 80%)
|
|
- ✅ No flaky tests
|
|
- ✅ Tests run in < 10 seconds (unit tests)
|
|
|
|
**Project Completion Criteria**:
|
|
- ✅ 80%+ overall coverage
|
|
- ✅ 90%+ coverage on critical paths
|
|
- ✅ All E2E tests passing
|
|
- ✅ Performance benchmarks met
|
|
- ✅ Zero known failing tests
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
**Documentation**:
|
|
- [Vitest Docs](https://vitest.dev/)
|
|
- [Vue Test Utils](https://test-utils.vuejs.org/)
|
|
- [Playwright Docs](https://playwright.dev/)
|
|
|
|
**Backend Tests** (Inspiration):
|
|
- `/mnt/NV2/Development/strat-gameplay-webapp/backend/tests/unit/`
|
|
- 730+ tests with 99.9% coverage - excellent patterns to learn from
|
|
|
|
---
|
|
|
|
**Document Version**: 1.0
|
|
**Last Updated**: 2025-01-10
|
|
**Status**: Active - guiding all frontend development
|