proves the code
compiles. It says nothing about whether the game
runs. Every failure below type-checks perfectly and still ships a broken game:
The harness starts the dev server itself, drives headless Chromium, and shuts the
server down again. No config, no test files required.
The deep checks need the game instance. Bundled games keep it in module scope, so
expose it in dev builds only:
Without it the harness still catches black screens, exceptions, and 404s, but skips
scene, FPS, and state assertions — and says so. Add the line; it costs nothing in
production. See
references/instrumenting-games.md
for deterministic-testing hooks
(seeded RNG, time control, state injection).
A scenario drives input and asserts on live game state.
is bound to the
running instance inside every
.
javascript
// playtest/combat.mjs
export default [
{ name: 'reaches gameplay', action: 'expect',
expect: { expression: `game.scene.isActive('GameScene')`, equals: true } },
{ name: 'walk right', action: 'key', key: 'ArrowRight', duration: 600 },
{ name: 'player advanced', action: 'expect',
expect: { expression: `game.scene.getScene('GameScene').player.x > 400`, equals: true } },
{ name: 'attack lands', action: 'press', key: 'Space',
expect: { expression: `game.registry.get('enemyHp')`, atMost: 90 } },
{ name: 'after-combat', action: 'screenshot' },
{ name: 'holds frame rate', action: 'expect',
expect: { expression: `game.loop.actualFps`, atLeast: 55 } },
];
Acceptance criteria from
or the phaser-architect agent translate
directly into scenario steps. Write the scenario
when the feature is specified,
not after it breaks:
Copy
into the project's own
so CI does not depend on the
plugin being installed.