Skip to content

Commit a7f2934

Browse files
author
DavidQ
committed
Retain Preview Generator V2 repo writer context and fix stale preview skip detection - PR_26128_032-preview-generator-repo-writer-retention
1 parent ae695c1 commit a7f2934

8 files changed

Lines changed: 291 additions & 24 deletions
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Playwright Preview Generator V2 Repo Writer Retention
2+
3+
## Commands
4+
- `node --check tools/preview-generator-v2/PreviewGeneratorV2App.js`
5+
- `node --check tools/preview-generator-v2/controls/StatusLogControl.js`
6+
- `node --check tests/playwright/tools/WorkspaceManagerV2.spec.mjs`
7+
- `node --check tests/playwright/tools/PreviewGeneratorV2Baseline.spec.mjs`
8+
- `npx playwright test tests/playwright/tools/WorkspaceManagerV2.spec.mjs --project=playwright --workers=1 --reporter=list -g "exports manifests and launches tools from fixed Workspace Manager V2 tiles|keeps Preview Generator V2 repo writer after Asset Manager V2 deletes the preview asset entry|loads Gravity Well and Pong manifests|logs actionable Preview Generator V2 output path resolution failures"`
9+
- `npx playwright test tests/playwright/tools/PreviewGeneratorV2Baseline.spec.mjs --project=playwright --workers=1 --reporter=list -g "exercises controls, required-field gating, accordions, paths layout, and status clear"`
10+
- `npm run test:workspace-v2`
11+
- `git diff --check`
12+
13+
## Results
14+
- JavaScript syntax checks: passed.
15+
- Focused Workspace Manager V2 / Preview Generator V2 coverage: passed 4/4.
16+
- Focused Preview Generator V2 baseline status-control coverage: passed 1/1.
17+
- Workspace Manager V2 suite: passed 18/18.
18+
- Diff whitespace check: passed.
19+
20+
## Targeted Coverage
21+
- Verified `Copy` appears left of `Clear` in Preview Generator V2 Status controls.
22+
- Verified `Copy` copies the full current status log.
23+
- Verified Copy logs success and missing-Clipboard failure states.
24+
- Verified Preview Generator V2 rehydrates repo selected/file writer context after returning from Asset Manager V2.
25+
- Verified deleting the preview asset entry in Asset Manager V2 does not leave Preview Generator V2 with `Repo selected: not selected`.
26+
- Verified Generate remains enabled when valid repo/game session context exists.
27+
- Verified missing `preview.svg` logs `CHK` with the full absolute path, logs `MISSING`, and generates instead of stale-skipping.
28+
- Verified `OK WRITE` appears after write verification and `Written: 1` follows the verified write path.
29+
30+
## Skipped
31+
- Full samples smoke test was skipped by request. The requested behavior is covered by targeted Playwright flows plus `npm run test:workspace-v2`.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Preview Generator V2 Repo Writer Retention
2+
3+
## Scope
4+
- Added a `Copy` button to the Preview Generator V2 Status header, positioned to the left of `Clear`.
5+
- Copy reads the full current Preview Generator V2 status log and logs an `OK`, `WARN`, or `FAIL` result.
6+
- Kept integration through session storage only.
7+
- No direct cross-tool communication was added.
8+
9+
## Repo Writer Retention
10+
- Preview Generator V2 continues to rehydrate repo context from:
11+
- `workspace.repo.reference`
12+
- `workspace.tools.preview-generator-v2`
13+
- Workspace launch no longer blocks generation when Asset Manager V2 session data no longer contains a preview-role asset entry.
14+
- If the preview asset entry is missing, Preview Generator V2 now falls back to the generated target:
15+
- `games/<game>/assets/images/preview.svg`
16+
- Valid repo sessions keep `Repo selected` populated instead of showing `not selected`.
17+
- Missing or invalid repo session data still logs a visible `FAIL` and keeps generation disabled.
18+
19+
## Skip And Write Verification
20+
- Force rewrite behavior and verified-write logging are preserved.
21+
- With Force rewrite disabled, Preview Generator V2 now checks the live target file handle for `preview.svg`.
22+
- `CHK` logs the full absolute path being checked.
23+
- If the checked file is missing, the log includes `MISSING` and generation proceeds.
24+
- If the checked file exists but cannot be verified, generation proceeds instead of using a stale skip.
25+
- `OK WRITE` and `Written` counts are still emitted only after write verification succeeds.
26+
27+
## Guardrails
28+
- No hidden fallback output paths were added.
29+
- No sample JSON was modified.
30+
- No roadmap content was modified.
31+
- No schema/runtime contracts were modified.
32+
33+
## Skipped
34+
- Full samples smoke test was skipped by request. The targeted Workspace V2 coverage exercises the Asset Manager return flow, missing preview target behavior, status copy behavior, and verified write path.

tests/playwright/tools/PreviewGeneratorV2Baseline.spec.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,8 @@ test.describe("Preview Generator V2 baseline", () => {
384384
const statusHeader = page.locator('#statusAccordion .accordion-v2__header[aria-controls="statusAccordionContent"]');
385385
const statusContent = page.locator("#statusAccordionContent");
386386
const statusHeaderOrder = await statusHeader.evaluate((header) => Array.from(header.querySelectorAll(":scope > span, :scope > div > span, :scope > div > button"), (element) => element.textContent.trim()));
387-
expect(statusHeaderOrder).toEqual(["Status", "+", "Clear"]);
387+
expect(statusHeaderOrder).toEqual(["Status", "+", "Copy", "Clear"]);
388+
await expect(statusHeader.locator("#copyLogBtn")).toBeVisible();
388389
await expect(statusHeader.locator("#clearLogBtn")).toBeVisible();
389390
await expect(statusHeader).toHaveAttribute("aria-expanded", "true");
390391
await expect(statusContent).toBeVisible();
@@ -406,6 +407,7 @@ test.describe("Preview Generator V2 baseline", () => {
406407
await expectAccordionToggles(page, contentId);
407408
}
408409
await expect(page.locator("#clearLogBtn")).toBeVisible();
410+
await expect(statusHeader.locator(".preview-generator-v2__status-header-actions #copyLogBtn")).toBeVisible();
409411
await expect(statusHeader.locator(".preview-generator-v2__status-header-actions #clearLogBtn")).toBeVisible();
410412
await page.locator("#captureModeFullScreen").check();
411413
await expect(page.locator("#log")).toContainText("Capture mode: Full Screen");

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1899,10 +1899,36 @@ test.describe("Workspace Manager V2 bootstrap", () => {
18991899
await expect(page.locator("#log")).not.toContainText("FAIL Workspace background hydration");
19001900
await expect(page.locator("#log")).toContainText("OK Workspace manifest preview source is valid at games/Asteroids/assets/images/preview.png.");
19011901
const previewStatusHeaderOrder = await page.locator(".preview-generator-v2__status-accordion-header").evaluate((header) => Array.from(header.querySelectorAll(":scope > span, :scope > div > span, :scope > div > button"), (element) => element.textContent.trim()));
1902-
expect(previewStatusHeaderOrder).toEqual(["Status", "+", "Clear"]);
1902+
expect(previewStatusHeaderOrder).toEqual(["Status", "+", "Copy", "Clear"]);
1903+
await expect(page.locator(".preview-generator-v2__status-header-actions #copyLogBtn")).toBeVisible();
1904+
await expect(page.locator(".preview-generator-v2__status-header-actions #clearLogBtn")).toBeVisible();
1905+
await page.evaluate(() => {
1906+
Object.defineProperty(navigator, "clipboard", {
1907+
configurable: true,
1908+
value: {
1909+
writeText: async (text) => {
1910+
window.__previewGeneratorV2CopiedLog = text;
1911+
}
1912+
}
1913+
});
1914+
});
1915+
const previewLogBeforeCopy = await page.locator("#log").textContent();
1916+
await page.locator("#copyLogBtn").click();
1917+
expect(await page.evaluate(() => window.__previewGeneratorV2CopiedLog)).toBe(previewLogBeforeCopy);
1918+
await expect(page.locator("#log")).toContainText("OK Copied Preview Generator V2 status log to clipboard");
1919+
await page.evaluate(() => {
1920+
Object.defineProperty(navigator, "clipboard", {
1921+
configurable: true,
1922+
value: undefined
1923+
});
1924+
});
1925+
await page.locator("#copyLogBtn").click();
1926+
await expect(page.locator("#log")).toContainText("FAIL Copy status log failed: Clipboard API is unavailable.");
19031927
await page.locator("#executeBtn").click();
19041928
await expect(page.locator("#log")).toContainText("Starting execution...", { timeout: 20000 });
19051929
await expect(page.locator("#log")).toContainText(`Repo root: ${displayRepoRootPath(server)}`, { timeout: 20000 });
1930+
await expect(page.locator("#log")).toContainText(`CHK ${displayAbsoluteOutputPath(server, "games/Asteroids/assets/images/preview.svg")}`, { timeout: 20000 });
1931+
await expect(page.locator("#log")).toContainText(`MISSING ${displayAbsoluteOutputPath(server, "games/Asteroids/assets/images/preview.svg")}`, { timeout: 20000 });
19061932
await expect(page.locator("#log")).toContainText("RUN Asteroids", { timeout: 20000 });
19071933
await expect(page.locator("#log")).toContainText("OUT games\\Asteroids\\assets\\images\\preview.svg", { timeout: 20000 });
19081934
await expect(page.locator("#log")).toContainText("OK WRITE Asteroids", { timeout: 20000 });
@@ -1930,6 +1956,72 @@ test.describe("Workspace Manager V2 bootstrap", () => {
19301956
}
19311957
});
19321958

1959+
test("keeps Preview Generator V2 repo writer after Asset Manager V2 deletes the preview asset entry", async ({ page }) => {
1960+
const server = await openWorkspaceManagerV2(page);
1961+
const pageErrors = [];
1962+
1963+
page.on("pageerror", (error) => {
1964+
pageErrors.push(error.message);
1965+
});
1966+
1967+
try {
1968+
await selectMockRepo(page);
1969+
await page.locator("#activeGameSelect").selectOption("Asteroids");
1970+
await expectWorkspaceReturnRehydrated(page);
1971+
await page.locator('[data-workspace-tool-id="asset-manager-v2"]').click();
1972+
await expect(page).toHaveURL(/asset-manager-v2\/index\.html.*launch=workspace/);
1973+
await expect(page.locator("#statusLog")).toHaveValue(/Workspace Manager V2 loaded 14 validated assets from tools\.asset-manager-v2\.assets/);
1974+
await page.locator('[data-delete-asset-id="assets.image.preview.preview"]').click();
1975+
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted assets\.image\.preview\.preview\./);
1976+
const editedAssetSession = await page.evaluate(() => JSON.parse(sessionStorage.getItem("workspace.tools.asset-manager-v2")));
1977+
expect(editedAssetSession.data.assets["assets.image.preview.preview"]).toBeUndefined();
1978+
expect(Object.keys(editedAssetSession.data.assets)).toHaveLength(13);
1979+
expect(editedAssetSession.dirty).toMatchObject({
1980+
isDirty: true,
1981+
reason: "asset-updated"
1982+
});
1983+
await page.evaluate(() => {
1984+
sessionStorage.setItem("workspace.repo.writes", JSON.stringify([{
1985+
contents: "<svg>stale cached prior preview</svg>",
1986+
path: "HTML-JavaScript-Gaming/games/Asteroids/assets/images/preview.svg"
1987+
}]));
1988+
});
1989+
await page.locator("#returnToWorkspaceButton").click();
1990+
await expect(page).toHaveURL(/workspace-manager-v2\/index\.html\?hostContextId=workspace-manager-v2-/);
1991+
await expectWorkspaceReturnRehydrated(page);
1992+
await expect(page.locator('[data-workspace-tool-id="asset-manager-v2"]')).toContainText("13 managed assets");
1993+
await expect(page.locator('[data-workspace-tool-id="asset-manager-v2"]')).toHaveAttribute("data-workspace-tool-dirty", "true");
1994+
await expect(page.locator("#statusLog")).toHaveValue(/INFO Refreshed asset-manager-v2 from workspace\.tools\.asset-manager-v2\.data: 13 managed assets; Dirty: true\./);
1995+
await page.locator('[data-workspace-tool-id="preview-generator-v2"]').click();
1996+
await expect(page).toHaveURL(/preview-generator-v2\/index\.html.*launch=workspace/);
1997+
await expect(page.locator("#repoSelectedValue")).toHaveText("HTML-JavaScript-Gaming");
1998+
await expect(page.locator("#executeBtn")).toBeEnabled();
1999+
await expect(page.locator("#previewTargetValue")).toHaveText("games/Asteroids/assets/images/preview.svg");
2000+
await expect(page.locator("#log")).toContainText("WARN Workspace manifest preview asset is missing from Asset Manager V2 data; generated preview output will target games/Asteroids/assets/images/preview.svg.");
2001+
await expect(page.locator("#log")).toContainText("OK Workspace repo session reference loaded from workspace.repo.reference for HTML-JavaScript-Gaming.");
2002+
await expect(page.locator("#log")).not.toContainText("Repo selected: not selected");
2003+
await page.locator("#executeBtn").click();
2004+
const log = page.locator("#log");
2005+
await expect(log).toContainText(`CHK ${displayAbsoluteOutputPath(server, "games/Asteroids/assets/images/preview.svg")}`, { timeout: 20000 });
2006+
await expect(log).toContainText(`MISSING ${displayAbsoluteOutputPath(server, "games/Asteroids/assets/images/preview.svg")}`, { timeout: 20000 });
2007+
await expect(log).toContainText("RUN Asteroids", { timeout: 20000 });
2008+
await expect(log).toContainText("OK WRITE Asteroids", { timeout: 20000 });
2009+
await expect(log).toContainText(`Write verification passed: file exists at ${displayAbsoluteOutputPath(server, "games/Asteroids/assets/images/preview.svg")}.`, { timeout: 20000 });
2010+
await expect(log).toContainText("Written: 1", { timeout: 20000 });
2011+
await expect(log).toContainText("Failed: 0", { timeout: 20000 });
2012+
await expect(log).not.toContainText("SKIP Asteroids");
2013+
const previewWrites = await page.evaluate(() => JSON.parse(sessionStorage.getItem("workspace.repo.writes") || "[]"));
2014+
expect(previewWrites).toHaveLength(2);
2015+
expect(previewWrites.at(-1).path).toBe("HTML-JavaScript-Gaming/games/Asteroids/assets/images/preview.svg");
2016+
expect(previewWrites.at(-1).contents).toContain("<svg");
2017+
expect(previewWrites.at(-1).contents).not.toContain("stale cached prior preview");
2018+
expect(pageErrors).toEqual([]);
2019+
} finally {
2020+
await coverageReporter.stop(page);
2021+
await server.close();
2022+
}
2023+
});
2024+
19332025
test("imports a schema-valid manifest and exports the imported session manifest", async ({ page }) => {
19342026
const server = await openWorkspaceManagerV2(page);
19352027
const pageErrors = [];

0 commit comments

Comments
 (0)