diff --git a/apps/web/playwright/e2e/composer/CIDER.spec.ts b/apps/web/playwright/e2e/composer/CIDER.spec.ts index 885f63f7f7..3c78beec43 100644 --- a/apps/web/playwright/e2e/composer/CIDER.spec.ts +++ b/apps/web/playwright/e2e/composer/CIDER.spec.ts @@ -20,6 +20,7 @@ test.describe("Composer", () => { botCreateOpts: { displayName: "Bob", }, + lockLeftPanelWidth: false, }); test.use({ @@ -185,6 +186,7 @@ test.describe("Composer", () => { await app.viewRoomByName("Bob"); const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" }); + await composer.click(); await composer.pressSequentially("@bob"); // Note that we include the user ID here as the room tile is also an 'option' role diff --git a/apps/web/playwright/e2e/composer/RTE.spec.ts b/apps/web/playwright/e2e/composer/RTE.spec.ts index 4294ca4b19..fc79208cfd 100644 --- a/apps/web/playwright/e2e/composer/RTE.spec.ts +++ b/apps/web/playwright/e2e/composer/RTE.spec.ts @@ -216,10 +216,16 @@ test.describe("Composer", () => { await expect(page.locator(".mx_ImageBody")).toBeVisible(); }); - test("renders in narrow viewports", { tag: "@screenshot" }, async ({ page, bot, app }) => { - // Shrink the viewport - await page.setViewportSize({ width: 750, height: 1080 }); - await expect(page.locator(".mx_MessageComposer_wrapper")).toMatchScreenshot("narrow.png"); + test.describe(() => { + test.use({ + lockLeftPanelWidth: false, + }); + + test("renders in narrow viewports", { tag: "@screenshot" }, async ({ page, bot, app }) => { + // Shrink the viewport + await page.setViewportSize({ width: 750, height: 1080 }); + await expect(page.locator(".mx_MessageComposer_wrapper")).toMatchScreenshot("narrow.png"); + }); }); test.describe("when Control+Enter is required to send", () => { diff --git a/apps/web/playwright/e2e/crypto/history-sharing.spec.ts b/apps/web/playwright/e2e/crypto/history-sharing.spec.ts index 055e38dc76..595bae44de 100644 --- a/apps/web/playwright/e2e/crypto/history-sharing.spec.ts +++ b/apps/web/playwright/e2e/crypto/history-sharing.spec.ts @@ -61,13 +61,18 @@ test.describe("History sharing", function () { // Bob should now be able to decrypt the event await expect(bobPage.getByText("A message from Alice")).toBeVisible(); - // Exclude message timestamps and RR avatars from the screenshot. Bob sometimes sees Alice's RR on the + // Mask message timestamps and exclude RR avatars from the screenshot. Bob sometimes sees Alice's RR on the // previous event, which is surprising but not what we're testing here. - const mask = [bobPage.locator(".mx_MessageTimestamp"), bobPage.locator(".mx_ReadReceiptGroup_container")]; + const mask = [bobPage.locator(".mx_MessageTimestamp")]; await expect(bobPage.locator(".mx_RoomView_timeline")).toMatchScreenshot( "shared-history-invite-accepted.png", { mask, + css: ` + .mx_ReadReceiptGroup_container { + display: none !important; + } + `, }, ); }, diff --git a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-collapse.spec.ts b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-collapse.spec.ts index 0112c73f55..1649cdc5c6 100644 --- a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-collapse.spec.ts +++ b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-collapse.spec.ts @@ -13,6 +13,7 @@ import type { Locator, Page } from "playwright-core"; test.describe("Collapsible Room list", () => { test.use({ displayName: "Alice", + lockLeftPanelWidth: false, }); test.beforeEach(async ({ page, app, user }) => { diff --git a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-panel.spec.ts b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-panel.spec.ts index 00bc0c4116..5f0bebd8fa 100644 --- a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-panel.spec.ts +++ b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-panel.spec.ts @@ -36,9 +36,12 @@ test.describe("Room list panel", () => { await expect(roomListView).toMatchScreenshot("room-list-panel.png"); }); - test("should respond to small screen sizes", { tag: "@screenshot" }, async ({ page }) => { - await page.setViewportSize({ width: 575, height: 600 }); - const roomListPanel = getRoomListView(page); - await expect(roomListPanel).toMatchScreenshot("room-list-panel-smallscreen.png"); + test.describe("small screen", () => { + test.use({ lockLeftPanelWidth: false }); + test("should respond to small screen sizes", { tag: "@screenshot" }, async ({ page }) => { + await page.setViewportSize({ width: 575, height: 600 }); + const roomListPanel = getRoomListView(page); + await expect(roomListPanel).toMatchScreenshot("room-list-panel-smallscreen.png"); + }); }); }); diff --git a/apps/web/playwright/snapshots/app-loading/stored-credentials.spec.ts/home-thin-viewport-linux.png b/apps/web/playwright/snapshots/app-loading/stored-credentials.spec.ts/home-thin-viewport-linux.png index 737a6dbb5b..5681da209e 100644 Binary files a/apps/web/playwright/snapshots/app-loading/stored-credentials.spec.ts/home-thin-viewport-linux.png and b/apps/web/playwright/snapshots/app-loading/stored-credentials.spec.ts/home-thin-viewport-linux.png differ diff --git a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png index 4ab37fc895..3c3aae1e6f 100644 Binary files a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png and b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png differ diff --git a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png index b3dff463b3..008e450ca3 100644 Binary files a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png and b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png differ diff --git a/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png b/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png index c0f8d16a16..455917ebe2 100644 Binary files a/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png and b/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png differ diff --git a/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png b/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png index 2ab2f51f56..ede6fe9fe7 100644 Binary files a/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png and b/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png differ diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png index 7a57fef52f..e9e4462317 100644 Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png differ diff --git a/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/local-room-create-failed-linux.png b/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/local-room-create-failed-linux.png index e604e01a1e..8e24d1a4da 100644 Binary files a/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/local-room-create-failed-linux.png and b/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/local-room-create-failed-linux.png differ diff --git a/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/message-failed-linux.png b/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/message-failed-linux.png index 673ab956d6..0060d327fc 100644 Binary files a/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/message-failed-linux.png and b/apps/web/playwright/snapshots/room/room-status-bar.spec.ts/message-failed-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png index 9fe134e7f6..5f0bfd67f9 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-bubble-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png index 04e0a88ffa..a29df151f4 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-irc-layout-linux.png differ diff --git a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png index 311df4e505..5dc707fb8a 100644 Binary files a/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png and b/apps/web/playwright/snapshots/timeline/timeline.spec.ts/long-strings-with-reply-modern-layout-linux.png differ diff --git a/packages/playwright-common/src/fixtures/panel.ts b/packages/playwright-common/src/fixtures/panel.ts new file mode 100644 index 0000000000..b7d6d4b5da --- /dev/null +++ b/packages/playwright-common/src/fixtures/panel.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ +import type { Page } from "playwright-core"; +import { test as base } from "./services.js"; + +/** + * This is the width the panel by default. + * Rounding this number to a whole number would mean updating a whole + * bunch of screenshots. + */ +const LEFT_PANEL_WIDTH = "369.6875px"; + +export const test = base.extend<{ + /** + * Whether the left panel should have its width fixed. + * This is done because the library that we use for rendering collapsible + * panels uses math to calculate the width which can sometimes leads to +/-1px + * difference. While this does not matter to the user, it can lead to screenshot + * tests failing. + * Defaults to true, should be set to false via {@link base.use} when you want to test the collapse + * behaviour. + */ + lockLeftPanelWidth: boolean; +}>({ + lockLeftPanelWidth: true, + page: async ({ lockLeftPanelWidth, page }, use) => { + const listener = async (page: Page) => { + try { + await page.addStyleTag({ + content: ` + #left-panel { + flex: 0 0 ${LEFT_PANEL_WIDTH} !important; + } + `, + }); + } catch (e) { + console.error("Failed to add style tag to stabilize left panel", e); + } + }; + if (lockLeftPanelWidth) page.on("load", listener); + await use(page); + if (lockLeftPanelWidth) page.off("load", listener); + }, +}); diff --git a/packages/playwright-common/src/fixtures/user.ts b/packages/playwright-common/src/fixtures/user.ts index 573a47ab60..900dca1a23 100644 --- a/packages/playwright-common/src/fixtures/user.ts +++ b/packages/playwright-common/src/fixtures/user.ts @@ -12,7 +12,7 @@ import { sample, uniqueId } from "lodash-es"; // We want to avoid using `mergeTests` in index.ts because it drops useful type // information about the fixtures. Instead, we add `services` into our fixture // suite by using its `test` as a base, so that there is a linear hierarchy. -import { test as base } from "./services.js"; +import { test as base } from "./panel.js"; import { type Credentials } from "../utils/api.js"; /** Adds an initScript to the given page which will populate localStorage appropriately so that Element will use the given credentials. */ diff --git a/packages/playwright-common/src/index.ts b/packages/playwright-common/src/index.ts index 60904b5e0f..9644a15982 100644 --- a/packages/playwright-common/src/index.ts +++ b/packages/playwright-common/src/index.ts @@ -84,18 +84,8 @@ export const test = base.extend({ config: async ({}, use) => use({}), labsFlags: async ({}, use) => use([]), disablePresence: async ({}, use) => use(false), - lockLeftPanelWidth: true, - page: async ({ homeserver, context, page, config, labsFlags, disablePresence, lockLeftPanelWidth }, use) => { + page: async ({ homeserver, context, page, config, labsFlags, disablePresence }, use) => { await routeConfigJson(context, homeserver.baseUrl, config, labsFlags, disablePresence); - if (lockLeftPanelWidth) { - await page.addStyleTag({ - content: ` - #left-panel { - flex: 0 0 369.6875px !important; - } - `, - }); - } await use(page); }, }); diff --git a/packages/shared-components/package.json b/packages/shared-components/package.json index eae853d214..4399a17fad 100644 --- a/packages/shared-components/package.json +++ b/packages/shared-components/package.json @@ -78,7 +78,7 @@ "matrix-web-i18n": "catalog:", "react-blurhash": "^0.3.0", "react-merge-refs": "^3.0.2", - "react-resizable-panels": "4.7.2", + "react-resizable-panels": "4.9.0", "react-virtuoso": "^4.14.0", "temporal-polyfill": "^0.3.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d808713597..793d924aaf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1080,8 +1080,8 @@ importers: specifier: ^3.0.2 version: 3.0.2(react@19.2.6) react-resizable-panels: - specifier: 4.7.2 - version: 4.7.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + specifier: 4.9.0 + version: 4.9.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-virtuoso: specifier: ^4.14.0 version: 4.18.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -11498,8 +11498,8 @@ packages: '@types/react': optional: true - react-resizable-panels@4.7.2: - resolution: {integrity: sha512-1L2vyeBG96hp7N6x6rzYXJ8EjYiDiffMsqj3cd+T9aOKwscvuyCn2CuZ5q3PoUSTIJUM6Q5DgXH1bdDe6uvh2w==} + react-resizable-panels@4.9.0: + resolution: {integrity: sha512-sEl+hA6y9/kxa0aPlrUC+G1lcShAf/PiIjoeC8kWXxa53RfAVplVCIxEl01Nwa4L2iRa5JXBXq1/mI8ch6qOZQ==} peerDependencies: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 @@ -25455,7 +25455,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - react-resizable-panels@4.7.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-resizable-panels@4.9.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: react: 19.2.6 react-dom: 19.2.6(react@19.2.6)