|
1 | | -# Codex Commands - PR_26126_022-preview-generator-v2-no-inline-style-and-auto-rewrite |
| 1 | +# Codex Commands - PR_26126_023-preview-generator-v2-label-consistency |
2 | 2 |
|
3 | 3 | ```bash |
4 | | -codex run "Create PR_26126_022-preview-generator-v2-no-inline-style-and-auto-rewrite. Fix Preview Generator V2 UI/behavior only. Preserve existing generation behavior. Remove all inline style attributes from HTML; move styling into the existing Preview Generator V2 stylesheet/classes using Palette Manager V2 style conventions. Do not show the text \"Only rewrite if preview.svg contains literal \\\"Capture timeout\\\"\" in the UI. Instead, if an existing preview.svg exists, automatically test its contents; only rewrite/regenerate when preview.svg contains the literal text \"Capture timeout\". If preview.svg exists and does not contain that text, skip rewrite and log the decision in the status textarea. Do not modify samples. Do not add schema. Produce review artifacts." |
| 4 | +codex run "Create PR_26126_023-preview-generator-v2-label-consistency. Fix Preview Generator V2 UI labels only. Preserve existing generation behavior. Make the header/title consistent: replace \"Preview SVG Generator > Preview Generator V2\" with \"Preview Generator V2\" and keep \"First-Class Tools Surface\" as the subtitle/context line. Remove duplicate labels inside the Asset folder section so \"Asset folder\" appears only once with value \"assets/images\". Remove duplicate labels inside Capture mode so \"Capture mode\" appears only once. Move \"Canvas Only\" above \"Full Screen (1600x900 HTML Page)\". Do not modify samples. Do not add schema. Produce review artifacts." |
5 | 5 | ``` |
6 | 6 |
|
7 | 7 | ## Validation Commands |
@@ -30,79 +30,48 @@ page.on('console', (message) => { |
30 | 30 | await page.route('https://cdn.jsdelivr.net/**', async (route) => { |
31 | 31 | await route.fulfill({ status: 200, contentType: 'text/javascript', body: 'window.html2canvas = window.html2canvas || undefined;' }); |
32 | 32 | }); |
33 | | -await page.addInitScript(() => { |
34 | | - const writes = []; |
35 | | - class FakeFile { constructor(text) { this._text = text; } async text() { return this._text; } } |
36 | | - class FakeFileHandle { |
37 | | - constructor(path, existing = null) { this.kind = 'file'; this.name = path.split('/').pop(); this.path = path; this._existing = existing; } |
38 | | - async getFile() { return new FakeFile(this._existing || ''); } |
39 | | - async createWritable() { const handle = this; const path = this.path; return { async write(content) { writes.push({ path, content: String(content) }); handle._existing = String(content); }, async close() {} }; } |
40 | | - } |
41 | | - class FakeDirectoryHandle { |
42 | | - constructor(name = 'HTML-JavaScript-Gaming', path = '') { this.kind = 'directory'; this.name = name; this.path = path; this.children = new Map(); } |
43 | | - async getDirectoryHandle(name) { const key = `dir:${name}`; if (!this.children.has(key)) { const nextPath = this.path ? `${this.path}/${name}` : name; this.children.set(key, new FakeDirectoryHandle(name, nextPath)); } return this.children.get(key); } |
44 | | - async getFileHandle(name, options = {}) { const key = `file:${name}`; if (!this.children.has(key)) { if (!options.create) throw new DOMException('Not found', 'NotFoundError'); const nextPath = this.path ? `${this.path}/${name}` : name; this.children.set(key, new FakeFileHandle(nextPath)); } return this.children.get(key); } |
45 | | - } |
46 | | - async function addFile(root, path, text) { |
47 | | - const parts = path.split('/').filter(Boolean); |
48 | | - let current = root; |
49 | | - for (const part of parts.slice(0, -1)) current = await current.getDirectoryHandle(part); |
50 | | - const fileHandle = await current.getFileHandle(parts.at(-1), { create: true }); |
51 | | - fileHandle._existing = text; |
52 | | - } |
53 | | - window.__previewGeneratorV2Writes = writes; |
54 | | - window.__previewGeneratorV2Root = new FakeDirectoryHandle(); |
55 | | - window.__previewGeneratorV2Ready = (async () => { |
56 | | - await addFile(window.__previewGeneratorV2Root, 'samples/phase-01/0107/assets/images/preview.svg', '<svg><text>Healthy preview</text></svg>'); |
57 | | - await addFile(window.__previewGeneratorV2Root, 'samples/phase-01/0102/assets/images/preview.svg', '<svg><text>Capture timeout</text></svg>'); |
58 | | - })(); |
59 | | - window.showDirectoryPicker = async () => { |
60 | | - await window.__previewGeneratorV2Ready; |
61 | | - return window.__previewGeneratorV2Root; |
62 | | - }; |
63 | | -}); |
64 | | -
|
65 | 33 | await page.goto(`${server.baseUrl}/tools/preview-generator-v2/index.html`, { waitUntil: 'domcontentloaded' }); |
66 | 34 | await page.waitForSelector('#shared-theme-header'); |
67 | 35 | await page.waitForFunction(() => Array.from(document.querySelectorAll('.preview-generator-v2 .accordion-v2__header')).every((header) => header.dataset.accordionV2Bound === 'true')); |
| 36 | +
|
| 37 | +const title = await page.title(); |
| 38 | +if (title !== 'Preview Generator V2') throw new Error(`Unexpected title: ${title}`); |
| 39 | +const h1 = await page.locator('.tools-platform-frame__title[data-tool-id="preview-generator-v2"]').innerText(); |
| 40 | +if (h1.trim() !== 'Preview Generator V2') throw new Error(`Unexpected h1: ${h1}`); |
| 41 | +const eyebrow = await page.locator('.tools-platform-frame__eyebrow').innerText(); |
| 42 | +if (eyebrow.trim() !== 'First-Class Tools Surface') throw new Error(`Unexpected context line: ${eyebrow}`); |
68 | 43 | const bodyText = await page.locator('body').innerText(); |
69 | | -if (bodyText.includes('Only rewrite if preview.svg contains literal')) throw new Error('Manual capture-timeout rewrite text should not be visible.'); |
70 | | -const previewInlineStyleCount = await page.locator('.preview-generator-v2 [style]').count(); |
71 | | -if (previewInlineStyleCount !== 0) throw new Error(`Expected no inline style attributes inside Preview Generator V2 UI, got ${previewInlineStyleCount}`); |
| 44 | +if (bodyText.includes('Preview SVG Generator')) throw new Error('Old Preview SVG Generator label is still visible.'); |
72 | 45 |
|
73 | | -await page.fill('#baseUrl', server.baseUrl); |
74 | | -await page.fill('#waitMs', '3000'); |
75 | | -await page.fill('#sampleList', '0107'); |
76 | | -await page.check('#targetTypeSamples'); |
77 | | -await page.click('#pickRepoBtn'); |
78 | | -await page.waitForFunction(() => !document.getElementById('executeBtn').disabled); |
79 | | -await page.click('#executeBtn'); |
80 | | -await page.waitForFunction(() => document.getElementById('log').textContent.includes('existing-preview-without-capture-timeout'), null, { timeout: 35000 }); |
81 | | -let writes = await page.evaluate(() => window.__previewGeneratorV2Writes || []); |
82 | | -if (writes.length !== 0) throw new Error(`Existing healthy preview should skip rewrite, got ${writes.length} writes.`); |
| 46 | +const assetLabelCount = await page.evaluate(() => (document.body.innerText.match(/Asset folder/g) || []).length); |
| 47 | +if (assetLabelCount !== 1) throw new Error(`Expected one visible Asset folder label, got ${assetLabelCount}`); |
| 48 | +const assetFolderValue = await page.locator('#assetFolder').inputValue(); |
| 49 | +if (assetFolderValue !== 'assets/images') throw new Error(`Unexpected asset folder value: ${assetFolderValue}`); |
| 50 | +const assetAria = await page.locator('#assetFolder').getAttribute('aria-label'); |
| 51 | +if (assetAria !== 'Asset folder') throw new Error(`Expected Asset folder aria label, got ${assetAria}`); |
83 | 52 |
|
84 | | -await page.click('#clearLogBtn'); |
85 | | -await page.fill('#sampleList', '0102'); |
86 | | -await page.waitForFunction(() => !document.getElementById('executeBtn').disabled); |
87 | | -await page.click('#executeBtn'); |
88 | | -await page.waitForFunction(() => document.getElementById('log').textContent.includes('contains Capture timeout; rewriting'), null, { timeout: 35000 }); |
89 | | -await page.waitForFunction(() => document.getElementById('lastGeneratedImagePreview') && !document.getElementById('lastGeneratedImagePreview').hidden, null, { timeout: 35000 }); |
90 | | -writes = await page.evaluate(() => window.__previewGeneratorV2Writes || []); |
91 | | -if (writes.length !== 1) throw new Error(`Existing Capture timeout preview should rewrite once, got ${writes.length} writes.`); |
92 | | -if (!writes[0].path.endsWith('samples/phase-01/0102/assets/images/preview.svg')) throw new Error(`Unexpected rewrite path: ${writes[0].path}`); |
| 53 | +const captureLabelCount = await page.evaluate(() => (document.body.innerText.match(/Capture mode/g) || []).length); |
| 54 | +if (captureLabelCount !== 1) throw new Error(`Expected one visible Capture mode label, got ${captureLabelCount}`); |
| 55 | +const captureTexts = await page.locator('#captureModeContent .preview-generator-v2__radio-option span').evaluateAll((nodes) => nodes.map((node) => node.textContent.trim())); |
| 56 | +const expectedCaptureTexts = ['Canvas Only', 'Full Screen (1600x900 HTML Page)']; |
| 57 | +if (JSON.stringify(captureTexts) !== JSON.stringify(expectedCaptureTexts)) throw new Error(`Unexpected capture order: ${captureTexts.join(' | ')}`); |
| 58 | +if (!(await page.locator('#captureModeCanvasOnly').isChecked())) throw new Error('Canvas Only should remain selected by default.'); |
| 59 | +if (await page.locator('legend', { hasText: 'Capture mode' }).count()) throw new Error('Capture mode legend should not render as duplicate label.'); |
| 60 | +
|
| 61 | +const generateButton = page.locator('#executeBtn'); |
| 62 | +if (!(await generateButton.isVisible())) throw new Error('Generate Preview button should remain visible.'); |
| 63 | +if (!(await generateButton.isDisabled())) throw new Error('Generate Preview should remain disabled until required fields are provided.'); |
93 | 64 | if (errors.length || consoleErrors.length) throw new Error([...errors, ...consoleErrors].join(' | ')); |
94 | 65 | await browser.close(); |
95 | 66 | await server.close(); |
96 | | -console.log('preview-generator-v2 automatic capture-timeout rewrite gate smoke valid'); |
| 67 | +console.log('preview-generator-v2 label consistency smoke valid'); |
97 | 68 | '@ | node --input-type=module - |
98 | 69 | ``` |
99 | 70 |
|
100 | 71 | ## Notes |
101 | 72 |
|
102 | | -The targeted Playwright smoke validates the visible manual capture-timeout checkbox text is absent, Preview Generator V2-owned UI has no inline style attributes, existing healthy previews skip rewrite with a status log, and existing timeout previews rewrite and update Last Generated Image. |
| 73 | +The targeted Playwright smoke validates the updated title/header labels, confirms the old `Preview SVG Generator` label is not visible, verifies Asset folder and Capture mode each render one visible label, confirms `Canvas Only` is listed before `Full Screen (1600x900 HTML Page)`, and checks the Generate Preview gate remains visible/disabled before required fields are provided. |
103 | 74 |
|
104 | 75 | `npm run test:workspace-v2` was attempted, but the script is not defined in this checkout. |
105 | 76 |
|
106 | | -Full samples smoke test was skipped because this PR is scoped to Preview Generator V2 UI/behavior only. |
107 | | - |
108 | | -Unstaged sample preview SVG changes were present before this PR and were left untouched. |
| 77 | +Full samples smoke test was skipped because this PR is scoped to Preview Generator V2 labels only. |
0 commit comments