Cheats & testing
pixi-reels is built so you can test mechanics, not canvases. Two tools make that possible:
createTestReelSet. a headless reel set wired to aFakeTicker. No renderer, no DOM.CheatEngine(in the private@pixi-reels/cheatspackage). a pluggable outcome source that forces grids, scatters, lines, cascades on demand.
Together they let you write assertions like “this mechanic produces exactly
3 scatters, triggers anticipation on reel 5, and emits spin:complete.”
1. Headless reel set#
import { createTestReelSet, expectGrid } from 'pixi-reels/testing';
const { reelSet, spinAndLand, destroy } = createTestReelSet({
reels: 5,
visibleCells: 3,
symbolIds: ['cherry', 'seven', 'wild', 'scatter'],
});
await spinAndLand([
{ visible: ['wild','wild','wild'] },
{ visible: ['wild','wild','wild'] },
{ visible: ['wild','wild','wild'] },
{ visible: ['wild','wild','wild'] },
{ visible: ['wild','wild','wild'] },
]);
expectGrid(reelSet, /* same 5×3 grid of 'wild' */);
destroy();
spinAndLand() calls spin() → setResult() → slamStop(), which
bypasses all timing and resolves on a microtask. Perfect for CI.
(slamStop() lands every reel outright; it does not go through the
two-stage skipSpin() boost.)
2. Force outcomes with cheats#
Cheats are pure functions that take a context and return a grid.
import { CheatEngine, forceScatters, forceLine, holdAndWinProgress } from '@pixi-reels/cheats';
const engine = new CheatEngine({
reelCount: 5, visibleCells: 3,
symbolIds: ['a', 'b', 'scatter', 'coin'],
seed: 42,
});
engine.register({
id: 'scat3',
label: 'Force 3 scatters',
enabled: true,
cheat: forceScatters(3, 'scatter'),
});
const { symbols, anticipationReels } = engine.next();
await spinAndLand(symbols.map((visible) => ({ visible })));
Built-in cheats#
| Cheat | Effect |
|---|---|
forceGrid(grid) | Every spin returns this exact grid. |
forceLine(cellIndex, id) | Fills one cell index across every reel with id. |
forceScatters(n, id) | Sprinkles exactly n of id at random cells. |
forceNearMiss(n, id, nearReel) | n-1 scatters, none on nearReel. Triggers anticipation there. |
forceCell(reel, cell, id) | Places id at [reel, cell]. |
holdAndWinProgress(coinId, p) | Keeps held coins, adds one new coin with probability p. |
cascadeSequence([g1, g2, ...]) | Emits grids in order. |
forceAnticipation(reels) | Sets anticipation reels without constraining symbols. |
Write your own#
A cheat is (ctx) => { symbols, anticipationReels, meta } | null. Returning
null means “pass through, let the next cheat or the RNG handle it.”
const forcePairWild: Cheat = (ctx) => {
const grid = /* random grid */;
grid[2][1] = 'wild';
grid[3][1] = 'wild';
return { symbols: grid, anticipationReels: [] };
};
3. Assert events fired correctly#
import { captureEvents } from 'pixi-reels/testing';
const log = captureEvents(reelSet, ['spin:start', 'spin:complete']);
await spinAndLand(grid); // grid is ColumnTarget[]
expect(log.map((e) => e.event)).toEqual(['spin:start', 'spin:complete']);
4. See a mechanic running#
The standalone demo apps left this repo in 2.0 and live in a separate one. The live, replayable mechanics are the recipes on this site. each page runs the exact source it shows, so you can read the wiring and watch it land.