From afb252f42e27bc5e97b9b3fc145d29019f4bf59f Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 13 Feb 2026 15:03:08 +0100 Subject: [PATCH] :wrench: Migrate text editor v2 tests to wasm viewport --- .../playwright/ui/pages/WasmWorkspacePage.js | 4 +- frontend/playwright/ui/pages/WorkspacePage.js | 71 ++++------- .../ui/specs/text-editor-v2.spec.js | 119 +++++++----------- ...size-selecting-a-part-of-it-starting-1.png | Bin 0 -> 12662 bytes ...cing-selecting-a-part-of-it-starting-1.png | Bin 0 -> 11392 bytes ...ight-selecting-a-part-of-it-starting-1.png | Bin 0 -> 11399 bytes .../main/ui/workspace/sidebar/layer_item.cljs | 2 + 7 files changed, 75 insertions(+), 121 deletions(-) create mode 100644 frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-font-size-selecting-a-part-of-it-starting-1.png create mode 100644 frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-letter-spacing-selecting-a-part-of-it-starting-1.png create mode 100644 frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-line-height-selecting-a-part-of-it-starting-1.png diff --git a/frontend/playwright/ui/pages/WasmWorkspacePage.js b/frontend/playwright/ui/pages/WasmWorkspacePage.js index 405f3eb55e..a877f03dba 100644 --- a/frontend/playwright/ui/pages/WasmWorkspacePage.js +++ b/frontend/playwright/ui/pages/WasmWorkspacePage.js @@ -35,8 +35,8 @@ export class WasmWorkspacePage extends WorkspacePage { return WasmWorkspacePage.mockConfigFlags(this.page, flags); } - constructor(page) { - super(page); + constructor(page, options) { + super(page, options); this.canvas = page.getByTestId("canvas-wasm-shapes"); } diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 46c975827a..c9f6fd97c7 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -35,45 +35,9 @@ export class WorkspacePage extends BaseWebSocketPage { } async waitForEditor() { - return this.page.waitForSelector('[data-itype="editor"]'); - } - - async waitForRoot() { - return this.page.waitForSelector('[data-itype="root"]'); - } - - async waitForParagraph(nth) { - if (!nth) { - return this.page.waitForSelector('[data-itype="paragraph"]'); - } - return this.page.waitForSelector( - `[data-itype="paragraph"]:nth-child(${nth})`, - ); - } - - async waitForParagraphStyle(nth, styleName) { - const paragraph = await this.waitForParagraph(nth); - return this.waitForStyle(paragraph, styleName); - } - - async waitForTextSpan(nth = 0) { - if (!nth) { - return this.page.waitForSelector('[data-itype="span"]'); - } - return this.page.waitForSelector( - `[data-itype="span"]:nth-child(${nth})`, - ); - } - - async waitForTextSpanContent(nth = 0) { - const textSpan = await this.waitForTextSpan(nth); - const textContent = await textSpan.textContent(); - return textContent; - } - - async waitForTextSpanStyle(nth, styleName) { - const textSpan = await this.waitForTextSpan(nth); - return this.waitForStyle(textSpan, styleName); + const typographyInput = + this.workspacePage.rightSidebar.getByLabel("Font Size"); + await expect(typographyInput).toBeVisible(); } async startEditing() { @@ -98,7 +62,7 @@ export class WorkspacePage extends BaseWebSocketPage { } async moveFromStart(offset = 0) { - await this.page.keyboard.press("ArrowLeft"); + await this.page.keyboard.press("Home"); await this.moveToRight(offset); } @@ -125,7 +89,7 @@ export class WorkspacePage extends BaseWebSocketPage { await expect(locator).toBeVisible(); await locator.focus(); await locator.fill(`${newValue}`); - await locator.blur(); + await this.page.keyboard.press("Enter"); } changeFontSize(newValue) { @@ -390,10 +354,12 @@ export class WorkspacePage extends BaseWebSocketPage { const timeToWait = options?.timeToWait ?? 100; await this.page.keyboard.press("T"); await this.page.waitForTimeout(timeToWait); + + const layersCountBefore = await this.layers.getByTestId("layer-row").count(); await this.clickAndMove(x1, y1, x2, y2); - await expect(this.page.getByTestId("text-editor")).toBeVisible(); if (initialText) { + await this.waitForSelectedShapeName("Text"); await this.page.keyboard.type(initialText); } } @@ -493,10 +459,23 @@ export class WorkspacePage extends BaseWebSocketPage { async expectSelectedLayer(name) { await expect( - this.layers - .getByTestId("layer-row") - .filter({ has: this.page.getByText(name) }), - ).toHaveClass(/selected/); + this.layers.getByRole("checkbox", { name, checked: true }), + ).toBeVisible(); + } + + async getSelectedShapeName() { + const selectedLayer = this.layers + .getByRole("checkbox", { checked: true }) + .first(); + await selectedLayer.waitFor({ state: "visible" }); + return (await selectedLayer.innerText()).trim(); + } + + async waitForSelectedShapeName(expectedName) { + const selectedLayer = this.layers + .getByRole("checkbox", { checked: true }) + .first(); + await expect(selectedLayer).toHaveText(expectedName); } async expectHiddenToolbarOptions() { diff --git a/frontend/playwright/ui/specs/text-editor-v2.spec.js b/frontend/playwright/ui/specs/text-editor-v2.spec.js index 299430fad1..e32e0536a3 100644 --- a/frontend/playwright/ui/specs/text-editor-v2.spec.js +++ b/frontend/playwright/ui/specs/text-editor-v2.spec.js @@ -1,14 +1,14 @@ import { test, expect } from "@playwright/test"; import { Clipboard } from "../../helpers/Clipboard"; -import { WorkspacePage } from "../pages/WorkspacePage"; +import { WasmWorkspacePage } from "../pages/WasmWorkspacePage"; const timeToWait = 100; test.beforeEach(async ({ page, context }) => { await Clipboard.enable(context, Clipboard.Permission.ALL); - await WorkspacePage.init(page); - await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]); + await WasmWorkspacePage.init(page); + await WasmWorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]); }); test.afterEach(async ({ context }) => { @@ -17,22 +17,21 @@ test.afterEach(async ({ context }) => { test("Create a new text shape", async ({ page }) => { const initialText = "Lorem ipsum"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); await workspace.goToWorkspace(); await workspace.createTextShape(190, 150, 300, 200, initialText); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe(initialText); - await workspace.textEditor.stopEditing(); + + await workspace.waitForSelectedShapeName(initialText); }); test("Create a new text shape from pasting text", async ({ page, context }) => { const textToPaste = "Lorem ipsum"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -43,13 +42,9 @@ test("Create a new text shape from pasting text", async ({ page, context }) => { await workspace.clickAt(190, 150); await workspace.paste("keyboard"); - - await page.waitForTimeout(timeToWait); - - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe(textToPaste); - await workspace.textEditor.stopEditing(); + + await expect(workspace.layers.getByText(textToPaste)).toBeVisible(); }); test("Create a new text shape from pasting text using context menu", async ({ @@ -57,7 +52,7 @@ test("Create a new text shape from pasting text using context menu", async ({ context, }) => { const textToPaste = "Lorem ipsum"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -67,17 +62,15 @@ test("Create a new text shape from pasting text using context menu", async ({ await workspace.clickAt(190, 150); await workspace.paste("context-menu"); - - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe(textToPaste); - await workspace.textEditor.stopEditing(); + + await expect(workspace.layers.getByText(textToPaste)).toBeVisible(); }); test("Update an already created text shape by appending text", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -87,15 +80,14 @@ test("Update an already created text shape by appending text", async ({ await workspace.textEditor.startEditing(); await workspace.textEditor.moveFromEnd(0); await page.keyboard.type(" dolor sit amet"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lorem ipsum dolor sit amet"); await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); }); test("Update an already created text shape by prepending text", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -105,15 +97,14 @@ test("Update an already created text shape by prepending text", async ({ await workspace.textEditor.startEditing(); await workspace.textEditor.moveFromStart(0); await page.keyboard.type("Dolor sit amet "); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Dolor sit amet Lorem ipsum"); await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Dolor sit amet Lorem ipsum"); }); test.skip("Update an already created text shape by inserting text in between", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -123,9 +114,8 @@ test.skip("Update an already created text shape by inserting text in between", a await workspace.textEditor.startEditing(); await workspace.textEditor.moveFromStart(5); await page.keyboard.type(" dolor sit amet"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lorem dolor sit amet ipsum"); await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Lorem dolor sit amet ipsum"); }); test("Update a new text shape appending text by pasting text", async ({ @@ -133,7 +123,7 @@ test("Update a new text shape appending text by pasting text", async ({ context, }) => { const textToPaste = " dolor sit amet"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -146,17 +136,16 @@ test("Update a new text shape appending text by pasting text", async ({ await workspace.textEditor.startEditing(); await workspace.textEditor.moveFromEnd(); await workspace.paste("keyboard"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lorem ipsum dolor sit amet"); await workspace.textEditor.stopEditing(); -}); + await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet"); + }); test.skip("Update a new text shape prepending text by pasting text", async ({ page, context, }) => { const textToPaste = "Dolor sit amet "; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -169,16 +158,17 @@ test.skip("Update a new text shape prepending text by pasting text", async ({ await workspace.textEditor.startEditing(); await workspace.textEditor.moveFromStart(); await workspace.paste("keyboard"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Dolor sit amet Lorem ipsum"); await workspace.textEditor.stopEditing(); + + await workspace.hideUI(); + await expect(workspace.canvas).toHaveScreenshot(); }); test("Update a new text shape replacing (starting) text with pasted text", async ({ page, }) => { const textToPaste = "Dolor sit amet"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -192,17 +182,15 @@ test("Update a new text shape replacing (starting) text with pasted text", async await workspace.paste("keyboard"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Dolor sit amet ipsum"); - await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Dolor sit amet ipsum"); }); test("Update a new text shape replacing (ending) text with pasted text", async ({ page, }) => { const textToPaste = "dolor sit amet"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -216,17 +204,15 @@ test("Update a new text shape replacing (ending) text with pasted text", async ( await workspace.paste("keyboard"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lorem dolor sit amet"); - await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Lorem dolor sit amet"); }); test("Update a new text shape replacing (in between) text with pasted text", async ({ page, }) => { const textToPaste = "dolor sit amet"; - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -240,16 +226,14 @@ test("Update a new text shape replacing (in between) text with pasted text", asy await workspace.paste("keyboard"); - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lordolor sit ametsum"); - await workspace.textEditor.stopEditing(); + await workspace.waitForSelectedShapeName("Lordolor sit ametsum"); }); test("Update text font size selecting a part of it (starting)", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -260,18 +244,16 @@ test("Update text font size selecting a part of it (starting)", async ({ await workspace.textEditor.startEditing(); await workspace.textEditor.selectFromStart(5); await workspace.textEditor.changeFontSize(36); - - const textContent1 = await workspace.textEditor.waitForTextSpanContent(1); - expect(textContent1).toBe("Lorem"); - const textContent2 = await workspace.textEditor.waitForTextSpanContent(2); - expect(textContent2).toBe(" ipsum"); await workspace.textEditor.stopEditing(); + + await workspace.hideUI(); + await expect(workspace.canvas).toHaveScreenshot(); }); -test.skip("Update text line height selecting a part of it (starting)", async ({ +test("Update text line height selecting a part of it (starting)", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -281,24 +263,17 @@ test.skip("Update text line height selecting a part of it (starting)", async ({ await workspace.clickLeafLayer("Lorem ipsum"); await workspace.textEditor.startEditing(); await workspace.textEditor.selectFromStart(5); - await workspace.textEditor.changeLineHeight(1.4); - - const lineHeight = await workspace.textEditor.waitForParagraphStyle( - 1, - "line-height", - ); - expect(lineHeight).toBe("1.4"); - - const textContent = await workspace.textEditor.waitForTextSpanContent(); - expect(textContent).toBe("Lorem ipsum"); - + await workspace.textEditor.changeLineHeight(4.4); await workspace.textEditor.stopEditing(); + + await workspace.hideUI(); + await expect(workspace.canvas).toHaveScreenshot(); }); test.skip("Update text letter spacing selecting a part of it (starting)", async ({ page, }) => { - const workspace = new WorkspacePage(page, { + const workspace = new WasmWorkspacePage(page, { textEditor: true, }); await workspace.setupEmptyFile(); @@ -309,16 +284,14 @@ test.skip("Update text letter spacing selecting a part of it (starting)", async await workspace.textEditor.startEditing(); await workspace.textEditor.selectFromStart(5); await workspace.textEditor.changeLetterSpacing(10); - - const textContent1 = await workspace.textEditor.waitForTextSpanContent(1); - expect(textContent1).toBe("Lorem"); - const textContent2 = await workspace.textEditor.waitForTextSpanContent(2); - expect(textContent2).toBe(" ipsum"); await workspace.textEditor.stopEditing(); + + await workspace.hideUI(); + await expect(workspace.canvas).toHaveScreenshot(); }); test("BUG 11552 - Apply styles to the current caret", async ({ page }) => { - const workspace = new WorkspacePage(page); + const workspace = new WasmWorkspacePage(page); await workspace.setupEmptyFile(); await workspace.mockGetFile("text-editor/get-file-11552.json"); await workspace.mockRPC( diff --git a/frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-font-size-selecting-a-part-of-it-starting-1.png b/frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-font-size-selecting-a-part-of-it-starting-1.png new file mode 100644 index 0000000000000000000000000000000000000000..0d31872f790e465f9b142dd57bfc6dd711998558 GIT binary patch literal 12662 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 u$pUirg?)c*vJ9Cs%G literal 0 HcmV?d00001 diff --git a/frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-letter-spacing-selecting-a-part-of-it-starting-1.png b/frontend/playwright/ui/specs/text-editor-v2.spec.js-snapshots/Update-text-letter-spacing-selecting-a-part-of-it-starting-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4b091658c9b94936d207e34c34ef237670c7c733 GIT binary patch literal 11392 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 F_$O3@gJA#w literal 0 HcmV?d00001 diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index 8c22f87fc5..550e072d07 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -84,6 +84,8 @@ :on-click on-select-shape :on-context-menu on-context-menu :data-testid "layer-row" + :role "checkbox" + :aria-checked selected? :class (stl/css-case :layer-row true :highlight highlighted?