import { expect, test, type Page } from "@playwright/test"; import { formatOpordGrid, generateOpordScenario } from "../../src/lib/minigames/opord-recall"; const FIXED_SEED = 424242; const QUIZ_OPTION_BUTTON = "button.font-mono"; async function login(page: Page): Promise { await page.goto("/login"); await page.fill("#username", "dev"); await page.fill("#password", "Test123"); await page.locator('button[type="submit"]').click(); await page.waitForURL((url) => !url.pathname.startsWith("/login"), { timeout: 30_000 }); } async function playOpordRound(page: Page): Promise { await page.goto(`/minigames/opord?seed=${FIXED_SEED}`); await expect(page.getByRole("heading", { name: "OPORD Recall" })).toBeVisible(); await page.getByRole("button", { name: "Receive briefing" }).click(); await expect(page.getByText("Study at your own pace")).toBeVisible(); // The briefing is drawn on canvas, so derive the expected grid from the lib. const scenario = generateOpordScenario(FIXED_SEED); const grid = formatOpordGrid(scenario.grid); await page.getByRole("button", { name: "Begin recall" }).click(); const questionCounter = page.getByText(/Question \d+ of \d+/); await expect(questionCounter).toBeVisible(); await expect(questionCounter).toContainText("Question 1 of 10"); // Question 1 is always the objective grid: answer it correctly. await page.locator(QUIZ_OPTION_BUTTON).filter({ hasText: grid }).first().click(); for (let index = 1; index < 10; index += 1) { await expect(questionCounter).toContainText(`Question ${index + 1} of 10`, { timeout: 10_000, }); await page.locator(QUIZ_OPTION_BUTTON).first().click(); } await expect(page.getByRole("heading", { name: "Debrief" })).toBeVisible({ timeout: 15_000 }); } test.describe("OPORD Recall", () => { test("plays a full round and records the score", async ({ page }) => { await login(page); await playOpordRound(page); // The grid answer alone scores 100; the XP metre shows a positive bonus. await expect(page.locator("body")).toContainText(/Recalled/); await expect(page.getByText(/^\+\d+$/).first()).toBeVisible(); // The briefing must not expose selectable text to copy. const bodyText = await page.locator("body").innerText(); expect(bodyText).not.toContain(formatOpordGrid(generateOpordScenario(FIXED_SEED).grid)); // The leaderboard refetches after the run and shows the recorded run for this user. const leaderboard = page.getByRole("table"); await expect(leaderboard).toBeVisible({ timeout: 15_000 }); const myRow = leaderboard.locator("tbody tr").filter({ hasText: "(you)" }).first(); await expect(myRow).toBeVisible({ timeout: 15_000 }); await expect(myRow.locator("td").last()).toContainText(/\d{3}/); }); });