본문으로 건너뛰기
cccskills
무료GitHub에 공개

pixi-vn-testing

Use when an AI agent (or any external script) needs to play-test a running Pixi'VN game end-to-end in a real browser — starting the game, advancing/branching the story, answering input prompts, going back, and reading/writing storage — via `Game.testing`, the opt-in devtools bridge exposed on `window`. Load this before wiring up automated/agent-driven playtesting, not for authoring story content itself (see `pixi-vn-narration`).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md15.8 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Pixi'VN Testing module

When to use this skill

Use this skill when the task is to play the game from the outside — driving it like a player would, but from a script or an AI agent evaluating JavaScript in the page, instead of clicking UI. Typical asks this covers: "start the game and play through label X", "verify choice B leads to ending Y", "check that this bug repros after going back three steps", "seed storage with gold: 100 and confirm the shop unlocks", "run this game headless as a regression test".

This assumes the game's dev server (or a build served on a URL) and a browser are already running/reachable — starting either one is outside this skill; it only covers the game-control API itself. Do not use this skill for writing story content (pixi-vn-narration), save-file persistence (pixi-vn-saves), or the go-back UI/internals (pixi-vn-history) — those are the modules this skill drives, not what it teaches.

1. Why you can't just call narration.continue({}) from the console

narration.continue, .call, .jump, narration.choices.select, and stepHistory.back all take a props argument typed as StepLabelProps — the exact object a real game passes from its UI layer. Most real projects augment StepLabelProps (via TS module augmentation, see pixi-vn) with app-specific fields: a router navigate, an i18n t, a toast, etc. (the official React template does exactly this — see pixi-vn-narration section 9). If a step calls props.navigate(...) or props.t(...), passing {} from a console/script call throws immediately.

Game.testing solves this once: the app pushes its real, live props to it (via setProps, see below) wherever it already builds them, and every action it exposes merges those in before delegating — so agent-driven calls behave exactly like a real player action, with no need to reconstruct or fake the app's props shape.

2. Enabling it

Game.testing is opt-in and disabled by default, and nothing about it depends on any particular bundler (Vite, Webpack, or none at all). Two independent pieces make it work:

  • Turning the window bridge on/off — Game.testing.enable(options?) / .disable().
  • Keeping it supplied with live props — Game.testing.setProps(props).

Turning it on/off

If the project uses @drincs/pixi-vn/vite's vitePluginPixivn, this is already handled: its testing option defaults to true, auto-enabling Game.testing for as long as vite dev keeps running (by injecting a small module into index.html) — never during vite build, regardless of the option. No app code needed for this part:

// vite.config.ts — this is already the default, shown here just to make it explicit
vitePluginPixivn({ testing: true }); // or { testing: { windowKey: "myGameTesting" } }, or false to opt out

Pass testing: false if you don't want this — e.g. a dev server shared with other people that you don't want remote-controllable.

Without the Vite plugin (a different bundler, or no bundler-level integration at all), call it yourself, gated behind your own dev-only check:

import { Game } from "@drincs/pixi-vn";

if (import.meta.env.DEV) {
  // or process.env.NODE_ENV !== "production", a Webpack DefinePlugin flag, etc.
  Game.testing.enable();
}
  • options.windowKey (default "pixiVN") — the property name the API is attached under on window.
  • Returns the same API object it attaches to window[windowKey], in case you want to keep a direct reference instead of going through window.
  • Calling enable again (e.g. on hot reload) replaces the previous session instead of stacking.
  • Game.testing.disable() tears it down (detaches from window, stops capturing errors).
  • Game.testing.isEnabled() reports whether a session is currently active.

Only enable this when you actually want the game remote-controllable. It hands out full control over game state (see §4) to anything that can run JavaScript in that page — treat it like any other devtools/debug backdoor.

Keeping it supplied with live props: setProps

Call Game.testing.setProps(props) unconditionally, wherever your app already builds its StepLabelProps — it's a cheap assignment, safe to call whether or not testing happens to be enabled right now, and every Game.testing action reads through whatever was passed here most recently:

import { Game, type StepLabelProps } from "@drincs/pixi-vn";

function useGameProps(): StepLabelProps {
  // ... build props the same way your app always has ...
  const props = { navigate, t, toast /* ... */ };
  Game.testing.setProps(props); // [!code focus]
  return props;
}

React template convention

The official React template's useGameProps() (src/lib/hooks/props-hooks.ts — see pixi-vn-narration section 9) already ends with exactly that Game.testing.setProps(props) call, and vite.config.ts's vitePluginPixivn(...) call needs no extra option — testing defaults to true. Since useGameProps() already runs on every render of every component that needs game props, this is the only line the template needs: no dedicated bridge component, no manual enable() call.

Routing through the app's own narration functions: setActions

Most real apps don't call narration.continue/stepHistory.back/etc. straight from a "next" button — they wrap them in their own functions that also handle UI-only concerns a test session should trigger too: a loading indicator, refusing to advance while a menu/dialog is open, refreshing cached interface data after the step resolves, and so on. By default Game.testing's actions skip all of that and call narration/stepHistory directly, so an agent driving the game through window.pixiVN can get out of sync with what the real UI would have done (e.g. it never sees the loading state, or advances past a guard the real "next" button respects).

Game.testing.setActions(actions) fixes this: register the app's own functions once, and continue()/goBack()/selectChoice()/start()/jump()/call() call them instead. Any action left out keeps calling narration/stepHistory directly, so this is opt-in per action:

import { Game } from "@drincs/pixi-vn";

Game.testing.setActions({
  continue: () => goNext(),
  back: () => goBack(),
  selectChoice: (item) => selectChoice(item),
  start: (label, props) => async () => {
    await props.navigate("/game");
    return await startNewGame(label, props);
  },
  jump: (label, props) => jump(label, props),
  call: (label, props) => call(label, props),
});

Mount that hook once near the app root (same idea as useGameProps() always calling setProps) and window.pixiVN.continue() now runs the exact same code path as a player clicking "next".

A few things follow from continue/back taking no arguments themselves:

  • The extraProps/options a caller passes to window.pixiVN.continue(...) / window.pixiVN.goBack(...) are ignored once an override is registered — the override manages its own props (typically via setProps) and usually has no notion of { steps, runNow }. Drive those through window.pixiVN.props/narration/stepHistory directly if you need that level of control.
  • selectChoice's override receives the already-resolved choice item (the same shape as getState().choices[i]), not the raw choiceIndex — the lookup (and the "no open choice with that index" error) still happens before the override runs.
  • setActions is safe to call whether or not testing is enabled yet, safe to call again to update the registered functions, and persists across disable()/enable() — same as setProps.

3. Driving the game

Every action below reads through window.pixiVN (or your custom windowKey), evaluated in the page — e.g. via a browser automation tool's "evaluate script" capability, or by pasting into the devtools console by hand. If the Chrome DevTools MCP server is available, prefer it over any other browser automation for this: evaluate_script runs the snippets below directly in the page, navigate_page/ click/take_snapshot drive UI the game itself doesn't expose through window.pixiVN (e.g. a login screen), take_screenshot lets you actually look at the canvas instead of only trusting getState(), and list_console_messages surfaces errors/warnings a script-only check would miss — checking console output after every meaningful step, not just at the end, has caught real bugs (e.g. a framework deprecation warning) that a headless script relying only on errors/return values missed. Fall back to a Playwright/chromium-cli script, or another MCP browser tool, only when Chrome DevTools MCP isn't available. All of the actions below are async except setInput, closeCurrentLabel/ closeAllLabels, and getState.

// Start the game from a label (id or Label object). Clears all game data first, like a real "New Game".
await window.pixiVN.start("start");

// Advance one step (same guard a "next" button uses — see canContinue in getState() below).
await window.pixiVN.continue();

// Call a sub-label (returns to the caller when it runs out of steps) / jump to another label (no return).
await window.pixiVN.call("some_label");
await window.pixiVN.jump("some_label");

// Pick an open choice by its choiceIndex (see getState().choices below for what's available).
await window.pixiVN.selectChoice(0);

// Answer a pending input prompt, then continue like the player pressing confirm would.
window.pixiVN.setInput("Liam");
await window.pixiVN.continue();

// Go back one (or more) steps.
await window.pixiVN.goBack();
await window.pixiVN.goBack({}, { steps: 3 });

// Close the current label / close every open label (can end the game — see pixi-vn-narration).
window.pixiVN.closeCurrentLabel();
window.pixiVN.closeAllLabels();

Every action accepts an optional second extraProps argument (first argument for selectChoice's props is its second argument, continue/goBack's is first) that's shallow-merged on top of the live props for that one call — useful to override just one field (e.g. a fake navigate to observe where the game tried to go) without touching the app's real wiring:

await window.pixiVN.continue({
  navigate: (to) => console.log("would navigate to", to),
});

Reading state before deciding the next action

Don't act blindly — read getState() first to see what's actually on screen and what actions are valid right now:

window.pixiVN.getState();
// {
//   dialogue: { text: "What do you choose?", character: {...} } | undefined,
//   dialogueGlue: false,
//   choices: [{ text: "A", choiceIndex: 0, ... }, { text: "Close", choiceIndex: 1, ... }] | undefined,
//   input: { isRequired: false, type: undefined, value: undefined },
//   canContinue: true,
//   canGoBack: true,
//   labelsOpened: [...],       // narration.labels.opened
//   currentLabelId: "choice_label",
//   stepCounter: 12,
// }
  • If choices is set, call selectChoice(choiceIndex) — don't call continue(), it won't advance past an open choice menu.
  • If input.isRequired is true, call setInput(value) before the next continue().
  • If canContinue is false for any other reason, a step is mid-flight (e.g. an async transition) — wait and re-check rather than firing another action immediately.

Driving the app's UI directly, not just narration

window.pixiVN.props is the same live object setProps was last called with — whatever your app's StepLabelProps augmentation defines. Most projects put a navigate function there (see pixi-vn and the wiki's "Navigate/switch between UI screens" page) so labels can switch screens; that same function is reachable here for full control over what's on screen, not just the story — e.g. opening a settings screen, jumping to a main menu, or checking where a bugged label tried to send the player, all without going through a choice/label:

window.pixiVN.props.navigate("/settings");

Anything else the app's StepLabelProps happens to expose (a toast, a t translator, ...) is reachable the same way. If your project doesn't augment StepLabelProps with a navigation function at all, this simply isn't available — driving screen navigation is then whatever mechanism the app itself uses outside of Pixi'VN (e.g. calling the router directly from the evaluated script).

4. Full state control

Beyond the guided actions above, the API also exposes the underlying singletons directly, for anything not covered by a dedicated method — most commonly storage, to set up a scenario without replaying the whole story to reach it, or to assert on state a dialogue doesn't surface:

// Arrange: seed state before starting/continuing.
window.pixiVN.storage.set("gold", 100);
window.pixiVN.storage.flags.set("met_liam", true);

// Assert: read state after driving the story forward.
window.pixiVN.storage.get("gold"); // 100

// Reset between test scenarios.
window.pixiVN.storage.clear(); // back to storage.default
window.pixiVN.Game.clear(); // wipes storage + narration + history + canvas + sound

// Snapshot / restore full game state instead of replaying steps to get back to a scenario.
const snapshot = window.pixiVN.Game.exportGameState();
// ... drive the game further, try something risky ...
await window.pixiVN.Game.restoreGameState(snapshot);

window.pixiVN.narration and window.pixiVN.stepHistory are the same narration/stepHistory singletons documented in pixi-vn-narration and pixi-vn-history — anything those skills document (narration.dialogue, narration.labels.current, stepHistory.narrativeHistory, ...) is reachable the same way here, read-only or not.

5. Catching errors during an automated run

An automated/agent-driven session can't "notice" a red error toast the way a human tester would. Game.testing.enable registers its own Game.addOnError handler for as long as it's active, so errors raised anywhere in the game (including inside steps) are captured instead of only surfacing in the UI:

window.pixiVN.errors;
// [{ error: Error("..."), timestamp: 1730000000000 }, ...]

window.pixiVN.clearErrors(); // e.g. between test cases, so failures don't bleed across scenarios

This is in addition to, not a replacement for, whatever Game.addOnError handlers the app itself registers (e.g. drawCanvasErrorHandler()) — both run.

Related skills

pixi-vn, pixi-vn-narration, pixi-vn-storage, pixi-vn-history, pixi-vn-saves

Content discovery and optional screen navigation

window.pixiVN.getLabelIds() returns every registered label ID, including labels not currently open. getAssetIds() returns registered PixiJS resolver aliases, including unloaded assets from the manifest; loading an asset is not required. Aliases are resolver keys and can include bundle-qualified keys. This read uses PixiJS resolver bookkeeping because PixiJS has no public enumeration API. Current choices remain available in getState().choices.

Applications can opt into router-independent screen navigation:

Game.testing.setActions({
  navigate: async (screen) => {
    await navigate({ to: screen });
    await queryClient.invalidateQueries();
  },
  // Include the application's other action overrides here too.
});

setActions replaces the full registration. When navigation is registered, await window.pixiVN.navigate("/game/map") waits for that callback. Otherwise navigate is undefined. No router-specific behavior is implemented by Pixi'VN.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

pixi-vn

무료

Index and entry point of the Pixi'VN skill set — start here for any task on a @drincs/pixi-vn project, then follow the skill map to the right skill (narration, canvas, characters, assets, sound, storage, saves, history, ui, minigames, testing, migration). Use when setting up a new or existing project, wiring the main.ts entry point, or calling the top-level Game API (Game.init, Game.start, Game.onEnd, Game.addOnError, Game.onNavigate, Game.clear) — every Pixi'VN project needs this before touching canvas, narration, sound, storage, or history.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

Use when adding, organizing, registering, or loading non-code files (images, audio, video) in a Pixi'VN project — local assets processed by PixiJS AssetPack vs. online/remote-hosted assets, the AssetsManifest/bundle/alias system, where to edit the manifest for each case, and when to load bundles (at startup, in the background, per-label, per-route). Load this before writing any code that references an image/sound/video path directly, or before adding entries to a manifest.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

Use when adding, moving, or removing images, sprites, text, or video on the Pixi'VN game canvas, or when applying or creating transitions (dissolve, fade, move, zoom, push, wipe, iris, split, flash, blur, pixelate, or a custom one built on `canvas.animate`/`filters.animate`), shake/animation effects, or ticker-based animations built on PixiJS. Covers the `canvas` singleton exported from `@drincs/pixi-vn`. For UI layers (HTML or PixiJS) mounted on top of the canvas, see `pixi-vn-ui` instead.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

Use when defining visual novel characters for a @drincs/pixi-vn game — creating a character with a name, color, icon and other props, registering it so it can be looked up by id, and referencing it from dialogue/narration. Load this before writing any `CharacterBaseModel` or `RegisteredCharacters` code.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

Use when implementing "go back" / "rewind" / step-navigation features in a Pixi'VN game, or when you need to read the player-facing dialogue/choice history (e.g. for a backlog or "history log" UI) — covers the History module's `stepHistory` API and the "step" vs "paragraph" goBackMode.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

Use when upgrading an existing @drincs/pixi-vn project to a newer version, or when a runtime/type error matches a known breaking change. Covers every breaking change between versions with before/after code.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

DRincs-Productions/pixi-vn1542026년 10월 22일 업데이트

DRincs-Productions의 스킬 모두 보기

이 스킬의 문제 신고