Files
whalehunting/.planning/codebase/TESTING.md
Thomas Richter 576799ae0e docs: map existing codebase
- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues
2026-02-04 23:16:04 +01:00

8.1 KiB

Testing Patterns

Analysis Date: 2026-02-04

Test Framework

Runner:

  • Vitest 4.0.16 - Unit test runner
  • Playwright 1.57.0 - E2E test runner

Config Files:

  • Unit tests: vitest.config.js
  • E2E tests: playwright.config.js

Run Commands:

npm run test              # Run unit tests (Vitest)
npm run test:ui          # Run tests with Vitest UI
npm run test:coverage    # Run tests with coverage report
npm run test:e2e         # Run E2E tests (Playwright)
npm run test:e2e:ui      # Run E2E tests with Playwright UI
npm run test:all         # Run all tests (unit + E2E)

Test File Organization

Location:

  • Unit tests: tests/unit/ directory
  • E2E tests: tests/e2e/ directory
  • Separate from source code (not co-located)

Naming:

  • Unit tests: .test.js suffix: game-logic.test.js
  • E2E tests: .spec.js suffix: game-flow.spec.js

Structure:

tests/
├── unit/
│   └── game-logic.test.js
└── e2e/
    └── game-flow.spec.js

Unit Test Structure

Framework: Vitest with globals enabled

Pattern from tests/unit/game-logic.test.js:

import { describe, it, expect } from 'vitest';

describe('Game Logic - Inventory Management', () => {
  describe('Barrel Calculations', () => {
    it('should calculate correct fuel barrel count', () => {
      const fuel = 100;
      const barrelCount = Math.ceil(fuel / 10);
      expect(barrelCount).toBe(10);
    });
  });
});

Test Organization:

  • Top-level describe() blocks group by feature/system (e.g., "Game Logic - Inventory Management")
  • Nested describe() blocks organize related tests (e.g., "Barrel Calculations")
  • Individual it() blocks test single logical assertions
  • Test names are descriptive: "should [expected behavior]"

E2E Test Structure

Framework: Playwright with page object patterns

Pattern from tests/e2e/game-flow.spec.js:

import { test, expect } from '@playwright/test';

test.describe('Whale Hunting Game - Main Flow', () => {
  test('should load the intro scene', async ({ page }) => {
    await page.goto('/');
    await page.waitForTimeout(2000);

    const canvas = page.locator('canvas');
    await expect(canvas).toBeVisible();

    await expect(page).toHaveTitle(/Whale Hunting/);
  });
});

Test Organization:

  • test.describe() blocks group related scenarios
  • Individual test() blocks test user flows
  • Setup patterns: test.beforeEach() for common navigation
  • Viewport configuration: test.use() for device-specific testing
  • Async/await pattern for all page interactions

Mocking

Framework: None configured or used

Patterns:

  • No mocks found in unit tests
  • Tests verify pure calculation logic (Math.ceil, Math.min)
  • E2E tests work with actual running game instance

What to Mock:

  • Not applicable; unit tests use pure functions without external dependencies
  • Phaser framework is not mocked (games tested in browser context)

What NOT to Mock:

  • Scene transitions (tested via canvas visibility)
  • Phaser game instances (tested via E2E)

Fixtures and Test Data

Test Data:

// Direct inline values
it('should calculate correct fuel barrel count', () => {
  const fuel = 100;
  const barrelCount = Math.ceil(fuel / 10);
  expect(barrelCount).toBe(10);
});

// Inventory object template
const inventory = { whaleOil: 0, fuel: 100, penguins: 0 };

Location:

  • No shared fixture files; test data created inline within test functions
  • Inventory object structure repeated across multiple tests

Coverage

Configuration:

// vitest.config.js
coverage: {
  provider: 'v8',
  reporter: ['text', 'html', 'lcov'],
  exclude: [
    'node_modules/',
    'dist/',
    '*.config.js',
    'tests/e2e/**'
  ]
}

View Coverage:

npm run test:coverage
# Creates HTML report in coverage/
# View: coverage/index.html

Excluded from Coverage:

  • E2E tests (tests/e2e/**)
  • Config files (*.config.js)
  • Dependencies and build output

Test Types

Unit Tests:

  • Scope: Pure calculation logic (barrel counts, fuel consumption, bounds checking)
  • Approach: Direct function calls with known inputs
  • No DOM interaction; test game logic in isolation
  • Coverage: tests/unit/game-logic.test.js
  • Examples:
    • Barrel calculation: Math.ceil(fuel / 10)
    • Inventory limits: Math.min(currentFuel + 10, maxFuel)
    • Whale health: Health deduction logic
    • Mobile detection: User agent regex testing
    • Crosshair bounds: Clamp function testing

Integration Tests:

  • Not explicitly used
  • Closest: E2E tests that verify scene transitions

E2E Tests:

  • Scope: Complete user workflows (game startup to hunting)
  • Approach: Browser automation via Playwright
  • Tests: Canvas visibility, scene transitions, button clicks, viewport scaling
  • Coverage: tests/e2e/game-flow.spec.js
  • Desktop & mobile scenarios tested with viewport configuration
  • Examples:
    • "should load the intro scene"
    • "should navigate to hunting grounds"
    • "should work on mobile viewport"
    • "should scale properly on desktop"

Common Patterns

Async Testing (E2E):

test('should start game from intro scene', async ({ page }) => {
  await page.goto('/');
  await page.waitForTimeout(2000);

  const canvas = page.locator('canvas');
  await canvas.click({ position: { x: 400, y: 400 } });

  await page.waitForTimeout(1000);
  await expect(canvas).toBeVisible();
});
  • async ({ page }) destructures Playwright fixture
  • await on page navigation and interactions
  • page.waitForTimeout() for animation/transition delays
  • page.locator() for element selection

Error Testing (Unit):

it('should not process whale without enough fuel', () => {
  let fuel = 1;
  const requiredFuel = 2;
  const canProcess = fuel >= requiredFuel;
  expect(canProcess).toBe(false);
});
  • Boolean logic to test conditions
  • No exception throwing; test state validation

Mobile Testing (E2E):

test.describe('Mobile Compatibility', () => {
  test.use({
    viewport: { width: 375, height: 667 },
    isMobile: true,
  });

  test('should work on mobile viewport', async ({ page }) => {
    await page.goto('/');
    await page.waitForTimeout(2000);

    const canvas = page.locator('canvas');
    await expect(canvas).toBeVisible();

    const canvasBox = await canvas.boundingBox();
    expect(canvasBox?.width).toBeLessThanOrEqual(375);
  });
});
  • test.use() sets viewport and device context
  • Tap interactions: canvas.tap()
  • BoundingBox assertions: verify canvas scaling

Test Isolation

Setup:

  • E2E tests use test.beforeEach() for repeated navigation setup
  • No shared state between tests
  • Each test starts fresh from home page

Teardown:

  • Playwright automatically closes browser context between tests
  • No explicit cleanup required

Configuration Details

Vitest (vitest.config.js):

  • Environment: jsdom (browser-like DOM)
  • Globals enabled: describe, it, expect available without imports
  • Include pattern: tests/unit/**/*.test.js
  • Server runs on port 51204 for Vitest UI

Playwright (playwright.config.js):

  • Projects: Chromium and mobile (iPhone 12)
  • Base URL: http://localhost:5173 (dev server)
  • Test directory: tests/e2e/
  • Retries: 2 in CI, 0 locally
  • Screenshots: Only on failure
  • Trace: On first retry
  • Server: Runs npm run dev before tests

Test Execution Flow

  1. Unit Tests (Vitest):

    • Runs isolated game logic tests
    • Uses jsdom environment
    • No Phaser instance needed
    • Fast execution
  2. E2E Tests (Playwright):

    • Starts dev server via npm run dev
    • Launches Chromium and mobile browsers
    • Navigates to http://localhost:5173
    • Performs user interactions
    • Verifies game state via canvas visibility
    • Slower execution but tests full stack

Coverage Gaps

Not Tested (Unit):

  • Scene rendering and graphics creation
  • Phaser animation timing and tweens
  • Player input handling (mouse/touch)
  • Collision detection logic

Not Tested (E2E):

  • Detailed game mechanics (whale health, harpoon trajectory)
  • Inventory state persistence
  • Exact positioning of game elements
  • Performance under load

Testing analysis: 2026-02-04