- 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
315 lines
8.1 KiB
Markdown
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*
|