const { test, expect } = require("@playwright/test") async function openApp(page) { await page.goto("/index.html") await page.evaluate(() => { localStorage.clear() sessionStorage.clear() }) await page.goto("/index.html") } async function skipIntro(page) { await expect(page.locator("#intro-overlay")).toBeVisible() await page.locator("#intro-skip").click() await expect(page.locator("#intro-overlay")).toBeHidden() await expect(page.locator("[data-guess-grid] .tile")).toHaveCount(30) await expect(page.locator("[data-key]")).toHaveCount(26) } async function expectGameReadyWithoutIntro(page) { await expect(page.locator("#intro-overlay")).toBeHidden() await expect(page.locator("[data-guess-grid] .tile")).toHaveCount(30) await expect(page.locator("[data-key]")).toHaveCount(26) } test("loads the intro and reveals the game board", async ({ page }) => { await openApp(page) await expect(page.locator("#intro-overlay")).toBeVisible() await expect(page.locator("#intro-tiles .intro-tile")).toHaveCount(6) await skipIntro(page) await expect(page.locator("#round-status")).not.toContainText("Loading") }) test("uses dark mode by default and preserves a light preference", async ({ page }) => { await openApp(page) await skipIntro(page) await expect(page.locator("body")).toHaveClass(/dark-theme/) await page.locator("#theme-button").click() await expect(page.locator("body")).not.toHaveClass(/dark-theme/) await expect(page.locator("#theme-button")).toHaveAttribute("aria-label", "Switch to dark mode") await page.reload() await expectGameReadyWithoutIntro(page) await expect(page.locator("body")).not.toHaveClass(/dark-theme/) }) test("does not replay the intro after a same-tab reload", async ({ page }) => { await openApp(page) await skipIntro(page) await page.reload() await expectGameReadyWithoutIntro(page) }) test("pressing Enter after changing theme does not re-trigger the theme button", async ({ page }) => { await openApp(page) await skipIntro(page) await page.locator("#theme-button").click() await expect(page.locator("body")).not.toHaveClass(/dark-theme/) await page.keyboard.press("Enter") await expect(page.locator("body")).not.toHaveClass(/dark-theme/) }) test("keyboard input fills and deletes board tiles", async ({ page }) => { await openApp(page) await skipIntro(page) const firstTile = page.locator("[data-guess-grid] .tile").first() await page.keyboard.press("A") await expect(firstTile).toHaveText("a") await expect(firstTile).toHaveAttribute("data-state", "active") await page.keyboard.press("Backspace") await expect(firstTile).toHaveText("") await expect(firstTile).not.toHaveAttribute("data-letter", /./) }) test("incomplete guesses show feedback without changing theme", async ({ page }) => { await openApp(page) await skipIntro(page) await page.keyboard.press("Enter") await expect(page.locator("[data-alert-container]")).toContainText("Not enough letters") await expect(page.locator("body")).toHaveClass(/dark-theme/) }) test("stats and leaderboard modals open", async ({ page }) => { await openApp(page) await skipIntro(page) await page.locator("#Stats-button").click() await expect(page.locator("#stats-modal")).toBeVisible() await page.locator("#stats-modal .close-button").click() await expect(page.locator("#stats-modal")).toBeHidden() await page.locator("#leaderboard-button").click() await expect(page.locator("#leaderboard-modal")).toBeVisible() }) test("definition lookup leaves loading state when a provider times out", async ({ page }) => { await page.route("https://api.dictionaryapi.dev/**", () => new Promise(() => {})) await page.route("https://en.wiktionary.org/**", route => route.abort()) await openApp(page) await skipIntro(page) await page.evaluate(() => { lastResult = { won: true, guesses: 1, word: "apple" } loadWordDefinition("apple", true) }) await expect(page.locator("#stats-definition")).toContainText("Definition not available at the moment.", { timeout: 6500 }) }) test("guests are prompted to sign in to save starter words", async ({ page }) => { await openApp(page) await skipIntro(page) const starterPanel = page.locator("#starter-panel") await expect(starterPanel).toBeVisible() await expect(starterPanel).toContainText("to save up to 3 favorite openers") await expect(page.locator("#starter-edit")).toBeHidden() await starterPanel.locator("[data-starter-sign-in]").click() await expect(page.locator("#auth-modal")).toBeVisible() }) test("starter words fill the current row and the editor keeps keys off the board", async ({ page }) => { await page.route("**/rest/v1/rpc/set_starter_words", route => route.fulfill({ json: ["crane", "arise"] })) await openApp(page) await skipIntro(page) await expect(page.locator("#round-status")).not.toContainText("Loading") await page.evaluate(() => { authSession = { user: { id: "test-user", email: "player@example.test" } } starterWords = ["plane", "arise"] starterWordsLoaded = true renderStarterPanel() }) const readFirstRow = () => page.locator("[data-guess-grid] .tile").evaluateAll(tiles => tiles.slice(0, 5).map(tile => tile.dataset.letter || "").join("") ) await page.keyboard.press("Q") await page.locator("[data-starter-word='arise']").click() expect(await readFirstRow()).toBe("arise") await page.locator("#starter-edit").click() const firstInput = page.locator(".starter-input").first() await expect(firstInput).toHaveValue("plane") await firstInput.fill("crane") await firstInput.press("Enter") await expect(page.locator("[data-alert-container]")).toContainText("Starter words saved") await expect(page.locator("[data-starter-word]")).toHaveText(["crane", "arise"]) expect(await readFirstRow()).toBe("arise") })