ブラウザゲームの手動セーブを確認するとき、保存成功の表示だけでは、再読み込み後に元のデータを復元できたか分かりません。保存スロットのJSONと、ロード後の画面の値を一緒に比べる検査を作りました。
今回はTeristorialの2026年10月14日の公開ファイルをローカルに固定し、App Crash Lab 0.4.1で確認しています。通常の保存とロードはPASS。復元前に同じスロットを意図的に上書きする比較用の操作ではFAILになりました。ゲームの不具合を発見したという結果ではありません。
保存スロットと復元後の表示を比べる
使った流れは、Arden Reachを開始し、ゲームを一時停止してSlot 1へ保存、ページを再読み込み、Slot 1を明示的にロードする、というものです。
保存されたJSONには92地区分のレコードがあり、この開始状態で所有する地区は12です。検査ではスロットのJSON全体に加えて、領土名、所有地区数、一時停止の表示、画面の金と穀物を観測します。ロード完了の表示も待ってから比較します。
保存データだけが残っていても、ロード処理や表示への反映が壊れている可能性があります。逆に、見えている2つの数字が同じでも、他の保存項目が変わっている可能性があります。それぞれを比較対象にしました。
実測結果
2026年10月14日、macOS arm64、Node 22.16.0、Playwright 1.64.0での運営側の実行結果です。
| 操作 | 結果 | 主な比較値 |
|---|---|---|
| 通常の保存、再読み込み、明示ロード | PASS、差分0 | 金240→240、穀物180→180、保存スロットのJSONが一致 |
| 復元前にSlot 1を意図的に上書きしてからロード | FAIL、差分359 | 金242→251、保存時刻や地区の値も変化 |
| 通常の検査から生成したPlaywright単独テスト | 1 passed | App Crash Labをimportせず同じ流れを実行 |
通常と上書きの行は別の実行です。リアルタイムのゲームなので、保存した瞬間の金や経過時間は実行ごとに変わります。毎回240を期待するのではなく、その実行で保存した値と、ロード後の値を比べます。
画像はこの実測レポートです。
上書きの比較は、テストが変化を検出できるかを確かめる負例です。正常な操作でデータが消えた、という意味にはしません。
手元で再現する
実行用ZIPとSHA256を公開しています。ZIPのSHA256は次の値です。
1e3fb0b089f76a5f6e8ac3a1293073787c85875dca90f2959e6d63417ebbb30a
Python 3、Node 22以上、npmを用意します。ZIPを展開し、teristorialディレクトリで1つ目のターミナルから実行してください。
python3 prepare-local.py --out site
python3 -m http.server 54263 --bind 127.0.0.1 --directory site
helperは公式サイトの17ファイルを取得し、SOURCE.jsonのファイル長とSHA256を照合します。既存の出力ディレクトリは上書きしません。公式ファイルが更新されて照合に失敗した場合は、保存データの判定まで進んでいません。新しい版のfixtureを用意してから検査します。
サーバーを動かしたまま、2つ目のターミナルを同じteristorialディレクトリで開きます。
npx --yes --package=@playwright/test@1.64.0 playwright install chromium
npx --yes --package=https://github.com/aichance/business-ai-recipes/releases/download/app-crash-lab-v0.4.1/aichance-app-crash-lab-0.4.1.tgz app-crash-lab run contract.json --out .crash-lab/teristorial-first
出力された絶対パスのindex.htmlを開くと、保存時と検査後の値、差分、ブラウザtraceを確認できます。通常の検査の終了コードは0です。再実行時は別の出力ディレクトリを使ってください。
次は意図的な上書きの比較です。
npx --yes --package=https://github.com/aichance/business-ai-recipes/releases/download/app-crash-lab-v0.4.1/aichance-app-crash-lab-0.4.1.tgz app-crash-lab run overwrite-control.json --out .crash-lab/teristorial-overwrite
こちらは終了コード1と比較段階のFAILが期待結果です。終了コード2なら設定、起動、インフラなどの問題で、保存の判定ができなかった状態です。
自分のアプリに移すときの観測項目
今回の設定の一部です。savedSlotは保存されたJSON全体、renderedGoldは画面の数字を観測しています。
{
"checks": ["reload"],
"observe": {
"savedSlot": {
"source": "localStorage",
"key": "teristorial:save:slot1",
"parse": "json"
},
"renderedGold": {
"source": "text",
"target": { "css": "#goldValue" }
}
}
}
これは設定の抜粋で、単独の実行用設定ではありません。実際のcontract.jsonには起動後の操作、保存成功の待機、ロード完了の待機などが入っています。自分のアプリでは、キーやselectorに加えて「いつ保存が完了したか」と「いつ復元が完了したか」を決めてください。
ソースを手元に持つ方は、自己アプリ用の導入プロンプトを普段のコーディングエージェントへ渡す方法もあります。実際の保存処理を調べ、アプリに合わせた設定を作り、ローカルで実行して結果を確認する依頼文です。CLI自体はLLMやAPIキーを使いません。
生成したテストを次の更新で使う
通常の検査で表示されたレポートディレクトリへ移動し、アプリのサーバーを動かしたまま実行します。
npm install --prefix . --no-save --package-lock=false --ignore-scripts @playwright/test@1.64.0
npx playwright test --config playwright.config.mjs
生成されたspecはPlaywrightをimportします。App Crash Lab本体をimportせず、選んだ保存操作の再現テストとして再実行できます。終わったらアプリサーバーをCtrl+Cで停止してください。
今回の範囲は、手動のSlot 1、保存JSON、選んだ復元後の表示です。自動セーブ、破損データの拒否、ブラウザ強制終了、クラウド保存、マルチプレイは検査していません。Windowsでのこのpackの実行も未検証です。
App Crash Labは私がAIで開発、検証、運用するMITのOSSです。ゲーム本体は作者の作品で、このZIPには再配布していません。検査設定とhelper、実測のRESULTS.jsonを配布しています。GitHubリポジトリを、次の保存機能の変更時にも使ってもらえるよう改善しています。自分のアプリでの結果や、設定で止まった箇所は短い報告フォームから教えてください。実データや資格情報は不要です。

