diff --git a/tests/browser/example-board-ui.test.js b/tests/browser/example-board-ui.test.js index 9eaaaf9b0..b610fbf89 100644 --- a/tests/browser/example-board-ui.test.js +++ b/tests/browser/example-board-ui.test.js @@ -38,6 +38,101 @@ function drawerMarkup() { return `
Add a case
${exampleMarkup()}
`; } +test("selecting an example pen color switches from erasing to drawing", async (t) => { + if (!browser) return t.skip("playwright chromium unavailable"); + const page = await browser.newPage(); + t.after(() => page.close()); + await page.goto(base); + await page.setContent(exampleMarkup()); + await page.evaluate(async (base) => { + const { mountExampleBoard } = await import(`${base}/example-board.js`); + window.ops = []; + const root = document.querySelector("#example-board"); + root.open = true; + mountExampleBoard(root, { + locked: () => false, + connected: () => false, + record: (ops) => window.ops.push(...ops), + publish: async () => true, + }); + }, base); + + const pen = page.getByRole("button", { name: "Pen", exact: true }); + const eraser = page.getByRole("button", { name: "Eraser", exact: true }); + const red = page.getByRole("button", { name: "Pen Red", exact: true }); + const pressedColors = () => + page + .locator(".board-color") + .evaluateAll((buttons) => + buttons + .filter((button) => button.getAttribute("aria-pressed") === "true") + .map((button) => button.getAttribute("aria-label")), + ); + const canvas = page.locator("canvas"); + async function draw() { + const box = await canvas.boundingBox(); + await page.mouse.move(box.x + 20, box.y + 20); + await page.mouse.down(); + await page.mouse.move(box.x + 80, box.y + 40); + await page.mouse.up(); + } + + assert.deepEqual(await pressedColors(), ["Pen Black"]); + await draw(); + assert.equal(await page.evaluate(() => window.ops.length), 1); + await eraser.click(); + await draw(); + assert.deepEqual( + await page.evaluate(() => { + const { color, width } = window.ops.at(-1); + return { color, width }; + }), + { color: "#ffffff", width: 24 }, + ); + await red.click(); + assert.equal(await page.evaluate(() => window.ops.length), 2); + await draw(); + assert.deepEqual( + await page.evaluate(() => + window.ops.slice(2).map(({ color, width }) => ({ color, width })), + ), + [{ color: "#c2261b", width: 3 }], + ); + assert.equal(await pen.getAttribute("aria-pressed"), "true"); + assert.equal(await eraser.getAttribute("aria-pressed"), "false"); + assert.deepEqual(await pressedColors(), ["Pen Red"]); + + await eraser.click(); + assert.equal(await pen.getAttribute("aria-pressed"), "false"); + assert.equal(await eraser.getAttribute("aria-pressed"), "true"); + assert.deepEqual(await pressedColors(), ["Pen Red"]); + await draw(); + assert.deepEqual( + await page.evaluate(() => { + const { color, width } = window.ops.at(-1); + return { color, width }; + }), + { color: "#ffffff", width: 24 }, + ); + await page.getByRole("button", { name: "Undo", exact: true }).click(); + assert.equal(await page.evaluate(() => window.ops.at(-1).op), "undo"); + + await red.click(); + await draw(); + await eraser.click(); + await pen.click(); + await draw(); + assert.deepEqual( + await page.evaluate(() => + window.ops.slice(-2).map(({ color, width }) => ({ color, width })), + ), + [ + { color: "#c2261b", width: 3 }, + { color: "#c2261b", width: 3 }, + ], + ); +}); + test("the example drawer preserves code, journals pen strokes, and sends the cleared state after reconnecting", async (t) => { if (!browser) return t.skip("playwright chromium unavailable"); const page = await browser.newPage({ diff --git a/web/example-board.js b/web/example-board.js index 004b70499..777b31ba1 100644 --- a/web/example-board.js +++ b/web/example-board.js @@ -128,17 +128,26 @@ export function mountExampleBoard( schedule(); } + function paintToolState() { + for (const button of tools.querySelectorAll("button")) + button.setAttribute("aria-pressed", String(button.dataset.tool === tool)); + for (const button of root.querySelectorAll(".board-color")) + button.setAttribute( + "aria-pressed", + String(button.dataset.color === color), + ); + } + for (const name of ["pen", "eraser"]) { const button = document.createElement("button"); button.type = "button"; button.className = "board-tool"; + button.dataset.tool = name; setToolIcon(button, name); - button.setAttribute("aria-pressed", String(name === tool)); button.addEventListener("click", () => { if (drag) return; tool = name; - for (const other of tools.querySelectorAll("button")) - other.setAttribute("aria-pressed", String(other === button)); + paintToolState(); }); tools.append(button); } @@ -146,17 +155,18 @@ export function mountExampleBoard( const button = document.createElement("button"); button.type = "button"; button.className = "board-color"; + button.dataset.color = value; button.style.background = value; button.setAttribute("aria-label", `Pen ${name}`); - button.setAttribute("aria-pressed", String(value === color)); button.addEventListener("click", () => { if (drag) return; color = value; - for (const other of root.querySelectorAll(".board-color")) - other.setAttribute("aria-pressed", String(other === button)); + tool = "pen"; + paintToolState(); }); root.querySelector("[data-colors]").append(button); } + paintToolState(); for (const action of ["undo", "clear"]) { const button = root.querySelector(`[data-action=${action}]`); setToolIcon(button, action);