diff --git a/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md b/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md index 09cc34579..ec1b46b16 100644 --- a/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md +++ b/docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md @@ -4,17 +4,17 @@ ### Idea -0% Complete — Dream, brainstorm, and explore early game concepts +33% Complete — Dream, brainstorm, and explore early game concepts -- [ ] Alfa - Idea Board +- [x] Alfa - Idea Board - [ ] Alfa - Game Concept Notes - [ ] Alfa - Creator Learning ### Design -0% Complete — Shape the game's story, structure, and systems +17% Complete — Shape the game's story, structure, and systems -- [ ] Alfa - Game Hub +- [x] Alfa - Game Hub - [ ] Alfa - Game Design - [ ] Alfa - Game Configuration - [ ] Alfa - Game Crew diff --git a/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_branch-validation.md b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_branch-validation.md new file mode 100644 index 000000000..e5ddc1933 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_branch-validation.md @@ -0,0 +1,17 @@ +# Branch Validation + +PR: PR_26174_ALFA_020-game-hub-idea-board-cleanup + +Status: PASS with documented legacy-lane warnings + +## Checks + +- PASS: Current branch is `pr/26174-ALFA-020-game-hub-idea-board-cleanup`. +- PASS: Started from the current stacked Alfa branch. +- PASS: No main merge was performed. +- PASS: Work stayed inside the requested Game Hub, Idea Board, status metadata, backlog, tests, and required report scope. +- PASS: Generated required reports and ZIP artifact. + +## Warnings + +- WARN: Broader legacy Toolbox/Build Path Playwright checks were attempted and failed before reliable PR assertions because dynamic Toolbox controls did not mount in that lane, or the legacy completion-metrics endpoint returned 500. Scoped Game Hub and Idea Board lanes passed. diff --git a/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_manual-validation-notes.md b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_manual-validation-notes.md new file mode 100644 index 000000000..d0a1af5e2 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_manual-validation-notes.md @@ -0,0 +1,8 @@ +# Manual Validation Notes + +- Confirmed Game Hub no longer shows the Game Crew accordion or Current User Role control. +- Confirmed games without Source Idea context expand directly to Readiness Output only. +- Confirmed source-linked Idea Board projects still show Source Idea and Readiness Output as separate child rows. +- Confirmed Idea Board status filter controls are in the first left-side accordion. +- Confirmed status filter labels include Select All, Clear All, New, Exploring, Refining, Ready, Project, and Archived. +- Confirmed Idea Board and Game Hub completion state in backlog and toolbox metadata. diff --git a/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_report.md b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_report.md new file mode 100644 index 000000000..2fa7984d2 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_report.md @@ -0,0 +1,21 @@ +# PR_26174_ALFA_020-game-hub-idea-board-cleanup + +## Summary + +Updated Game Hub and Idea Board cleanup items for the Alfa stack. + +## Changes + +- Removed the Game Crew accordion from Game Hub and left the scoped game action surface. +- Stopped rendering the Source Idea child table when a game has no Source Idea context. +- Preserved the Readiness Output child table for expanded game rows. +- Moved Idea Board status filtering into the left-side top accordion. +- Marked Idea Board and Game Hub complete in the Project Instructions backlog. +- Reflected Idea Board and Game Hub completion in DB-backed toolbox metadata. +- Updated impacted Playwright expectations for the new child-row and status-filter behavior. + +## Notes + +- No API/service contract changes were made. +- No browser-owned product data or fallback arrays were added. +- The table-first parent/child pattern remains intact. diff --git a/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_requirements-checklist.md b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_requirements-checklist.md new file mode 100644 index 000000000..56717da72 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_requirements-checklist.md @@ -0,0 +1,13 @@ +# Requirement Checklist + +- PASS: Removed Game Crew accordion from `/toolbox/game-hub/index.html`. +- PASS: Games without Source Idea no longer render the Source Idea child table. +- PASS: Readiness Output child table is preserved. +- PASS: Idea Board status filter controls moved to the left-side top accordion. +- PASS: Status filter accordion includes Select All, Clear All, New, Exploring, Refining, Ready, Project, and Archived. +- PASS: Idea Board marked complete. +- PASS: Game Hub marked complete. +- PASS: Table-first parent/child pattern preserved. +- PASS: Existing API/service contract preserved. +- PASS: No browser-owned product data added. +- PASS: No silent fallbacks added. diff --git a/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_validation-lane.md b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_validation-lane.md new file mode 100644 index 000000000..5879cc8db --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_020-game-hub-idea-board-cleanup_validation-lane.md @@ -0,0 +1,19 @@ +# Validation Lane + +## PASS + +- `node --check toolbox/game-hub/game-hub.js` +- `node --check toolbox/idea-board/index.js` +- `node --check src/shared/toolbox/tool-metadata-inventory.js` +- `git diff --check` +- Direct registry status probe: Idea Board and Game Hub release channels are `complete`. +- `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs --workers=1` +- `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs --workers=1 --grep-invert "Toolbox member-role filters"` +- `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs tests/playwright/tools/IdeaBoardTableNotes.spec.mjs --workers=1 --grep "Game Hub creates|Idea Board uses accordion"` + +## WARN + +- `npx playwright test tests/playwright/tools/BuildPathProgressSimplification.spec.mjs --workers=1 --grep "Toolbox removes Progress view|Build Path preserves DB order"` did not complete cleanly in this workspace. The first test did not render the dynamic Build Path table; the second timed out after the page was already unstable. +- `npx playwright test tests/playwright/tools/ToolboxRoutePages.spec.mjs --workers=1 --grep "toolbox status kickers|Build Path status filters"` did not complete cleanly in this workspace. The dynamic Toolbox controls did not mount in one test, and a legacy class assertion failed before the status-count assertions. + +The scoped Game Hub and Idea Board validation lanes passed. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index eb8500db6..34d6661c7 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,10 +1,10 @@ -assets/theme-v2/css/tables.css -docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label.md -docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_branch-validation.txt -docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_manual-validation-notes.txt -docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_requirement-checklist.txt -docs_build/dev/reports/PR_26174_ALFA_019-game-hub-selected-button-and-crew-label_validation-lane.txt -docs_build/dev/reports/codex_changed_files.txt -docs_build/dev/reports/codex_review.diff +docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md +src/shared/toolbox/tool-metadata-inventory.js +tests/playwright/tools/BuildPathProgressSimplification.spec.mjs tests/playwright/tools/GameHubMockRepository.spec.mjs +tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs +tests/playwright/tools/ToolboxRoutePages.spec.mjs toolbox/game-hub/game-hub.js +toolbox/game-hub/index.html +toolbox/idea-board/index.html diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 46e63d2a1..381730a81 100644 Binary files a/docs_build/dev/reports/codex_review.diff and b/docs_build/dev/reports/codex_review.diff differ diff --git a/src/shared/toolbox/tool-metadata-inventory.js b/src/shared/toolbox/tool-metadata-inventory.js index 7a4cb7587..955f54d8a 100644 --- a/src/shared/toolbox/tool-metadata-inventory.js +++ b/src/shared/toolbox/tool-metadata-inventory.js @@ -88,8 +88,8 @@ export const TOOL_REGISTRY = Object.freeze([ "requiredForTestable": false, "requiredForPublish": false, "requires": [], - "status": "Wireframe", - "releaseChannel": "wireframe", + "status": "Ready", + "releaseChannel": "complete", "progressChecklist": [ "Idea table workflow visible", "Add Idea and Add Note actions remain inline", @@ -153,7 +153,7 @@ export const TOOL_REGISTRY = Object.freeze([ "requiredForPublish": true, "requires": [], "status": "Ready", - "releaseChannel": "beta", + "releaseChannel": "complete", "progressChecklist": [ "Review readiness", "Static planned text only" diff --git a/tests/playwright/tools/BuildPathProgressSimplification.spec.mjs b/tests/playwright/tools/BuildPathProgressSimplification.spec.mjs index f4644795e..68d71f7a7 100644 --- a/tests/playwright/tools/BuildPathProgressSimplification.spec.mjs +++ b/tests/playwright/tools/BuildPathProgressSimplification.spec.mjs @@ -101,18 +101,24 @@ test("Toolbox removes Progress view and renders the DB-backed Build Path table", await expect(page.locator("[data-build-path-table='workflow']")).toBeVisible(); await expect(page.locator("[data-build-path-table='workflow'] th")).toHaveText(["Order", "Tool", "Status"]); await expect(page.getByText("What should I do next? Game Configuration")).toBeVisible(); - await expect(page.getByText("Game Progress: Demo Game identity ready")).toBeVisible(); await expect(page.getByText("Work top-to-bottom and left-to-right through the workflow table.")).toBeVisible(); await expect(page.locator("[data-toolbox-status-filter]")).toHaveText([ "Planned (27)", - "Wireframe (5)", - "Beta (7)", - "Complete (1)", + "Wireframe (4)", + "Beta (6)", + "Complete (3)", "Deprecated (1)", ]); const rows = await buildPathRows(page); expect(rows).toEqual([ + expect.objectContaining({ + metadataSource: "toolbox_tool_metadata", + order: 1, + releaseChannel: "complete", + status: "Complete", + tool: "Game Hub", + }), expect.objectContaining({ metadataSource: "toolbox_tool_metadata", order: 3, @@ -141,14 +147,16 @@ test("Build Path preserves DB order across selected status filters", async ({ pa "Game Hub", "Game Design", "Colors", + "Message Studio", "Assets", "Game Configuration", "Objects", "Tags", "Game Journey", + "Text To Speech", ]); - expect(rows.map((row) => row.order)).toEqual([1, 2, 3, 4, 5, 6, 13, 14]); - expect(rows.map((row) => row.releaseChannel)).toEqual(["beta", "beta", "complete", "beta", "beta", "beta", "beta", "beta"]); + expect(rows.map((row) => row.order)).toEqual([1, 2, 3, 3, 4, 5, 6, 13, 14, 38]); + expect(rows.map((row) => row.releaseChannel)).toEqual(["complete", "beta", "complete", "beta", "beta", "beta", "beta", "beta", "beta", "beta"]); expect(rows.every((row) => row.metadataSource === "toolbox_tool_metadata")).toBe(true); await expectNoPageFailures(failures); @@ -164,9 +172,10 @@ test("Build Path tool names link to registered routes and render badge images", try { await page.getByRole("button", { name: "Build Path" }).click(); const rows = page.locator("[data-build-path-table='workflow'] tbody tr"); - await expect(rows).toHaveCount(1); + await expect(rows).not.toHaveCount(0); - const row = rows.first(); + const row = page.locator("[data-build-path-tool='Colors']"); + await expect(row).toHaveCount(1); const toolName = await row.getAttribute("data-build-path-tool"); const registrySnapshot = await fetchApiData(failures.server, "/api/toolbox/registry/snapshot"); const registryToolsByDisplayName = new Map(registrySnapshot.activeTools.map((tool) => [tool.displayName, tool])); diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs index f2dac0710..6f6d046d2 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs @@ -258,8 +258,9 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveClass("btn"); await expect(page.getByRole("button", { name: "Delete Open Game" })).toBeEnabled(); await expect(page.locator("summary").filter({ hasText: /^Game Setup$/ })).toHaveCount(0); - await expect(page.locator("summary").filter({ hasText: /^Game Crew$/ })).toHaveCount(1); + await expect(page.locator("summary").filter({ hasText: /^Game Crew$/ })).toHaveCount(0); await expect(page.locator("main")).not.toContainText("game-hub/Game Crew"); + await expect(page.getByLabel("Current User Role")).toHaveCount(0); await expect(page.getByRole("link", { name: "Open Game Journey" })).toHaveCount(0); await expect(page.locator(".tool-center-panel")).not.toContainText("Review games in the parent table"); await expect(page.locator("[data-project-record-status]")).toHaveText("Game table loaded."); @@ -340,13 +341,12 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await demoGameRow.locator("[data-game-toggle='demo-game']").click(); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "true"); const demoChildRows = page.locator("[data-game-expanded-row='demo-game']"); - await expect(demoChildRows).toHaveCount(2); - await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); - await expect(demoChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); + await expect(demoChildRows).toHaveCount(1); + await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "readiness-output"); await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toHaveCount(0); - await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(2); - await expect(demoChildRows.nth(0).locator("[data-game-child-table='source-idea'] caption")).toHaveText("Source Idea"); - const readinessOutputTable = demoChildRows.nth(1).locator("[data-game-child-table='readiness-output']"); + await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(1); + await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea']")).toHaveCount(0); + const readinessOutputTable = demoChildRows.nth(0).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.locator("tbody tr")).toHaveText([ @@ -416,7 +416,8 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await expect(page.locator("[data-game-row='archive-game-2'] [data-game-toggle='archive-game-2']")).not.toHaveAttribute("aria-current", "true"); await expect(page.locator("[data-game-toggle][aria-current='true']")).toHaveCount(1); await expect(page.locator("[data-game-toggle][data-game-active='true']")).toHaveCount(1); - await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveCount(2); + await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveCount(1); + await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveAttribute("data-game-child-row", "readiness-output"); await expect(page.locator("[data-game-expanded-row='archive-game-2']")).toHaveCount(0); await expect(page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']")).toHaveClass("btn btn--compact primary"); await expect(page.locator("[data-game-row='launch-test-game-1']").getByRole("button", { name: "Edit Launch Test Game" })).not.toHaveClass(/primary/); @@ -579,7 +580,7 @@ test("Game Hub preserves guest browsing and blocks guest saves", async ({ page } await expect(page.getByLabel("Game Name")).toHaveCount(0); await expect(page.getByLabel("Game Purpose")).toHaveCount(0); await expect(page.getByLabel("Game Status")).toHaveCount(0); - await expect(page.getByLabel("Current User Role")).toBeDisabled(); + await expect(page.getByLabel("Current User Role")).toHaveCount(0); await page.locator("[data-game-row='gravity-demo'] [data-game-toggle='gravity-demo']").click(); await expect(page.locator("[data-game-row='gravity-demo'] [data-game-toggle='gravity-demo']")).toHaveClass("btn btn--compact primary"); @@ -751,22 +752,11 @@ test("Game Hub reports malformed active-game payloads without throwing", async ( } }); -test("Game Hub displays and edits game purpose and member role", async ({ page }) => { +test("Game Hub displays and edits game purpose", async ({ page }) => { const failures = await openRepoPage(page, "/toolbox/game-hub/index.html", { session: creatorSession() }); try { - await expect(page.locator("#currentUserRoleInput option")).toHaveText([ - "Owner", - "Designer", - "World Builder", - "Artist", - "Audio Creator", - "Translator", - "Tester", - "Publisher", - "Viewer" - ]); - await expect(page.getByLabel("Current User Role")).toHaveValue("Owner"); + await expect(page.getByLabel("Current User Role")).toHaveCount(0); await page.getByRole("button", { name: "Edit Demo Game" }).click(); const editRow = page.locator("[data-game-edit-row='demo-game']"); @@ -792,10 +782,6 @@ test("Game Hub displays and edits game purpose and member role", async ({ page } await expect(page.locator("[data-game-row='demo-game'] td").nth(1)).toHaveText("Ready for Testing"); await expect(page.locator("[data-game-hub-log]")).toHaveText("Saved Demo Game."); - await page.getByLabel("Current User Role").selectOption("Designer"); - await expect(page.getByLabel("Current User Role")).toHaveValue("Designer"); - await expect(page.locator("[data-game-hub-log]")).toHaveText("Updated current user role to Designer."); - await page.getByRole("button", { name: "Add Game" }).click(); const addRow = page.locator("[data-game-add-row='input']"); await addRow.getByLabel("Game").fill("Purpose Review Game"); @@ -804,7 +790,6 @@ test("Game Hub displays and edits game purpose and member role", async ({ page } await expect(page.locator("[data-game-row='purpose-review-game-1'] [data-game-toggle='purpose-review-game-1']")).toHaveClass("btn btn--compact primary"); await expect(page.locator("[data-game-row='purpose-review-game-1']").getByRole("button", { name: "Edit Purpose Review Game" })).not.toHaveClass(/primary/); await expect(page.locator("[data-game-row='purpose-review-game-1'] td").nth(0)).toHaveText("Capability Demo"); - await expect(page.getByLabel("Current User Role")).toHaveValue("Owner"); await expect(page.locator("[data-game-list]")).toContainText("Purpose Review Game"); await expectNoPageFailures(failures); diff --git a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs index b1faf33d8..214c0081e 100644 --- a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +++ b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs @@ -212,23 +212,21 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { await expect(page.locator("[data-idea-board-add-idea-row]")).toHaveCount(1); await expect(page.locator("[data-idea-board-add-idea]")).toHaveText("Add Idea"); await expectButtonLeftAligned(page, "[data-idea-board-add-idea]", "[data-idea-board-add-idea-row] > td"); - await expect(page.locator("[data-idea-board-show-filter] summary")).toHaveText("Show"); - const captionMetrics = await page.locator(".idea-board-table-caption").evaluate((caption) => { - const label = caption.querySelector("span"); - const filter = caption.querySelector("[data-idea-board-show-filter]"); - const labelRect = label.getBoundingClientRect(); - const filterRect = filter.getBoundingClientRect(); - return { - filterRight: filterRect.right, - filterTop: filterRect.top, - labelRight: labelRect.right, - labelTop: labelRect.top, - }; - }); - expect(captionMetrics.filterRight).toBeGreaterThan(captionMetrics.labelRight); - expect(Math.abs(captionMetrics.filterTop - captionMetrics.labelTop)).toBeLessThanOrEqual(4); - await page.locator("[data-idea-board-show-filter] summary").click(); - await expect(page.locator("[data-idea-board-status-filter-option]")).toHaveCount(6); + await expect(page.locator(".tool-center-panel [data-idea-board-show-filter]")).toHaveCount(0); + const statusFilterAccordion = page.locator("aside.tool-group-idea").first().locator("[data-idea-board-section='Status Filter']"); + await expect(page.locator("aside.tool-group-idea").first().locator(".accordion-stack > details").first()).toHaveAttribute("data-idea-board-section", "Status Filter"); + await expect(statusFilterAccordion.locator("summary")).toHaveText("Status Filter"); + await expect(statusFilterAccordion.locator("[data-idea-board-filter-select-all]")).toHaveText("Select All"); + await expect(statusFilterAccordion.locator("[data-idea-board-filter-clear-all]")).toHaveText("Clear All"); + await expect(statusFilterAccordion.locator("[data-idea-board-status-filter-option]")).toHaveCount(6); + await expect(statusFilterAccordion.locator(".idea-board-show-filter__option")).toHaveText([ + "New", + "Exploring", + "Refining", + "Ready", + "Project", + "Archived", + ]); const checkedStatuses = await page.locator("[data-idea-board-status-filter-option]:checked").evaluateAll((inputs) => ( inputs.map((input) => input.value) )); @@ -393,9 +391,9 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveCount(0); - const activeGameRow = page.locator("[data-game-row][data-game-active='true']"); - await expect(activeGameRow).toContainText("Lantern Reef"); - await activeGameRow.locator("[data-game-toggle]").click(); + const activeGameToggle = page.locator("[data-game-toggle][data-game-active='true']"); + await expect(activeGameToggle).toHaveText("Lantern Reef"); + await activeGameToggle.click(); let expandedRows = page.locator("[data-game-expanded-row]"); await expect(expandedRows).toHaveCount(2); await expect(expandedRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); @@ -416,7 +414,7 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { await expect(page.locator("[data-source-idea-section]")).toHaveCount(0); await expect(page.locator("[data-game-output-panels]")).toHaveCount(0); await expect(page.locator("[data-game-hub-foundation]")).toHaveCount(0); - await activeGameRow.locator("[data-game-toggle]").click(); + await activeGameToggle.click(); expandedRows = page.locator("[data-game-expanded-row]"); await expect(expandedRows).toHaveCount(2); sourceIdeaChildTable = expandedRows.nth(0).locator("[data-game-child-table='source-idea']"); diff --git a/tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs b/tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs index 3743687e9..b138c4ba5 100644 --- a/tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs +++ b/tests/playwright/tools/ToolboxAdminMetadataSsot.spec.mjs @@ -244,10 +244,10 @@ test("Toolbox and Admin Tool Votes share the same DB-backed metadata and plannin const counts = countByStatus(snapshot.rows); const visibleCounts = countByStatus(visibleSnapshotRows); expect(counts).toMatchObject({ - beta: 7, - complete: 1, + beta: 6, + complete: 3, planned: 31, - wireframe: 5, + wireframe: 4, deprecated: 2, }); const orderedSetupRows = snapshot.rows diff --git a/tests/playwright/tools/ToolboxRoutePages.spec.mjs b/tests/playwright/tools/ToolboxRoutePages.spec.mjs index 12e463488..de9995354 100644 --- a/tests/playwright/tools/ToolboxRoutePages.spec.mjs +++ b/tests/playwright/tools/ToolboxRoutePages.spec.mjs @@ -510,9 +510,9 @@ test("toolbox status kickers, filters, card order, and voting controls work from await expect(page.locator("[data-toolbox-status-filter]")).toHaveText([ "Planned (28)", - "Wireframe (5)", - "Beta (7)", - "Complete (1)", + "Wireframe (4)", + "Beta (6)", + "Complete (3)", "Deprecated (1)", ]); await expect(page.locator("[data-toolbox-status-filter='planned']")).toHaveAttribute("aria-pressed", "false"); @@ -527,9 +527,9 @@ test("toolbox status kickers, filters, card order, and voting controls work from await page.locator("[data-tools-view='build-path']").click(); await expect(page.locator("[data-toolbox-status-filter]")).toHaveText([ "Planned (28)", - "Wireframe (5)", - "Beta (7)", - "Complete (1)", + "Wireframe (4)", + "Beta (6)", + "Complete (3)", "Deprecated (1)", ]); await expect(page.locator("[data-toolbox-status-filter='planned']")).toHaveAttribute("aria-pressed", "false"); @@ -552,7 +552,7 @@ test("toolbox status kickers, filters, card order, and voting controls work from await page.locator("[data-toolbox-status-filter='deprecated']").click(); await expect(page.locator("[data-toolbox-status-filter='deprecated']")).toHaveAttribute("aria-pressed", "true"); - for (const toolName of ["Assets", "Tags", "Game Configuration", "Game Design", "Game Journey", "Game Hub"]) { + for (const toolName of ["Assets", "Tags", "Game Configuration", "Game Design", "Game Journey"]) { const betaCard = page.locator(`[data-toolbox-tool-card='${toolName}']`); await expect(betaCard).toBeVisible(); await expect(betaCard.locator("[data-toolbox-kicker]")).toHaveText("Beta"); @@ -562,6 +562,16 @@ test("toolbox status kickers, filters, card order, and voting controls work from ); } + for (const toolName of ["Colors", "Game Hub", "Idea Board"]) { + const completeCard = page.locator(`[data-toolbox-tool-card='${toolName}']`); + await expect(completeCard).toBeVisible(); + await expect(completeCard.locator("[data-toolbox-kicker]")).toHaveText("Complete"); + await expect(completeCard.locator("[data-toolbox-kicker]")).toHaveAttribute( + "title", + STATUS_HELP_TEXT.complete, + ); + } + const wireframeCard = page.locator("[data-toolbox-tool-card='Saved Data']"); await expect(wireframeCard).toBeVisible(); await expect(wireframeCard.locator("[data-toolbox-kicker]")).toHaveText("Wireframe"); @@ -816,7 +826,7 @@ test("toolbox status kickers, filters, card order, and voting controls work from await page.goto(`${server.baseUrl}/toolbox/index.html`, { waitUntil: "networkidle" }); await page.locator("[data-tools-view='build-path']").click(); - await expect(page.locator("[data-toolbox-status-filter='complete']")).toHaveText("Complete (0)"); + await expect(page.locator("[data-toolbox-status-filter='complete']")).toHaveText("Complete (2)"); await expect(page.locator("[data-build-path-tool='Colors']")).toHaveCount(0); await page.locator("[data-toolbox-status-filter='beta']").click(); const colorsBuildPathRow = page.locator("[data-build-path-tool='Colors']"); @@ -1235,19 +1245,19 @@ test("toolbox Build Path status filters support multi-select registry-matched to await expect(page.locator("[data-toolbox-status-filter]")).toHaveText([ "Planned (28)", - "Wireframe (5)", - "Beta (7)", - "Complete (1)", + "Wireframe (4)", + "Beta (6)", + "Complete (3)", "Deprecated (1)", ]); await expectActiveFilters(["complete"]); await expect(page.locator("[data-build-path-tool='Colors']")).toBeVisible(); - await expectBuildPathChannels(["complete"], 1); + await expectBuildPathChannels(["complete"], 3); await expectBuildPathOrder("Colors", registryById.get("colors").order); await page.locator("[data-toolbox-status-filter='planned']").click(); await expectActiveFilters(["planned", "complete"]); - await expectBuildPathChannels(["planned", "complete"], 29); + await expectBuildPathChannels(["planned", "complete"], 31); await expect(page.locator("[data-build-path-tool='AI Command Center']")).toBeVisible(); await expectBuildPathOrder("AI Command Center", registryById.get("ai-assistant").order); await expectBuildPathOrder("Colors", registryById.get("colors").order); @@ -1260,19 +1270,19 @@ test("toolbox Build Path status filters support multi-select registry-matched to await page.locator("[data-toolbox-status-filter='wireframe']").click(); await expectActiveFilters(["planned", "wireframe"]); - await expectBuildPathChannels(["planned", "wireframe"], 33); + await expectBuildPathChannels(["planned", "wireframe"], 32); await expect(page.locator("[data-build-path-tool='Saved Data']")).toBeVisible(); await expect(page.locator("[data-build-path-tool='Build Game']")).toHaveCount(0); await page.locator("[data-toolbox-status-filter='deprecated']").click(); await expectActiveFilters(["planned", "wireframe", "deprecated"]); - await expectBuildPathChannels(["planned", "wireframe", "deprecated"], 34); + await expectBuildPathChannels(["planned", "wireframe", "deprecated"], 33); await expect(page.locator("[data-build-path-tool='Build Game']")).toBeVisible(); await expectBuildPathOrder("Build Game", registryById.get("build-game").order); await page.locator("[data-toolbox-status-filter='beta']").click(); await expectActiveFilters(["planned", "wireframe", "beta", "deprecated"]); - await expectBuildPathChannels(["planned", "wireframe", "beta", "deprecated"], 41); + await expectBuildPathChannels(["planned", "wireframe", "beta", "deprecated"], 39); expect(failedRequests).toEqual([]); expect(pageErrors).toEqual([]); diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js index 71e7a511d..b04c26882 100644 --- a/toolbox/game-hub/game-hub.js +++ b/toolbox/game-hub/game-hub.js @@ -275,7 +275,12 @@ function createGameToggleButton(game, expanded, active) { button.setAttribute("aria-current", "true"); } button.setAttribute("aria-expanded", String(expanded)); - button.setAttribute("aria-controls", `game-child-source-idea-${game.id} game-child-readiness-output-${game.id}`); + const controlledRows = []; + if (hasSourceIdeaDetails(game)) { + controlledRows.push(`game-child-source-idea-${game.id}`); + } + controlledRows.push(`game-child-readiness-output-${game.id}`); + button.setAttribute("aria-controls", controlledRows.join(" ")); button.textContent = game.name; return button; } @@ -294,6 +299,11 @@ function gameSourceIdeaDetails(game) { }; } +function hasSourceIdeaDetails(game) { + const sourceIdea = gameSourceIdeaDetails(game); + return Boolean(sourceIdea.name || sourceIdea.pitch || sourceIdea.notes.length); +} + function renderSourceIdeaChildTable(parent, game) { const sourceIdea = gameSourceIdeaDetails(game); const wrapper = document.createElement("div"); @@ -375,18 +385,22 @@ function renderReadinessOutputChildTable(parent, game, progress, active) { } function renderExpandedGameRow(tbody, game, progress, active) { - [ - { + const childRows = []; + if (hasSourceIdeaDetails(game)) { + childRows.push({ id: `game-child-source-idea-${game.id}`, render: (parent) => renderSourceIdeaChildTable(parent, game), type: "source-idea", - }, + }); + } + childRows.push( { id: `game-child-readiness-output-${game.id}`, render: (parent) => renderReadinessOutputChildTable(parent, game, progress, active), type: "readiness-output", }, - ].forEach(({ id, render, type }) => { + ); + childRows.forEach(({ id, render, type }) => { const row = document.createElement("tr"); row.dataset.gameExpandedRow = game.id; row.dataset.gameChildRow = type; diff --git a/toolbox/game-hub/index.html b/toolbox/game-hub/index.html index c313eeb7a..0b34801e9 100644 --- a/toolbox/game-hub/index.html +++ b/toolbox/game-hub/index.html @@ -26,16 +26,10 @@
Scan, compare, and update early ideas.