import { test, expect } from "@playwright/test"; import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; const romPath = resolve(process.env.CRYSTAL_ROM_PATH ?? "Pokemon - Crystal Version (UE) (V1.1) [C][!].gbc"); test.beforeEach(() => test.skip(!existsSync(romPath), "Requires the local user ROM; trainer script is a test fixture.")); test("trainer UI refuses RUN, wins against Joey and saves the reward", async ({ page }) => { // Route substitution stays in the test harness; production imports no fixture. await page.route("**/src/game/runtime.ts", async (route) => { const response = await route.fetch(); const source = await response.text(); const needle = "const world = new GameWorld(assets);"; expect(source).toContain(needle); await route.fulfill({ response, body: source.replace(needle, `const { trainerFixtureWorld } = await import('/tests/trainer-fixture.ts'); const world = trainerFixtureWorld(assets, undefined, false, true);`) }); }); await page.clock.install({ time: new Date("2026-09-22T12:00:00") }); await page.goto("/"); await page.locator("#rom-file").setInputFiles(romPath); await expect(page.locator("#status")).toContainText("verified"); await page.clock.pauseAt(new Date("2026-09-22T12:05:00")); await page.locator("#play").click(); await page.keyboard.press("KeyX"); await expect(page.locator("#battle")).toBeVisible(); await expect(page.locator("#battle-foe-name")).toHaveText("RATTATA"); let triedRun = false; const messages = new Set(); for (let guard = 0; guard < 60; guard++) { if (!(await page.locator("#battle").isVisible())) break; messages.add(await page.locator("#battle-text").innerText()); const menu = page.locator("#battle-menu"); const fight = menu.getByRole("button", { name: "FIGHT", exact: true }); if (await fight.count()) { if (!triedRun) { triedRun = true; await menu.getByRole("button", { name: "RUN", exact: true }).click(); } else { await fight.click(); await menu.getByRole("button", { name: /TACKLE/ }).click(); } } else if (await menu.getByRole("button", { name: /Continue/ }).count()) { await menu.getByRole("button", { name: /Continue/ }).click(); } else await page.clock.runFor(1200); } await expect(page.locator("#battle")).toBeHidden(); await expect(page.locator("#game-notice")).toHaveText(""); const shown = [...messages].join("\n"); expect(shown).toContain("JOEY"); expect(shown).toMatch(/No!.*running/s); expect(shown).toContain("Ack! I lost again!"); expect(shown).toContain("64"); await page.locator("#tab-saves").click(); const download = page.waitForEvent("download"); await page.locator("#backup").click(); const path = (await (await download).path())!; const record = JSON.parse(readFileSync(path, "utf8")); const state = JSON.parse(Buffer.from(record.state, "base64").toString("utf8")); expect(state.money).toBe(3064); expect(state.pokedex.seen).toContain(19); expect(state.events).toContain(256); });