From f1cdbae59c46c5064de6e4841f728a68f6a4d33e Mon Sep 17 00:00:00 2001 From: Will Hunt <2072976+Half-Shot@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:50:18 +0000 Subject: [PATCH] Update URL Preview playwright tests to use new endpoint (#32573) * Update URL Preview playwright tests to use new endpoint For https://github.com/matrix-org/matrix-js-sdk/pull/5191 * Drop extra preview test * Adopt axe, fixup paths, etc for url previews * work dammit * begone unwanted screenshot * Make github happy? --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> --- playwright/e2e/messages/messages.spec.ts | 49 ++++++++------ playwright/e2e/timeline/timeline.spec.ts | 61 ------------------ .../timeline.spec.ts/url-preview-linux.png | Bin 10355 -> 0 bytes 3 files changed, 28 insertions(+), 82 deletions(-) delete mode 100644 playwright/snapshots/timeline/timeline.spec.ts/url-preview-linux.png diff --git a/playwright/e2e/messages/messages.spec.ts b/playwright/e2e/messages/messages.spec.ts index 641929e61c..4224737595 100644 --- a/playwright/e2e/messages/messages.spec.ts +++ b/playwright/e2e/messages/messages.spec.ts @@ -220,9 +220,10 @@ test.describe("Message url previews", () => { await use({ roomId }); }, }); - test("should render a basic preview", { tag: "@screenshot" }, async ({ page, user, app, room }) => { - // TODO: This should be changed to _matrix/client/v1/media/preview_url when the matrix-js-sdk is updated. - await page.route("**/_matrix/media/v3/preview_url**", (route, request) => { + test("should render a basic preview", { tag: "@screenshot" }, async ({ page, user, app, room, axe }) => { + axe.disableRules("color-contrast"); + + await page.route(/.*\/_matrix\/(client\/v1\/media|media\/v3)\/preview_url.*/, (route, request) => { const requestedPage = new URL(request.url()).searchParams.get("url"); expect(requestedPage).toEqual("https://example.org/"); return route.fulfill({ @@ -234,25 +235,31 @@ test.describe("Message url previews", () => { await page.goto(`#/room/${room.roomId}`); const msgTile = await sendMessage(page, "https://example.org"); await expect(msgTile.getByRole("link", { name: "A simple site" })).toBeVisible(); + await expect(axe).toHaveNoViolations(); await expect(msgTile).toMatchScreenshot("preview-basic.png", screenshotOptions(page)); }); - test("should render a preview with a thumbnail", { tag: "@screenshot" }, async ({ page, user, bot, app, room }) => { - const mxc = (await bot.uploadContent(MEDIA_FILE, { name: "image.png", type: "image/png" })).content_uri; - // TODO: This should be changed to _matrix/client/v1/media/preview_url when the matrix-js-sdk is updated. - await page.route("**/_matrix/media/v3/preview_url**", (route, request) => { - const requestedPage = new URL(request.url()).searchParams.get("url"); - expect(requestedPage).toEqual("https://example.org/"); - return route.fulfill({ - json: { - "og:title": "A simple site", - "og:description": "And with a brief description", - "og:image": mxc, - }, + test( + "should render a preview with a thumbnail", + { tag: "@screenshot" }, + async ({ page, user, bot, app, room, axe }) => { + axe.disableRules("color-contrast"); + const mxc = (await bot.uploadContent(MEDIA_FILE, { name: "image.png", type: "image/png" })).content_uri; + await page.route(/.*\/_matrix\/(client\/v1\/media|media\/v3)\/preview_url.*/, (route, request) => { + const requestedPage = new URL(request.url()).searchParams.get("url"); + expect(requestedPage).toEqual("https://example.org/"); + return route.fulfill({ + json: { + "og:title": "A simple site", + "og:description": "And with a brief description", + "og:image": mxc, + }, + }); }); - }); - await page.goto(`#/room/${room.roomId}`); - const msgTile = await sendMessage(page, "https://example.org"); - await expect(msgTile.getByRole("link", { name: "A simple site" })).toBeVisible(); - await expect(msgTile).toMatchScreenshot("preview-with-thumb.png", screenshotOptions(page)); - }); + await page.goto(`#/room/${room.roomId}`); + const msgTile = await sendMessage(page, "https://example.org"); + await expect(msgTile.getByRole("link", { name: "A simple site" })).toBeVisible(); + await expect(axe).toHaveNoViolations(); + await expect(msgTile).toMatchScreenshot("preview-with-thumb.png", screenshotOptions(page)); + }, + ); }); diff --git a/playwright/e2e/timeline/timeline.spec.ts b/playwright/e2e/timeline/timeline.spec.ts index ad93a241ff..5553bbd3de 100644 --- a/playwright/e2e/timeline/timeline.spec.ts +++ b/playwright/e2e/timeline/timeline.spec.ts @@ -783,67 +783,6 @@ test.describe("Timeline", () => { ).toBeVisible(); }); - test("should render url previews", { tag: "@screenshot" }, async ({ page, app, room, axe, context }) => { - axe.disableRules("color-contrast"); - - // Element Web uses a Service Worker to rewrite unauthenticated media requests to authenticated ones, but - // the page can't see this happening. We intercept the route at the BrowserContext to ensure we get it - // post-worker, but we can't waitForResponse on that, so the page context is still used there. Because - // the page doesn't see the rewrite, it waits for the unauthenticated route. This is only confusing until - // the js-sdk (and thus the app as a whole) switches to using authenticated endpoints by default, hopefully. - await context.route( - "**/_matrix/client/v1/media/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*", - async (route) => { - await route.fulfill({ - path: "playwright/sample-files/riot.png", - }); - }, - ); - await page.route( - "**/_matrix/media/v3/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*", - async (route) => { - await route.fulfill({ - json: { - "og:title": "Element Call", - "og:description": null, - "og:image:width": 48, - "og:image:height": 48, - "og:image": "mxc://matrix.org/2022-08-16_yaiSVSRIsNFfxDnV", - "og:image:type": "image/png", - "matrix:image:size": 2121, - }, - }); - }, - ); - - const requestPromises: Promise[] = [ - page.waitForResponse("**/_matrix/media/v3/preview_url?url=https%3A%2F%2Fcall.element.io%2F&ts=*"), - // see context.route above for why we listen for the unauthenticated endpoint - page.waitForResponse("**/_matrix/media/v3/thumbnail/matrix.org/2022-08-16_yaiSVSRIsNFfxDnV?*"), - ]; - - await app.client.sendMessage(room.roomId, "https://call.element.io/"); - await page.goto(`/#/room/${room.roomId}`); - - await expect(page.locator(".mx_LinkPreviewWidget").getByText("Element Call")).toBeVisible(); - await Promise.all(requestPromises); - - await expect(axe).toHaveNoViolations(); - - await app.timeline.scrollToBottom(); - await expect(page.locator(".mx_EventTile_last")).toMatchScreenshot("url-preview.png", { - // Exclude timestamp and read marker from snapshot - css: ` - .mx_MessageTimestamp { - visibility: hidden; - } - .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker { - display: none !important; - } - `, - }); - }); - test.describe("on search results panel", () => { test( "should highlight search result words regardless of formatting", diff --git a/playwright/snapshots/timeline/timeline.spec.ts/url-preview-linux.png b/playwright/snapshots/timeline/timeline.spec.ts/url-preview-linux.png deleted file mode 100644 index 0f7d951d699470cfd4d8df02c325459519c7598e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10355 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 ZY^*`ae75?x-WVEK^3uvu6(5bh{Wp-{1hoJF