Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Branch Validation: PASS

PASS - Current branch: pr/26174-ALFA-005-idea-project-validation-polish.
PASS - Stack base: pr/26174-ALFA-004-game-hub-progress-count-model.
PASS - Changes are scoped to missing targeted Idea Board tests and required reports.
PASS - No merge to main performed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Manual Validation Notes: PASS

PASS - Confirmed non-Ready Idea Board rows do not expose Create Project.
PASS - Confirmed a Refining idea exposes Create Project only after status changes to Ready.
PASS - Confirmed a converted Project idea has no Edit/Delete controls, no Add Note control, and no note edit/delete actions.
PASS - Confirmed guest Create Project redirects to /account/sign-in.html and does not call createGame.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Requirement Checklist: PASS

PASS - Validates Ready-only Create Project behavior.
PASS - Validates converted Project ideas stay locked/read-only.
PASS - Validates guest Create Project redirects to account/sign-in.html.
PASS - Added missing targeted tests only; no production code changed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Validation Lane: PASS

Targeted Playwright impacted lane:
PASS - npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board gates Create Project|Idea Board guest Create Project"

Notes:
- Full workspace smoke was not run; targeted impacted Playwright validation was used per request.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# PR_26174_ALFA_005-idea-project-validation-polish

## Purpose

Polish Idea Board project-creation validation coverage.

## Summary

- Added a focused targeted Playwright test for Ready-only Create Project gating.
- Validated converted Project ideas expose only project-safe actions and keep notes read-only.
- Reused the existing targeted guest Create Project redirect test as part of this validation lane.

## Validation

PASS - `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board gates Create Project|Idea Board guest Create Project"`
13 changes: 6 additions & 7 deletions docs_build/dev/reports/codex_changed_files.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
src/dev-runtime/persistence/tool-repositories/game-journey-mock-repository.js
tests/playwright/tools/GameJourneyTool.spec.mjs
tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
docs_build/dev/reports/codex_review.diff
docs_build/dev/reports/codex_changed_files.txt
docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model.md
docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-branch-validation.txt
docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-requirement-checklist.txt
docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-validation-lane.txt
docs_build/dev/reports/PR_26174_ALFA_004-game-hub-progress-count-model-manual-validation-notes.txt
docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish.md
docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-branch-validation.txt
docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-requirement-checklist.txt
docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-validation-lane.txt
docs_build/dev/reports/PR_26174_ALFA_005-idea-project-validation-polish-manual-validation-notes.txt
347 changes: 123 additions & 224 deletions docs_build/dev/reports/codex_review.diff

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions tests/playwright/tools/IdeaBoardTableNotes.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,123 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => {
}
});

test("Idea Board gates Create Project to Ready ideas and locks converted projects", async ({ page }) => {
const server = await startRepoServer({
gameJourneyCompletionMetricsLegacyDbPath: null,
gameJourneyCompletionMetricsPostgresClient: createGameJourneyCompletionMetricsPostgresClientStub(),
});
const previousApiUrl = process.env.GAMEFOUNDRY_API_URL;
const previousSiteUrl = process.env.GAMEFOUNDRY_SITE_URL;
const previousSupabaseEnv = {
GAMEFOUNDRY_DATABASE_URL: process.env.GAMEFOUNDRY_DATABASE_URL,
GAMEFOUNDRY_SUPABASE_ANON_KEY: process.env.GAMEFOUNDRY_SUPABASE_ANON_KEY,
GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY: process.env.GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY,
GAMEFOUNDRY_SUPABASE_URL: process.env.GAMEFOUNDRY_SUPABASE_URL,
};
process.env.GAMEFOUNDRY_API_URL = `${server.baseUrl}/api`;
process.env.GAMEFOUNDRY_SITE_URL = server.baseUrl;
process.env.GAMEFOUNDRY_DATABASE_URL = "postgres://idea-board:test@127.0.0.1:5432/idea_board";
process.env.GAMEFOUNDRY_SUPABASE_ANON_KEY = "idea-board-anon-key";
process.env.GAMEFOUNDRY_SUPABASE_SERVICE_ROLE_KEY = "idea-board-service-role-key";
process.env.GAMEFOUNDRY_SUPABASE_URL = `${server.baseUrl}/fake-supabase`;
const createGameRequests = [];
const failedRequests = [];
const pageErrors = [];
const consoleErrors = [];

page.on("request", (request) => {
const requestUrl = request.url();
if (requestUrl.includes("/api/toolbox/game-hub/repositories/") && requestUrl.includes("/methods/createGame")) {
createGameRequests.push(request.postDataJSON());
}
});
page.on("response", (response) => {
if (response.status() >= 400) failedRequests.push(`${response.status()} ${response.url()}`);
});
page.on("pageerror", (error) => {
const text = error.stack || error.message;
if (!isBrowserExtensionNoise(text)) pageErrors.push(error.message);
});
page.on("console", (message) => {
if (message.type() === "error" && !isBrowserExtensionNoise(message.text())) consoleErrors.push(message.text());
});

try {
await page.route("**/api/platform-settings/banner", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({
data: { banner: { active: false, message: "", tone: "info" } },
ok: true,
}),
});
});
await page.route("**/api/toolbox/registry/snapshot", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({
data: {
activeTools: [],
readinessByStatus: {},
tools: [],
toolboxContract: {},
},
ok: true,
}),
});
});
await page.request.post(`${server.baseUrl}/api/session/user`, {
data: { userKey: MOCK_DB_KEYS.users.user1 },
});

await page.goto(`${server.baseUrl}/toolbox/idea-board/index.html`, { waitUntil: "networkidle" });
await expect(page.locator("[data-idea-board-idea-row='top-thoughts'] [data-idea-board-idea-action='create-project']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-idea-row='sky-orchard'] [data-idea-board-idea-action='create-project']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-idea-row='clockwork-courier'] [data-idea-board-idea-action='create-project']")).toHaveCount(0);

await page.locator("[data-idea-board-add-idea]").click();
await page.locator("[data-idea-board-idea-input]").fill("Validation Reef");
await page.locator("[data-idea-board-pitch-input]").fill("Verify project gating and read-only conversion.");
await page.locator("[data-idea-board-idea-status-input]").selectOption("Refining");
await page.locator("[data-idea-board-idea-action='save']").click();
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] td").nth(1)).toHaveText("Refining");
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action='create-project']")).toHaveCount(0);
expect(createGameRequests).toEqual([]);

await page.locator("[data-idea-board-idea-cell='validation-reef']").click();
await page.locator("[data-idea-board-add-note='validation-reef']").click();
await page.locator("[data-idea-board-note-input]").fill("This note should become read-only project context.");
await page.locator("[data-idea-board-note-action='save']").click();
await expect(page.locator("[data-idea-board-notes-count='validation-reef']")).toHaveText("1 Note");

await page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action='edit']").click();
await page.locator("[data-idea-board-idea-status-input]").selectOption("Ready");
await page.locator("[data-idea-board-idea-action='save']").click();
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] td").nth(1)).toHaveText("Ready");
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action]")).toHaveText(["Edit", "Create Project", "Delete"]);

await page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action='create-project']").click();
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] td").nth(1)).toHaveText("Project");
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action]")).toHaveText(["Open in Game Hub", "Archive"]);
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action='edit']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-idea-row='validation-reef'] [data-idea-board-idea-action='delete']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-add-note='validation-reef']")).toHaveCount(0);
await expect(page.locator("[data-idea-board-notes-table='validation-reef'] [data-idea-board-note-action]")).toHaveCount(0);
await expect(page.locator("[data-idea-board-note-input-row]")).toHaveCount(0);
expect(createGameRequests).toHaveLength(1);
expect(Object.keys(createGameRequests[0].args[0]).sort()).toEqual(["name", "purpose", "sourceIdea", "status"]);

expect(failedRequests).toEqual([]);
expect(pageErrors).toEqual([]);
expect(consoleErrors).toEqual([]);
} finally {
restoreEnvValue("GAMEFOUNDRY_API_URL", previousApiUrl);
restoreEnvValue("GAMEFOUNDRY_SITE_URL", previousSiteUrl);
Object.entries(previousSupabaseEnv).forEach(([key, value]) => restoreEnvValue(key, value));
await server.close();
}
});

test("Idea Board guest Create Project redirects to sign in without creating a project", async ({ page }) => {
const server = await startRepoServer();
const previousApiUrl = process.env.GAMEFOUNDRY_API_URL;
Expand Down
Loading