Port the Pokédex list, entries and tab (#35)
The start menu's POKéDEX runs Pokedex in New mode: the list up to the last seen Pokémon, its cursor and page moves, and DisplayDexEntry with the category, number, footprint, and the height, weight and pages once caught. The Pokédex tab shows the same records from the loaded game. Order, entries and footprints are compared with the pinned PRET source. AREA, CRY, PRNT, the option and search screens, Old and A to Z modes and Unown mode remain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
03aa913a94
commit
cdee576818
20 files changed
+1624
-29
No files matched your search
@@ -0,0 +1,91 @@
|
||||
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("the start menu's POKéDEX lists, opens entries and returns", async ({ page }) => {
|
||||
const errors: string[] = [];
|
||||
page.on("pageerror", (error) => errors.push(error.message));
|
||||
await start(page);
|
||||
const dex = page.locator("#pokedex-screen");
|
||||
await page.keyboard.press("Enter");
|
||||
await page.locator("#dialogue-choice").getByRole("option", { name: /^POKéDEX/ }).click();
|
||||
await expect(dex).toBeVisible();
|
||||
await expect(page.locator("#native-dialogue")).toBeHidden();
|
||||
const rows = dex.locator(".pokedex-list li");
|
||||
await expect(rows).toHaveCount(7);
|
||||
await expect(rows.first()).toHaveText(/CHIKORITA/);
|
||||
await expect(rows.first()).toHaveAttribute("aria-current", "true");
|
||||
await expect(rows.nth(1)).toHaveText(/-----/);
|
||||
await expect(dex.locator(".pokedex-counts")).toContainText("SEEN");
|
||||
await expect(dex.locator(".pokedex-pic")).toHaveAttribute("aria-label", "CHIKORITA");
|
||||
// Down three rows to CYNDAQUIL, which is owned.
|
||||
for (let i = 0; i < 3; i++) await page.keyboard.press("ArrowDown");
|
||||
await expect(rows.nth(3)).toHaveAttribute("aria-current", "true");
|
||||
await expect(rows.nth(3).locator(".pokedex-ball")).toHaveAttribute("aria-label", "Owned");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(dex).toHaveAttribute("data-screen", "entry");
|
||||
await expect(dex.locator(".pokedex-entry-details")).toContainText("FIRE MOUSE");
|
||||
await expect(dex.locator(".pokedex-entry-details")).toContainText(`HT 1'08"`);
|
||||
await expect(dex.locator(".pokedex-page")).toHaveText("P.1");
|
||||
await page.keyboard.press("KeyX");
|
||||
await expect(dex.locator(".pokedex-page")).toHaveText("P.2");
|
||||
// Up steps back to the previous seen Pokémon, which is not caught.
|
||||
await page.keyboard.press("ArrowUp");
|
||||
await expect(dex.locator(".pokedex-entry-details")).toContainText("CHIKORITA");
|
||||
await expect(dex.locator(".pokedex-entry-details")).toContainText(`HT ?'??"`);
|
||||
await expect(dex.locator(".pokedex-text")).toHaveText("");
|
||||
// B returns to the list, then to the start menu.
|
||||
await page.keyboard.press("KeyZ");
|
||||
await expect(dex).toHaveAttribute("data-screen", "list");
|
||||
await page.keyboard.press("KeyZ");
|
||||
await expect(dex).toBeHidden();
|
||||
await expect(page.locator("#dialogue-choice").getByRole("option", { name: /^POKéDEX/ })).toBeVisible();
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
test("the Pokédex tab shows seen and owned records from the game", async ({ page }) => {
|
||||
await start(page);
|
||||
await page.locator("#tab-pokedex").click();
|
||||
const panel = page.locator("#panel-pokedex");
|
||||
await expect(panel.locator("h2")).toHaveText("SEEN 3 · OWN 1");
|
||||
const list = panel.locator(".pokedex-tab-list li");
|
||||
// PIDGEY is tenth in New mode.
|
||||
await expect(list).toHaveCount(10);
|
||||
await expect(list.nth(1)).toHaveText(/-----/);
|
||||
await panel.getByRole("button", { name: /CYNDAQUIL/ }).click();
|
||||
await expect(panel.locator(".pokedex-tab-entry")).toContainText("FIRE MOUSE");
|
||||
await expect(panel.locator(".pokedex-tab-entry .pokedex-text")).toHaveCount(2);
|
||||
await panel.getByRole("button", { name: /PIDGEY/ }).click();
|
||||
await expect(panel.locator(".pokedex-tab-entry")).toContainText("TINY BIRD");
|
||||
await expect(panel.locator(".pokedex-tab-entry")).toContainText("Catch this Pokémon");
|
||||
await expect(panel.locator(".pokedex-tab-entry .pokedex-text")).toHaveCount(0);
|
||||
});
|
||||
Reference in new issue
Block a user