Skip to main content

phaser-game-testing

Test Phaser games and canvas/WebGL applications with deterministic automation. Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y for Phaser 3 games. Use agent-browser CLI for browser automation, Vitest/Jest/RTL, flaky test triage, CI stabilization, and Phaser games needing deterministic input plus screenshot/state assertions. Trigger: "test phaser game", "phaser testing", "game testing", "canvas testing", "webgl testing", "test", "E2E", "flaky", "visual regression", "Playwright".

Source facts

Repository
pmarashian/cursor-agent-skills
Last source activity
February 13, 2026 at 19:51
Detected SKILL.md language
English
Stars
2
Forks
1

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
9 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
phaser-game-testing
description
Test Phaser games and canvas/WebGL applications with deterministic automation. Plan, implement, and debug frontend tests: unit/integration/E2E/visual/a11y for Phaser 3 games. Use agent-browser CLI for browser automation, Vitest/Jest/RTL, flaky test triage, CI stabilization, and Phaser games needing deterministic input plus screenshot/state assertions. Trigger: "test phaser game", "phaser testing", "game testing", "canvas testing", "webgl testing", "test", "E2E", "flaky", "visual regression", "Playwright".
# Phaser Game Testing Test Phaser 3 games reliably: enable safe refactors by choosing the right test layer, making canvas/WebGL games observable, and eliminating nondeterminism so failures are actionable. ## Philosophy: Confidence Per Minute Frontend tests fail for two reasons: the product is broken, or the test is lying. Your job is to maximize signal and minimize "test is lying". **Before writing a test, ask**: - What user risk am I covering (money, progression, auth, data loss, crashes)? - What's the narrowest layer that catches this bug class (pure logic vs UI vs full browser)? - What nondeterminism exists (time, RNG, async loading, network, animations, fonts, GPU)? - What "ready" signal can I wait on besides `setTimeout`? - What should a failure print/screenshot so it's diagnosable in CI? **Core principles**: 1. **Test the contract, not the implementation**: assert stable user-meaningful outcomes and public seams. 2. **Prefer determinism over retries**: make time/RNG/network controllable; remove flake at the source. 3. **Observe like a debugger**: console errors, network failures, screenshots, and state dumps on failure. 4. **One critical flow first**: a reliable smoke test beats 50 flaky tests. ## Unit Testing for Pure Logic **Decision Tree**: Is this pure logic? → Use unit tests, not browser automation. For pure logic utilities (maze generation, score sorting, storage, math algorithms), use **Vitest** for fast, deterministic unit tests. Reserve browser automation (agent-browser) for integration contracts and UI flows. ### What Should Be Unit Tested - ✅ **Maze generation algorithms** - Deterministic with seeded RNG - ✅ **Score sorting/validation** - Pure data transformations - ✅ **Storage utilities** - localStorage wrappers, serialization - ✅ **Math/algorithm utilities** - Pathfinding, damage calculations - ✅ **State management logic** - Reducers, state machines - ✅ **RNG utilities** - Seeded random number generators ### Vitest Setup Pattern ```bash npm install -D vitest @vitest/ui ``` ```typescript // vitest.config.ts import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, environment: 'node', }, }); ``` ```json // package.json { "scripts": { "test": "vitest", "test:ui": "vitest --ui" } } ``` ### Workflow: Logic Changes 1. **Write unit test first** → Verify logic in isolation 2. **Run tests** → `npm test` 3. **Test in browser if needed** → Only for integration verification ### Anti-Pattern: Browser Automation for Pure Functions ❌ **Wrong**: Using agent-browser to test a maze generation function ```bash agent-browser eval "generateMaze(10, 10, 42)" ``` ✅ **Correct**: Unit test with Vitest ```typescript import { describe, it, expect } from 'vitest'; import { generateMaze } from './maze'; describe('generateMaze', () => { it('generates same maze with same seed', () => { const maze1 = generateMaze(10, 10, 42); const maze2 = generateMaze(10, 10, 42); expect(maze1).toEqual(maze2); }); }); ``` See `references/unit-testing-setup.md` for complete Vitest configuration and examples. ## Test Layer Decision Tree Pick the cheapest layer that provides needed confidence: | Layer | Speed | Use For | | ------------- | ----------- | --------------------------------------------------------------------------------- | | **Unit** | Fastest | Pure functions, reducers, validators, math, pathfinding, deterministic simulation | | **Component** | Medium | UI behavior with mocked IO (React Testing Library, Vue Testing Library) | | **E2E** | Slowest | Critical user flows across routing, storage, real bundling/runtime | | **Visual** | Specialized | Layout/pixel regressions; for canvas/WebGL, only after locking determinism | ## Quick Start: First Smoke Test 1. **Define 1 critical flow**: "page loads → user can start → one key action works" 2. **Add a test seam** to the app (see below) 3. **Choose runner**: agent-browser CLI for E2E, unit tests for logic 4. **Fail loudly**: treat console errors and failed requests as test failures 5. **Stabilize**: seed RNG, freeze time, fix viewport, disable animations ## Concrete agent-browser Workflow: Testing a Game Step-by-step sequence for testing a Phaser/canvas game: **Important**: For Phaser games, **skip `snapshot -i`** and use `window.__TEST__` directly. ``` 1. agent-browser open http://localhost:3000?test=1&seed=42 2. agent-browser eval "new Promise(r => { const c = () => window.__TEST__?.ready ? r(true) : setTimeout(c, 100); c(); })" (Wait for game ready) 3. agent-browser errors (Fail if any errors) 4. agent-browser eval "window.__TEST__.commands.goToScene('GameScene')" (Use test seam commands instead of DOM clicks) 5. agent-browser eval "window.__TEST__.gameState()" (Assert game state is correct) 6. agent-browser press ArrowRight (Or WASD for movement) 7. agent-browser eval "window.__TEST__.gameState().player.x" (Verify movement happened) 8. agent-browser screenshot gameplay-state.png (Visual evidence after deterministic setup) ``` **Note**: For DOM-based UI (menus, buttons), you may still use `snapshot -i` and `click @e1`, but prefer test seam commands when available. ## Standardized Test Seam Pattern **Important**: Agents should **skip `snapshot -i`** for Phaser games and go directly to `window.__TEST__`. The test seam provides all necessary access without DOM inspection. ### TestManager Singleton Pattern Use a centralized TestManager singleton for consistent test seam management across all scenes: ```javascript // utils/TestManager.js class TestManager { constructor() { this.ready = false; this.seed = null; this.sceneKey = null; this.scenes = new Map(); // sceneKey -> scene instance } registerScene(sceneKey, sceneInstance) { this.scenes.set(sceneKey, sceneInstance); this.sceneKey = sceneKey; } getCurrentScene() { return this.scenes.get(this.sceneKey); } gameState() { const scene = this.getCurrentScene(); if (!scene) return { scene: null }; return { scene: this.sceneKey, score: scene.gameState?.score || 0, timer: scene.gameState?.timer || 0, inventory: scene.gameState?.inventory || [], // Add scene-specific state via scene.getTestState() ...(scene.getTestState ? scene.getTestState() : {}) }; } commands = { clickStartGame: () => { const scene = this.getCurrentScene(); scene?.startGame?.(); }, collectCoin: (x, y) => { const scene = this.getCurrentScene(); scene?.collectCoin?.(x, y); }, goToScene: (key, data) => { const game = this.getCurrentScene()?.scene?.game; if (game) { game.scene.start(key, data); } } }; } // Initialize singleton window.__TEST__ = new TestManager(); ``` ### BaseScene Pattern Create a BaseScene class that automatically registers with TestManager: ```javascript // scenes/BaseScene.js export class BaseScene extends Phaser.Scene { constructor(config) { super(config); } create() { // Auto-register with TestManager if (window.__TEST__) { window.__TEST__.registerScene(this.scene.key, this); } // Scene-specific initialization this.initScene(); } // Override in subclasses initScene() {} // Override to provide scene-specific test state getTestState() { return {}; } } ``` ### Consistent `window.__TEST__` Structure All scenes should expose the same structure: ```javascript window.__TEST__ = { sceneKey: null, // Current active scene (managed by TestManager) ready: false, // true after first interactive frame seed: null, // current RNG seed gameState: () => ({ // JSON-serializable snapshot scene: window.__TEST__.sceneKey, score: gameState.score, timer: gameState.timer, inventory: gameState.inventory, // Scene-specific state added via getTestState() }), commands: { // Functional triggers (not raw Phaser API) clickStartGame: () => {}, collectCoin: (x, y) => {}, goToScene: (key, data) => {}, reset: () => {}, seed: (n) => {}, } }; ``` ### Anti-Pattern: Don't Implement `__TEST__` Individually ❌ **Wrong**: Each scene implements its own `window.__TEST__` structure ```javascript // In GameScene.js window.__TEST__ = { /* GameScene-specific */ }; // In MenuScene.js window.__TEST__ = { /* MenuScene-specific */ }; ``` ✅ **Correct**: Use TestManager + BaseScene for consistent structure ```javascript // All scenes extend BaseScene class GameScene extends BaseScene { getTestState() { return { player: { x: this.player.x, y: this.player.y } }; } } ``` ### Agent Workflow with Standardized Seams 1. **Skip DOM snapshots**: `agent-browser snapshot -i` is unnecessary 2. **Go directly to test seam**: `agent-browser eval "window.__TEST__.ready"` 3. **Use commands**: `agent-browser eval "window.__TEST__.commands.goToScene('GameScene')"` 4. **Check state**: `agent-browser eval "window.__TEST__.gameState()"` See `references/test-seam-standardization.md` for complete implementation guide. ## Recommended Test Seams (Legacy Pattern) For projects not yet using TestManager, the basic pattern still works: ```javascript window.__TEST__ = { ready: false, // true after first interactive frame seed: null, // current RNG seed sceneKey: null, // current scene/route state: () => ({ // JSON-serializable snapshot scene: this.sceneKey, player: { x, y, hp }, score: gameState.score, entities: entities.map((e) => ({ id: e.id, type: e.type, x: e.x, y: e.y })), }), commands: { // optional mutation commands reset: () => {}, seed: (n) => {}, skipIntro: () => {}, }, }; ``` **Rule**: Expose IDs + essential fields, not raw Phaser/engine objects. **Note**: Prefer the TestManager pattern above for new projects. ## Anti-Patterns to Avoid ❌ **Testing the wrong layer**: E2E tests for pure logic _Why tempting_: "Let's just test everything through the browser" _Better_: Unit tests for logic; reserve E2E for integration contracts ❌ **Testing implementation details**: Asserting DOM structure/classnames _Why tempting_: Easy to assert what you can see in DevTools _Better_: Assert user-meaningful outputs (text, score, HP changes) ❌ **Sleep-driven tests**: `wait 2s then click` _Why tempting_: Simple and "works on my machine" _Better_: Wait on explicit readiness (DOM marker, `window.__TEST__.ready`) ❌ **Uncontrolled randomness**: RNG/time in assertions _Why tempting_: "The game uses random, so the test should too" _Better_: Seed RNG (`?seed=42`), freeze time, assert stable invariants ❌ **Pixel snapshots without determinism**: Canvas screenshots that flake _Why tempting_: "I'll catch visual bugs automatically" _Better_: Deterministic mode first; then screenshot at known stable frames ❌ **Retries as a strategy**: "Just bump retries to 3" _Why tempting_: Quick fix that makes CI green _Better_: Fix the flake source; retries hide real problems ## Debugging Failed Tests When a test fails, gather evidence in this order: 1. **Console errors**: `agent-browser errors` or `agent-browser console` 2. **Network failures**: `agent-browser network requests` → check for non-2xx 3. **Screenshot**: `agent-browser screenshot failure-state.png` → visual state at failure 4. **App state**: `agent-browser eval "window.__TEST__.state()"`
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub