From 098082850f451e701a004b6d18fc4790f33197c8 Mon Sep 17 00:00:00 2001 From: DavidQ Date: Mon, 22 Jun 2026 18:02:59 -0400 Subject: [PATCH] PR_26174_ALFA_012-game-hub-parent-child-final-validation --- ...ild-final-validation-branch-validation.txt | 6 + ...nal-validation-manual-validation-notes.txt | 7 + ...final-validation-requirement-checklist.txt | 10 + ...child-final-validation-validation-lane.txt | 10 + ...-game-hub-parent-child-final-validation.md | 16 ++ .../dev/reports/codex_changed_files.txt | 11 +- docs_build/dev/reports/codex_review.diff | 268 +++++++++--------- .../tools/GameHubMockRepository.spec.mjs | 125 ++++++++ 8 files changed, 306 insertions(+), 147 deletions(-) create mode 100644 docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt create mode 100644 docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt create mode 100644 docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt create mode 100644 docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt create mode 100644 docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md diff --git a/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt new file mode 100644 index 000000000..d656315dd --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt @@ -0,0 +1,6 @@ +Branch Validation: PASS + +PASS - Current branch is pr/26174-ALFA-012-game-hub-parent-child-final-validation. +PASS - Expected branch is pr/26174-ALFA-012-game-hub-parent-child-final-validation. +PASS - Continued stack from PR_26174_ALFA_011 branch lineage. +PASS - Work stayed scoped to targeted Game Hub Playwright validation and required reports. diff --git a/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt new file mode 100644 index 000000000..a50a8c4e8 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt @@ -0,0 +1,7 @@ +Manual Validation Notes: PASS + +- Reviewed the expanded Open Games row behavior through targeted Playwright assertions. +- Confirmed Source Idea and Readiness Output use distinct child-table selectors and captions. +- Confirmed Source Idea child context exposes no form controls, buttons, or editable regions. +- Confirmed readiness checklist rows preserve the incoming service-contract order. +- Confirmed creator-safe empty and unavailable states still pass targeted validation. diff --git a/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt new file mode 100644 index 000000000..ddf3ecac6 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt @@ -0,0 +1,10 @@ +Requirement Checklist: PASS + +PASS - Open Games parent rows are validated. +PASS - Expanded game shows Source Idea child table. +PASS - Expanded game shows Readiness Output child table. +PASS - Source Idea is read-only with no edit/delete controls. +PASS - Readiness Output is separate from Source Idea. +PASS - Empty and unavailable states still pass targeted validation. +PASS - Added targeted Playwright coverage only. +PASS - No unrelated cleanup was introduced. diff --git a/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt new file mode 100644 index 000000000..a645cc03d --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt @@ -0,0 +1,10 @@ +Validation Lane: PASS + +Command: +npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub validates Open Games parent and child tables|Game Hub shows a creator-safe empty state|Game Hub shows a creator-safe unavailable state" + +Result: +3 passed. + +Additional check: +git diff --check diff --git a/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md new file mode 100644 index 000000000..fa41c315b --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md @@ -0,0 +1,16 @@ +# PR_26174_ALFA_012-game-hub-parent-child-final-validation + +## Purpose + +Final validation and report pass for the Game Hub parent/child table stack. + +## Summary + +- Added targeted Playwright coverage for Open Games parent rows and expanded child tables. +- Validated Source Idea and Readiness Output render as separate child tables. +- Validated Source Idea context is read-only and readiness output preserves incoming checklist order. +- Re-ran targeted empty and unavailable state checks. + +## Validation + +PASS - `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub validates Open Games parent and child tables|Game Hub shows a creator-safe empty state|Game Hub shows a creator-safe unavailable state"` diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 137f2a6f7..3c4039b1f 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,9 +1,8 @@ -toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs docs_build/dev/reports/codex_review.diff docs_build/dev/reports/codex_changed_files.txt -docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table.md -docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-branch-validation.txt -docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-requirement-checklist.txt -docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-validation-lane.txt -docs_build/dev/reports/PR_26174_ALFA_011-game-hub-readiness-output-child-table-manual-validation-notes.txt +docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation.md +docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-branch-validation.txt +docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-requirement-checklist.txt +docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-validation-lane.txt +docs_build/dev/reports/PR_26174_ALFA_012-game-hub-parent-child-final-validation-manual-validation-notes.txt diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 04ad8ebfe..41e24b645 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,149 +1,135 @@ diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs -index 695b0a609..dac65c676 100644 +index dac65c676..4e617ed35 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs -@@ -288,6 +288,22 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { - "StatusUnder Construction", - "OwnerUser 1", - ]); -+ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(3); -+ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea'] caption")).toHaveText("Source Idea"); -+ const readinessOutputTable = page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='readiness-output']"); -+ await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); -+ await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); -+ await expect(readinessOutputTable.locator("tbody tr")).toHaveText([ -+ "Game StatusUnder Construction", -+ "Game ProgressDemo Game identity ready", -+ "Launch ProgressPublish blocked until configuration and required assets are ready", -+ "Current FocusComplete Game Configuration", -+ "Recommended Next ToolGame Configuration", -+ "Game identityComplete", -+ "Game configurationUnder Construction", -+ "Playable buildPlanned", -+ "Publishing reviewPlanned", -+ ]); - await demoGameRow.locator("[data-game-toggle='demo-game']").click(); - await expect(page.locator("[data-game-expanded-row='demo-game']")).toHaveCount(0); - -diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js -index 67f56ca41..4b21814d7 100644 ---- a/toolbox/game-hub/game-hub.js -+++ b/toolbox/game-hub/game-hub.js -@@ -319,7 +319,53 @@ function renderSourceIdeaChildTable(parent, game) { - parent.append(wrapper); - } +@@ -335,6 +335,131 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { + } + }); --function renderExpandedGameRow(tbody, game) { -+function renderReadinessOutputChildTable(parent, game, progress, active) { -+ const readiness = active && isRecord(progress) -+ ? progress -+ : { -+ currentFocus: "Open this game to review readiness", -+ gameProgress: `${game.name} identity ready`, -+ gameStatus: game.status || "No status", -+ publishingProgress: "Open this game to review launch progress", -+ recommendedNextTool: "Game Hub", -+ progressChecklist: [], -+ }; -+ const wrapper = document.createElement("div"); -+ wrapper.className = "table-wrapper"; -+ const table = document.createElement("table"); -+ table.className = "data-table data-table--fixed"; -+ table.dataset.gameChildTable = "readiness-output"; -+ table.setAttribute("aria-label", `${game.name} readiness output`); -+ table.innerHTML = "Readiness OutputOutputStatus"; -+ const body = document.createElement("tbody"); -+ [ -+ ["Game Status", readiness.gameStatus], -+ ["Game Progress", readiness.gameProgress], -+ ["Launch Progress", readiness.publishingProgress], -+ ["Current Focus", readiness.currentFocus], -+ ["Recommended Next Tool", readiness.recommendedNextTool], -+ ].forEach(([label, value]) => { -+ const row = document.createElement("tr"); -+ row.append(createCell(label, "th"), createCell(value || "Not available")); -+ row.firstElementChild.scope = "row"; -+ body.append(row); -+ }); ++test("Game Hub validates Open Games parent and child tables", async ({ page }) => { ++ const sourceLinkedGame = { ++ id: "lantern-reef", ++ ownerKey: MOCK_DB_KEYS.users.user1, ++ name: "Lantern Reef", ++ purpose: "Game", ++ status: "Ready for Testing", ++ ownerDisplayName: "User 1", ++ members: [ ++ { ++ displayName: "User 1", ++ gameId: "lantern-reef", ++ permission: "Owner", ++ role: "Owner", ++ userKey: MOCK_DB_KEYS.users.user1, ++ }, ++ ], ++ sourceIdea: { ++ idea: "Lantern Reef", ++ pitch: "Guide reef keepers through dusk storms.", ++ notes: [ ++ "Keep traversal gentle.", ++ "Use warm lantern art.", ++ ], ++ }, ++ }; ++ const journeyBuckets = [ ++ "Idea", ++ "Design", ++ "Graphics", ++ "Audio", ++ "Objects", ++ "Worlds", ++ "Interface", ++ "Controls", ++ "Rules", ++ "Progression", ++ "Play Test", ++ "Publish", ++ "Share", ++ ]; + -+ const checklist = Array.isArray(readiness.progressChecklist) ? readiness.progressChecklist : []; -+ checklist.forEach((item) => { -+ const row = document.createElement("tr"); -+ row.dataset.readinessChecklistRow = item.label || "Checklist"; -+ row.append(createCell(item.label || "Checklist", "th"), createCell(item.status || "Not available")); -+ row.firstElementChild.scope = "row"; -+ body.append(row); ++ await page.route("**/api/toolbox/game-hub/repositories/*/methods/getActiveGame", async (route) => { ++ await route.fulfill({ ++ body: JSON.stringify({ ++ data: { result: sourceLinkedGame }, ++ ok: true, ++ rule: "Browser -> Server API -> Data Source", ++ }), ++ contentType: "application/json; charset=utf-8", ++ status: 200, ++ }); ++ }); ++ await page.route("**/api/toolbox/game-hub/repositories/*/methods/getGameProgress", async (route) => { ++ await route.fulfill({ ++ body: JSON.stringify({ ++ data: { ++ result: { ++ currentFocus: "Review source idea context", ++ gameProgress: "Lantern Reef identity ready", ++ gameStatus: "Ready for Testing", ++ publishingProgress: "Launch review pending", ++ recommendedNextTool: "Game Journey", ++ progressChecklist: journeyBuckets.map((label) => ({ label, status: "Planned" })), ++ }, ++ }, ++ ok: true, ++ rule: "Browser -> Server API -> Data Source", ++ }), ++ contentType: "application/json; charset=utf-8", ++ status: 200, ++ }); + }); ++ await page.route("**/api/toolbox/game-hub/repositories/*/methods/listGames", async (route) => { ++ await route.fulfill({ ++ body: JSON.stringify({ ++ data: { result: [sourceLinkedGame] }, ++ ok: true, ++ rule: "Browser -> Server API -> Data Source", ++ }), ++ contentType: "application/json; charset=utf-8", ++ status: 200, ++ }); ++ }); ++ const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); + -+ table.append(body); -+ wrapper.append(table); -+ parent.append(wrapper); -+} ++ try { ++ await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); ++ const parentRows = page.locator("[data-game-parent-table='open-games'] tbody > [data-game-row]"); ++ await expect(parentRows).toHaveCount(1); ++ const gameRow = page.locator("[data-game-row='lantern-reef']"); ++ await expect(gameRow).toContainText("Lantern Reef"); ++ await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "false"); + -+function renderExpandedGameRow(tbody, game, progress, active) { - const row = document.createElement("tr"); - row.dataset.gameExpandedRow = game.id; - row.id = `game-child-${game.id}`; -@@ -329,16 +375,18 @@ function renderExpandedGameRow(tbody, game) { - stack.className = "content-stack content-stack--compact"; - renderGameSummaryChildTable(stack, game); - renderSourceIdeaChildTable(stack, game); -+ renderReadinessOutputChildTable(stack, game, progress, active); - content.append(stack); - row.append(content); - tbody.append(row); - } - --function renderGameParentRow(tbody, game, activeGame) { -+function renderGameParentRow(tbody, game, activeGame, progress) { - const expanded = state.expandedGameId === game.id; -+ const active = activeGame?.id === game.id; - const row = document.createElement("tr"); - row.dataset.gameRow = game.id; -- if (activeGame?.id === game.id) { -+ if (active) { - row.dataset.gameActive = "true"; - } - -@@ -353,12 +401,12 @@ function renderGameParentRow(tbody, game, activeGame) { - ); - - const actions = document.createElement("td"); -- actions.append(createGameButton(game, activeGame?.id === game.id)); -+ actions.append(createGameButton(game, active)); - row.append(actions); - tbody.append(row); - - if (expanded) { -- renderExpandedGameRow(tbody, game); -+ renderExpandedGameRow(tbody, game, progress, active); - } - } - -@@ -389,7 +437,7 @@ function renderProjectInformation(activeGame, currentMember, progress) { - : "Project Information loaded. Sign in to save changes."); - } - --function renderGameList() { -+function renderGameList(progress) { - if (!elements.gameList) { - return; - } -@@ -421,7 +469,7 @@ function renderGameList() { - table.setAttribute("aria-label", "Open Games"); - table.innerHTML = "Open GamesGamePurposeStatusOwnerActions"; - const body = document.createElement("tbody"); -- listResult.forEach((game) => renderGameParentRow(body, game, activeGame)); -+ listResult.forEach((game) => renderGameParentRow(body, game, activeGame, progress)); - table.append(body); - wrapper.append(table); - elements.gameList.append(wrapper); -@@ -558,7 +606,7 @@ function renderWorkspace() { - } - } - -- renderGameList(); -+ renderGameList(progress); - renderMembersTable(activeGame); - renderTableCounts(); - renderChecklist(progress); ++ await gameRow.locator("[data-game-toggle='lantern-reef']").click(); ++ await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "true"); ++ const expandedRow = page.locator("[data-game-expanded-row='lantern-reef']"); ++ await expect(expandedRow).toHaveCount(1); ++ await expect(expandedRow.locator("[data-game-child-table]")).toHaveCount(3); ++ ++ const sourceIdeaTable = expandedRow.locator("[data-game-child-table='source-idea']"); ++ await expect(sourceIdeaTable.locator("caption")).toHaveText("Source Idea"); ++ await expect(sourceIdeaTable.locator("tbody tr")).toHaveText([ ++ "IdeaLantern Reef", ++ "PitchGuide reef keepers through dusk storms.", ++ "Note 1Keep traversal gentle.", ++ "Note 2Use warm lantern art.", ++ ]); ++ await expect(sourceIdeaTable.locator("button, input, textarea, select, [contenteditable='true'], [role='button']")).toHaveCount(0); ++ await expect(sourceIdeaTable).not.toContainText(/Edit|Delete|Current Focus|Recommended Next Tool/); ++ ++ const readinessOutputTable = expandedRow.locator("[data-game-child-table='readiness-output']"); ++ await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); ++ await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); ++ await expect(readinessOutputTable).not.toContainText(/Guide reef keepers|Keep traversal gentle|Use warm lantern art/); ++ await expect(readinessOutputTable.locator("[data-readiness-checklist-row] th")).toHaveText(journeyBuckets); ++ ++ await expect(page.locator("[data-game-list] [data-game-list-status='empty']")).toHaveCount(0); ++ await expect(page.locator("[data-game-list] [data-game-list-status='unavailable']")).toHaveCount(0); ++ await expectNoPageFailures(failures); ++ } finally { ++ await failures.server.close(); ++ } ++}); ++ + test("Game Hub preserves guest browsing and blocks guest saves", async ({ page }) => { + const failures = await openRepoPage(page, "/toolbox/game-hub/index.html"); diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs index dac65c676..4e617ed35 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs @@ -335,6 +335,131 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { } }); +test("Game Hub validates Open Games parent and child tables", async ({ page }) => { + const sourceLinkedGame = { + id: "lantern-reef", + ownerKey: MOCK_DB_KEYS.users.user1, + name: "Lantern Reef", + purpose: "Game", + status: "Ready for Testing", + ownerDisplayName: "User 1", + members: [ + { + displayName: "User 1", + gameId: "lantern-reef", + permission: "Owner", + role: "Owner", + userKey: MOCK_DB_KEYS.users.user1, + }, + ], + sourceIdea: { + idea: "Lantern Reef", + pitch: "Guide reef keepers through dusk storms.", + notes: [ + "Keep traversal gentle.", + "Use warm lantern art.", + ], + }, + }; + const journeyBuckets = [ + "Idea", + "Design", + "Graphics", + "Audio", + "Objects", + "Worlds", + "Interface", + "Controls", + "Rules", + "Progression", + "Play Test", + "Publish", + "Share", + ]; + + await page.route("**/api/toolbox/game-hub/repositories/*/methods/getActiveGame", async (route) => { + await route.fulfill({ + body: JSON.stringify({ + data: { result: sourceLinkedGame }, + ok: true, + rule: "Browser -> Server API -> Data Source", + }), + contentType: "application/json; charset=utf-8", + status: 200, + }); + }); + await page.route("**/api/toolbox/game-hub/repositories/*/methods/getGameProgress", async (route) => { + await route.fulfill({ + body: JSON.stringify({ + data: { + result: { + currentFocus: "Review source idea context", + gameProgress: "Lantern Reef identity ready", + gameStatus: "Ready for Testing", + publishingProgress: "Launch review pending", + recommendedNextTool: "Game Journey", + progressChecklist: journeyBuckets.map((label) => ({ label, status: "Planned" })), + }, + }, + ok: true, + rule: "Browser -> Server API -> Data Source", + }), + contentType: "application/json; charset=utf-8", + status: 200, + }); + }); + await page.route("**/api/toolbox/game-hub/repositories/*/methods/listGames", async (route) => { + await route.fulfill({ + body: JSON.stringify({ + data: { result: [sourceLinkedGame] }, + ok: true, + rule: "Browser -> Server API -> Data Source", + }), + contentType: "application/json; charset=utf-8", + status: 200, + }); + }); + const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); + + try { + await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); + const parentRows = page.locator("[data-game-parent-table='open-games'] tbody > [data-game-row]"); + await expect(parentRows).toHaveCount(1); + const gameRow = page.locator("[data-game-row='lantern-reef']"); + await expect(gameRow).toContainText("Lantern Reef"); + await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "false"); + + await gameRow.locator("[data-game-toggle='lantern-reef']").click(); + await expect(gameRow.locator("[data-game-toggle='lantern-reef']")).toHaveAttribute("aria-expanded", "true"); + const expandedRow = page.locator("[data-game-expanded-row='lantern-reef']"); + await expect(expandedRow).toHaveCount(1); + await expect(expandedRow.locator("[data-game-child-table]")).toHaveCount(3); + + const sourceIdeaTable = expandedRow.locator("[data-game-child-table='source-idea']"); + await expect(sourceIdeaTable.locator("caption")).toHaveText("Source Idea"); + await expect(sourceIdeaTable.locator("tbody tr")).toHaveText([ + "IdeaLantern Reef", + "PitchGuide reef keepers through dusk storms.", + "Note 1Keep traversal gentle.", + "Note 2Use warm lantern art.", + ]); + await expect(sourceIdeaTable.locator("button, input, textarea, select, [contenteditable='true'], [role='button']")).toHaveCount(0); + await expect(sourceIdeaTable).not.toContainText(/Edit|Delete|Current Focus|Recommended Next Tool/); + + const readinessOutputTable = expandedRow.locator("[data-game-child-table='readiness-output']"); + await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); + await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); + await expect(readinessOutputTable).not.toContainText(/Guide reef keepers|Keep traversal gentle|Use warm lantern art/); + await expect(readinessOutputTable.locator("[data-readiness-checklist-row] th")).toHaveText(journeyBuckets); + + await expect(page.locator("[data-game-list] [data-game-list-status='empty']")).toHaveCount(0); + await expect(page.locator("[data-game-list] [data-game-list-status='unavailable']")).toHaveCount(0); + await expectNoPageFailures(failures); + } finally { + await failures.server.close(); + } +}); + test("Game Hub preserves guest browsing and blocks guest saves", async ({ page }) => { const failures = await openRepoPage(page, "/toolbox/game-hub/index.html");