Skip to content

Commit 366af98

Browse files
committed
Implement Project Workspace with mock repository and targeted tests - PR_26155_045-049-project-workspace-rebuild
1 parent 56d03de commit 366af98

14 files changed

Lines changed: 989 additions & 65 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Project Workspace Create Open Delete
2+
3+
Stack item: PR_26155_046-project-workspace-create-open-delete
4+
5+
## Summary
6+
- Updated `toolbox/project-workspace/index.html` to keep the Theme V2 tool template shell while adding create, open, and delete controls.
7+
- Added `toolbox/project-workspace/project-workspace.js` to drive the page from the mock repository.
8+
- Single-user behavior uses `creator-user`; table shape remains multi-user-ready.
9+
10+
## Behavior
11+
- Create Project adds a mock `projects` row and an Owner membership row.
12+
- Open Project selects an existing mock project from the visible project list.
13+
- Delete Open Project removes the active mock project and related membership rows, then opens the first remaining project when available.
14+
15+
## Constraints
16+
- No page-local CSS, tool-local CSS, inline styles, inline scripts, or inline event handlers were added.
17+
- No persistence, real save/load, auth, cloud, or Game Design implementation was added.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Project Workspace Mock Repository
2+
3+
Stack item: PR_26155_045-project-workspace-mock-repository
4+
5+
## Summary
6+
- Added `toolbox/project-workspace/project-workspace-mock-repository.js`.
7+
- Implemented an in-memory, SQL-shaped mock repository for Project Workspace only.
8+
- No real database, auth, cloud, or persistence was added.
9+
10+
## Tables
11+
- `users`: `id`, `displayName`, `email`, `role`.
12+
- `projects`: `id`, `ownerUserId`, `name`, `status`.
13+
- `project_members`: `projectId`, `userId`, `permission`.
14+
15+
## Seed Records
16+
- `Admin User`
17+
- `Creator User`
18+
- `Guest Preview User`
19+
- `Demo Project`, owned by `Creator User`
20+
21+
## Actions
22+
- `resetProjectData()` resets users, projects, memberships, and the open project to the seeded demo state.
23+
- `seedDemoProject()` restores the demo project and its memberships without creating duplicate seed rows.
24+
- `clearTestData()` keeps seed users and removes mock projects and memberships.
25+
26+
## Notes
27+
- The repository supports single-user Project Workspace behavior through `creator-user` while keeping the multi-user-ready `project_members` table shape.
28+
- Project progress is derived from the active mock project state.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Project Workspace MSJ Tests
2+
3+
Stack item: PR_26155_049-project-workspace-msj-tests
4+
5+
## Summary
6+
- Added `tests/playwright/tools/ProjectWorkspaceMockRepository.spec.mjs`.
7+
- Added the repeatable package script `npm run test:lane:project-workspace`.
8+
- Registered the `project-workspace` targeted lane in `scripts/run-targeted-test-lanes.mjs`.
9+
- Updated the existing Project Workspace contract assertion in `tests/playwright/tools/RootToolsFutureState.spec.mjs` to expect mock-project-driven progress text.
10+
11+
## Coverage
12+
- Project Workspace create, open, and delete behavior.
13+
- Reset, seed, and clear test data controls.
14+
- Progress panel updates from mock project state.
15+
- Admin-only Project Data controls.
16+
- Guest and Creator views do not expose Project Data controls.
17+
- Toolbox Progress and Build Path views consume the mock active project state.
18+
- Console errors and failed requests are captured by the targeted Playwright tests.
19+
20+
## Validation Notes
21+
- Impacted lane: `project-workspace`.
22+
- Legacy command lane also run: `npm run test:workspace-v2`, because active Project Workspace/Toolbox contract wiring changed. This is a legacy package-script name; reports and user-facing language use Project Workspace.
23+
- Skipped lanes: `tool-runtime`, `game-runtime`, `integration`, `engine-src`, `samples`, full samples smoke.
24+
- Skipped-lane rationale: no engine runtime, game runtime, sample, integration handoff, parser, shared DB, cloud, or cross-tool behavior changed outside the active Project Workspace and Toolbox bridge.
25+
26+
## Commands
27+
- PASS: `npm run test:lane:project-workspace`
28+
- PASS: `npm run test:workspace-v2`
29+
- PASS: `node --check toolbox/project-workspace/project-workspace-mock-repository.js`
30+
- PASS: `node --check toolbox/project-workspace/project-workspace.js`
31+
- PASS: `node --check toolbox/tools-page-accordions.js`
32+
- PASS: `node --check scripts/run-targeted-test-lanes.mjs`
33+
- PASS: `git diff --check`
34+
35+
## Manual Test Notes
36+
- Verified through Playwright that Project Workspace loads with no console errors.
37+
- Verified create/open/delete project interactions.
38+
- Verified progress panels update after mock project creation and deletion.
39+
- Verified admin-only reset/seed/clear controls.
40+
- Verified guest/user roles cannot see Project Data controls.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Project Workspace Progress Panel
2+
3+
Stack item: PR_26155_047-project-workspace-progress-panel
4+
5+
## Summary
6+
- Implemented Project Workspace progress display from mock repository state.
7+
- Progress fields update when a mock project is created, opened, or deleted.
8+
9+
## Displayed Fields
10+
- Project Status
11+
- Project Progress
12+
- Publishing Progress
13+
- Current Focus
14+
- Recommended Next Tool
15+
- Progress checklist
16+
17+
## Current Model
18+
- Active projects report `Under Construction`, project identity readiness, `Complete Game Configuration`, and `Game Configuration` as the recommended next tool.
19+
- No active project reports `No Project`, `No active project`, `Create or seed a project`, and `Project Workspace` as the recommended next tool.
20+
21+
## Constraints
22+
- Progress is in-memory only.
23+
- No DB, persistence, auth, or real publish validation was added.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Project Workspace Toolbox Integration
2+
3+
Stack item: PR_26155_048-project-workspace-toolbox-integration
4+
5+
## Summary
6+
- Updated `toolbox/tools-page-accordions.js` to import the Project Workspace mock repository.
7+
- Updated `toolbox/index.html` to load `tools-page-accordions.js` as a module and show a mock Project Data status line.
8+
- Connected mock state into the existing Toolbox Progress and Build Path views where the current renderer allows.
9+
10+
## Role Simulation
11+
- Guest and Creator views keep Project Data controls hidden.
12+
- Admin view shows Project Data controls.
13+
- Role switching remains URL-param based: `?role=guest`, `?role=user`, `?role=admin`.
14+
15+
## Project Data Controls
16+
- Reset Project Data resets the mock repository to the seeded demo project.
17+
- Seed Demo Project restores the seeded demo project.
18+
- Clear Test Data removes mock projects and memberships while keeping seed users.
19+
- Controls are dev-mode/mock-only and do not persist or perform destructive real-world actions.
20+
21+
## Toolbox Views
22+
- Progress view now shows the active mock project name and project-derived progress copy.
23+
- Build Path view now includes the active mock project in the Project Workspace path note.
24+
- No unrelated Toolbox renderer rewrite was performed.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"test:lanes": "node ./scripts/run-targeted-test-lanes.mjs",
1111
"test:lanes:preflight": "node ./scripts/run-targeted-test-lanes.mjs --zero-browser-only",
1212
"test:lane:workspace-contract": "node ./scripts/run-targeted-test-lanes.mjs --lane workspace-contract",
13+
"test:lane:project-workspace": "node ./scripts/run-targeted-test-lanes.mjs --lane project-workspace",
1314
"test:lane:tool-runtime": "node ./scripts/run-targeted-test-lanes.mjs --lane tool-runtime",
1415
"test:lane:game-runtime": "node ./scripts/run-targeted-test-lanes.mjs --lane game-runtime",
1516
"test:lane:integration": "node ./scripts/run-targeted-test-lanes.mjs --lane integration",

scripts/run-targeted-test-lanes.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,26 @@ const laneDefinitions = Object.freeze({
122122
requiresPreflight: true,
123123
reason: "Workspace V2 command now validates the future-state tools surface without exercising deprecated toolbox/old_* routes."
124124
},
125+
"project-workspace": {
126+
affectedSurface: "Project Workspace mock repository, Project Workspace UI, and Toolbox Progress/Build Path project-state bridge",
127+
commands: [
128+
playwrightCommand("tests/playwright/tools/ProjectWorkspaceMockRepository.spec.mjs")
129+
],
130+
dependencies: [],
131+
discoveryTargets: [
132+
"tests/playwright/tools/ProjectWorkspaceMockRepository.spec.mjs"
133+
],
134+
fixtures: [
135+
"repo-served Project Workspace page",
136+
"repo-served Toolbox page with role simulation",
137+
"in-memory SQL-shaped mock project repository"
138+
],
139+
fixturePaths: [],
140+
ownership: "tools",
141+
playwrightDir: "tests/playwright/tools",
142+
requiresPreflight: true,
143+
reason: "Project Workspace rebuild slice validates mock users/projects/project_members data actions, project lifecycle controls, and project-driven Progress/Build Path copy without exercising unrelated toolbox routes."
144+
},
125145
"tool-runtime": {
126146
affectedSurface: "Active public toolbox and Tool Template V2 contract",
127147
commands: [
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { expect, test } from "@playwright/test";
2+
import { startRepoServer } from "../../helpers/playwrightRepoServer.mjs";
3+
import { clearPlaywrightStorage, installPlaywrightStorageIsolation } from "../../helpers/playwrightStorageIsolation.mjs";
4+
5+
test.beforeEach(async ({ page }) => {
6+
await installPlaywrightStorageIsolation(page, {
7+
lane: "project-workspace",
8+
surface: "project workspace mock repository"
9+
});
10+
});
11+
12+
test.afterEach(async ({ page }) => {
13+
await clearPlaywrightStorage(page);
14+
});
15+
16+
async function openRepoPage(page, pathName) {
17+
const server = await startRepoServer();
18+
const failedRequests = [];
19+
const pageErrors = [];
20+
const consoleErrors = [];
21+
22+
page.on("pageerror", (error) => {
23+
pageErrors.push(error.message);
24+
});
25+
page.on("console", (message) => {
26+
if (message.type() === "error") {
27+
consoleErrors.push(message.text());
28+
}
29+
});
30+
page.on("response", (response) => {
31+
if (response.status() >= 400) {
32+
failedRequests.push(`${response.status()} ${response.url()}`);
33+
}
34+
});
35+
page.on("requestfailed", (request) => {
36+
failedRequests.push(`FAILED ${request.url()}`);
37+
});
38+
39+
await page.goto(`${server.baseUrl}${pathName}`, { waitUntil: "networkidle" });
40+
return { failedRequests, pageErrors, consoleErrors, server };
41+
}
42+
43+
async function expectNoPageFailures(failures) {
44+
expect(failures.failedRequests).toEqual([]);
45+
expect(failures.pageErrors).toEqual([]);
46+
expect(failures.consoleErrors).toEqual([]);
47+
}
48+
49+
test("Project Workspace creates, opens, and deletes mock projects", async ({ page }) => {
50+
const failures = await openRepoPage(page, "/toolbox/project-workspace/index.html");
51+
52+
try {
53+
await expect(page.locator(".tool-workspace")).toBeVisible();
54+
await expect(page.locator("style, [style], script:not([src])")).toHaveCount(0);
55+
await expect(page.locator("[data-active-project-name]")).toHaveText("Demo Project");
56+
await expect(page.locator("[data-project-list]")).toContainText("Demo Project");
57+
58+
await page.getByLabel("Project Name").fill("Launch Test Project");
59+
await page.getByRole("button", { name: "Create Project" }).click();
60+
await expect(page.locator("[data-active-project-name]")).toHaveText("Launch Test Project");
61+
await expect(page.locator("[data-project-list]")).toContainText("Launch Test Project");
62+
await expect(page.locator("[data-project-workspace-log]")).toHaveText("Created and opened Launch Test Project.");
63+
64+
await page.getByLabel("Project Name").fill("Archive Project");
65+
await page.getByRole("button", { name: "Create Project" }).click();
66+
await expect(page.locator("[data-active-project-name]")).toHaveText("Archive Project");
67+
68+
await page.getByRole("button", { name: "Open Launch Test Project" }).click();
69+
await expect(page.locator("[data-active-project-name]")).toHaveText("Launch Test Project");
70+
await expect(page.locator("[data-project-workspace-log]")).toHaveText("Opened Launch Test Project.");
71+
72+
await page.getByRole("button", { name: "Delete Open Project" }).click();
73+
await expect(page.locator("[data-active-project-name]")).not.toHaveText("Launch Test Project");
74+
await expect(page.locator("[data-project-list]")).not.toContainText("Launch Test Project");
75+
await expect(page.locator("[data-project-workspace-log]")).toHaveText("Deleted Launch Test Project.");
76+
77+
await expectNoPageFailures(failures);
78+
} finally {
79+
await failures.server.close();
80+
}
81+
});
82+
83+
test("Project Workspace progress panels update from mock project state", async ({ page }) => {
84+
const failures = await openRepoPage(page, "/toolbox/project-workspace/index.html");
85+
86+
try {
87+
await expect(page.locator("[data-project-status]")).toHaveText("Under Construction");
88+
await expect(page.locator("[data-project-progress]")).toHaveText("Demo Project identity ready");
89+
await expect(page.locator("[data-publishing-progress]")).toHaveText("Publish blocked until configuration and required assets are ready");
90+
await expect(page.locator("[data-current-focus]")).toHaveText("Complete Game Configuration");
91+
await expect(page.locator("[data-recommended-next-tool]").first()).toHaveText("Game Configuration");
92+
await expect(page.locator("[data-project-progress-checklist]")).toContainText("Project identity: Complete");
93+
94+
await page.getByLabel("Project Name").fill("Progress Review Project");
95+
await page.getByRole("button", { name: "Create Project" }).click();
96+
await expect(page.locator("[data-project-status]")).toHaveText("Under Construction");
97+
await expect(page.locator("[data-project-progress]")).toHaveText("Progress Review Project identity ready");
98+
await expect(page.locator("[data-table-counts], [data-project-table-counts]")).toContainText("projects");
99+
await expect(page.locator("[data-project-table-counts]")).toContainText("2");
100+
await expect(page.locator("[data-project-members-table]")).toContainText("Owner");
101+
102+
await page.getByRole("button", { name: "Delete Open Project" }).click();
103+
await expect(page.locator("[data-active-project-name]")).toHaveText("Demo Project");
104+
await expect(page.locator("[data-project-progress]")).toHaveText("Demo Project identity ready");
105+
106+
await expectNoPageFailures(failures);
107+
} finally {
108+
await failures.server.close();
109+
}
110+
});
111+
112+
test("Toolbox Project Data controls are admin-only and drive mock Progress and Build Path views", async ({ page }) => {
113+
const failures = await openRepoPage(page, "/toolbox/index.html?role=guest");
114+
115+
try {
116+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText(/GUEST VIEW.*Preview only.*Sign in to create/);
117+
await expect(page.locator("[data-project-data-menu]")).toBeHidden();
118+
await expect(page.locator("[data-project-data-action]:visible")).toHaveCount(0);
119+
120+
await page.goto(`${failures.server.baseUrl}/toolbox/index.html?role=user`, { waitUntil: "networkidle" });
121+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText(/CREATOR VIEW.*Project tools enabled.*Switch to Admin View/);
122+
await expect(page.locator("[data-project-data-menu]")).toBeHidden();
123+
await expect(page.locator("[data-project-data-action]:visible")).toHaveCount(0);
124+
125+
await page.goto(`${failures.server.baseUrl}/toolbox/index.html?role=admin`, { waitUntil: "networkidle" });
126+
await expect(page.locator("[data-toolbox-role-banner]")).toHaveText(/ADMIN VIEW.*Planned tools visible.*Switch to Creator View/);
127+
await expect(page.locator("[data-project-data-menu]")).toBeVisible();
128+
await page.locator("[data-project-data-menu] summary").click();
129+
130+
await page.getByRole("button", { name: "Clear Test Data" }).click();
131+
await expect(page.locator("[data-project-data-status]")).toHaveText("Test data cleared. Active project: none.");
132+
await page.getByRole("button", { name: "Progress" }).click();
133+
await expect(page.getByText("Active Project: No active project", { exact: true })).toBeVisible();
134+
await expect(page.getByText("Project Progress: No active project")).toBeVisible();
135+
await page.getByRole("button", { name: "Build Path" }).click();
136+
await expect(page.getByText(/Active mock project: none/)).toBeVisible();
137+
138+
await page.getByRole("button", { name: "Seed Demo Project" }).click();
139+
await expect(page.locator("[data-project-data-status]")).toHaveText("Demo Project seeded. Active project: Demo Project.");
140+
await expect(page.getByText(/Active mock project: Demo Project/)).toBeVisible();
141+
142+
await page.getByRole("button", { name: "Progress" }).click();
143+
await expect(page.getByText("Active Project: Demo Project", { exact: true })).toBeVisible();
144+
await expect(page.getByText("Project Progress: Demo Project identity ready")).toBeVisible();
145+
146+
await page.getByRole("button", { name: "Reset Project Data" }).click();
147+
await expect(page.locator("[data-project-data-status]")).toHaveText("Project Data reset. Active project: Demo Project.");
148+
await expect(page.getByText("Active Project: Demo Project", { exact: true })).toBeVisible();
149+
150+
await expectNoPageFailures(failures);
151+
} finally {
152+
await failures.server.close();
153+
}
154+
});

tests/playwright/tools/RootToolsFutureState.spec.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,10 +227,11 @@ test("root tools surface links current tool pages without old_* routes", async (
227227
await expect(page.locator("[data-tools-accordion-list] [data-toolbox-readiness='Under Construction']").first()).toBeVisible();
228228
await expect(page.locator("[data-tools-accordion-list] [data-toolbox-readiness='Wireframe']").first()).toBeVisible();
229229
await expect(page.locator("[data-tools-accordion-list] .control-card h3", { hasText: /^Progress$/ })).toHaveCount(0);
230-
await expect(page.getByText("Project Progress: Core path under construction")).toBeVisible();
230+
await expect(page.getByText("Active Project: Demo Project")).toBeVisible();
231+
await expect(page.getByText("Project Progress: Demo Project identity ready")).toBeVisible();
231232
await expect(page.getByText("Publishing Progress: Publish blocked until configuration and required assets are ready")).toBeVisible();
232233
await expect(page.getByText("Current Focus: Complete Game Configuration")).toBeVisible();
233-
await expect(page.getByText("Recommended Next Tool: Build Game")).toBeVisible();
234+
await expect(page.getByText("Recommended Next Tool: Game Configuration")).toBeVisible();
234235
await expect(page.getByText(/requiredForTestable:/).first()).toBeVisible();
235236
await expect(page.getByText(/requiredForPublish:/).first()).toBeVisible();
236237
await expect(page.getByText(/requires:/).first()).toBeVisible();

toolbox/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<button class="btn" type="button" data-project-data-action="reset">Reset Project Data</button>
2121
<button class="btn" type="button" data-project-data-action="seed">Seed Demo Project</button>
2222
<button class="btn" type="button" data-project-data-action="clear">Clear Test Data</button>
23+
<div class="status" role="status" data-project-data-status>Project Data controls are mock-only.</div>
2324
</div>
2425
</details>
2526
</div>
@@ -45,7 +46,7 @@ <h1>Creator tools organized by build surface.</h1>
4546
</main>
4647
<div data-partial="footer"></div>
4748
<script src="../assets/theme-v2/js/gamefoundry-partials.js" defer></script>
48-
<script src="tools-page-accordions.js" defer></script>
49+
<script type="module" src="tools-page-accordions.js"></script>
4950
</body>
5051

5152
</html>

0 commit comments

Comments
 (0)