strat-gameplay-webapp/.claude/implementation/testing-strategy.md
Cal Corum eab61ad966 CLAUDE: Phases 3.5, F1-F5 Complete - Statistics & Frontend Components
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>
2025-11-14 09:52:30 -06:00

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