From 49621608bbb4c7107ed8d19b87d4d9fd6cac90fc Mon Sep 17 00:00:00 2001 From: Tim Morgan Date: Mon, 15 Jun 2026 23:19:19 -0700 Subject: [PATCH] test(e2e): move game reset to fixture, extract play-word flow helper Address two findings from the UI/E2E test guidance audit: - Move the per-test game reset (goto, localStorage.clear, reload) out of the inline beforeEach and into an auto Playwright fixture (freshGame) via test.extend, so lifecycle setup is injected per test rather than re-implemented in the spec. - Extract the duplicated per-word play loop (used for "all fourtiles" and "all words") into a layered flow helper: playWord (single action) and playWords (flow), composed from the existing GameBoardPage primitives and synchronizing with web-first assertions only. The thin star and found-words page objects are left as-is. Co-Authored-By: Claude Opus 4.8 (1M context) --- e2e/fixtures.ts | 12 ++++++++++++ e2e/game-helpers.ts | 36 +++++++++++++++++++++++++++++++++++- e2e/gameplay.spec.ts | 24 ++++-------------------- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/e2e/fixtures.ts b/e2e/fixtures.ts index a09071c..be4f954 100644 --- a/e2e/fixtures.ts +++ b/e2e/fixtures.ts @@ -4,12 +4,24 @@ import { FoundWordsPanel } from './pages/FoundWordsPanel' import { FourtileStarsPanel } from './pages/FourtileStarsPanel' interface Fixtures { + freshGame: Record board: GameBoardPage foundWordsPanel: FoundWordsPanel stars: FourtileStarsPanel } export const test = base.extend({ + freshGame: [ + async ({ page }, use) => { + await page.goto('/') + await page.evaluate(() => { + localStorage.clear() + }) + await page.reload() + await use({}) + }, + { auto: true }, + ], board: async ({ page }, use) => { await use(new GameBoardPage(page)) }, diff --git a/e2e/game-helpers.ts b/e2e/game-helpers.ts index 4af9f30..f1dac99 100644 --- a/e2e/game-helpers.ts +++ b/e2e/game-helpers.ts @@ -1,5 +1,6 @@ -import type { Page } from '@playwright/test' +import { expect, type Page } from '@playwright/test' import { difference, sample, without } from 'lodash-es' +import type { GameBoardPage } from './pages/GameBoardPage' async function getDataAttr(page: Page, attr: string): Promise { const value = await page.getByTestId('game-board').getAttribute(attr) @@ -97,3 +98,36 @@ export async function tilesForWordFromPage(page: Page, word: string): Promise { + const wordTiles = await tilesForWordFromPage(page, word) + await board.clickTiles(wordTiles) + await board.clickAdd() + await expect(board.getCurrentWord()).toHaveText('', { timeout: options.clearTimeout }) +} + +/** Plays every word in order, waiting for the board to settle between each. */ +export async function playWords( + page: Page, + board: GameBoardPage, + wordList: string[], + options: PlayWordOptions = {}, +): Promise { + for (const word of wordList) { + await playWord(page, board, word, options) + } +} diff --git a/e2e/gameplay.spec.ts b/e2e/gameplay.spec.ts index 85f530f..2063e35 100644 --- a/e2e/gameplay.spec.ts +++ b/e2e/gameplay.spec.ts @@ -6,18 +6,13 @@ import { fourtiles, tiles, words, - tilesForWordFromPage, + playWords, } from './game-helpers' test.describe('Gameplay', () => { let wordsRemaining: number - test.beforeEach(async ({ page, foundWordsPanel }) => { - await page.goto('/') - await page.evaluate(() => { - localStorage.clear() - }) - await page.reload() + test.beforeEach(async ({ foundWordsPanel }) => { wordsRemaining = parseInt(await foundWordsPanel.getWordsRemaining().innerText(), 10) }) @@ -114,12 +109,7 @@ test.describe('Gameplay', () => { test.beforeEach(async ({ page, board }) => { fourtileWords = await fourtiles(page) - for (const fourtile of fourtileWords) { - const tiles = await tilesForWordFromPage(page, fourtile) - await board.clickTiles(tiles) - await board.clickAdd() - await expect(board.getCurrentWord()).toHaveText('') - } + await playWords(page, board, fourtileWords) }) test('pops confetti', async ({ page }) => { @@ -151,13 +141,7 @@ test.describe('Gameplay', () => { test.setTimeout(120_000) test.beforeEach(async ({ page, board }) => { - const allWords = await words(page) - for (const word of allWords) { - const wordTiles = await tilesForWordFromPage(page, word) - await board.clickTiles(wordTiles) - await board.clickAdd() - await expect(board.getCurrentWord()).toHaveText('', { timeout: 10_000 }) - } + await playWords(page, board, await words(page), { clearTimeout: 10_000 }) }) test('pops confetti and unicorns', async ({ page }) => {