import { test, expect, type Page } 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"); async function start(page: Page) { // Replace only this test's runtime module response, the same way // battle-browser.spec.ts substitutes an encounter fixture: no fixture // import or map override exists in the application or production build. 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); // Fixture worlds stand in for a saved session, so no introduction runs. expect(source).toContain("this.world?.beginIntro();"); await route.fulfill({ response, body: source.replace("this.world?.beginIntro();", "").replace(needle, `const { martFixtureWorld } = await import('/tests/mart-fixture.ts'); const world = martFixtureWorld(assets);`) }); }); 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(); } async function exportState(page: Page) { 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")); await page.locator("#tab-game").click(); return JSON.parse(Buffer.from(record.state, "base64").toString("utf8")); } test.beforeEach(() => test.skip(!existsSync(romPath), "Requires the local user ROM; only map-enabling is a test fixture.")); test("buys a Potion from the Cherrygrove clerk with the keyboard", async ({ page }) => { await start(page); // The fixture places the player at the counter, already facing the clerk. await page.keyboard.press("KeyX"); await expect(page.locator("#mart")).toBeVisible(); await expect(page.locator("#mart-menu")).toContainText("BUY"); await page.keyboard.press("KeyX"); // BUY is already highlighted await expect(page.locator("#mart-menu")).toContainText("POTION"); await page.keyboard.press("KeyX"); // the Potion is first and already highlighted await expect(page.locator(".mart-quantity output")).toHaveText(/×1/); await page.keyboard.press("ArrowUp"); // quantity 2, exercising the stepper await expect(page.locator(".mart-quantity output")).toHaveText(/×2/); await page.keyboard.press("KeyX"); // confirm the quantity await expect(page.locator("#dialogue-text")).toContainText("will be"); await expect(page.locator("#dialogue-choice")).toContainText("YES"); await page.keyboard.press("KeyX"); // YES is already highlighted await expect(page.locator("#dialogue-text")).toContainText("Here you are"); await page.keyboard.press("KeyX"); // continue // Back at the item list (BuyMenu loops for another purchase); leave. await expect(page.locator("#mart-menu")).toContainText("POTION"); await page.keyboard.press("KeyZ"); // back out of the list await page.keyboard.press("KeyZ"); // quit the BUY/SELL/QUIT menu await expect(page.locator("#dialogue-text")).toContainText("Please come again"); await page.keyboard.press("KeyX"); await expect(page.locator("#mart")).toBeHidden(); await expect(page.locator("#game-notice")).toHaveText(""); const state = await exportState(page); expect(state.money).toBe(3000 - 300 * 2); expect(state.bag.items).toEqual([{ item: 18, quantity: 2 }]); });