// Run against an isolated mock data root and a fresh Playwright browser context. // The caller supplies its existing page; this script installs no browser tools. import assert from "node:assert/strict"; export async function checkLayersBrowser(page, baseUrl) { const errors = [], dialogs = [], policy = { value: "dismiss" }; const onError = error => errors.push(error.message); const onDialog = async dialog => { dialogs.push(dialog.message()); try { if (policy.value === "dismiss") await dialog.dismiss(); else await dialog.accept(policy.value === "accept" ? undefined : policy.value); } catch (error) { errors.push(error.message); } }; const click = id => page.locator(`#${id}`).click(); const draft = async () => JSON.parse(await page.evaluate(() => localStorage.getItem("matrixController:scene"))); const ready = () => page.locator('#board-status[data-state="ready"]').waitFor(); page.on("pageerror", onError); page.on("dialog", onDialog); try { await page.goto(`${baseUrl}/#canvas`); await ready(); const backgroundBox = await page.locator("#layer-background-enabled").boundingBox(); assert.equal(backgroundBox.width, 20); assert.equal(backgroundBox.height, 20); const paintBox = await page.locator("#paint-color").boundingBox(); const colorBox = await page.locator("#background-color").boundingBox(); if (page.viewportSize().width >= 900) assert.ok(Math.abs(paintBox.y - colorBox.y) < 1); assert.equal(await page.locator("#layer-list button").count(), 1); assert.equal(await page.locator("#delete-layer").getAttribute("aria-disabled"), "true"); await click("new-text-layer"); await click("create-text"); await click("create-text"); await ready(); assert.equal(await page.locator("#text-element-list button").count(), 2); await click("new-text-layer"); assert.equal(await page.locator("#text-element-list button").count(), 0); await click("delete-layer"); assert.equal(await page.locator("#text-element-list button").count(), 2); policy.value = "浏览器图层验收"; await click("rename-layer"); assert.match(await page.locator("#layer-list button").first().innerText(), /浏览器图层验收/); await click("lower-layer"); await click("raise-layer"); await click("copy-layer"); assert.match(await page.locator("#layer-copy-dialog").evaluate(el => getComputedStyle(el).backgroundColor), /^rgb\(/); await page.locator('#layer-copy-submit:not([aria-disabled])').waitFor(); await click("layer-copy-submit"); assert.equal(await page.locator("#layer-list button").count(), 3); await click("delete-layer"); await page.locator("#text-element-list button").first().click(); await ready(); const before = await draft(); await page.locator("#text-x").fill("127"); assert.deepEqual(await draft(), before); policy.value = "dismiss"; await page.locator("#text-x").press("Enter"); assert.equal(await page.locator("#text-x").inputValue(), "32"); policy.value = "accept"; await page.locator("#text-x").fill("127"); await page.locator("#text-x").press("Enter"); await ready(); const clip = (await draft()).layers[1].elements[0].clip; assert.ok(clip); await page.locator("#text-x").fill("32"); await page.locator("#text-x").press("Enter"); await ready(); assert.deepEqual((await draft()).layers[1].elements[0].clip, clip); await page.locator("#text-y").fill("128"); await page.locator("#text-y").press("Enter"); assert.equal(await page.locator("#text-element-list button").count(), 1); await click("start-canvas-edit"); await click("canvas-assistive-toggle"); await click("undo-canvas-edit"); assert.equal(await page.locator("#text-element-list button").count(), 2); await click("exit-canvas-edit"); await click("new-brush-layer"); await page.locator("#layer-background-enabled").check(); await click("clear-canvas"); assert.equal(await page.locator("#layer-background-enabled").isChecked(), true); await click("start-canvas-edit"); await click("canvas-assistive-toggle"); assert.equal(await page.locator("#toggle-expanded-board").getAttribute("aria-pressed"), "false"); await click("toggle-expanded-board"); await click("canvas-assistive-toggle"); const rect = await page.locator("#scene-overlay").boundingBox(); await page.mouse.click(rect.x + rect.width * 190 / 192, rect.y + rect.height * 80 / 192); await click("canvas-assistive-toggle"); await click("move-current-layer"); const step = page.locator('.offset-steps[data-axis="x"] [data-offset="10"]'); policy.value = "dismiss"; const uncommitted = await draft(); await step.click(); assert.deepEqual(await draft(), uncommitted); policy.value = "accept"; await step.click(); assert.equal(await page.locator("#layer-offset-x").inputValue(), "10"); await click("undo-canvas-edit"); assert.deepEqual(await draft(), uncommitted); await click("move-canvas-view"); await click("canvas-assistive-toggle"); assert.equal(await page.locator("#layer-move-controls").isVisible(), true); await click("canvas-assistive-toggle"); const viewBefore = await draft(); await page.mouse.move(rect.x + rect.width / 2, rect.y + rect.height / 2); await page.mouse.down(); await page.mouse.move(rect.x + rect.width / 2 + 20, rect.y + rect.height / 2 + 12); await page.mouse.up(); await page.mouse.wheel(0, -80); assert.deepEqual(await draft(), viewBefore); await click("canvas-assistive-toggle"); const viewStyle = await page.locator("#board-stage").getAttribute("style"); const undoBefore = await page.locator("#undo-canvas-edit").getAttribute("aria-disabled"); await click("exit-layer-move"); assert.equal(await page.locator("body").getAttribute("data-canvas-editor-mode"), "draw"); assert.equal(await page.locator("body").evaluate(el => el.classList.contains("canvas-edit-active")), true); assert.equal(await page.locator("#layer-move-controls").getAttribute("hidden"), ""); assert.equal(await page.locator("#board-stage").getAttribute("style"), viewStyle); assert.deepEqual(await draft(), viewBefore); assert.equal(await page.locator("#undo-canvas-edit").getAttribute("aria-disabled"), undoBefore); await click("canvas-assistive-toggle"); await click("exit-canvas-edit"); const saved = await draft(); await page.reload(); await ready(); assert.deepEqual(await draft(), saved); await click("start-canvas-edit"); await click("canvas-assistive-toggle"); assert.equal(await page.locator("#toggle-expanded-board").getAttribute("aria-pressed"), "true"); await click("toggle-expanded-board"); assert.deepEqual(await draft(), saved); await click("exit-canvas-edit"); await page.locator("#layer-list button").filter({ hasText: "浏览器图层验收" }).click(); await ready(); await click("start-canvas-edit"); await click("canvas-assistive-toggle"); await click("move-current-layer"); assert.equal(await page.locator("#continue-canvas-edit").getAttribute("aria-disabled"), null); const textBeforeMove = await draft(); await page.locator('.offset-steps[data-axis="y"] [data-offset="1"]').click(); const textMoved = await draft(); await click("exit-layer-move"); assert.deepEqual(await draft(), textMoved); assert.match(await page.locator("#canvas-edit-mode-hint").innerText(), /文字编辑模式/); await click("canvas-assistive-toggle"); await click("undo-canvas-edit"); assert.deepEqual(await draft(), textBeforeMove); await click("exit-canvas-edit"); assert.deepEqual(errors, []); return { passed: true, nativeConfirmations: dialogs.length, layers: saved.layers.length, errors }; } finally { page.off("pageerror", onError); page.off("dialog", onDialog); } }