mantimon-tcg/frontend/TEST_COVERAGE_PLAN.md
Cal Corum 345ef7af9d Create Phaser testing infrastructure - TEST-001 complete (55 tests)
Build foundation for game engine testing with comprehensive mocks and utilities:

Infrastructure Created:
- src/test/mocks/phaser.ts (33 tests)
  * MockEventEmitter - Event system with on/once/off/emit
  * MockScene - Scene lifecycle and factories
  * MockGame - Game instance with scale and scene manager
  * MockContainer - Game object container with child management
  * MockSprite - Image sprites with texture support
  * MockText - Styled text objects
  * MockGraphics - Shape drawing API
  * MockLoader - Asset loading simulation

- src/test/helpers/gameTestUtils.ts (22 tests)
  * createMockGameState() - Complete game state with players
  * createMockCardDefinition() - Card definitions with type helpers
  * createMockCardInstance() - Card instances with damage/status
  * createGameScenario() - Full game setups with cards in zones
  * setupMockScene() - Scene setup with game instance
  * Type-specific helpers: createMockPokemonCard(), createMockEnergyCard(), etc.

- src/test/README.md
  * Complete documentation with usage examples
  * Testing patterns and best practices
  * Troubleshooting guide

This infrastructure enables testing of all Phaser game objects (Board, Card, Zone,
MatchScene, etc.) without requiring WebGL/Canvas. All 1,256 tests passing.

Foundation for TEST-002 through TEST-009 (scene and state testing).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-03 10:25:51 -06:00

18 KiB

Test Coverage Improvement Plan

Status: In Progress
Created: 2026-02-02
Started: 2026-02-02
Target Completion: 6 weeks
Current Coverage: 63% → ~65% (after quick wins 1-2)
Target Coverage: 85%

See PROJECT_PLAN_TEST_COVERAGE.json for full structured plan.


📝 Lessons Learned (Session 2026-02-02)

Quick Wins Completed

  • TEST-015: CardBack tests (25 tests, ~2h)
  • TEST-020: Socket message factories (20 tests, ~2h)
  • Total: 45 new tests, 1000 → 1045 tests (+4.5%)

Key Learnings for Future Testing

1. Phaser Mocking Pattern (from TEST-015)

Problem: Phaser classes can't be imported directly in tests (WebGL/Canvas dependencies)

Solution: Mock Phaser classes inline within vi.mock() factory function

// ✅ CORRECT - Define mock classes inside vi.mock factory
vi.mock('phaser', () => {
  class MockContainerFactory {
    x: number
    y: number
    // ... mock implementation
  }
  
  return {
    default: {
      GameObjects: {
        Container: MockContainerFactory,
      },
    },
  }
})

// ❌ WRONG - Referencing external classes causes hoisting issues
class MockContainer { /* ... */ }
vi.mock('phaser', () => ({
  default: {
    GameObjects: {
      Container: MockContainer, // ReferenceError: Cannot access before initialization
    },
  },
}))

Why: vi.mock() is hoisted to the top of the file, so it runs before any class definitions. Mock classes must be defined within the factory function.

2. Disabling ESLint for Test Mocks

When mocking complex Phaser objects, any types are acceptable in tests:

/* eslint-disable @typescript-eslint/no-explicit-any */
// Disabling explicit-any for test mocks - Phaser types are complex and mocking requires any

Rationale: Mocking Phaser's deep type hierarchies is impractical. Focus on testing behavior, not perfect type accuracy in mocks.

3. Test Docstrings Are Essential

Every test must include a docstring explaining "what" and "why":

it('creates a card back with sprite when texture exists', () => {
  /**
   * Test that CardBack uses sprite when texture is available.
   *
   * When the card back texture is loaded, CardBack should create
   * a sprite instead of fallback graphics.
   */
  // test implementation
})

Why: These docstrings make tests self-documenting and help future developers understand intent.

4. Test Actual Dimensions, Not Assumptions

Mistake Made: Initially used wrong card dimensions (assumed 120x167 for large, actual was 150x210)

Lesson: Always check actual constants/values in codebase before writing assertions. Don't assume dimensions or magic numbers.

// ✅ Verified actual CARD_SIZES from types/phaser.ts
expect(dimensions.width).toBe(150)  // large: 150x210
expect(dimensions.height).toBe(210)

// ❌ Assumed without checking
expect(dimensions.width).toBe(120)  // Wrong!

5. Integration Tests Validate Full Lifecycle

Include at least one integration test that exercises the full object lifecycle:

it('can create, resize, and destroy a card back', () => {
  /**
   * Test full lifecycle of a CardBack.
   *
   * This integration test verifies that a CardBack can be
   * created, resized multiple times, and destroyed without errors.
   */
  const cardBack = new CardBack(mockScene, 100, 200, 'small')
  cardBack.setCardSize('medium')
  cardBack.setCardSize('large')
  expect(() => cardBack.destroy()).not.toThrow()
})

Why: Unit tests verify individual methods, integration tests verify they work together correctly.

6. Factory Function Testing Strategy

When testing message/object factories (TEST-020):

  1. Test structure first - Verify all required fields present
  2. Test uniqueness - IDs/tokens should be unique on each call
  3. Test variations - Test with/without optional parameters
  4. Test integration - Verify factories work together (same game, different messages)
// Structure
expect(message.type).toBe('join_game')
expect(message.message_id).toBeTruthy()

// Uniqueness
const msg1 = createJoinGameMessage('game-1')
const msg2 = createJoinGameMessage('game-1')
expect(msg1.message_id).not.toBe(msg2.message_id)

// Variations
const withLastEvent = createJoinGameMessage('game-1', 'event-123')
expect(withLastEvent.last_event_id).toBe('event-123')

const withoutLastEvent = createJoinGameMessage('game-1')
expect(withoutLastEvent.last_event_id).toBeUndefined()

7. Avoid Testing Browser Internals

Mistake Made: Attempted to mock global.crypto which is read-only

Lesson: Don't test browser APIs directly. Test your wrapper functions instead. If a function uses crypto.randomUUID(), test that it returns valid UUIDs, not that it calls the browser API.

// ✅ Test the wrapper's behavior
it('generates a unique message ID', () => {
  const id = generateMessageId()
  expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
})

// ❌ Don't test browser internals
it('uses crypto.randomUUID when available', () => {
  global.crypto = { randomUUID: vi.fn() } // TypeError: Cannot set property
})

8. Pre-commit Hooks Catch Everything

Since fixing pre-existing lint errors and enabling pre-commit hooks (PR #2):

  • All TypeScript errors caught immediately
  • All lint errors caught immediately
  • All test failures caught immediately
  • No bypassing with --no-verify

Impact: Zero bugs made it into commits during this session. Pre-commit hooks are working perfectly.

9. Quick Wins Build Momentum

Starting with simple, high-value tests (CardBack, socket factories) built confidence and validated patterns:

  • CardBack: Simple game object, validated Phaser mocking
  • Socket factories: Straightforward logic, 100% coverage quickly
  • Both: Provided immediate value (45 tests) in 4 hours

Strategy: When starting a new test area, pick the simplest component first to validate your approach.

10. Coverage Numbers Update Automatically

After adding tests, re-run coverage to see actual improvement:

npm run test -- --coverage

Observed Gains:

  • CardBack: 0% → ~95% coverage (238 lines)
  • Socket factories: 0% → 100% coverage (54 lines)
  • Overall: 63% → ~65% (+2%)

Executive Summary

The Mantimon TCG frontend has excellent test discipline (1000 passing tests) but has critical gaps in the game engine layer:

  • Strong: Composables (84%), Components (90%), Stores (88%)
  • Critical Gap: Game Engine (0% coverage, ~5,500 lines)
  • ⚠️ Needs Work: WebSocket (27-45% coverage)

Biggest Risk: Phaser game engine is completely untested - rendering bugs, state sync issues, and visual glitches won't be caught until manual testing or production.


Priority Overview

🔴 Critical (Week 1-3)

Task Component Impact
TEST-001 Phaser testing infrastructure Prerequisite for all game engine tests
TEST-002 MatchScene lifecycle Core game scene rendering
TEST-003 Board layout & zones Prize zone bug, zone management
TEST-004 Card rendering Card display and interactions
TEST-005 StateRenderer sync Backend ↔ Phaser synchronization
TEST-007 Socket connection WebSocket reliability
TEST-008 Game action handling Action queueing and responses
TEST-009 Game management Create/join game workflows

Total: ~80 hours, 8 tasks

🟡 High (Week 4)

Task Component Impact
TEST-006 Zone classes Zone types and card management
TEST-010 PreloadScene Asset loading
TEST-011 HandManager Drag/drop edge cases
TEST-012 DamageCounter Damage display
TEST-013 Asset loader Asset management
TEST-014 ResizeManager Responsive layout

Total: ~31 hours, 6 tasks

🟢 Medium/Low (Week 5-6)

  • Page tests (HomePage, CampaignPage, MatchPage)
  • Edge case tests (user store, drag/drop, deck builder)
  • Infrastructure (visual regression, CI coverage, documentation)

Total: ~45 hours, remaining tasks


Week-by-Week Roadmap

Week 1: Foundation (28 hours)

Theme: Phaser Testing Infrastructure & Core Objects

Tasks:

  • TEST-001: Create Phaser mocks and test utilities (8h) COMPLETE (55 tests: 33 mock tests + 22 utility tests)
  • TEST-003: Test Board layout and zones (10h)
  • TEST-006: Test Zone base class and subclasses (10h)

Deliverables:

  • Phaser testing infrastructure ready - MockScene, MockGame, MockContainer, MockSprite, MockText, MockGraphics
  • Test utilities created - createMockGameState, createMockCard, setupMockScene, createGameScenario
  • Documentation complete - src/test/README.md with usage examples
  • Board and Zone classes tested
  • Game engine coverage: 0% → 20%

Blockers: None - can start immediately


Week 2: Core Engine (32 hours)

Theme: Scenes & State Synchronization

Tasks:

  • TEST-002: Test MatchScene initialization (8h)
  • TEST-004: Test Card rendering and interactions (12h)
  • TEST-005: Test StateRenderer synchronization (12h)

Deliverables:

  • MatchScene lifecycle tested
  • Card display and interactions tested
  • State sync tested (prize zone fix validated!)
  • Game engine coverage: 20% → 50%

Blockers: Requires TEST-001 (Week 1)


Week 3: Network Layer (20 hours)

Theme: WebSocket & Multiplayer Reliability

Tasks:

  • TEST-007: Test socket connection lifecycle (6h)
  • TEST-008: Test game action handling (8h)
  • TEST-009: Test game creation/joining (6h)

Deliverables:

  • WebSocket reliability tests
  • Action queueing tested
  • Game management tested
  • Socket coverage: 27% → 80%

Blockers: None - independent of game engine


Week 4: Engine Completion (21 hours)

Theme: Remaining Game Components

Tasks:

  • TEST-010: Test PreloadScene (4h)
  • TEST-011: Test HandManager drag/drop (6h)
  • TEST-012: Test DamageCounter (3h)
  • TEST-013: Test asset loader (4h)
  • TEST-014: Test ResizeManager (4h)

Deliverables:

  • All major game components tested
  • Game engine coverage: 50% → 65%
  • Overall coverage: 63% → 73%

Week 5: Integration & UI (22 hours)

Theme: Pages and Integration Testing

Tasks:

  • TEST-016: User store edge cases (3h) COMPLETE (20 tests)
  • TEST-017: Drag/drop edge cases (3h) COMPLETE (17 edge case tests added)
  • TEST-018: Deck builder edge cases (4h) COMPLETE (75 tests: DeckActionButtons, DeckHeader, DeckCardRow)
  • TEST-019: Page tests (Home, Campaign, Match) (6h) COMPLETE (44 tests: HomePage, CampaignPage, MatchPage)
  • TEST-021: Animation sequences (6h)

Deliverables:

  • All pages tested
  • Integration scenarios covered
  • Overall coverage: 73% → 78%

Week 6: Infrastructure (21 hours)

Theme: Testing Tooling & Documentation

Tasks:

  • TEST-022: Visual regression with Playwright (8h)
  • TEST-023: CI coverage reporting (3h)
  • TEST-024: Testing documentation (4h)
  • TEST-020: Socket type guards (2h)
  • TEST-015: CardBack tests (2h) (Quick win!)
  • TEST-025: Mutation testing (optional) (6h)

Deliverables:

  • Visual regression testing enabled
  • CI coverage tracking
  • Testing guide for team
  • Overall coverage: 78% → 82%+

Quick Wins (Do First!)

These tasks are simple and provide immediate value:

  1. TEST-015: CardBack (2h) - Simple component, easy test, gets game/ coverage started
  2. TEST-020: Type guards (2h) - Straightforward logic tests, improves socket reliability
  3. TEST-016: User store (3h) - Boosts coverage from 52% to 90%+

Total: 7 hours for measurable coverage improvement


Production Blockers

These must be tested before production release:

  1. TEST-005: StateRenderer - State desync bugs cause multiplayer issues
  2. TEST-007: Socket client - Connection problems make game unplayable
  3. TEST-008: useGameSocket - Lost/duplicate actions break game flow

Testing Challenges & Solutions

Challenge 1: Phaser Requires WebGL/Canvas

Problem: Phaser games need WebGL/Canvas, jsdom doesn't support it

Solutions:

  1. Mock Phaser classes - Create minimal mocks (TEST-001)
  2. Extract logic - Pull game logic out of Phaser into pure functions
  3. Visual regression - Use Playwright for rendering tests (TEST-022)
  4. Integration tests - Test via PhaserGame.vue wrapper

Example:

// ❌ Hard to test - logic inside Phaser class
class Card extends Phaser.GameObjects.Sprite {
  update() {
    if (this.health <= 0) {
      this.destroy()
      this.scene.addScore(10)
    }
  }
}

// ✅ Easy to test - logic extracted
function shouldDestroyCard(health: number): boolean {
  return health <= 0
}

class Card extends Phaser.GameObjects.Sprite {
  update() {
    if (shouldDestroyCard(this.health)) {
      this.destroy()
      this.scene.addScore(10)
    }
  }
}

Challenge 2: WebSocket State Management

Problem: Real-time behavior, connection state, race conditions

Solutions:

  1. Mock socket.io-client - Already doing this well
  2. Test state machines - Focus on state transitions
  3. Fake timers - Control async timing
  4. Error injection - Simulate disconnect/reconnect

Challenge 3: Animation Testing

Problem: Animations are async and timing-dependent

Solutions:

  1. Fake timers - Advance time manually with vi.useFakeTimers()
  2. Test completion - Verify animation completes, not individual frames
  3. Integration tests - Test animation sequences end-to-end (TEST-021)

Coverage Targets

Area Current Week 2 Week 4 Week 6 3 Months
Overall 63% 68% 73% 78% 85%
Game Engine 0% 40% 60% 65% 80%
Composables 84% 84% 86% 90% 95%
Components 90% 90% 90% 92% 95%
Stores 88% 88% 90% 92% 95%
Socket 27% 27% 80% 85% 90%

Getting Started

Build momentum with easy tasks:

# Week 1 - Quick Wins (7 hours)
npm run test -- src/game/objects/CardBack.spec.ts  # Create this
npm run test -- src/socket/types.spec.ts           # Expand this
npm run test -- src/stores/user.spec.ts            # Expand this

# Result: +5% coverage, confidence boost

Option 2: Critical Path (Production-Focused)

Tackle blockers immediately:

# Week 1-3 - Critical Path (80 hours)
# TEST-001: Phaser infrastructure (8h)
# TEST-002: MatchScene (8h)
# TEST-003: Board (10h)
# TEST-004: Card (12h)
# TEST-005: StateRenderer (12h)
# TEST-007: Socket client (6h)
# TEST-008: useGameSocket (8h)
# TEST-009: useGames (6h)

# Result: Core game and network tested, ready for production

Option 3: Parallel Development (Team of 2-3)

Split work across developers:

# Developer 1: Game Engine
- Week 1: TEST-001, TEST-003, TEST-006
- Week 2: TEST-002, TEST-004, TEST-005

# Developer 2: Network Layer
- Week 1: TEST-007, TEST-008
- Week 2: TEST-009, TEST-010, TEST-011

# Developer 3: UI & Infrastructure
- Week 1: Quick wins (TEST-015, TEST-016, TEST-020)
- Week 2: TEST-019, TEST-021
- Week 3: TEST-022, TEST-023, TEST-024

# Result: Complete in 3 weeks instead of 6

Success Metrics

Week 2 Checkpoint

  • Phaser infrastructure works well
  • Board tests validate prize zone fix
  • StateRenderer tests catch desync bugs
  • Coverage: 63% → 68%

Week 4 Checkpoint

  • All major game components tested
  • WebSocket layer reliable
  • Coverage: 68% → 73%
  • Team confident in test suite

Week 6 Completion

  • Visual regression enabled
  • CI coverage tracking
  • Testing documentation complete
  • Coverage: 73% → 78%
  • Ready for production

3 Month Goal

  • Coverage: 85%+
  • All critical code paths tested
  • Mutation testing shows strong tests
  • New features include tests by default

Team Resources

Documentation

  • TEST-024: Create src/test/README.md with testing patterns
  • Add Phaser testing examples
  • Document common pitfalls and solutions

Infrastructure

  • TEST-022: Playwright visual regression
  • TEST-023: CI coverage reporting
  • TEST-025: Stryker mutation testing (optional)

Training

  • Share knowledge as infrastructure is built
  • Code review testing PRs thoroughly
  • Pair program on first few Phaser tests

Questions & Discussion

Should we aim for 100% coverage?

No. 85% is realistic and valuable. Some code is hard to test:

  • Animation frames and tweens
  • WebGL rendering details
  • Entry points (main.ts, router config)

Focus on logic and state management, not rendering details.

Should we stop feature work to fix coverage?

Depends on priorities. Options:

  1. Aggressive: Pause features for 2-3 weeks, hit 80% coverage
  2. Balanced: Dedicate 1-2 days/week to testing, reach 80% in 8-10 weeks
  3. Maintenance: Add tests alongside features, reach 80% in 3-6 months

Recommendation: Balanced approach - TEST-001 through TEST-009 are critical for production confidence, do those first.

What if tests are too slow?

Strategies:

  • Use vi.mock() liberally - mock Phaser, socket, API
  • Use vi.useFakeTimers() - control async timing
  • Run tests in parallel: vitest --threads
  • Use it.skip() for expensive tests during development
  • Run full suite in CI only

Next Steps

  1. Review this plan - Discuss with team, adjust priorities
  2. Start with TEST-001 - Build Phaser testing infrastructure
  3. Quick win: TEST-015 - Test CardBack to validate infrastructure
  4. Tackle critical path - TEST-002 through TEST-009
  5. Track progress - Update PROJECT_PLAN_TEST_COVERAGE.json as tasks complete


Let's build confidence in our game engine! 🎮