用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/dkolba/bruff --skill write-game-tests命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | write-game-tests |
| description | Write the three required test levels for game logic — unit, property-based, and deterministic replay/snapshot |
Use when writing tests for packages/game. Three levels are required depending on what is being tested.
Use the current test harness vocabulary:
advanceGameState(state, inputs) is the pure logical step. Empty inputs returns the same state; queued input advances exactly one logical tick, applies player input first, then the tick action.frameIndex counts logical ticks, not rendered frames. Render-only stepFrames(n) calls with no queued input must not move enemies or increment frameIndex.cell and board occupancy invariants; actors must not carry xPos / yPos.createFrameStepDriver / stepFrames(n) exercise the effects-layer driver with a manualClock; they may render and advance animation time even when state does not tick.projectRenderCommands(state) is the pure foreground draw plan. It should be tested with literal GameState values and exact ReadonlyArray<RenderCommand> expectations.executeRenderCommand / executeRenderCommands are effects-layer Canvas executors. They are tested with the browser provider and a real CanvasRenderingContext2D spy, not from pure render tests.window.__bruffTestApi is browser-facing only and is tested through effects tests or Playwright, never from pure state tests.Co-locate as *.test.ts next to the source file. Run via Vitest.
Rules:
RenderCommand and RenderStats data rather than Canvas calls.import { describe, expect, it } from "vitest";
import type { GameState } from "../core/types.ts";
import updatePlayer from "./update-player.js";
describe("updatePlayer", () => {
it("moves north when input is arrowup", () => {
const state: GameState = /* … */;
expect(updatePlayer(state, { type: "move-up" })).toStrictEqual({
...state,
player: { ...state.player, cell: { column: 3, row: 2 } },
playerMoved: true,
});
});
});
Use Vitest + @fast-check/vitest.
Properties to test:
frameIndex never decreases, increments only for logical ticks with input, actors stay inside board, and no two actors occupy the same cell after valid transitions.frameIndex.import { test, fc } from "@fast-check/vitest";
import { expect } from "vitest";
test.prop([fc.integer()])(
"PRNG produces same sequence for same seed",
(seed) => {
const seq1 = runPrng(seed, 10);
const seq2 = runPrng(seed, 10);
expect(seq1).toStrictEqual(seq2);
},
);
Capture a full deterministic run and assert the final state (or a hash of it) matches a stored snapshot.
Pattern:
seed in a replay fixture.runReplay(fixture).GameState matches a committed JSON snapshot.packages/game/tests/fixtures/; snapshots live in packages/game/tests/snapshots/.import { expect, it } from "vitest";
import fixtureJson from "../../tests/fixtures/canonical-replay.json";
import snapshotJson from "../../tests/snapshots/canonical-replay.json";
import { parseReplayFixture } from "./replay-fixture.js";
import { runReplay } from "./run-replay.js";
it("produces deterministic output for fixed seed and input sequence", () => {
const fixture = parseReplayFixture(fixtureJson);
expect(fixture.type).toBe("ok");
if (fixture.type === "error") {
return;
}
expect(runReplay(fixture.value)).toStrictEqual({
type: "ok",
value: snapshotJson,
});
});
RenderCommand branch and command ordering.getState() / getRenderStats() return clones, dispatchInput() normalises raw input, and attachment is gated by __BRUFF_TEST_MODE__.await expect(locator).toHaveScreenshot("name.png") with an @snapshot test title tag; never leave raw locator.screenshot() captures unasserted. Update Arcade E2E screenshot baselines with pnpm run --filter @bruff/arcade test:e2e:update-snapshots.core/, state/, input/, or render/ tests; effects tests may use browser APIs deliberately.Math.random(), Date.now(), or raw performance.now() inside any test (seed and clock everything).