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

315 lines
8.1 KiB
Markdown

# 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:**
```bash
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`:**
```javascript
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`:**
```javascript
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:**
```javascript
// 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:**
```javascript
// vitest.config.js
coverage: {
provider: 'v8',
reporter: ['text', 'html', 'lcov'],
exclude: [
'node_modules/',
'dist/',
'*.config.js',
'tests/e2e/**'
]
}
```
**View Coverage:**
```bash
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):**
```javascript
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):**
```javascript
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):**
```javascript
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*