Files
Crystal/tests/settings-browser.spec.ts
T
samyandClaude Opus 5.5 f289615bd2
Checks / web (push) Failing after 18s
Private ROM checks / private-rom (push) Successful in 14m40s
Add text size and keyboard settings (#38)
Settings sets the text size (100, 125 or 150 %) and the key of each
button, kept in this browser's storage and validated on load. Every font
size scales with --text-scale, and the dialogue box scrolls instead of
growing past the screen. The game screen's label, the Play message and
Help's control list name the current keys.

Screen reader announcements for every native screen and touch control
remapping remain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:42:59 +02:00

86 lines
3.9 KiB
TypeScript

import { test, expect, type Page } 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.beforeEach(() => test.skip(!existsSync(romPath), "Requires the local user ROM."));
async function start(page: Page) {
await page.route("**/src/game/runtime.ts", async (route) => {
const response = await route.fetch(),
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 { pokedexFixtureWorld } = await import('/tests/pokedex-fixture.ts'); const world = pokedexFixtureWorld(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();
}
test("settings rebind a key, scale the text and survive a reload", async ({ page }) => {
await start(page);
const dialog = page.locator("#settings-dialog");
await page.locator("#settings").click();
await expect(dialog).toBeVisible();
const startRow = dialog.locator("#key-table tr", { hasText: "START" });
await expect(startRow).toContainText("Enter");
// Escape cancels a key change without closing the dialog.
await startRow.getByRole("button", { name: /Change the key for START/ }).click();
await expect(startRow).toContainText("Press a key");
await page.keyboard.press("Escape");
await expect(dialog).toBeVisible();
await expect(page.locator("#settings-status")).toHaveText("Key change cancelled.");
// Space is reserved; Q becomes START.
await startRow.getByRole("button", { name: /Change the key for START/ }).click();
await page.keyboard.press("Space");
await expect(page.locator("#settings-status")).toContainText("keeps its usual role");
await startRow.getByRole("button", { name: /Change the key for START/ }).click();
await page.keyboard.press("KeyQ");
await expect(startRow).toContainText("Q");
await expect(page.locator("#settings-status")).toHaveText("START is now Q.");
await dialog.getByLabel("Large", { exact: true }).check();
await expect(page.locator("html")).toHaveAttribute("style", /--text-scale: 1.25/);
await page.locator("#close-settings").click();
await expect(page.locator("#game-screen")).toHaveAttribute("aria-label", /Q is START/);
// Enter no longer opens the start menu; Q does.
await page.locator("#play").click();
await page.locator("#game-screen").focus();
await page.keyboard.press("Enter");
await page.clock.runFor(200);
await expect(page.locator("#dialogue-choice")).toBeHidden();
await page.keyboard.press("KeyQ");
await expect(page.locator("#dialogue-choice").getByRole("option", { name: /^POKéDEX/ })).toBeVisible();
// The choices stay after a reload.
await page.reload();
await expect(page.locator("html")).toHaveAttribute("style", /--text-scale: 1.25/);
await page.locator("#settings").click();
await expect(page.locator("#key-table tr", { hasText: "START" })).toContainText("Q");
await page.locator("#reset-keys").click();
await expect(page.locator("#key-table tr", { hasText: "START" })).toContainText("Enter");
});
test("help lists the current keys", async ({ page }) => {
await start(page);
await page.locator("#help").click();
const keys = page.locator("#help-keys");
await expect(keys).toContainText("Interact / Continue");
await expect(keys).toContainText("X (A)");
await expect(keys).toContainText("Enter (START)");
});