Comprehensive testing of Board game object covering: - Zone creation and layout management (with/without prizes) - Zone highlighting (single, bulk, clear) - Coordinate queries and hit detection - Layout updates and resize handling - Cleanup and lifecycle management All tests passing (1,337 total, +55) Test coverage: - Constructor and initialization (3 tests) - setLayout() zone rendering (7 tests) - getLayout() retrieval (3 tests) - highlightZone() single zone highlighting (6 tests) - highlightAllZones() bulk highlighting (4 tests) - clearHighlights() reset (3 tests) - getZonePosition() queries (4 tests) - isPointInZone() hit detection (4 tests) - getZoneAtPoint() zone lookup (4 tests) - destroy() cleanup (5 tests) - createBoard() factory (3 tests) - Integration scenarios (4 tests) - Edge cases (5 tests) Enhanced Phaser mocks: - Added lineBetween() method to MockGraphics - Added cameras property to test setup Status: TEST-003 marked complete in project plan Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
19 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):
- Test structure first - Verify all required fields present
- Test uniqueness - IDs/tokens should be unique on each call
- Test variations - Test with/without optional parameters
- 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) ✅ COMPLETE (55 tests: constructor, setLayout, highlighting, zone queries, coordinate detection, destroy, factory, integration, edge cases)
- 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 class tested - Zone rendering, highlighting, coordinate queries for both game modes (with/without prizes)
- 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) ✅ COMPLETE (26 tests: init, create, update, shutdown, events, state updates, resize)
- TEST-004: Test Card rendering and interactions (12h)
- TEST-005: Test StateRenderer synchronization (12h)
Deliverables:
- ✅ MatchScene lifecycle tested - All scene lifecycle methods covered
- ✅ Event handling tested - Bridge communication and cleanup verified
- 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:
- TEST-015: CardBack (2h) - Simple component, easy test, gets game/ coverage started
- TEST-020: Type guards (2h) - Straightforward logic tests, improves socket reliability
- 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:
- TEST-005: StateRenderer - State desync bugs cause multiplayer issues
- TEST-007: Socket client - Connection problems make game unplayable
- 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:
- Mock Phaser classes - Create minimal mocks (TEST-001)
- Extract logic - Pull game logic out of Phaser into pure functions
- Visual regression - Use Playwright for rendering tests (TEST-022)
- 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:
- Mock socket.io-client - Already doing this well
- Test state machines - Focus on state transitions
- Fake timers - Control async timing
- Error injection - Simulate disconnect/reconnect
Challenge 3: Animation Testing
Problem: Animations are async and timing-dependent
Solutions:
- Fake timers - Advance time manually with
vi.useFakeTimers() - Test completion - Verify animation completes, not individual frames
- 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
Option 1: Quick Wins First (Recommended)
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.mdwith 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:
- Aggressive: Pause features for 2-3 weeks, hit 80% coverage
- Balanced: Dedicate 1-2 days/week to testing, reach 80% in 8-10 weeks
- 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
- Review this plan - Discuss with team, adjust priorities
- Start with TEST-001 - Build Phaser testing infrastructure
- Quick win: TEST-015 - Test CardBack to validate infrastructure
- Tackle critical path - TEST-002 through TEST-009
- Track progress - Update
PROJECT_PLAN_TEST_COVERAGE.jsonas tasks complete
Related Documents
PROJECT_PLAN_TEST_COVERAGE.json- Full structured planTESTING.md- Current testing guide (recently added!)CONTRIBUTING.md- Never use --no-verify policyVISUAL-TEST-GUIDE.md- Visual testing reference
Let's build confidence in our game engine! 🎮✅