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", ); test.beforeEach(() => test.skip(!existsSync(romPath), "Requires the local user ROM."), ); async function start(page: Page) { await page.clock.install({ time: new Date("2026-09-25T14:37: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-25T14:37:05")); await page.locator("#play").click(); } /** Press A until the dialogue shows `text`. */ async function advanceTo(page: Page, text: string) { const dialogue = page.locator("#dialogue-text"); for (let guard = 0; guard < 40; guard++) { if ((await dialogue.textContent())?.includes(text)) return; await page.keyboard.press("KeyX"); } await expect(dialogue).toContainText(text); } test("a new game asks for gender, the clock and a name before the bedroom", async ({ page, }) => { const errors: string[] = []; page.on("pageerror", (error) => errors.push(error.message)); await start(page); const screen = page.locator("#game-screen"), choice = page.locator("#dialogue-choice"), text = page.locator("#dialogue-text"), picture = page.locator("#pokepic-canvas"); await expect(screen).toHaveAttribute("data-intro", "true"); await expect(page.locator("#intro-screen")).toBeVisible(); await expect(text).toHaveText("Are you a boy?\nOr are you a girl?"); // Down, then A chooses "Girl"; B is ignored. await page.keyboard.press("KeyZ"); await page.keyboard.press("ArrowDown"); await expect(choice.getByRole("option", { name: "Girl" })).toHaveAttribute( "aria-selected", "true", ); await page.keyboard.press("KeyX"); await advanceTo(page, "What time is it?"); // The hour starts at the device time; Up three times is 5 PM. const value = choice.locator("output"); await expect(value).toHaveText("DAY 2 o'clock"); for (let i = 0; i < 3; i++) await page.keyboard.press("ArrowUp"); await expect(value).toHaveText("DAY 5 o'clock"); await page.keyboard.press("KeyX"); await expect(text).toHaveText("What?\nDAY 5 o'clock?"); await choice.getByRole("button", { name: "YES" }).click(); await expect(text).toHaveText("How many minutes?"); await expect(value).toHaveText("37 min."); await choice.getByRole("button", { name: "Earlier" }).click(); await expect(value).toHaveText("36 min."); await choice.getByRole("button", { name: "OK" }).click(); await expect(text).toHaveText("Whoa! 36 min.?"); await page.keyboard.press("KeyX"); await expect(text).toHaveText("DAY 5:36!\nYikes! I over-\nslept!"); await page.keyboard.press("KeyX"); await expect(picture).toHaveAttribute("aria-label", "Professor Oak"); await advanceTo(page, "This world is in-"); await expect(picture).toHaveAttribute("aria-label", "WOOPER"); await advanceTo(page, "Now, what did you"); await expect(picture).toHaveAttribute("aria-label", "Kris"); await page.keyboard.press("KeyX"); await expect(choice.getByRole("option")).toHaveText([ "NEW NAME", "KRIS", "AMANDA", "JUANA", "JODI", ]); await page.keyboard.press("KeyX"); await expect(text).toHaveText("YOUR NAME?"); await choice.locator("input").fill("LYRA"); await choice.locator("input").press("Enter"); await expect(text).toHaveText("LYRA, are you\nready?"); await advanceTo(page, "I'll be seeing you"); await page.keyboard.press("KeyX"); await expect(screen).toHaveAttribute("data-intro", "false"); await expect(page.locator("#intro-screen")).toBeHidden(); await expect(page.locator("#pokepic")).toBeHidden(); await expect(screen).toHaveAttribute("data-map", "24:7"); await page.locator("#tab-saves").click(); const download = page.waitForEvent("download"); await page.locator("#backup").click(); const record = JSON.parse( readFileSync((await (await download).path())!, "utf8"), ); const save = JSON.parse(Buffer.from(record.state, "base64").toString("utf8")); expect(save).toMatchObject({ intro: false, name: "LYRA", avatar: "kris", clock: { minuteOffset: 17 * 60 + 36 - (14 * 60 + 37) }, }); expect(errors).toEqual([]); }); test("a session saved during the introduction starts it again", async ({ page, }) => { await start(page); await page.keyboard.press("KeyX"); await advanceTo(page, "What time is it?"); // Leaving the game tab pauses and saves. await page.locator("#tab-saves").click(); await expect(page.locator("#save-status")).toContainText("Session saved"); await page.reload(); await page.locator("#rom-file").setInputFiles(romPath); await expect(page.locator("#status")).toContainText("saved session"); await page.locator("#play").click(); await expect(page.locator("#game-screen")).toHaveAttribute( "data-intro", "true", ); await expect(page.locator("#dialogue-text")).toHaveText( "Are you a boy?\nOr are you a girl?", ); });