import { test, expect } from "@playwright/test"; import { existsSync } 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("development server does not serve ROMs or the private reference checkout", async ({ request, }) => { for (const path of [ "/Pokemon%20-%20Crystal%20Version%20(UE)%20(V1.1)%20%5BC%5D%5B!%5D.gbc", "/.reference/pokecrystal/README.md", ]) { const response = await request.get(path); expect([403, 404]).toContain(response.status()); } }); test("native player loads the actual ROM without loading an emulator and persists movement", async ({ page, context, }, testInfo) => { test.skip( !existsSync(romPath), "Requires a local user-supplied ROM; never downloaded or committed.", ); const requests: string[] = [], errors: string[] = []; page.on("request", (request) => requests.push(request.url())); page.on("pageerror", (error) => errors.push(error.message)); await page.goto("/"); await page.locator("#player-name").fill("KAWA"); await page.locator("#player-avatar").selectOption("kris"); await page.locator("#rom-file").setInputFiles(romPath); await expect(page.locator("#status")).toContainText("verified"); await expect(page.locator("#game-screen")).toHaveAttribute( "data-map", "24:7", ); await page.locator("#play").click(); await expect(page.locator("#game-screen")).toHaveAttribute("data-x", "3"); await page.keyboard.down("ArrowUp"); await expect(page.locator("#game-screen")).toHaveAttribute("data-y", "2"); await page.keyboard.up("ArrowUp"); await page.waitForTimeout(150); await page.keyboard.press("KeyX"); await expect(page.locator("#native-dialogue")).toBeVisible(); await expect(page.locator("#dialogue-page")).toHaveText("1 / 4"); for (let i = 0; i < 4; i++) await page.locator("#dialogue-next").click(); await expect(page.locator("#native-dialogue")).toBeHidden(); await page.locator("#play").click(); await expect(page.locator("#save-status")).toContainText("Session saved"); const exported = page.waitForEvent("download"); await page.locator("#backup").click(); const backup = await exported; page.once("dialog", (dialog) => dialog.accept()); await page.locator("#save-file").setInputFiles((await backup.path())!); await expect(page.locator("#status")).toContainText("Backup restored"); await page.reload(); await page.locator("#rom-file").setInputFiles(romPath); await expect(page.locator("#status")).toContainText("saved session"); await expect(page.locator("#game-screen")).toHaveAttribute("data-y", "2"); await page.locator("#play").click(); await page.keyboard.press("KeyX"); await expect(page.locator("#dialogue-page")).toHaveText("1 / 1"); await page.locator("#dialogue-next").click(); for (const [width, height] of [ [1440, 1000], [1024, 900], [390, 844], [320, 740], [844, 390], ]) { await page.setViewportSize({ width, height }); await page.evaluate(() => scrollTo(0, 0)); expect( await page.evaluate( () => document.documentElement.scrollWidth <= innerWidth, ), ).toBe(true); if (width <= 390 || height <= 390) { const box = (await page.locator(".console").boundingBox())!; expect(box.y + box.height).toBeLessThanOrEqual(height); } } await page.setViewportSize({ width: 390, height: 844 }); await page.screenshot({ path: testInfo.outputPath("native-local-rom.png"), fullPage: true, }); const other = await context.newPage(); await other.goto("/"); await other.locator("#rom-file").setInputFiles(romPath); await expect(other.locator("#status")).toContainText("another tab"); await other.close(); expect( requests.some((url) => /binjgb|\.wasm|\/src\/emulator\//.test(url)), ).toBe(false); expect(requests.every((url) => new URL(url).hostname === "127.0.0.1")).toBe( true, ); expect(errors).toEqual([]); });