diff --git a/package.json b/package.json
index d0a073ec2e..68ee57efcd 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,7 @@
         "test:playwright:open": "yarn test:playwright --ui",
         "test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
         "test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
-        "test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright",
+        "test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright --grep @screenshot",
         "coverage": "yarn test --coverage",
         "analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
         "analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
diff --git a/playwright/e2e/app-loading/feature-detection.spec.ts b/playwright/e2e/app-loading/feature-detection.spec.ts
index 16e17a8054..ee61fb5662 100644
--- a/playwright/e2e/app-loading/feature-detection.spec.ts
+++ b/playwright/e2e/app-loading/feature-detection.spec.ts
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
 
 import { test, expect } from "../../element-web-test";
 
-test(`shows error page if browser lacks Intl support`, async ({ page }) => {
+test(`shows error page if browser lacks Intl support`, { tag: "@screenshot" }, async ({ page }) => {
     await page.addInitScript({ content: `delete window.Intl;` });
     await page.goto("/");
 
@@ -21,7 +21,7 @@ test(`shows error page if browser lacks Intl support`, async ({ page }) => {
     await expect(page).toMatchScreenshot("unsupported-browser.png");
 });
 
-test(`shows error page if browser lacks WebAssembly support`, async ({ page }) => {
+test(`shows error page if browser lacks WebAssembly support`, { tag: "@screenshot" }, async ({ page }) => {
     await page.addInitScript({ content: `delete window.WebAssembly;` });
     await page.goto("/");
 
diff --git a/playwright/e2e/audio-player/audio-player.spec.ts b/playwright/e2e/audio-player/audio-player.spec.ts
index c2081dfcd8..2bb9ab0be4 100644
--- a/playwright/e2e/audio-player/audio-player.spec.ts
+++ b/playwright/e2e/audio-player/audio-player.spec.ts
@@ -134,18 +134,22 @@ test.describe("Audio player", () => {
         ).toBeVisible();
     });
 
-    test("should be correctly rendered - light theme", async ({ page, app }) => {
+    test("should be correctly rendered - light theme", { tag: "@screenshot" }, async ({ page, app }) => {
         await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
         await takeSnapshots(page, app, "Selected EventTile of audio player (light theme)");
     });
 
-    test("should be correctly rendered - light theme with monospace font", async ({ page, app }) => {
-        await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
+    test(
+        "should be correctly rendered - light theme with monospace font",
+        { tag: "@screenshot" },
+        async ({ page, app }) => {
+            await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
 
-        await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
-    });
+            await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
+        },
+    );
 
-    test("should be correctly rendered - high contrast theme", async ({ page, app }) => {
+    test("should be correctly rendered - high contrast theme", { tag: "@screenshot" }, async ({ page, app }) => {
         // Disable system theme in case ThemeWatcher enables the theme automatically,
         // so that the high contrast theme can be enabled
         await app.settings.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
@@ -161,7 +165,7 @@ test.describe("Audio player", () => {
         await takeSnapshots(page, app, "Selected EventTile of audio player (high contrast)");
     });
 
-    test("should be correctly rendered - dark theme", async ({ page, app }) => {
+    test("should be correctly rendered - dark theme", { tag: "@screenshot" }, async ({ page, app }) => {
         // Enable dark theme
         await app.settings.setValue("theme", null, SettingLevel.ACCOUNT, "dark");
 
@@ -207,93 +211,101 @@ test.describe("Audio player", () => {
         expect(download.suggestedFilename()).toBe("1sec.ogg");
     });
 
-    test("should support replying to audio file with another audio file", async ({ page, app }) => {
-        await uploadFile(page, "playwright/sample-files/1sec.ogg");
+    test(
+        "should support replying to audio file with another audio file",
+        { tag: "@screenshot" },
+        async ({ page, app }) => {
+            await uploadFile(page, "playwright/sample-files/1sec.ogg");
 
-        // Assert the audio player is rendered
-        await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
+            // Assert the audio player is rendered
+            await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
 
-        // Find and click "Reply" button on MessageActionBar
-        const tile = page.locator(".mx_EventTile_last");
-        await tile.hover();
-        await tile.getByRole("button", { name: "Reply", exact: true }).click();
-
-        // Reply to the player with another audio file
-        await uploadFile(page, "playwright/sample-files/1sec.ogg");
-
-        // Assert that the audio player is rendered
-        await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
-
-        // Assert that replied audio file is rendered as file button inside ReplyChain
-        const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
-        // Assert that the file button has file name
-        await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
-
-        await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
-    });
-
-    test("should support creating a reply chain with multiple audio files", async ({ page, app, user }) => {
-        // Note: "mx_ReplyChain" element is used not only for replies which
-        // create a reply chain, but also for a single reply without a replied
-        // message. This test checks whether a reply chain which consists of
-        // multiple audio file replies is rendered properly.
-
-        const tile = page.locator(".mx_EventTile_last");
-
-        // Find and click "Reply" button
-        const clickButtonReply = async () => {
-            await tile.scrollIntoViewIfNeeded();
+            // Find and click "Reply" button on MessageActionBar
+            const tile = page.locator(".mx_EventTile_last");
             await tile.hover();
             await tile.getByRole("button", { name: "Reply", exact: true }).click();
-        };
 
-        await uploadFile(page, "playwright/sample-files/upload-first.ogg");
+            // Reply to the player with another audio file
+            await uploadFile(page, "playwright/sample-files/1sec.ogg");
 
-        // Assert that the audio player is rendered
-        await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
+            // Assert that the audio player is rendered
+            await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
 
-        await clickButtonReply();
+            // Assert that replied audio file is rendered as file button inside ReplyChain
+            const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
+            // Assert that the file button has file name
+            await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
 
-        // Reply to the player with another audio file
-        await uploadFile(page, "playwright/sample-files/upload-second.ogg");
+            await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
+        },
+    );
 
-        // Assert that the audio player is rendered
-        await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
+    test(
+        "should support creating a reply chain with multiple audio files",
+        { tag: "@screenshot" },
+        async ({ page, app, user }) => {
+            // Note: "mx_ReplyChain" element is used not only for replies which
+            // create a reply chain, but also for a single reply without a replied
+            // message. This test checks whether a reply chain which consists of
+            // multiple audio file replies is rendered properly.
 
-        await clickButtonReply();
+            const tile = page.locator(".mx_EventTile_last");
 
-        // Reply to the player with yet another audio file to create a reply chain
-        await uploadFile(page, "playwright/sample-files/upload-third.ogg");
+            // Find and click "Reply" button
+            const clickButtonReply = async () => {
+                await tile.scrollIntoViewIfNeeded();
+                await tile.hover();
+                await tile.getByRole("button", { name: "Reply", exact: true }).click();
+            };
 
-        // Assert that the audio player is rendered
-        await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
+            await uploadFile(page, "playwright/sample-files/upload-first.ogg");
 
-        // Assert that there are two "mx_ReplyChain" elements
-        await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
+            // Assert that the audio player is rendered
+            await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
 
-        // Assert that one line contains the user name
-        await expect(tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName)).toBeVisible();
+            await clickButtonReply();
 
-        // Assert that the other line contains the file button
-        await expect(tile.locator(".mx_ReplyChain .mx_MFileBody")).toBeVisible();
+            // Reply to the player with another audio file
+            await uploadFile(page, "playwright/sample-files/upload-second.ogg");
 
-        // Click "In reply to"
-        await tile.locator(".mx_ReplyChain .mx_ReplyChain_show", { hasText: "In reply to" }).click();
+            // Assert that the audio player is rendered
+            await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
 
-        const replyChain = tile.locator(".mx_ReplyChain:first-of-type");
-        // Assert that "In reply to" has disappeared
-        await expect(replyChain.getByText("In reply to")).not.toBeVisible();
+            await clickButtonReply();
 
-        // Assert that the file button contains the name of the file sent at first
-        await expect(
-            replyChain
-                .locator(".mx_MFileBody_info[role='button']")
-                .locator(".mx_MFileBody_info_filename", { hasText: "upload-first.ogg" }),
-        ).toBeVisible();
+            // Reply to the player with yet another audio file to create a reply chain
+            await uploadFile(page, "playwright/sample-files/upload-third.ogg");
 
-        // Take snapshots
-        await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
-    });
+            // Assert that the audio player is rendered
+            await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
+
+            // Assert that there are two "mx_ReplyChain" elements
+            await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
+
+            // Assert that one line contains the user name
+            await expect(tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName)).toBeVisible();
+
+            // Assert that the other line contains the file button
+            await expect(tile.locator(".mx_ReplyChain .mx_MFileBody")).toBeVisible();
+
+            // Click "In reply to"
+            await tile.locator(".mx_ReplyChain .mx_ReplyChain_show", { hasText: "In reply to" }).click();
+
+            const replyChain = tile.locator(".mx_ReplyChain:first-of-type");
+            // Assert that "In reply to" has disappeared
+            await expect(replyChain.getByText("In reply to")).not.toBeVisible();
+
+            // Assert that the file button contains the name of the file sent at first
+            await expect(
+                replyChain
+                    .locator(".mx_MFileBody_info[role='button']")
+                    .locator(".mx_MFileBody_info_filename", { hasText: "upload-first.ogg" }),
+            ).toBeVisible();
+
+            // Take snapshots
+            await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
+        },
+    );
 
     test("should be rendered, play, and support replying on a thread", async ({ page, app }) => {
         await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
diff --git a/playwright/e2e/chat-export/html-export.spec.ts b/playwright/e2e/chat-export/html-export.spec.ts
index 9a66a4907a..f914cccd96 100644
--- a/playwright/e2e/chat-export/html-export.spec.ts
+++ b/playwright/e2e/chat-export/html-export.spec.ts
@@ -89,43 +89,47 @@ test.describe("HTML Export", () => {
         },
     });
 
-    test("should export html successfully and match screenshot", async ({ page, app, room }) => {
-        // Set a fixed time rather than masking off the line with the time in it: we don't need to worry
-        // about the width changing and we can actually test this line looks correct.
-        page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
+    test(
+        "should export html successfully and match screenshot",
+        { tag: "@screenshot" },
+        async ({ page, app, room }) => {
+            // Set a fixed time rather than masking off the line with the time in it: we don't need to worry
+            // about the width changing and we can actually test this line looks correct.
+            page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
 
-        // Send a bunch of messages to populate the room
-        for (let i = 1; i < 10; i++) {
-            const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
-            if (i == 1) {
-                await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
+            // Send a bunch of messages to populate the room
+            for (let i = 1; i < 10; i++) {
+                const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
+                if (i == 1) {
+                    await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
+                }
             }
-        }
 
-        // Wait for all the messages to be displayed
-        await expect(
-            page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText("Testing 9"),
-        ).toBeVisible();
+            // Wait for all the messages to be displayed
+            await expect(
+                page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText("Testing 9"),
+            ).toBeVisible();
 
-        await app.toggleRoomInfoPanel();
-        await page.getByRole("menuitem", { name: "Export Chat" }).click();
+            await app.toggleRoomInfoPanel();
+            await page.getByRole("menuitem", { name: "Export Chat" }).click();
 
-        const downloadPromise = page.waitForEvent("download");
-        await page.getByRole("button", { name: "Export", exact: true }).click();
-        const download = await downloadPromise;
+            const downloadPromise = page.waitForEvent("download");
+            await page.getByRole("button", { name: "Export", exact: true }).click();
+            const download = await downloadPromise;
 
-        const dirPath = path.join(os.tmpdir(), "html-export-test");
-        const zipPath = `${dirPath}.zip`;
-        await download.saveAs(zipPath);
+            const dirPath = path.join(os.tmpdir(), "html-export-test");
+            const zipPath = `${dirPath}.zip`;
+            await download.saveAs(zipPath);
 
-        const zip = await extractZipFileToPath(zipPath, dirPath);
-        await page.goto(`file://${dirPath}/${Object.keys(zip.files)[0]}/messages.html`);
-        await expect(page).toMatchScreenshot("html-export.png", {
-            mask: [
-                // We need to mask the whole thing because the width of the time part changes
-                page.locator(".mx_TimelineSeparator"),
-                page.locator(".mx_MessageTimestamp"),
-            ],
-        });
-    });
+            const zip = await extractZipFileToPath(zipPath, dirPath);
+            await page.goto(`file://${dirPath}/${Object.keys(zip.files)[0]}/messages.html`);
+            await expect(page).toMatchScreenshot("html-export.png", {
+                mask: [
+                    // We need to mask the whole thing because the width of the time part changes
+                    page.locator(".mx_TimelineSeparator"),
+                    page.locator(".mx_MessageTimestamp"),
+                ],
+            });
+        },
+    );
 });
diff --git a/playwright/e2e/crypto/crypto.spec.ts b/playwright/e2e/crypto/crypto.spec.ts
index 2ab49e72ec..668c17d931 100644
--- a/playwright/e2e/crypto/crypto.spec.ts
+++ b/playwright/e2e/crypto/crypto.spec.ts
@@ -204,30 +204,29 @@ test.describe("Cryptography", function () {
         await expect(page.locator(".mx_Dialog")).toHaveCount(1);
     });
 
-    test("creating a DM should work, being e2e-encrypted / user verification", async ({
-        page,
-        app,
-        bot: bob,
-        user: aliceCredentials,
-    }) => {
-        await app.client.bootstrapCrossSigning(aliceCredentials);
-        await startDMWithBob(page, bob);
-        // send first message
-        await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
-        await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
-        await checkDMRoom(page);
-        const bobRoomId = await bobJoin(page, bob);
-        await testMessages(page, bob, bobRoomId);
-        await verify(app, bob);
+    test(
+        "creating a DM should work, being e2e-encrypted / user verification",
+        { tag: "@screenshot" },
+        async ({ page, app, bot: bob, user: aliceCredentials }) => {
+            await app.client.bootstrapCrossSigning(aliceCredentials);
+            await startDMWithBob(page, bob);
+            // send first message
+            await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
+            await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
+            await checkDMRoom(page);
+            const bobRoomId = await bobJoin(page, bob);
+            await testMessages(page, bob, bobRoomId);
+            await verify(app, bob);
 
-        // Assert that verified icon is rendered
-        await page.getByTestId("base-card-back-button").click();
-        await page.getByLabel("Room info").nth(1).click();
-        await expect(page.locator('.mx_RoomSummaryCard_badges [data-kind="green"]')).toContainText("Encrypted");
+            // Assert that verified icon is rendered
+            await page.getByTestId("base-card-back-button").click();
+            await page.getByLabel("Room info").nth(1).click();
+            await expect(page.locator('.mx_RoomSummaryCard_badges [data-kind="green"]')).toContainText("Encrypted");
 
-        // Take a snapshot of RoomSummaryCard with a verified E2EE icon
-        await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
-    });
+            // Take a snapshot of RoomSummaryCard with a verified E2EE icon
+            await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
+        },
+    );
 
     test("should allow verification when there is no existing DM", async ({
         page,
diff --git a/playwright/e2e/editing/editing.spec.ts b/playwright/e2e/editing/editing.spec.ts
index 206d91982e..e22ec250b9 100644
--- a/playwright/e2e/editing/editing.spec.ts
+++ b/playwright/e2e/editing/editing.spec.ts
@@ -66,126 +66,130 @@ test.describe("Editing", () => {
         botCreateOpts: { displayName: "Bob" },
     });
 
-    test("should render and interact with the message edit history dialog", async ({ page, user, app, room }) => {
-        // Click the "Remove" button on the message edit history dialog
-        const clickButtonRemove = async (locator: Locator) => {
-            const eventTileLine = locator.locator(".mx_EventTile_line");
-            await eventTileLine.hover();
-            await eventTileLine.getByRole("button", { name: "Remove" }).click();
-        };
+    test(
+        "should render and interact with the message edit history dialog",
+        { tag: "@screenshot" },
+        async ({ page, user, app, room }) => {
+            // Click the "Remove" button on the message edit history dialog
+            const clickButtonRemove = async (locator: Locator) => {
+                const eventTileLine = locator.locator(".mx_EventTile_line");
+                await eventTileLine.hover();
+                await eventTileLine.getByRole("button", { name: "Remove" }).click();
+            };
 
-        await page.goto(`#/room/${room.roomId}`);
+            await page.goto(`#/room/${room.roomId}`);
 
-        // Send "Message"
-        await sendEvent(app, room.roomId);
+            // Send "Message"
+            await sendEvent(app, room.roomId);
 
-        // Edit "Message" to "Massage"
-        await editLastMessage(page, "Massage");
+            // Edit "Message" to "Massage"
+            await editLastMessage(page, "Massage");
 
-        // Assert that the edit label is visible
-        await expect(page.locator(".mx_EventTile_edited")).toBeVisible();
+            // Assert that the edit label is visible
+            await expect(page.locator(".mx_EventTile_edited")).toBeVisible();
 
-        await clickEditedMessage(page, "Massage");
+            await clickEditedMessage(page, "Massage");
 
-        // Assert that the message edit history dialog is rendered
-        const dialog = page.getByRole("dialog");
-        const li = dialog.getByRole("listitem").last();
-        // Assert CSS styles which are difficult or cannot be detected with snapshots are applied as expected
-        await expect(li).toHaveCSS("clear", "both");
+            // Assert that the message edit history dialog is rendered
+            const dialog = page.getByRole("dialog");
+            const li = dialog.getByRole("listitem").last();
+            // Assert CSS styles which are difficult or cannot be detected with snapshots are applied as expected
+            await expect(li).toHaveCSS("clear", "both");
 
-        const timestamp = li.locator(".mx_EventTile .mx_MessageTimestamp");
-        await expect(timestamp).toHaveCSS("position", "absolute");
-        await expect(timestamp).toHaveCSS("inset-inline-start", "0px");
-        await expect(timestamp).toHaveCSS("text-align", "center");
+            const timestamp = li.locator(".mx_EventTile .mx_MessageTimestamp");
+            await expect(timestamp).toHaveCSS("position", "absolute");
+            await expect(timestamp).toHaveCSS("inset-inline-start", "0px");
+            await expect(timestamp).toHaveCSS("text-align", "center");
 
-        // Assert that monospace characters can fill the content line as expected
-        await expect(li.locator(".mx_EventTile .mx_EventTile_content")).toHaveCSS("margin-inline-end", "0px");
+            // Assert that monospace characters can fill the content line as expected
+            await expect(li.locator(".mx_EventTile .mx_EventTile_content")).toHaveCSS("margin-inline-end", "0px");
 
-        // Assert that zero block start padding is applied to mx_EventTile as expected
-        // See: .mx_EventTile on _EventTile.pcss
-        await expect(li.locator(".mx_EventTile")).toHaveCSS("padding-block-start", "0px");
+            // Assert that zero block start padding is applied to mx_EventTile as expected
+            // See: .mx_EventTile on _EventTile.pcss
+            await expect(li.locator(".mx_EventTile")).toHaveCSS("padding-block-start", "0px");
 
-        // Assert that the date separator is rendered at the top
-        await expect(dialog.getByRole("listitem").first().locator("h2", { hasText: "today" })).toHaveCSS(
-            "text-transform",
-            "capitalize",
-        );
+            // Assert that the date separator is rendered at the top
+            await expect(dialog.getByRole("listitem").first().locator("h2", { hasText: "today" })).toHaveCSS(
+                "text-transform",
+                "capitalize",
+            );
 
-        {
-            // Assert that the edited message is rendered under the date separator
-            const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
-            // Assert that the edited message body consists of both deleted character and inserted character
-            // Above the first "e" of "Message" was replaced with "a"
-            await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
+            {
+                // Assert that the edited message is rendered under the date separator
+                const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
+                // Assert that the edited message body consists of both deleted character and inserted character
+                // Above the first "e" of "Message" was replaced with "a"
+                await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
 
-            const body = tile.locator(".mx_EventTile_content .mx_EventTile_body");
-            await expect(body.locator(".mx_EditHistoryMessage_deletion").getByText("e")).toBeVisible();
-            await expect(body.locator(".mx_EditHistoryMessage_insertion").getByText("a")).toBeVisible();
-        }
+                const body = tile.locator(".mx_EventTile_content .mx_EventTile_body");
+                await expect(body.locator(".mx_EditHistoryMessage_deletion").getByText("e")).toBeVisible();
+                await expect(body.locator(".mx_EditHistoryMessage_insertion").getByText("a")).toBeVisible();
+            }
 
-        // Assert that the original message is rendered at the bottom
-        await expect(
-            dialog
-                .locator("li:nth-child(3) .mx_EventTile")
-                .locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Message" }),
-        ).toBeVisible();
+            // Assert that the original message is rendered at the bottom
+            await expect(
+                dialog
+                    .locator("li:nth-child(3) .mx_EventTile")
+                    .locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Message" }),
+            ).toBeVisible();
 
-        // Take a snapshot of the dialog
-        await expect(dialog).toMatchScreenshot("message-edit-history-dialog.png", {
-            mask: [page.locator(".mx_MessageTimestamp")],
-        });
+            // Take a snapshot of the dialog
+            await expect(dialog).toMatchScreenshot("message-edit-history-dialog.png", {
+                mask: [page.locator(".mx_MessageTimestamp")],
+            });
 
-        {
-            const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
-            await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
-            // Click the "Remove" button again
-            await clickButtonRemove(tile);
-        }
+            {
+                const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
+                await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
+                // Click the "Remove" button again
+                await clickButtonRemove(tile);
+            }
 
-        // Do nothing and close the dialog to confirm that the message edit history dialog is rendered
-        await app.closeDialog();
+            // Do nothing and close the dialog to confirm that the message edit history dialog is rendered
+            await app.closeDialog();
 
-        {
-            // Assert that the message edit history dialog is rendered again after it was closed
-            const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
-            await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
-            // Click the "Remove" button again
-            await clickButtonRemove(tile);
-        }
+            {
+                // Assert that the message edit history dialog is rendered again after it was closed
+                const tile = dialog.locator("li:nth-child(2) .mx_EventTile");
+                await expect(tile.locator(".mx_EventTile_body")).toHaveText("Meassage");
+                // Click the "Remove" button again
+                await clickButtonRemove(tile);
+            }
 
-        // This time remove the message really
-        const textInputDialog = page.locator(".mx_TextInputDialog");
-        await textInputDialog.getByRole("textbox", { name: "Reason (optional)" }).fill("This is a test."); // Reason
-        await textInputDialog.getByRole("button", { name: "Remove" }).click();
+            // This time remove the message really
+            const textInputDialog = page.locator(".mx_TextInputDialog");
+            await textInputDialog.getByRole("textbox", { name: "Reason (optional)" }).fill("This is a test."); // Reason
+            await textInputDialog.getByRole("button", { name: "Remove" }).click();
 
-        // Assert that the message edit history dialog is rendered again
-        const messageEditHistoryDialog = page.locator(".mx_MessageEditHistoryDialog");
-        // Assert that the date is rendered
-        await expect(
-            messageEditHistoryDialog.getByRole("listitem").first().locator("h2", { hasText: "today" }),
-        ).toHaveCSS("text-transform", "capitalize");
+            // Assert that the message edit history dialog is rendered again
+            const messageEditHistoryDialog = page.locator(".mx_MessageEditHistoryDialog");
+            // Assert that the date is rendered
+            await expect(
+                messageEditHistoryDialog.getByRole("listitem").first().locator("h2", { hasText: "today" }),
+            ).toHaveCSS("text-transform", "capitalize");
 
-        // Assert that the original message is rendered under the date on the dialog
-        await expect(
-            messageEditHistoryDialog
-                .locator("li:nth-child(2) .mx_EventTile")
-                .locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Message" }),
-        ).toBeVisible();
+            // Assert that the original message is rendered under the date on the dialog
+            await expect(
+                messageEditHistoryDialog
+                    .locator("li:nth-child(2) .mx_EventTile")
+                    .locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Message" }),
+            ).toBeVisible();
 
-        // Assert that the edited message is gone
-        await expect(
-            messageEditHistoryDialog.locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Meassage" }),
-        ).not.toBeVisible();
+            // Assert that the edited message is gone
+            await expect(
+                messageEditHistoryDialog.locator(".mx_EventTile_content .mx_EventTile_body", { hasText: "Meassage" }),
+            ).not.toBeVisible();
 
-        await app.closeDialog();
+            await app.closeDialog();
 
-        // Assert that the redaction placeholder is rendered
-        await expect(
-            page
-                .locator(".mx_RoomView_MessageList")
-                .locator(".mx_EventTile_last .mx_RedactedBody", { hasText: "Message deleted" }),
-        ).toBeVisible();
-    });
+            // Assert that the redaction placeholder is rendered
+            await expect(
+                page
+                    .locator(".mx_RoomView_MessageList")
+                    .locator(".mx_EventTile_last .mx_RedactedBody", { hasText: "Message deleted" }),
+            ).toBeVisible();
+        },
+    );
 
     test("should render 'View Source' button in developer mode on the message edit history dialog", async ({
         page,
diff --git a/playwright/e2e/file-upload/image-upload.spec.ts b/playwright/e2e/file-upload/image-upload.spec.ts
index eb473d83b2..76782e90e8 100644
--- a/playwright/e2e/file-upload/image-upload.spec.ts
+++ b/playwright/e2e/file-upload/image-upload.spec.ts
@@ -25,7 +25,7 @@ test.describe("Image Upload", () => {
         ).toBeVisible();
     });
 
-    test("should show image preview when uploading an image", async ({ page, app }) => {
+    test("should show image preview when uploading an image", { tag: "@screenshot" }, async ({ page, app }) => {
         await page
             .locator(".mx_MessageComposer_actions input[type='file']")
             .setInputFiles("playwright/sample-files/riot.png");
diff --git a/playwright/e2e/forgot-password/forgot-password.spec.ts b/playwright/e2e/forgot-password/forgot-password.spec.ts
index c148900afd..0a12514d9e 100644
--- a/playwright/e2e/forgot-password/forgot-password.spec.ts
+++ b/playwright/e2e/forgot-password/forgot-password.spec.ts
@@ -26,7 +26,7 @@ test.describe("Forgot Password", () => {
             }),
     });
 
-    test("renders properly", async ({ page, homeserver }) => {
+    test("renders properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
         await page.goto("/");
 
         await page.getByRole("link", { name: "Sign in" }).click();
@@ -39,7 +39,7 @@ test.describe("Forgot Password", () => {
         await expect(page.getByRole("main")).toMatchScreenshot("forgot-password.png");
     });
 
-    test("renders email verification dialog properly", async ({ page, homeserver }) => {
+    test("renders email verification dialog properly", { tag: "@screenshot" }, async ({ page, homeserver }) => {
         const user = await homeserver.registerUser(username, password);
 
         await homeserver.setThreepid(user.userId, "email", email);
diff --git a/playwright/e2e/invite/invite-dialog.spec.ts b/playwright/e2e/invite/invite-dialog.spec.ts
index c8bd8eb404..eb434eb5b5 100644
--- a/playwright/e2e/invite/invite-dialog.spec.ts
+++ b/playwright/e2e/invite/invite-dialog.spec.ts
@@ -19,7 +19,7 @@ test.describe("Invite dialog", function () {
 
     const botName = "BotAlice";
 
-    test("should support inviting a user to a room", async ({ page, app, user, bot }) => {
+    test("should support inviting a user to a room", { tag: "@screenshot" }, async ({ page, app, user, bot }) => {
         // Create and view a room
         await app.client.createRoom({ name: "Test Room" });
         await app.viewRoomByName("Test Room");
@@ -73,52 +73,63 @@ test.describe("Invite dialog", function () {
         await expect(page.getByText(`${botName} joined the room`)).toBeVisible();
     });
 
-    test("should support inviting a user to Direct Messages", async ({ page, app, user, bot }) => {
-        await page.locator(".mx_RoomList").getByRole("button", { name: "Start chat" }).click();
+    test(
+        "should support inviting a user to Direct Messages",
+        { tag: "@screenshot" },
+        async ({ page, app, user, bot }) => {
+            await page.locator(".mx_RoomList").getByRole("button", { name: "Start chat" }).click();
 
-        const other = page.locator(".mx_InviteDialog_other");
-        // Assert that the header is rendered
-        await expect(other.locator(".mx_Dialog_header .mx_Dialog_title").getByText("Direct Messages")).toBeVisible();
+            const other = page.locator(".mx_InviteDialog_other");
+            // Assert that the header is rendered
+            await expect(
+                other.locator(".mx_Dialog_header .mx_Dialog_title").getByText("Direct Messages"),
+            ).toBeVisible();
 
-        // Assert that the bar is rendered
-        await expect(other.locator(".mx_InviteDialog_addressBar")).toBeVisible();
+            // Assert that the bar is rendered
+            await expect(other.locator(".mx_InviteDialog_addressBar")).toBeVisible();
 
-        // Take a snapshot of the invite dialog
-        await expect(page.locator(".mx_Dialog")).toMatchScreenshot("invite-dialog-dm-without-user.png");
+            // Take a snapshot of the invite dialog
+            await expect(page.locator(".mx_Dialog")).toMatchScreenshot("invite-dialog-dm-without-user.png");
 
-        await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
+            await other.getByTestId("invite-dialog-input").fill(bot.credentials.userId);
 
-        await expect(other.locator(".mx_InviteDialog_tile_nameStack").getByText(bot.credentials.userId)).toBeVisible();
-        await other.locator(".mx_InviteDialog_tile_nameStack").getByText(botName).click();
+            await expect(
+                other.locator(".mx_InviteDialog_tile_nameStack").getByText(bot.credentials.userId),
+            ).toBeVisible();
+            await other.locator(".mx_InviteDialog_tile_nameStack").getByText(botName).click();
 
-        await expect(
-            other.locator(".mx_InviteDialog_userTile_pill .mx_InviteDialog_userTile_name").getByText(botName),
-        ).toBeVisible();
+            await expect(
+                other.locator(".mx_InviteDialog_userTile_pill .mx_InviteDialog_userTile_name").getByText(botName),
+            ).toBeVisible();
 
-        // Take a snapshot of the invite dialog with a user pill
-        await expect(page.locator(".mx_Dialog")).toMatchScreenshot("invite-dialog-dm-with-user-pill.png");
+            // Take a snapshot of the invite dialog with a user pill
+            await expect(page.locator(".mx_Dialog")).toMatchScreenshot("invite-dialog-dm-with-user-pill.png");
 
-        // Open a direct message UI
-        await other.getByRole("button", { name: "Go" }).click();
+            // Open a direct message UI
+            await other.getByRole("button", { name: "Go" }).click();
 
-        // Assert that the invite dialog disappears
-        await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
+            // Assert that the invite dialog disappears
+            await expect(page.locator(".mx_InviteDialog_other")).not.toBeVisible();
 
-        // Assert that the hovered user name on invitation UI does not have background color
-        // TODO: implement the test on room-header.spec.ts
-        const roomHeader = page.locator(".mx_RoomHeader");
-        await roomHeader.locator(".mx_RoomHeader_heading").hover();
-        await expect(roomHeader.locator(".mx_RoomHeader_heading")).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
+            // Assert that the hovered user name on invitation UI does not have background color
+            // TODO: implement the test on room-header.spec.ts
+            const roomHeader = page.locator(".mx_RoomHeader");
+            await roomHeader.locator(".mx_RoomHeader_heading").hover();
+            await expect(roomHeader.locator(".mx_RoomHeader_heading")).toHaveCSS(
+                "background-color",
+                "rgba(0, 0, 0, 0)",
+            );
 
-        // Send a message to invite the bots
-        const composer = app.getComposer().locator("[contenteditable]");
-        await composer.fill("Hello}");
-        await composer.press("Enter");
+            // Send a message to invite the bots
+            const composer = app.getComposer().locator("[contenteditable]");
+            await composer.fill("Hello}");
+            await composer.press("Enter");
 
-        // Assert that they were invited and joined
-        await expect(page.getByText(`${botName} joined the room`)).toBeVisible();
+            // Assert that they were invited and joined
+            await expect(page.getByText(`${botName} joined the room`)).toBeVisible();
 
-        // Assert that the message is displayed at the bottom
-        await expect(page.locator(".mx_EventTile_last").getByText("Hello")).toBeVisible();
-    });
+            // Assert that the message is displayed at the bottom
+            await expect(page.locator(".mx_EventTile_last").getByText("Hello")).toBeVisible();
+        },
+    );
 });
diff --git a/playwright/e2e/messages/messages.spec.ts b/playwright/e2e/messages/messages.spec.ts
index 0d5a5da472..1c518199a0 100644
--- a/playwright/e2e/messages/messages.spec.ts
+++ b/playwright/e2e/messages/messages.spec.ts
@@ -63,7 +63,7 @@ test.describe("Message rendering", () => {
         { direction: "ltr", displayName: "Quentin" },
         { direction: "rtl", displayName: "كوينتين" },
     ].forEach(({ direction, displayName }) => {
-        test.describe(`with ${direction} display name`, () => {
+        test.describe(`with ${direction} display name`, { tag: "@screenshot" }, () => {
             test.use({
                 displayName,
                 room: async ({ user, app }, use) => {
@@ -72,14 +72,18 @@ test.describe("Message rendering", () => {
                 },
             });
 
-            test("should render a basic LTR text message", async ({ page, user, app, room }) => {
-                await page.goto(`#/room/${room.roomId}`);
+            test(
+                "should render a basic LTR text message",
+                { tag: "@screenshot" },
+                async ({ page, user, app, room }) => {
+                    await page.goto(`#/room/${room.roomId}`);
 
-                const msgTile = await sendMessage(page, "Hello, world!");
-                await expect(msgTile).toMatchScreenshot(`basic-message-ltr-${direction}displayname.png`, {
-                    mask: [page.locator(".mx_MessageTimestamp")],
-                });
-            });
+                    const msgTile = await sendMessage(page, "Hello, world!");
+                    await expect(msgTile).toMatchScreenshot(`basic-message-ltr-${direction}displayname.png`, {
+                        mask: [page.locator(".mx_MessageTimestamp")],
+                    });
+                },
+            );
 
             test("should render an LTR emote", async ({ page, user, app, room }) => {
                 await page.goto(`#/room/${room.roomId}`);
diff --git a/playwright/e2e/permalinks/permalinks.spec.ts b/playwright/e2e/permalinks/permalinks.spec.ts
index bd7884ea78..746e15d288 100644
--- a/playwright/e2e/permalinks/permalinks.spec.ts
+++ b/playwright/e2e/permalinks/permalinks.spec.ts
@@ -24,7 +24,7 @@ test.describe("permalinks", () => {
         displayName: "Alice",
     });
 
-    test("shoud render permalinks as expected", async ({ page, app, user, homeserver }) => {
+    test("shoud render permalinks as expected", { tag: "@screenshot" }, async ({ page, app, user, homeserver }) => {
         const bob = new Bot(page, homeserver, { displayName: "Bob" });
         const charlotte = new Bot(page, homeserver, { displayName: "Charlotte" });
         await bob.prepareClient();
diff --git a/playwright/e2e/pinned-messages/pinned-messages.spec.ts b/playwright/e2e/pinned-messages/pinned-messages.spec.ts
index ef2c1b27d4..06d6db8058 100644
--- a/playwright/e2e/pinned-messages/pinned-messages.spec.ts
+++ b/playwright/e2e/pinned-messages/pinned-messages.spec.ts
@@ -10,35 +10,38 @@ import { test } from "./index";
 import { expect } from "../../element-web-test";
 
 test.describe("Pinned messages", () => {
-    test("should show the empty state when there are no pinned messages", async ({ page, app, room1, util }) => {
-        await util.goTo(room1);
-        await util.openRoomInfo();
-        await util.assertPinnedCountInRoomInfo(0);
-        await util.openPinnedMessagesList();
-        await util.assertEmptyPinnedMessagesList();
-    });
+    test(
+        "should show the empty state when there are no pinned messages",
+        { tag: "@screenshot" },
+        async ({ page, app, room1, util }) => {
+            await util.goTo(room1);
+            await util.openRoomInfo();
+            await util.assertPinnedCountInRoomInfo(0);
+            await util.openPinnedMessagesList();
+            await util.assertEmptyPinnedMessagesList();
+        },
+    );
 
-    test("should pin one message and to have the pinned message badge in the timeline", async ({
-        page,
-        app,
-        room1,
-        util,
-    }) => {
-        await util.goTo(room1);
-        await util.receiveMessages(room1, ["Msg1"]);
-        await util.pinMessages(["Msg1"]);
+    test(
+        "should pin one message and to have the pinned message badge in the timeline",
+        { tag: "@screenshot" },
+        async ({ page, app, room1, util }) => {
+            await util.goTo(room1);
+            await util.receiveMessages(room1, ["Msg1"]);
+            await util.pinMessages(["Msg1"]);
 
-        const tile = util.getEventTile("Msg1");
-        await expect(tile).toMatchScreenshot("pinned-message-Msg1.png", {
-            mask: [tile.locator(".mx_MessageTimestamp")],
-            // Hide the jump to bottom button in the timeline to avoid flakiness
-            css: `
+            const tile = util.getEventTile("Msg1");
+            await expect(tile).toMatchScreenshot("pinned-message-Msg1.png", {
+                mask: [tile.locator(".mx_MessageTimestamp")],
+                // Hide the jump to bottom button in the timeline to avoid flakiness
+                css: `
                 .mx_JumpToBottomButton {
                     display: none !important;
                 }
             `,
-        });
-    });
+            });
+        },
+    );
 
     test("should pin messages and show them in the room info panel", async ({ page, app, room1, util }) => {
         await util.goTo(room1);
@@ -73,7 +76,7 @@ test.describe("Pinned messages", () => {
         await util.assertPinnedCountInRoomInfo(2);
     });
 
-    test("should unpin all messages", async ({ page, app, room1, util }) => {
+    test("should unpin all messages", { tag: "@screenshot" }, async ({ page, app, room1, util }) => {
         await util.goTo(room1);
         await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
         await util.pinMessages(["Msg1", "Msg2", "Msg4"]);
@@ -98,7 +101,7 @@ test.describe("Pinned messages", () => {
         await util.assertPinnedCountInRoomInfo(0);
     });
 
-    test("should display one message in the banner", async ({ page, app, room1, util }) => {
+    test("should display one message in the banner", { tag: "@screenshot" }, async ({ page, app, room1, util }) => {
         await util.goTo(room1);
         await util.receiveMessages(room1, ["Msg1"]);
         await util.pinMessages(["Msg1"]);
@@ -106,7 +109,7 @@ test.describe("Pinned messages", () => {
         await expect(util.getBanner()).toMatchScreenshot("pinned-message-banner-1-Msg1.png");
     });
 
-    test("should display 2 messages in the banner", async ({ page, app, room1, util }) => {
+    test("should display 2 messages in the banner", { tag: "@screenshot" }, async ({ page, app, room1, util }) => {
         await util.goTo(room1);
         await util.receiveMessages(room1, ["Msg1", "Msg2"]);
         await util.pinMessages(["Msg1", "Msg2"]);
@@ -123,7 +126,7 @@ test.describe("Pinned messages", () => {
         await expect(util.getBanner()).toMatchScreenshot("pinned-message-banner-2-Msg2.png");
     });
 
-    test("should display 4 messages in the banner", async ({ page, app, room1, util }) => {
+    test("should display 4 messages in the banner", { tag: "@screenshot" }, async ({ page, app, room1, util }) => {
         await util.goTo(room1);
         await util.receiveMessages(room1, ["Msg1", "Msg2", "Msg3", "Msg4"]);
         await util.pinMessages(["Msg1", "Msg2", "Msg3", "Msg4"]);
diff --git a/playwright/e2e/polls/polls.spec.ts b/playwright/e2e/polls/polls.spec.ts
index 4fd8195581..e1d3ebe7e3 100644
--- a/playwright/e2e/polls/polls.spec.ts
+++ b/playwright/e2e/polls/polls.spec.ts
@@ -93,7 +93,7 @@ test.describe("Polls", () => {
         });
     });
 
-    test("should be creatable and votable", async ({ page, app, bot, user }) => {
+    test("should be creatable and votable", { tag: "@screenshot" }, async ({ page, app, bot, user }) => {
         const roomId: string = await app.client.createRoom({});
         await app.client.inviteUser(roomId, bot.credentials.userId);
         await page.goto("/#/room/" + roomId);
@@ -219,107 +219,121 @@ test.describe("Polls", () => {
         await expect(page.locator(".mx_ErrorDialog")).toBeAttached();
     });
 
-    test("should be displayed correctly in thread panel", async ({ page, app, user, bot, homeserver }) => {
-        const botCharlie = new Bot(page, homeserver, { displayName: "BotCharlie" });
-        await botCharlie.prepareClient();
+    test(
+        "should be displayed correctly in thread panel",
+        { tag: "@screenshot" },
+        async ({ page, app, user, bot, homeserver }) => {
+            const botCharlie = new Bot(page, homeserver, { displayName: "BotCharlie" });
+            await botCharlie.prepareClient();
 
-        const roomId: string = await app.client.createRoom({});
-        await app.client.inviteUser(roomId, bot.credentials.userId);
-        await app.client.inviteUser(roomId, botCharlie.credentials.userId);
-        await page.goto("/#/room/" + roomId);
+            const roomId: string = await app.client.createRoom({});
+            await app.client.inviteUser(roomId, bot.credentials.userId);
+            await app.client.inviteUser(roomId, botCharlie.credentials.userId);
+            await page.goto("/#/room/" + roomId);
 
-        // wait until the bots joined
-        await expect(page.getByText("BotBob and one other were invited and joined")).toBeAttached({ timeout: 10000 });
+            // wait until the bots joined
+            await expect(page.getByText("BotBob and one other were invited and joined")).toBeAttached({
+                timeout: 10000,
+            });
 
-        const locator = await app.openMessageComposerOptions();
-        await locator.getByRole("menuitem", { name: "Poll" }).click();
+            const locator = await app.openMessageComposerOptions();
+            await locator.getByRole("menuitem", { name: "Poll" }).click();
 
-        const pollParams = {
-            title: "Does the polls feature work?",
-            options: ["Yes", "No", "Maybe"],
-        };
-        await createPoll(page, pollParams);
+            const pollParams = {
+                title: "Does the polls feature work?",
+                options: ["Yes", "No", "Maybe"],
+            };
+            await createPoll(page, pollParams);
 
-        // Wait for message to send, get its ID and save as @pollId
-        const pollId = await page
-            .locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
-            .filter({ hasText: pollParams.title })
-            .getAttribute("data-scroll-tokens");
+            // Wait for message to send, get its ID and save as @pollId
+            const pollId = await page
+                .locator(".mx_RoomView_body .mx_EventTile[data-scroll-tokens]")
+                .filter({ hasText: pollParams.title })
+                .getAttribute("data-scroll-tokens");
 
-        // Bob starts thread on the poll
-        await bot.sendMessage(
-            roomId,
-            {
-                body: "Hello there",
-                msgtype: "m.text",
-            },
-            pollId,
-        );
+            // Bob starts thread on the poll
+            await bot.sendMessage(
+                roomId,
+                {
+                    body: "Hello there",
+                    msgtype: "m.text",
+                },
+                pollId,
+            );
 
-        // open the thread summary
-        await page.getByRole("button", { name: "Open thread" }).click();
+            // open the thread summary
+            await page.getByRole("button", { name: "Open thread" }).click();
 
-        // Bob votes 'Maybe' in the poll
-        await botVoteForOption(page, bot, roomId, pollId, pollParams.options[2]);
+            // Bob votes 'Maybe' in the poll
+            await botVoteForOption(page, bot, roomId, pollId, pollParams.options[2]);
 
-        // Charlie votes 'No'
-        await botVoteForOption(page, botCharlie, roomId, pollId, pollParams.options[1]);
+            // Charlie votes 'No'
+            await botVoteForOption(page, botCharlie, roomId, pollId, pollParams.options[1]);
 
-        // no votes shown until I vote, check votes have arrived in main tl
-        await expect(
-            page
-                .locator(".mx_RoomView_body .mx_MPollBody_totalVotes")
-                .getByText("2 votes cast. Vote to see the results"),
-        ).toBeAttached();
+            // no votes shown until I vote, check votes have arrived in main tl
+            await expect(
+                page
+                    .locator(".mx_RoomView_body .mx_MPollBody_totalVotes")
+                    .getByText("2 votes cast. Vote to see the results"),
+            ).toBeAttached();
 
-        // and thread view
-        await expect(
-            page.locator(".mx_ThreadView .mx_MPollBody_totalVotes").getByText("2 votes cast. Vote to see the results"),
-        ).toBeAttached();
+            // and thread view
+            await expect(
+                page
+                    .locator(".mx_ThreadView .mx_MPollBody_totalVotes")
+                    .getByText("2 votes cast. Vote to see the results"),
+            ).toBeAttached();
 
-        // Take snapshots of poll on ThreadView
-        await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
-        await expect(page.locator(".mx_ThreadView .mx_EventTile[data-layout='bubble']").first()).toBeVisible();
-        await expect(page.locator(".mx_ThreadView")).toMatchScreenshot("ThreadView_with_a_poll_on_bubble_layout.png", {
-            mask: [page.locator(".mx_MessageTimestamp")],
-        });
+            // Take snapshots of poll on ThreadView
+            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
+            await expect(page.locator(".mx_ThreadView .mx_EventTile[data-layout='bubble']").first()).toBeVisible();
+            await expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
+                "ThreadView_with_a_poll_on_bubble_layout.png",
+                {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                },
+            );
 
-        await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
-        await expect(page.locator(".mx_ThreadView .mx_EventTile[data-layout='group']").first()).toBeVisible();
+            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
+            await expect(page.locator(".mx_ThreadView .mx_EventTile[data-layout='group']").first()).toBeVisible();
 
-        await expect(page.locator(".mx_ThreadView")).toMatchScreenshot("ThreadView_with_a_poll_on_group_layout.png", {
-            mask: [page.locator(".mx_MessageTimestamp")],
-        });
+            await expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
+                "ThreadView_with_a_poll_on_group_layout.png",
+                {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                },
+            );
 
-        const roomViewLocator = page.locator(".mx_RoomView_body");
-        // vote 'Maybe' in the main timeline poll
-        await getPollOption(page, pollId, pollParams.options[2], roomViewLocator).click();
-        // both me and bob have voted Maybe
-        await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 2, roomViewLocator);
+            const roomViewLocator = page.locator(".mx_RoomView_body");
+            // vote 'Maybe' in the main timeline poll
+            await getPollOption(page, pollId, pollParams.options[2], roomViewLocator).click();
+            // both me and bob have voted Maybe
+            await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 2, roomViewLocator);
 
-        const threadViewLocator = page.locator(".mx_ThreadView");
-        // votes updated in thread view too
-        await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 2, threadViewLocator);
-        // change my vote to 'Yes'
-        await getPollOption(page, pollId, pollParams.options[0], threadViewLocator).click();
+            const threadViewLocator = page.locator(".mx_ThreadView");
+            // votes updated in thread view too
+            await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 2, threadViewLocator);
+            // change my vote to 'Yes'
+            await getPollOption(page, pollId, pollParams.options[0], threadViewLocator).click();
 
-        // Bob updates vote to 'No'
-        await botVoteForOption(page, bot, roomId, pollId, pollParams.options[1]);
+            // Bob updates vote to 'No'
+            await botVoteForOption(page, bot, roomId, pollId, pollParams.options[1]);
 
-        // me: yes, bob: no, charlie: no
-        const expectVoteCounts = async (optLocator: Locator) => {
-            // I voted yes
-            await expectPollOptionVoteCount(page, pollId, pollParams.options[0], 1, optLocator);
-            // Bob and Charlie voted no
-            await expectPollOptionVoteCount(page, pollId, pollParams.options[1], 2, optLocator);
-            // 0 for maybe
-            await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 0, optLocator);
-        };
+            // me: yes, bob: no, charlie: no
+            const expectVoteCounts = async (optLocator: Locator) => {
+                // I voted yes
+                await expectPollOptionVoteCount(page, pollId, pollParams.options[0], 1, optLocator);
+                // Bob and Charlie voted no
+                await expectPollOptionVoteCount(page, pollId, pollParams.options[1], 2, optLocator);
+                // 0 for maybe
+                await expectPollOptionVoteCount(page, pollId, pollParams.options[2], 0, optLocator);
+            };
 
-        // check counts are correct in main timeline tile
-        await expectVoteCounts(page.locator(".mx_RoomView_body"));
+            // check counts are correct in main timeline tile
+            await expectVoteCounts(page.locator(".mx_RoomView_body"));
 
-        // and in thread view tile
-        await expectVoteCounts(page.locator(".mx_ThreadView"));
-    });
+            // and in thread view tile
+            await expectVoteCounts(page.locator(".mx_ThreadView"));
+        },
+    );
 });
diff --git a/playwright/e2e/register/email.spec.ts b/playwright/e2e/register/email.spec.ts
index 7a80f0bbf7..665e20ef01 100644
--- a/playwright/e2e/register/email.spec.ts
+++ b/playwright/e2e/register/email.spec.ts
@@ -38,34 +38,33 @@ test.describe("Email Registration", async () => {
         await page.goto("/#/register");
     });
 
-    test("registers an account and lands on the use case selection screen", async ({
-        page,
-        mailhog,
-        request,
-        checkA11y,
-    }) => {
-        await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
-        // Hide the server text as it contains the randomly allocated Homeserver port
-        const screenshotOptions = { mask: [page.locator(".mx_ServerPicker_server")] };
+    test(
+        "registers an account and lands on the use case selection screen",
+        { tag: "@screenshot" },
+        async ({ page, mailhog, request, checkA11y }) => {
+            await expect(page.getByRole("textbox", { name: "Username" })).toBeVisible();
+            // Hide the server text as it contains the randomly allocated Homeserver port
+            const screenshotOptions = { mask: [page.locator(".mx_ServerPicker_server")] };
 
-        await page.getByRole("textbox", { name: "Username" }).fill("alice");
-        await page.getByPlaceholder("Password", { exact: true }).fill("totally a great password");
-        await page.getByPlaceholder("Confirm password").fill("totally a great password");
-        await page.getByPlaceholder("Email").fill("alice@email.com");
-        await page.getByRole("button", { name: "Register" }).click();
+            await page.getByRole("textbox", { name: "Username" }).fill("alice");
+            await page.getByPlaceholder("Password", { exact: true }).fill("totally a great password");
+            await page.getByPlaceholder("Confirm password").fill("totally a great password");
+            await page.getByPlaceholder("Email").fill("alice@email.com");
+            await page.getByRole("button", { name: "Register" }).click();
 
-        await expect(page.getByText("Check your email to continue")).toBeVisible();
-        await expect(page).toMatchScreenshot("registration_check_your_email.png", screenshotOptions);
-        await checkA11y();
+            await expect(page.getByText("Check your email to continue")).toBeVisible();
+            await expect(page).toMatchScreenshot("registration_check_your_email.png", screenshotOptions);
+            await checkA11y();
 
-        await expect(page.getByText("An error was encountered when sending the email")).not.toBeVisible();
+            await expect(page.getByText("An error was encountered when sending the email")).not.toBeVisible();
 
-        const messages = await mailhog.api.messages();
-        expect(messages.items).toHaveLength(1);
-        expect(messages.items[0].to).toEqual("alice@email.com");
-        const [emailLink] = messages.items[0].text.match(/http.+/);
-        await request.get(emailLink); // "Click" the link in the email
+            const messages = await mailhog.api.messages();
+            expect(messages.items).toHaveLength(1);
+            expect(messages.items[0].to).toEqual("alice@email.com");
+            const [emailLink] = messages.items[0].text.match(/http.+/);
+            await request.get(emailLink); // "Click" the link in the email
 
-        await expect(page.locator(".mx_UseCaseSelection_skip")).toBeVisible();
-    });
+            await expect(page.locator(".mx_UseCaseSelection_skip")).toBeVisible();
+        },
+    );
 });
diff --git a/playwright/e2e/register/register.spec.ts b/playwright/e2e/register/register.spec.ts
index 2dd3779573..19608ee174 100644
--- a/playwright/e2e/register/register.spec.ts
+++ b/playwright/e2e/register/register.spec.ts
@@ -15,66 +15,73 @@ test.describe("Registration", () => {
         await page.goto("/#/register");
     });
 
-    test("registers an account and lands on the home screen", async ({ homeserver, page, checkA11y, crypto }) => {
-        await page.getByRole("button", { name: "Edit", exact: true }).click();
-        await expect(page.getByRole("button", { name: "Continue", exact: true })).toBeVisible();
+    test(
+        "registers an account and lands on the home screen",
+        { tag: "@screenshot" },
+        async ({ homeserver, page, checkA11y, crypto }) => {
+            await page.getByRole("button", { name: "Edit", exact: true }).click();
+            await expect(page.getByRole("button", { name: "Continue", exact: true })).toBeVisible();
 
-        await expect(page.locator(".mx_Dialog")).toMatchScreenshot("server-picker.png");
-        await checkA11y();
+            await expect(page.locator(".mx_Dialog")).toMatchScreenshot("server-picker.png");
+            await checkA11y();
 
-        await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
-        await page.getByRole("button", { name: "Continue", exact: true }).click();
-        // wait for the dialog to go away
-        await expect(page.getByRole("dialog")).not.toBeVisible();
+            await page.getByRole("textbox", { name: "Other homeserver" }).fill(homeserver.config.baseUrl);
+            await page.getByRole("button", { name: "Continue", exact: true }).click();
+            // wait for the dialog to go away
+            await expect(page.getByRole("dialog")).not.toBeVisible();
 
-        await expect(page.getByRole("textbox", { name: "Username", exact: true })).toBeVisible();
-        // Hide the server text as it contains the randomly allocated Homeserver port
-        const screenshotOptions = { mask: [page.locator(".mx_ServerPicker_server")], includeDialogBackground: true };
-        await expect(page).toMatchScreenshot("registration.png", screenshotOptions);
-        await checkA11y();
+            await expect(page.getByRole("textbox", { name: "Username", exact: true })).toBeVisible();
+            // Hide the server text as it contains the randomly allocated Homeserver port
+            const screenshotOptions = {
+                mask: [page.locator(".mx_ServerPicker_server")],
+                includeDialogBackground: true,
+            };
+            await expect(page).toMatchScreenshot("registration.png", screenshotOptions);
+            await checkA11y();
 
-        await page.getByRole("textbox", { name: "Username", exact: true }).fill("alice");
-        await page.getByPlaceholder("Password", { exact: true }).fill("totally a great password");
-        await page.getByPlaceholder("Confirm password", { exact: true }).fill("totally a great password");
-        await page.getByRole("button", { name: "Register", exact: true }).click();
+            await page.getByRole("textbox", { name: "Username", exact: true }).fill("alice");
+            await page.getByPlaceholder("Password", { exact: true }).fill("totally a great password");
+            await page.getByPlaceholder("Confirm password", { exact: true }).fill("totally a great password");
+            await page.getByRole("button", { name: "Register", exact: true }).click();
 
-        const dialog = page.getByRole("dialog");
-        await expect(dialog).toBeVisible();
-        await expect(page).toMatchScreenshot("email-prompt.png", screenshotOptions);
-        await checkA11y();
-        await dialog.getByRole("button", { name: "Continue", exact: true }).click();
+            const dialog = page.getByRole("dialog");
+            await expect(dialog).toBeVisible();
+            await expect(page).toMatchScreenshot("email-prompt.png", screenshotOptions);
+            await checkA11y();
+            await dialog.getByRole("button", { name: "Continue", exact: true }).click();
 
-        await expect(page.locator(".mx_InteractiveAuthEntryComponents_termsPolicy")).toBeVisible();
-        await expect(page).toMatchScreenshot("terms-prompt.png", screenshotOptions);
-        await checkA11y();
+            await expect(page.locator(".mx_InteractiveAuthEntryComponents_termsPolicy")).toBeVisible();
+            await expect(page).toMatchScreenshot("terms-prompt.png", screenshotOptions);
+            await checkA11y();
 
-        const termsPolicy = page.locator(".mx_InteractiveAuthEntryComponents_termsPolicy");
-        await termsPolicy.getByRole("checkbox").click(); // Click the checkbox before terms of service anchor link
-        await expect(termsPolicy.getByLabel("Privacy Policy")).toBeVisible();
+            const termsPolicy = page.locator(".mx_InteractiveAuthEntryComponents_termsPolicy");
+            await termsPolicy.getByRole("checkbox").click(); // Click the checkbox before terms of service anchor link
+            await expect(termsPolicy.getByLabel("Privacy Policy")).toBeVisible();
 
-        await page.getByRole("button", { name: "Accept", exact: true }).click();
+            await page.getByRole("button", { name: "Accept", exact: true }).click();
 
-        await expect(page.locator(".mx_UseCaseSelection_skip")).toBeVisible();
-        await expect(page).toMatchScreenshot("use-case-selection.png", screenshotOptions);
-        await checkA11y();
-        await page.getByRole("button", { name: "Skip", exact: true }).click();
+            await expect(page.locator(".mx_UseCaseSelection_skip")).toBeVisible();
+            await expect(page).toMatchScreenshot("use-case-selection.png", screenshotOptions);
+            await checkA11y();
+            await page.getByRole("button", { name: "Skip", exact: true }).click();
 
-        await expect(page).toHaveURL(/\/#\/home$/);
+            await expect(page).toHaveURL(/\/#\/home$/);
 
-        /*
-         * Cross-signing checks
-         */
-        // check that the device considers itself verified
-        await page.getByRole("button", { name: "User menu", exact: true }).click();
-        await page.getByRole("menuitem", { name: "All settings", exact: true }).click();
-        await page.getByRole("tab", { name: "Sessions", exact: true }).click();
-        await expect(page.getByTestId("current-session-section").getByTestId("device-metadata-isVerified")).toHaveText(
-            "Verified",
-        );
+            /*
+             * Cross-signing checks
+             */
+            // check that the device considers itself verified
+            await page.getByRole("button", { name: "User menu", exact: true }).click();
+            await page.getByRole("menuitem", { name: "All settings", exact: true }).click();
+            await page.getByRole("tab", { name: "Sessions", exact: true }).click();
+            await expect(
+                page.getByTestId("current-session-section").getByTestId("device-metadata-isVerified"),
+            ).toHaveText("Verified");
 
-        // check that cross-signing keys have been uploaded.
-        await crypto.assertDeviceIsCrossSigned();
-    });
+            // check that cross-signing keys have been uploaded.
+            await crypto.assertDeviceIsCrossSigned();
+        },
+    );
 
     test("should require username to fulfil requirements and be available", async ({ homeserver, page }) => {
         await page.getByRole("button", { name: "Edit", exact: true }).click();
diff --git a/playwright/e2e/release-announcement/releaseAnnouncement.spec.ts b/playwright/e2e/release-announcement/releaseAnnouncement.spec.ts
index 57c27caf1a..e18d72ddba 100644
--- a/playwright/e2e/release-announcement/releaseAnnouncement.spec.ts
+++ b/playwright/e2e/release-announcement/releaseAnnouncement.spec.ts
@@ -18,7 +18,7 @@ test.describe("Release announcement", () => {
         labsFlags: ["threadsActivityCentre"],
     });
 
-    test("should display the release announcement process", async ({ page, app, util }) => {
+    test("should display the release announcement process", { tag: "@screenshot" }, async ({ page, app, util }) => {
         // The TAC release announcement should be displayed
         await util.assertReleaseAnnouncementIsVisible("Threads Activity Centre");
         // Hide the release announcement
diff --git a/playwright/e2e/right-panel/file-panel.spec.ts b/playwright/e2e/right-panel/file-panel.spec.ts
index 1cb39aad25..c535bcdfbb 100644
--- a/playwright/e2e/right-panel/file-panel.spec.ts
+++ b/playwright/e2e/right-panel/file-panel.spec.ts
@@ -40,7 +40,7 @@ test.describe("FilePanel", () => {
     });
 
     test.describe("render", () => {
-        test("should render empty state", async ({ page }) => {
+        test("should render empty state", { tag: "@screenshot" }, async ({ page }) => {
             // Wait until the information about the empty state is rendered
             await expect(page.locator(".mx_EmptyState")).toBeVisible();
 
@@ -48,7 +48,7 @@ test.describe("FilePanel", () => {
             await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("empty.png");
         });
 
-        test("should list tiles on the panel", async ({ page }) => {
+        test("should list tiles on the panel", { tag: "@screenshot" }, async ({ page }) => {
             // Upload multiple files
             await uploadFile(page, "playwright/sample-files/riot.png"); // Image
             await uploadFile(page, "playwright/sample-files/1sec.ogg"); // Audio
diff --git a/playwright/e2e/right-panel/notification-panel.spec.ts b/playwright/e2e/right-panel/notification-panel.spec.ts
index 1d10af9798..55a6be0450 100644
--- a/playwright/e2e/right-panel/notification-panel.spec.ts
+++ b/playwright/e2e/right-panel/notification-panel.spec.ts
@@ -21,7 +21,7 @@ test.describe("NotificationPanel", () => {
         await app.client.createRoom({ name: ROOM_NAME });
     });
 
-    test("should render empty state", async ({ page, app }) => {
+    test("should render empty state", { tag: "@screenshot" }, async ({ page, app }) => {
         await app.viewRoomByName(ROOM_NAME);
 
         await page.getByRole("button", { name: "Notifications" }).click();
diff --git a/playwright/e2e/right-panel/right-panel.spec.ts b/playwright/e2e/right-panel/right-panel.spec.ts
index 110f24a501..1e9b8ebe1d 100644
--- a/playwright/e2e/right-panel/right-panel.spec.ts
+++ b/playwright/e2e/right-panel/right-panel.spec.ts
@@ -38,7 +38,7 @@ test.describe("RightPanel", () => {
     });
 
     test.describe("in rooms", () => {
-        test("should handle long room address and long room name", async ({ page, app }) => {
+        test("should handle long room address and long room name", { tag: "@screenshot" }, async ({ page, app }) => {
             await app.client.createRoom({ name: ROOM_NAME_LONG });
             await viewRoomSummaryByName(page, app, ROOM_NAME_LONG);
 
diff --git a/playwright/e2e/room-directory/room-directory.spec.ts b/playwright/e2e/room-directory/room-directory.spec.ts
index f078a858a2..f299a929bb 100644
--- a/playwright/e2e/room-directory/room-directory.spec.ts
+++ b/playwright/e2e/room-directory/room-directory.spec.ts
@@ -47,34 +47,40 @@ test.describe("Room Directory", () => {
         expect(resp.chunk[0].room_id).toEqual(roomId);
     });
 
-    test("should allow finding published rooms in directory", async ({ page, app, user, bot }) => {
-        const name = "This is a public room";
-        await bot.createRoom({
-            visibility: "public" as Visibility,
-            name,
-            room_alias_name: "test1234",
-        });
+    test(
+        "should allow finding published rooms in directory",
+        { tag: "@screenshot" },
+        async ({ page, app, user, bot }) => {
+            const name = "This is a public room";
+            await bot.createRoom({
+                visibility: "public" as Visibility,
+                name,
+                room_alias_name: "test1234",
+            });
 
-        await page.getByRole("button", { name: "Explore rooms" }).click();
+            await page.getByRole("button", { name: "Explore rooms" }).click();
 
-        const dialog = page.locator(".mx_SpotlightDialog");
-        await dialog.getByRole("textbox", { name: "Search" }).fill("Unknown Room");
-        await expect(
-            dialog.getByText("If you can't find the room you're looking for, ask for an invite or create a new room."),
-        ).toHaveClass("mx_SpotlightDialog_otherSearches_messageSearchText");
+            const dialog = page.locator(".mx_SpotlightDialog");
+            await dialog.getByRole("textbox", { name: "Search" }).fill("Unknown Room");
+            await expect(
+                dialog.getByText(
+                    "If you can't find the room you're looking for, ask for an invite or create a new room.",
+                ),
+            ).toHaveClass("mx_SpotlightDialog_otherSearches_messageSearchText");
 
-        await expect(page.locator(".mx_Dialog")).toMatchScreenshot("filtered-no-results.png");
+            await expect(page.locator(".mx_Dialog")).toMatchScreenshot("filtered-no-results.png");
 
-        await dialog.getByRole("textbox", { name: "Search" }).fill("test1234");
-        await expect(dialog.getByText(name)).toHaveClass("mx_SpotlightDialog_result_publicRoomName");
+            await dialog.getByRole("textbox", { name: "Search" }).fill("test1234");
+            await expect(dialog.getByText(name)).toHaveClass("mx_SpotlightDialog_result_publicRoomName");
 
-        await expect(page.locator(".mx_Dialog")).toMatchScreenshot("filtered-one-result.png");
+            await expect(page.locator(".mx_Dialog")).toMatchScreenshot("filtered-one-result.png");
 
-        await page
-            .locator(".mx_SpotlightDialog .mx_SpotlightDialog_option")
-            .getByRole("button", { name: "Join" })
-            .click();
+            await page
+                .locator(".mx_SpotlightDialog .mx_SpotlightDialog_option")
+                .getByRole("button", { name: "Join" })
+                .click();
 
-        await expect(page).toHaveURL("/#/room/#test1234:localhost");
-    });
+            await expect(page).toHaveURL("/#/room/#test1234:localhost");
+        },
+    );
 });
diff --git a/playwright/e2e/room/room-header.spec.ts b/playwright/e2e/room/room-header.spec.ts
index 6ecf4b3b33..971508b25b 100644
--- a/playwright/e2e/room/room-header.spec.ts
+++ b/playwright/e2e/room/room-header.spec.ts
@@ -20,7 +20,7 @@ test.describe("Room Header", () => {
         test.use({
             labsFlags: ["feature_notifications"],
         });
-        test("should render default buttons properly", async ({ page, app, user }) => {
+        test("should render default buttons properly", { tag: "@screenshot" }, async ({ page, app, user }) => {
             await app.client.createRoom({ name: "Test Room" });
             await app.viewRoomByName("Test Room");
 
@@ -51,34 +51,38 @@ test.describe("Room Header", () => {
             await expect(header).toMatchScreenshot("room-header.png");
         });
 
-        test("should render a very long room name without collapsing the buttons", async ({ page, app, user }) => {
-            const LONG_ROOM_NAME =
-                "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore " +
-                "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
-                "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " +
-                "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
-                "officia deserunt mollit anim id est laborum.";
+        test(
+            "should render a very long room name without collapsing the buttons",
+            { tag: "@screenshot" },
+            async ({ page, app, user }) => {
+                const LONG_ROOM_NAME =
+                    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore " +
+                    "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
+                    "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum " +
+                    "dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
+                    "officia deserunt mollit anim id est laborum.";
 
-            await app.client.createRoom({ name: LONG_ROOM_NAME });
-            await app.viewRoomByName(LONG_ROOM_NAME);
+                await app.client.createRoom({ name: LONG_ROOM_NAME });
+                await app.viewRoomByName(LONG_ROOM_NAME);
 
-            const header = page.locator(".mx_RoomHeader");
-            // Wait until the room name is set
-            await expect(page.locator(".mx_RoomHeader_heading").getByText(LONG_ROOM_NAME)).toBeVisible();
+                const header = page.locator(".mx_RoomHeader");
+                // Wait until the room name is set
+                await expect(page.locator(".mx_RoomHeader_heading").getByText(LONG_ROOM_NAME)).toBeVisible();
 
-            // Assert the size of buttons on RoomHeader are specified and the buttons are not compressed
-            // Note these assertions do not check the size of mx_LegacyRoomHeader_name button
-            const buttons = header.locator(".mx_Flex").getByRole("button");
-            await expect(buttons).toHaveCount(5);
+                // Assert the size of buttons on RoomHeader are specified and the buttons are not compressed
+                // Note these assertions do not check the size of mx_LegacyRoomHeader_name button
+                const buttons = header.locator(".mx_Flex").getByRole("button");
+                await expect(buttons).toHaveCount(5);
 
-            for (const button of await buttons.all()) {
-                await expect(button).toBeVisible();
-                await expect(button).toHaveCSS("height", "32px");
-                await expect(button).toHaveCSS("width", "32px");
-            }
+                for (const button of await buttons.all()) {
+                    await expect(button).toBeVisible();
+                    await expect(button).toHaveCSS("height", "32px");
+                    await expect(button).toHaveCSS("width", "32px");
+                }
 
-            await expect(header).toMatchScreenshot("room-header-long-name.png");
-        });
+                await expect(header).toMatchScreenshot("room-header-long-name.png");
+            },
+        );
     });
 
     test.describe("with a video room", () => {
@@ -99,30 +103,34 @@ test.describe("Room Header", () => {
         test.describe("and with feature_notifications enabled", () => {
             test.use({ labsFlags: ["feature_video_rooms", "feature_notifications"] });
 
-            test("should render buttons for chat, room info, threads and facepile", async ({ page, app, user }) => {
-                await createVideoRoom(page, app);
+            test(
+                "should render buttons for chat, room info, threads and facepile",
+                { tag: "@screenshot" },
+                async ({ page, app, user }) => {
+                    await createVideoRoom(page, app);
 
-                const header = page.locator(".mx_RoomHeader");
+                    const header = page.locator(".mx_RoomHeader");
 
-                // There's two room info button - the header itself and the i button
-                const infoButtons = header.getByRole("button", { name: "Room info" });
-                await expect(infoButtons).toHaveCount(2);
-                await expect(infoButtons.first()).toBeVisible();
-                await expect(infoButtons.last()).toBeVisible();
+                    // There's two room info button - the header itself and the i button
+                    const infoButtons = header.getByRole("button", { name: "Room info" });
+                    await expect(infoButtons).toHaveCount(2);
+                    await expect(infoButtons.first()).toBeVisible();
+                    await expect(infoButtons.last()).toBeVisible();
 
-                // Facepile
-                await expect(header.locator(".mx_FacePile")).toBeVisible();
+                    // Facepile
+                    await expect(header.locator(".mx_FacePile")).toBeVisible();
 
-                // Chat, Threads and Notification buttons
-                await expect(header.getByRole("button", { name: "Chat" })).toBeVisible();
-                await expect(header.getByRole("button", { name: "Threads" })).toBeVisible();
-                await expect(header.getByRole("button", { name: "Notifications" })).toBeVisible();
+                    // Chat, Threads and Notification buttons
+                    await expect(header.getByRole("button", { name: "Chat" })).toBeVisible();
+                    await expect(header.getByRole("button", { name: "Threads" })).toBeVisible();
+                    await expect(header.getByRole("button", { name: "Notifications" })).toBeVisible();
 
-                // Assert that there is not a button except those buttons
-                await expect(header.getByRole("button")).toHaveCount(7);
+                    // Assert that there is not a button except those buttons
+                    await expect(header.getByRole("button")).toHaveCount(7);
 
-                await expect(header).toMatchScreenshot("room-header-video-room.png");
-            });
+                    await expect(header).toMatchScreenshot("room-header-video-room.png");
+                },
+            );
         });
 
         test("should render a working chat button which opens the timeline on a right panel", async ({
diff --git a/playwright/e2e/settings/account-user-settings-tab.spec.ts b/playwright/e2e/settings/account-user-settings-tab.spec.ts
index 5492094f93..7390ccfd8d 100644
--- a/playwright/e2e/settings/account-user-settings-tab.spec.ts
+++ b/playwright/e2e/settings/account-user-settings-tab.spec.ts
@@ -23,7 +23,7 @@ test.describe("Account user settings tab", () => {
         },
     });
 
-    test("should be rendered properly", async ({ uut, user }) => {
+    test("should be rendered properly", { tag: "@screenshot" }, async ({ uut, user }) => {
         await expect(uut).toMatchScreenshot("account.png");
 
         // Assert that the top heading is rendered
@@ -71,7 +71,7 @@ test.describe("Account user settings tab", () => {
         );
     });
 
-    test("should respond to small screen sizes", async ({ page, uut }) => {
+    test("should respond to small screen sizes", { tag: "@screenshot" }, async ({ page, uut }) => {
         await page.setViewportSize({ width: 700, height: 600 });
         await expect(uut).toMatchScreenshot("account-smallscreen.png");
     });
diff --git a/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts b/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts
index de6c9c527a..c60ecb99d2 100644
--- a/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts
+++ b/playwright/e2e/settings/appearance-user-settings-tab/appearance-user-settings-tab.spec.ts
@@ -13,7 +13,7 @@ test.describe("Appearance user settings tab", () => {
         displayName: "Hanako",
     });
 
-    test("should be rendered properly", async ({ page, user, app }) => {
+    test("should be rendered properly", { tag: "@screenshot" }, async ({ page, user, app }) => {
         const tab = await app.settings.openUserSettings("Appearance");
 
         // Click "Show advanced" link button
@@ -25,19 +25,23 @@ test.describe("Appearance user settings tab", () => {
         await expect(tab).toMatchScreenshot("appearance-tab.png");
     });
 
-    test("should support changing font size by using the font size dropdown", async ({ page, app, user }) => {
-        await app.settings.openUserSettings("Appearance");
+    test(
+        "should support changing font size by using the font size dropdown",
+        { tag: "@screenshot" },
+        async ({ page, app, user }) => {
+            await app.settings.openUserSettings("Appearance");
 
-        const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
-        const fontDropdown = tab.locator(".mx_FontScalingPanel_Dropdown");
-        await expect(fontDropdown.getByLabel("Font size")).toBeVisible();
+            const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
+            const fontDropdown = tab.locator(".mx_FontScalingPanel_Dropdown");
+            await expect(fontDropdown.getByLabel("Font size")).toBeVisible();
 
-        // Default browser font size is 16px and the select value is 0
-        // -4 value is 12px
-        await fontDropdown.getByLabel("Font size").selectOption({ value: "-4" });
+            // Default browser font size is 16px and the select value is 0
+            // -4 value is 12px
+            await fontDropdown.getByLabel("Font size").selectOption({ value: "-4" });
 
-        await expect(page).toMatchScreenshot("window-12px.png", { includeDialogBackground: true });
-    });
+            await expect(page).toMatchScreenshot("window-12px.png", { includeDialogBackground: true });
+        },
+    );
 
     test("should support enabling system font", async ({ page, app, user }) => {
         await app.settings.openUserSettings("Appearance");
diff --git a/playwright/e2e/settings/appearance-user-settings-tab/message-layout-panel.ts b/playwright/e2e/settings/appearance-user-settings-tab/message-layout-panel.ts
index a0288baf1d..157942a585 100644
--- a/playwright/e2e/settings/appearance-user-settings-tab/message-layout-panel.ts
+++ b/playwright/e2e/settings/appearance-user-settings-tab/message-layout-panel.ts
@@ -20,20 +20,24 @@ test.describe("Appearance user settings tab", () => {
             await util.openAppearanceTab();
         });
 
-        test("should change the message layout from modern to bubble", async ({ page, app, user, util }) => {
-            await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-modern.png");
+        test(
+            "should change the message layout from modern to bubble",
+            { tag: "@screenshot" },
+            async ({ page, app, user, util }) => {
+                await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-modern.png");
 
-            await util.getBubbleLayout().click();
+                await util.getBubbleLayout().click();
 
-            // Assert that modern are irc layout are not selected
-            await expect(util.getBubbleLayout()).toBeChecked();
-            await expect(util.getModernLayout()).not.toBeChecked();
-            await expect(util.getIRCLayout()).not.toBeChecked();
+                // Assert that modern are irc layout are not selected
+                await expect(util.getBubbleLayout()).toBeChecked();
+                await expect(util.getModernLayout()).not.toBeChecked();
+                await expect(util.getIRCLayout()).not.toBeChecked();
 
-            // Assert that the room layout is set to bubble layout
-            await util.assertBubbleLayout();
-            await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-bubble.png");
-        });
+                // Assert that the room layout is set to bubble layout
+                await util.assertBubbleLayout();
+                await util.assertScreenshot(util.getMessageLayoutPanel(), "message-layout-panel-bubble.png");
+            },
+        );
 
         test("should enable compact layout when the modern layout is selected", async ({ page, app, user, util }) => {
             await expect(util.getCompactLayoutCheckbox()).not.toBeChecked();
diff --git a/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts b/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts
index 4f3b75b5ba..63b53caa23 100644
--- a/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts
+++ b/playwright/e2e/settings/appearance-user-settings-tab/theme-choice-panel.spec.ts
@@ -20,31 +20,39 @@ test.describe("Appearance user settings tab", () => {
             await util.openAppearanceTab();
         });
 
-        test("should be rendered with the light theme selected", async ({ page, app, util }) => {
-            // Assert that 'Match system theme' is not checked
-            await expect(util.getMatchSystemThemeCheckbox()).not.toBeChecked();
+        test(
+            "should be rendered with the light theme selected",
+            { tag: "@screenshot" },
+            async ({ page, app, util }) => {
+                // Assert that 'Match system theme' is not checked
+                await expect(util.getMatchSystemThemeCheckbox()).not.toBeChecked();
 
-            // Assert that the light theme is selected
-            await expect(util.getLightTheme()).toBeChecked();
-            // Assert that the dark and high contrast themes are not selected
-            await expect(util.getDarkTheme()).not.toBeChecked();
-            await expect(util.getHighContrastTheme()).not.toBeChecked();
+                // Assert that the light theme is selected
+                await expect(util.getLightTheme()).toBeChecked();
+                // Assert that the dark and high contrast themes are not selected
+                await expect(util.getDarkTheme()).not.toBeChecked();
+                await expect(util.getHighContrastTheme()).not.toBeChecked();
 
-            await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-light.png");
-        });
+                await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-light.png");
+            },
+        );
 
-        test("should disable the themes when the system theme is clicked", async ({ page, app, util }) => {
-            await util.getMatchSystemThemeCheckbox().click();
+        test(
+            "should disable the themes when the system theme is clicked",
+            { tag: "@screenshot" },
+            async ({ page, app, util }) => {
+                await util.getMatchSystemThemeCheckbox().click();
 
-            // Assert that the themes are disabled
-            await expect(util.getLightTheme()).toBeDisabled();
-            await expect(util.getDarkTheme()).toBeDisabled();
-            await expect(util.getHighContrastTheme()).toBeDisabled();
+                // Assert that the themes are disabled
+                await expect(util.getLightTheme()).toBeDisabled();
+                await expect(util.getDarkTheme()).toBeDisabled();
+                await expect(util.getHighContrastTheme()).toBeDisabled();
 
-            await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-match-system-enabled.png");
-        });
+                await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-match-system-enabled.png");
+            },
+        );
 
-        test("should change the theme to dark", async ({ page, app, util }) => {
+        test("should change the theme to dark", { tag: "@screenshot" }, async ({ page, app, util }) => {
             // Assert that the light theme is selected
             await expect(util.getLightTheme()).toBeChecked();
 
@@ -63,19 +71,23 @@ test.describe("Appearance user settings tab", () => {
                 labsFlags: ["feature_custom_themes"],
             });
 
-            test("should render the custom theme section", async ({ page, app, util }) => {
+            test("should render the custom theme section", { tag: "@screenshot" }, async ({ page, app, util }) => {
                 await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme.png");
             });
 
-            test("should be able to add and remove a custom theme", async ({ page, app, util }) => {
-                await util.addCustomTheme();
+            test(
+                "should be able to add and remove a custom theme",
+                { tag: "@screenshot" },
+                async ({ page, app, util }) => {
+                    await util.addCustomTheme();
 
-                await expect(util.getCustomTheme()).not.toBeChecked();
-                await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-added.png");
+                    await expect(util.getCustomTheme()).not.toBeChecked();
+                    await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-added.png");
 
-                await util.removeCustomTheme();
-                await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-removed.png");
-            });
+                    await util.removeCustomTheme();
+                    await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-removed.png");
+                },
+            );
         });
     });
 });
diff --git a/playwright/e2e/settings/general-room-settings-tab.spec.ts b/playwright/e2e/settings/general-room-settings-tab.spec.ts
index 47582bf0c0..828ba5285b 100644
--- a/playwright/e2e/settings/general-room-settings-tab.spec.ts
+++ b/playwright/e2e/settings/general-room-settings-tab.spec.ts
@@ -20,7 +20,7 @@ test.describe("General room settings tab", () => {
         await app.viewRoomByName(roomName);
     });
 
-    test("should be rendered properly", async ({ page, app }) => {
+    test("should be rendered properly", { tag: "@screenshot" }, async ({ page, app }) => {
         const settings = await app.settings.openRoomSettings("General");
 
         // Assert that "Show less" details element is rendered
diff --git a/playwright/e2e/settings/preferences-user-settings-tab.spec.ts b/playwright/e2e/settings/preferences-user-settings-tab.spec.ts
index 0880853ee8..8dc2570b42 100644
--- a/playwright/e2e/settings/preferences-user-settings-tab.spec.ts
+++ b/playwright/e2e/settings/preferences-user-settings-tab.spec.ts
@@ -23,7 +23,7 @@ test.describe("Preferences user settings tab", () => {
         },
     });
 
-    test("should be rendered properly", async ({ app, page, user }) => {
+    test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page, user }) => {
         page.setViewportSize({ width: 1024, height: 3300 });
         const tab = await app.settings.openUserSettings("Preferences");
         // Assert that the top heading is rendered
diff --git a/playwright/e2e/settings/security-user-settings-tab.spec.ts b/playwright/e2e/settings/security-user-settings-tab.spec.ts
index 6eab830623..e7562698c3 100644
--- a/playwright/e2e/settings/security-user-settings-tab.spec.ts
+++ b/playwright/e2e/settings/security-user-settings-tab.spec.ts
@@ -36,7 +36,7 @@ test.describe("Security user settings tab", () => {
         });
 
         test.describe("AnalyticsLearnMoreDialog", () => {
-            test("should be rendered properly", async ({ app, page }) => {
+            test("should be rendered properly", { tag: "@screenshot" }, async ({ app, page }) => {
                 const tab = await app.settings.openUserSettings("Security");
                 await tab.getByRole("button", { name: "Learn more" }).click();
                 await expect(page.locator(".mx_AnalyticsLearnMoreDialog_wrapper .mx_Dialog")).toMatchScreenshot(
diff --git a/playwright/e2e/share-dialog/share-dialog.spec.ts b/playwright/e2e/share-dialog/share-dialog.spec.ts
index 2999b74ca0..e0993dd1bc 100644
--- a/playwright/e2e/share-dialog/share-dialog.spec.ts
+++ b/playwright/e2e/share-dialog/share-dialog.spec.ts
@@ -16,7 +16,7 @@ test.describe("Share dialog", () => {
         },
     });
 
-    test("should share a room", async ({ page, app, room }) => {
+    test("should share a room", { tag: "@screenshot" }, async ({ page, app, room }) => {
         await app.viewRoomById(room.roomId);
         await app.toggleRoomInfoPanel();
         await page.getByRole("menuitem", { name: "Copy link" }).click();
@@ -29,7 +29,7 @@ test.describe("Share dialog", () => {
         });
     });
 
-    test("should share a room member", async ({ page, app, room, user }) => {
+    test("should share a room member", { tag: "@screenshot" }, async ({ page, app, room, user }) => {
         await app.viewRoomById(room.roomId);
         await app.client.sendMessage(room.roomId, { body: "hello", msgtype: "m.text" });
 
@@ -46,7 +46,7 @@ test.describe("Share dialog", () => {
         });
     });
 
-    test("should share an event", async ({ page, app, room }) => {
+    test("should share an event", { tag: "@screenshot" }, async ({ page, app, room }) => {
         await app.viewRoomById(room.roomId);
         await app.client.sendMessage(room.roomId, { body: "hello", msgtype: "m.text" });
 
diff --git a/playwright/e2e/spaces/spaces.spec.ts b/playwright/e2e/spaces/spaces.spec.ts
index 575450c641..233cdee3b4 100644
--- a/playwright/e2e/spaces/spaces.spec.ts
+++ b/playwright/e2e/spaces/spaces.spec.ts
@@ -55,7 +55,7 @@ test.describe("Spaces", () => {
         botCreateOpts: { displayName: "BotBob" },
     });
 
-    test("should allow user to create public space", async ({ page, app, user }) => {
+    test("should allow user to create public space", { tag: "@screenshot" }, async ({ page, app, user }) => {
         const contextMenu = await openSpaceCreateMenu(page);
         await expect(contextMenu).toMatchScreenshot("space-create-menu.png");
 
@@ -88,7 +88,7 @@ test.describe("Spaces", () => {
         await expect(page.getByRole("treeitem", { name: "Jokes" })).toBeVisible();
     });
 
-    test("should allow user to create private space", async ({ page, app, user }) => {
+    test("should allow user to create private space", { tag: "@screenshot" }, async ({ page, app, user }) => {
         const menu = await openSpaceCreateMenu(page);
         await menu.getByRole("button", { name: "Private" }).click();
 
@@ -216,49 +216,47 @@ test.describe("Spaces", () => {
         await expect(hierarchyList.getByRole("treeitem", { name: "Gaming" }).getByRole("button")).toBeVisible();
     });
 
-    test("should render subspaces in the space panel only when expanded", async ({
-        page,
-        app,
-        user,
-        axe,
-        checkA11y,
-    }) => {
-        axe.disableRules([
-            // Disable this check as it triggers on nested roving tab index elements which are in practice fine
-            "nested-interactive",
-            // XXX: We have some known contrast issues here
-            "color-contrast",
-        ]);
+    test(
+        "should render subspaces in the space panel only when expanded",
+        { tag: "@screenshot" },
+        async ({ page, app, user, axe, checkA11y }) => {
+            axe.disableRules([
+                // Disable this check as it triggers on nested roving tab index elements which are in practice fine
+                "nested-interactive",
+                // XXX: We have some known contrast issues here
+                "color-contrast",
+            ]);
 
-        const childSpaceId = await app.client.createSpace({
-            name: "Child Space",
-            initial_state: [],
-        });
-        await app.client.createSpace({
-            name: "Root Space",
-            initial_state: [spaceChildInitialState(childSpaceId)],
-        });
+            const childSpaceId = await app.client.createSpace({
+                name: "Child Space",
+                initial_state: [],
+            });
+            await app.client.createSpace({
+                name: "Root Space",
+                initial_state: [spaceChildInitialState(childSpaceId)],
+            });
 
-        // Find collapsed Space panel
-        const spaceTree = page.getByRole("tree", { name: "Spaces" });
-        await expect(spaceTree.getByRole("button", { name: "Root Space" })).toBeVisible();
-        await expect(spaceTree.getByRole("button", { name: "Child Space" })).not.toBeVisible();
+            // Find collapsed Space panel
+            const spaceTree = page.getByRole("tree", { name: "Spaces" });
+            await expect(spaceTree.getByRole("button", { name: "Root Space" })).toBeVisible();
+            await expect(spaceTree.getByRole("button", { name: "Child Space" })).not.toBeVisible();
 
-        await checkA11y();
-        await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-collapsed.png");
+            await checkA11y();
+            await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-collapsed.png");
 
-        // This finds the expand button with the class name "mx_SpaceButton_toggleCollapse". Note there is another
-        // button with the same name with different class name "mx_SpacePanel_toggleCollapse".
-        await spaceTree.getByRole("button", { name: "Expand" }).click();
-        await expect(page.locator(".mx_SpacePanel:not(.collapsed)")).toBeVisible(); // TODO: replace :not() selector
+            // This finds the expand button with the class name "mx_SpaceButton_toggleCollapse". Note there is another
+            // button with the same name with different class name "mx_SpacePanel_toggleCollapse".
+            await spaceTree.getByRole("button", { name: "Expand" }).click();
+            await expect(page.locator(".mx_SpacePanel:not(.collapsed)")).toBeVisible(); // TODO: replace :not() selector
 
-        const item = page.locator(".mx_SpaceItem", { hasText: "Root Space" });
-        await expect(item).toBeVisible();
-        await expect(item.locator(".mx_SpaceItem", { hasText: "Child Space" })).toBeVisible();
+            const item = page.locator(".mx_SpaceItem", { hasText: "Root Space" });
+            await expect(item).toBeVisible();
+            await expect(item.locator(".mx_SpaceItem", { hasText: "Child Space" })).toBeVisible();
 
-        await checkA11y();
-        await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-expanded.png");
-    });
+            await checkA11y();
+            await expect(page.locator(".mx_SpacePanel")).toMatchScreenshot("space-panel-expanded.png");
+        },
+    );
 
     test("should not soft crash when joining a room from space hierarchy which has a link in its topic", async ({
         page,
diff --git a/playwright/e2e/spaces/threads-activity-centre/threadsActivityCentre.spec.ts b/playwright/e2e/spaces/threads-activity-centre/threadsActivityCentre.spec.ts
index 452c9a520a..ecf458c060 100644
--- a/playwright/e2e/spaces/threads-activity-centre/threadsActivityCentre.spec.ts
+++ b/playwright/e2e/spaces/threads-activity-centre/threadsActivityCentre.spec.ts
@@ -16,16 +16,18 @@ test.describe("Threads Activity Centre", () => {
         labsFlags: ["threadsActivityCentre"],
     });
 
-    test("should have the button correctly aligned and displayed in the space panel when expanded", async ({
-        util,
-    }) => {
-        // Open the space panel
-        await util.expandSpacePanel();
-        // The buttons in the space panel should be aligned when expanded
-        await expect(util.getSpacePanel()).toMatchScreenshot("tac-button-expanded.png");
-    });
+    test(
+        "should have the button correctly aligned and displayed in the space panel when expanded",
+        { tag: "@screenshot" },
+        async ({ util }) => {
+            // Open the space panel
+            await util.expandSpacePanel();
+            // The buttons in the space panel should be aligned when expanded
+            await expect(util.getSpacePanel()).toMatchScreenshot("tac-button-expanded.png");
+        },
+    );
 
-    test("should not show indicator when there is no thread", async ({ room1, util }) => {
+    test("should not show indicator when there is no thread", { tag: "@screenshot" }, async ({ room1, util }) => {
         // No indicator should be shown
         await util.assertNoTacIndicator();
 
@@ -62,7 +64,7 @@ test.describe("Threads Activity Centre", () => {
         await util.assertHighlightIndicator();
     });
 
-    test("should show the rooms with unread threads", async ({ room1, room2, util, msg }) => {
+    test("should show the rooms with unread threads", { tag: "@screenshot" }, async ({ room1, room2, util, msg }) => {
         await util.goTo(room2);
         await util.populateThreads(room1, room2, msg);
         // The indicator should be shown
@@ -79,7 +81,7 @@ test.describe("Threads Activity Centre", () => {
         await expect(util.getTacPanel()).toMatchScreenshot("tac-panel-mix-unread.png");
     });
 
-    test("should update with a thread is read", async ({ room1, room2, util, msg }) => {
+    test("should update with a thread is read", { tag: "@screenshot" }, async ({ room1, room2, util, msg }) => {
         await util.goTo(room2);
         await util.populateThreads(room1, room2, msg);
 
@@ -128,7 +130,7 @@ test.describe("Threads Activity Centre", () => {
         await expect(page.locator(".mx_SpotlightDialog")).not.toBeVisible();
     });
 
-    test("should have the correct hover state", async ({ util, page }) => {
+    test("should have the correct hover state", { tag: "@screenshot" }, async ({ util, page }) => {
         await util.hoverTacButton();
         await expect(util.getSpacePanel()).toMatchScreenshot("tac-hovered.png");
 
@@ -138,7 +140,7 @@ test.describe("Threads Activity Centre", () => {
         await expect(util.getSpacePanel()).toMatchScreenshot("tac-hovered-expanded.png");
     });
 
-    test("should mark all threads as read", async ({ room1, room2, util, msg, page }) => {
+    test("should mark all threads as read", { tag: "@screenshot" }, async ({ room1, room2, util, msg, page }) => {
         await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
 
         await util.assertNotificationTac();
diff --git a/playwright/e2e/threads/threads.spec.ts b/playwright/e2e/threads/threads.spec.ts
index a2642a49d1..06ec57653c 100644
--- a/playwright/e2e/threads/threads.spec.ts
+++ b/playwright/e2e/threads/threads.spec.ts
@@ -25,7 +25,7 @@ test.describe("Threads", () => {
     });
 
     // Flaky: https://github.com/vector-im/element-web/issues/26452
-    test.skip("should be usable for a conversation", async ({ page, app, bot }) => {
+    test.skip("should be usable for a conversation", { tag: "@screenshot" }, async ({ page, app, bot }) => {
         const roomId = await app.client.createRoom({});
         await app.client.inviteUser(roomId, bot.credentials.userId);
         await bot.joinRoom(roomId);
@@ -150,7 +150,7 @@ test.describe("Threads", () => {
         ).toHaveCSS("padding-inline-start", ThreadViewGroupSpacingStart);
 
         // Take snapshot of group layout (IRC layout is not available on ThreadView)
-        expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
+        await expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
             "ThreadView_with_reaction_and_a_hidden_event_on_group_layout.png",
             {
                 mask: mask,
@@ -174,7 +174,7 @@ test.describe("Threads", () => {
             .toHaveCSS("margin-inline-start", "0px");
 
         // Take snapshot of bubble layout
-        expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
+        await expect(page.locator(".mx_ThreadView")).toMatchScreenshot(
             "ThreadView_with_reaction_and_a_hidden_event_on_bubble_layout.png",
             {
                 mask: mask,
@@ -351,57 +351,61 @@ test.describe("Threads", () => {
         });
     });
 
-    test("should send location and reply to the location on ThreadView", async ({ page, app, bot }) => {
-        const roomId = await app.client.createRoom({});
-        await app.client.inviteUser(roomId, bot.credentials.userId);
-        await bot.joinRoom(roomId);
-        await page.goto("/#/room/" + roomId);
+    test(
+        "should send location and reply to the location on ThreadView",
+        { tag: "@screenshot" },
+        async ({ page, app, bot }) => {
+            const roomId = await app.client.createRoom({});
+            await app.client.inviteUser(roomId, bot.credentials.userId);
+            await bot.joinRoom(roomId);
+            await page.goto("/#/room/" + roomId);
 
-        // Exclude timestamp, read marker, and maplibregl-map from snapshots
-        const css =
-            ".mx_MessageTimestamp, .mx_MessagePanel_myReadMarker, .maplibregl-map { visibility: hidden !important; }";
+            // Exclude timestamp, read marker, and maplibregl-map from snapshots
+            const css =
+                ".mx_MessageTimestamp, .mx_MessagePanel_myReadMarker, .maplibregl-map { visibility: hidden !important; }";
 
-        let locator = page.locator(".mx_RoomView_body");
-        // User sends message
-        let textbox = locator.getByRole("textbox", { name: "Send a message…" });
-        await textbox.fill("Hello Mr. Bot");
-        await textbox.press("Enter");
-        // Wait for message to send, get its ID and save as @threadId
-        const threadId = await locator
-            .locator(".mx_EventTile[data-scroll-tokens]")
-            .filter({ hasText: "Hello Mr. Bot" })
-            .getAttribute("data-scroll-tokens");
+            let locator = page.locator(".mx_RoomView_body");
+            // User sends message
+            let textbox = locator.getByRole("textbox", { name: "Send a message…" });
+            await textbox.fill("Hello Mr. Bot");
+            await textbox.press("Enter");
+            // Wait for message to send, get its ID and save as @threadId
+            const threadId = await locator
+                .locator(".mx_EventTile[data-scroll-tokens]")
+                .filter({ hasText: "Hello Mr. Bot" })
+                .getAttribute("data-scroll-tokens");
 
-        // Bot starts thread
-        await bot.sendMessage(roomId, "Hello there", threadId);
+            // Bot starts thread
+            await bot.sendMessage(roomId, "Hello there", threadId);
 
-        // User clicks thread summary
-        await page.locator(".mx_RoomView_body .mx_ThreadSummary").click();
+            // User clicks thread summary
+            await page.locator(".mx_RoomView_body .mx_ThreadSummary").click();
 
-        // User sends location on ThreadView
-        await expect(page.locator(".mx_ThreadView")).toBeAttached();
-        await (await app.openMessageComposerOptions(true)).getByRole("menuitem", { name: "Location" }).click();
-        await page.getByTestId(`share-location-option-Pin`).click();
-        await page.locator("#mx_LocationPicker_map").click();
-        await page.getByRole("button", { name: "Share location" }).click();
-        await expect(page.locator(".mx_ThreadView .mx_EventTile_last .mx_MLocationBody")).toBeAttached({
-            timeout: 10000,
-        });
+            // User sends location on ThreadView
+            await expect(page.locator(".mx_ThreadView")).toBeAttached();
+            await (await app.openMessageComposerOptions(true)).getByRole("menuitem", { name: "Location" }).click();
+            await page.getByTestId(`share-location-option-Pin`).click();
+            await page.locator("#mx_LocationPicker_map").click();
+            await page.getByRole("button", { name: "Share location" }).click();
+            await expect(page.locator(".mx_ThreadView .mx_EventTile_last .mx_MLocationBody")).toBeAttached({
+                timeout: 10000,
+            });
 
-        // User replies to the location
-        locator = page.locator(".mx_ThreadView");
-        await locator.locator(".mx_EventTile_last").hover();
-        await locator.locator(".mx_EventTile_last").getByRole("button", { name: "Reply" }).click();
-        textbox = locator.getByRole("textbox", { name: "Reply to thread…" });
-        await textbox.fill("Please come here");
-        await textbox.press("Enter");
-        // Wait until the reply is sent
-        await expect(locator.locator(".mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible();
+            // User replies to the location
+            locator = page.locator(".mx_ThreadView");
+            await locator.locator(".mx_EventTile_last").hover();
+            await locator.locator(".mx_EventTile_last").getByRole("button", { name: "Reply" }).click();
+            textbox = locator.getByRole("textbox", { name: "Reply to thread…" });
+            await textbox.fill("Please come here");
+            await textbox.press("Enter");
+            // Wait until the reply is sent
+            await expect(locator.locator(".mx_EventTile_last .mx_EventTile_receiptSent")).toBeVisible();
 
-        // Take a snapshot of reply to the shared location
-        await page.addStyleTag({ content: css });
-        await expect(page.locator(".mx_ThreadView")).toMatchScreenshot("Reply_to_the_location_on_ThreadView.png");
-    });
+            // Take a snapshot of reply to the shared location
+            await page.addStyleTag({ content: css });
+            await expect(page.locator(".mx_ThreadView")).toMatchScreenshot("Reply_to_the_location_on_ThreadView.png");
+        },
+    );
 
     test("right panel behaves correctly", async ({ page, app, user }) => {
         // Create room
diff --git a/playwright/e2e/timeline/timeline.spec.ts b/playwright/e2e/timeline/timeline.spec.ts
index e8ef0e577c..7aaabb9759 100644
--- a/playwright/e2e/timeline/timeline.spec.ts
+++ b/playwright/e2e/timeline/timeline.spec.ts
@@ -137,182 +137,190 @@ test.describe("Timeline", () => {
     });
 
     test.describe("configure room", () => {
-        test("should create and configure a room on IRC layout", async ({ page, app, room }) => {
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
-            await expect(
-                page.locator(
-                    ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
-                    { hasText: `${OLD_NAME} created and configured the room.` },
-                ),
-            ).toBeVisible();
+        test(
+            "should create and configure a room on IRC layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+                await expect(
+                    page.locator(
+                        ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
+                        { hasText: `${OLD_NAME} created and configured the room.` },
+                    ),
+                ).toBeVisible();
 
-            // wait for the date separator to appear to have a stable screenshot
-            await expect(page.locator(".mx_TimelineSeparator")).toHaveText("today");
+                // wait for the date separator to appear to have a stable screenshot
+                await expect(page.locator(".mx_TimelineSeparator")).toHaveText("today");
 
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("configured-room-irc-layout.png");
-        });
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("configured-room-irc-layout.png");
+            },
+        );
 
-        test("should have an expanded generic event list summary (GELS) on IRC layout", async ({ page, app, room }) => {
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+        test(
+            "should have an expanded generic event list summary (GELS) on IRC layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
 
-            // Wait until configuration is finished
-            await expect(
-                page.locator(
-                    ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
-                    { hasText: `${OLD_NAME} created and configured the room.` },
-                ),
-            ).toBeVisible();
+                // Wait until configuration is finished
+                await expect(
+                    page.locator(
+                        ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
+                        { hasText: `${OLD_NAME} created and configured the room.` },
+                    ),
+                ).toBeVisible();
 
-            const gels = page.locator(".mx_GenericEventListSummary");
-            // Click "expand" link button
-            await gels.getByRole("button", { name: "Expand" }).click();
-            // Assert that the "expand" link button worked
-            await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
+                const gels = page.locator(".mx_GenericEventListSummary");
+                // Click "expand" link button
+                await gels.getByRole("button", { name: "Expand" }).click();
+                // Assert that the "expand" link button worked
+                await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
 
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-irc-layout.png", {
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
-                    .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
-                        display: none !important;
-                    }
-                `,
-            });
-        });
-
-        test("should have an expanded generic event list summary (GELS) on compact modern/group layout", async ({
-            page,
-            app,
-            room,
-        }) => {
-            await page.goto(`/#/room/${room.roomId}`);
-
-            // Set compact modern layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
-            await app.settings.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
-
-            // Wait until configuration is finished
-            await expect(
-                page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
-                    hasText: `${OLD_NAME} created and configured the room.`,
-                }),
-            ).toBeVisible();
-
-            const gels = page.locator(".mx_GenericEventListSummary");
-            // Click "expand" link button
-            await gels.getByRole("button", { name: "Expand" }).click();
-            // Assert that the "expand" link button worked
-            await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
-
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-modern-layout.png", {
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
-                    .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
-                        display: none !important;
-                    }
-                `,
-            });
-        });
-
-        test("should click 'collapse' on the first hovered info event line inside GELS on bubble layout", async ({
-            page,
-            app,
-            room,
-        }) => {
-            // This test checks clickability of the "Collapse" link button, which had been covered with
-            // MessageActionBar's safe area - https://github.com/vector-im/element-web/issues/22864
-
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
-            await expect(
-                page.locator(
-                    ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='bubble'] .mx_GenericEventListSummary_summary",
-                    { hasText: `${OLD_NAME} created and configured the room.` },
-                ),
-            ).toBeVisible();
-
-            const gels = page.locator(".mx_GenericEventListSummary");
-            // Click "expand" link button
-            await gels.getByRole("button", { name: "Expand" }).click();
-            // Assert that the "expand" link button worked
-            await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
-
-            // Make sure spacer is not visible on bubble layout
-            await expect(
-                page.locator(".mx_GenericEventListSummary[data-layout=bubble] .mx_GenericEventListSummary_spacer"),
-            ).not.toBeVisible(); // See: _GenericEventListSummary.pcss
-
-            // Save snapshot of expanded generic event list summary on bubble layout
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-bubble-layout.png", {
-                // Exclude timestamp from snapshot
-                mask: [page.locator(".mx_MessageTimestamp")],
-            });
-
-            // Click "collapse" link button on the first hovered info event line
-            const firstTile = gels.locator(".mx_GenericEventListSummary_unstyledList .mx_EventTile_info:first-of-type");
-            await firstTile.hover();
-            await expect(firstTile.getByRole("toolbar", { name: "Message Actions" })).toBeVisible();
-            await gels.getByRole("button", { name: "Collapse" }).click();
-
-            // Assert that "collapse" link button worked
-            await expect(gels.getByRole("button", { name: "Expand" })).toBeVisible();
-
-            // Save snapshot of collapsed generic event list summary on bubble layout
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("collapsed-gels-bubble-layout.png", {
-                mask: [page.locator(".mx_MessageTimestamp")],
-            });
-        });
-
-        test("should add inline start margin to an event line on IRC layout", async ({
-            page,
-            app,
-            room,
-            axe,
-            checkA11y,
-        }) => {
-            axe.disableRules("color-contrast");
-
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
-
-            // Wait until configuration is finished
-            await expect(
-                page.locator(
-                    ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
-                    { hasText: `${OLD_NAME} created and configured the room.` },
-                ),
-            ).toBeVisible();
-
-            // Click "expand" link button
-            await page.locator(".mx_GenericEventListSummary").getByRole("button", { name: "Expand" }).click();
-
-            // Check the event line has margin instead of inset property
-            // cf. _EventTile.pcss
-            //  --EventTile_irc_line_info-margin-inline-start
-            //  = calc(var(--name-width) + var(--icon-width) + 1 * var(--right-padding))
-            //  = 80 + 14 + 5 = 99px
-
-            const firstEventLineIrc = page.locator(
-                ".mx_EventTile_info[data-layout=irc]:first-of-type .mx_EventTile_line",
-            );
-            await expect(firstEventLineIrc).toHaveCSS("margin-inline-start", "99px");
-            await expect(firstEventLineIrc).toHaveCSS("inset-inline-start", "0px");
-
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "event-line-inline-start-margin-irc-layout.png",
-                {
-                    // Exclude timestamp and read marker from snapshot
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-irc-layout.png", {
                     mask: [page.locator(".mx_MessageTimestamp")],
                     css: `
                     .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
                         display: none !important;
                     }
                 `,
-                },
-            );
-            await checkA11y();
-        });
+                });
+            },
+        );
+
+        test(
+            "should have an expanded generic event list summary (GELS) on compact modern/group layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                await page.goto(`/#/room/${room.roomId}`);
+
+                // Set compact modern layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
+                await app.settings.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
+
+                // Wait until configuration is finished
+                await expect(
+                    page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
+                        hasText: `${OLD_NAME} created and configured the room.`,
+                    }),
+                ).toBeVisible();
+
+                const gels = page.locator(".mx_GenericEventListSummary");
+                // Click "expand" link button
+                await gels.getByRole("button", { name: "Expand" }).click();
+                // Assert that the "expand" link button worked
+                await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
+
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-modern-layout.png", {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                    css: `
+                    .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
+                        display: none !important;
+                    }
+                `,
+                });
+            },
+        );
+
+        test(
+            "should click 'collapse' on the first hovered info event line inside GELS on bubble layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                // This test checks clickability of the "Collapse" link button, which had been covered with
+                // MessageActionBar's safe area - https://github.com/vector-im/element-web/issues/22864
+
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
+                await expect(
+                    page.locator(
+                        ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='bubble'] .mx_GenericEventListSummary_summary",
+                        { hasText: `${OLD_NAME} created and configured the room.` },
+                    ),
+                ).toBeVisible();
+
+                const gels = page.locator(".mx_GenericEventListSummary");
+                // Click "expand" link button
+                await gels.getByRole("button", { name: "Expand" }).click();
+                // Assert that the "expand" link button worked
+                await expect(gels.getByRole("button", { name: "Collapse" })).toBeVisible();
+
+                // Make sure spacer is not visible on bubble layout
+                await expect(
+                    page.locator(".mx_GenericEventListSummary[data-layout=bubble] .mx_GenericEventListSummary_spacer"),
+                ).not.toBeVisible(); // See: _GenericEventListSummary.pcss
+
+                // Save snapshot of expanded generic event list summary on bubble layout
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-bubble-layout.png", {
+                    // Exclude timestamp from snapshot
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                });
+
+                // Click "collapse" link button on the first hovered info event line
+                const firstTile = gels.locator(
+                    ".mx_GenericEventListSummary_unstyledList .mx_EventTile_info:first-of-type",
+                );
+                await firstTile.hover();
+                await expect(firstTile.getByRole("toolbar", { name: "Message Actions" })).toBeVisible();
+                await gels.getByRole("button", { name: "Collapse" }).click();
+
+                // Assert that "collapse" link button worked
+                await expect(gels.getByRole("button", { name: "Expand" })).toBeVisible();
+
+                // Save snapshot of collapsed generic event list summary on bubble layout
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("collapsed-gels-bubble-layout.png", {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                });
+            },
+        );
+
+        test(
+            "should add inline start margin to an event line on IRC layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room, axe, checkA11y }) => {
+                axe.disableRules("color-contrast");
+
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+
+                // Wait until configuration is finished
+                await expect(
+                    page.locator(
+                        ".mx_RoomView_body .mx_GenericEventListSummary[data-layout='irc'] .mx_GenericEventListSummary_summary",
+                        { hasText: `${OLD_NAME} created and configured the room.` },
+                    ),
+                ).toBeVisible();
+
+                // Click "expand" link button
+                await page.locator(".mx_GenericEventListSummary").getByRole("button", { name: "Expand" }).click();
+
+                // Check the event line has margin instead of inset property
+                // cf. _EventTile.pcss
+                //  --EventTile_irc_line_info-margin-inline-start
+                //  = calc(var(--name-width) + var(--icon-width) + 1 * var(--right-padding))
+                //  = 80 + 14 + 5 = 99px
+
+                const firstEventLineIrc = page.locator(
+                    ".mx_EventTile_info[data-layout=irc]:first-of-type .mx_EventTile_line",
+                );
+                await expect(firstEventLineIrc).toHaveCSS("margin-inline-start", "99px");
+                await expect(firstEventLineIrc).toHaveCSS("inset-inline-start", "0px");
+
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "event-line-inline-start-margin-irc-layout.png",
+                    {
+                        // Exclude timestamp and read marker from snapshot
+                        mask: [page.locator(".mx_MessageTimestamp")],
+                        css: `
+                    .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
+                        display: none !important;
+                    }
+                `,
+                    },
+                );
+                await checkA11y();
+            },
+        );
     });
 
     test.describe("message displaying", () => {
@@ -332,289 +340,311 @@ test.describe("Timeline", () => {
             ).toBeVisible();
         };
 
-        test("should align generic event list summary with messages and emote on IRC layout", async ({
-            page,
-            app,
-            room,
-        }) => {
-            // This test aims to check:
-            // 1. Alignment of collapsed GELS (generic event list summary) and messages
-            // 2. Alignment of expanded GELS and messages
-            // 3. Alignment of expanded GELS and placeholder of deleted message
-            // 4. Alignment of expanded GELS, placeholder of deleted message, and emote
+        test(
+            "should align generic event list summary with messages and emote on IRC layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                // This test aims to check:
+                // 1. Alignment of collapsed GELS (generic event list summary) and messages
+                // 2. Alignment of expanded GELS and messages
+                // 3. Alignment of expanded GELS and placeholder of deleted message
+                // 4. Alignment of expanded GELS, placeholder of deleted message, and emote
 
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
 
-            // Wait until configuration is finished
-            await expect(
-                page
-                    .locator(".mx_GenericEventListSummary_summary")
-                    .getByText(`${OLD_NAME} created and configured the room.`),
-            ).toBeVisible();
+                // Wait until configuration is finished
+                await expect(
+                    page
+                        .locator(".mx_GenericEventListSummary_summary")
+                        .getByText(`${OLD_NAME} created and configured the room.`),
+                ).toBeVisible();
 
-            // Send messages
-            const composer = app.getComposerField();
-            await composer.fill("Hello Mr. Bot");
-            await composer.press("Enter");
-            await composer.fill("Hello again, Mr. Bot");
-            await composer.press("Enter");
+                // Send messages
+                const composer = app.getComposerField();
+                await composer.fill("Hello Mr. Bot");
+                await composer.press("Enter");
+                await composer.fill("Hello again, Mr. Bot");
+                await composer.press("Enter");
 
-            // Make sure the second message was sent
-            await expect(
-                page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"),
-            ).toBeVisible();
+                // Make sure the second message was sent
+                await expect(
+                    page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"),
+                ).toBeVisible();
 
-            // 1. Alignment of collapsed GELS (generic event list summary) and messages
-            // Check inline start spacing of collapsed GELS
-            // See: _EventTile.pcss
-            // .mx_GenericEventListSummary[data-layout="irc"] > .mx_EventTile_line
-            //  = var(--name-width) + var(--icon-width) + var(--MessageTimestamp-width) + 2 * var(--right-padding)
-            //  = 80 + 14 + 46 + 2 * 5
-            //  = 150px
-            await expect(page.locator(".mx_GenericEventListSummary[data-layout=irc] > .mx_EventTile_line")).toHaveCSS(
-                "padding-inline-start",
-                "150px",
-            );
-            // Check width and spacing values of elements in .mx_EventTile, which should be equal to 150px
-            // --right-padding should be applied
-            for (const locator of await page.locator(".mx_EventTile > a").all()) {
-                if (await locator.isVisible()) {
-                    await expect(locator).toHaveCSS("margin-right", "5px");
+                // 1. Alignment of collapsed GELS (generic event list summary) and messages
+                // Check inline start spacing of collapsed GELS
+                // See: _EventTile.pcss
+                // .mx_GenericEventListSummary[data-layout="irc"] > .mx_EventTile_line
+                //  = var(--name-width) + var(--icon-width) + var(--MessageTimestamp-width) + 2 * var(--right-padding)
+                //  = 80 + 14 + 46 + 2 * 5
+                //  = 150px
+                await expect(
+                    page.locator(".mx_GenericEventListSummary[data-layout=irc] > .mx_EventTile_line"),
+                ).toHaveCSS("padding-inline-start", "150px");
+                // Check width and spacing values of elements in .mx_EventTile, which should be equal to 150px
+                // --right-padding should be applied
+                for (const locator of await page.locator(".mx_EventTile > a").all()) {
+                    if (await locator.isVisible()) {
+                        await expect(locator).toHaveCSS("margin-right", "5px");
+                    }
                 }
-            }
-            // --name-width width zero inline end margin should be applied
-            for (const locator of await page.locator(".mx_EventTile .mx_DisambiguatedProfile").all()) {
-                await expect(locator).toHaveCSS("width", "80px");
-                await expect(locator).toHaveCSS("margin-inline-end", "0px");
-            }
-            // --icon-width should be applied
-            for (const locator of await page.locator(".mx_EventTile .mx_EventTile_avatar > .mx_BaseAvatar").all()) {
-                await expect(locator).toHaveCSS("width", "14px");
-            }
-            // var(--MessageTimestamp-width) should be applied
-            for (const locator of await page.locator(".mx_EventTile > a").all()) {
-                await expect(locator).toHaveCSS("min-width", "46px");
-            }
-            // Record alignment of collapsed GELS and messages on messagePanel
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "collapsed-gels-and-messages-irc-layout.png",
-                {
+                // --name-width width zero inline end margin should be applied
+                for (const locator of await page.locator(".mx_EventTile .mx_DisambiguatedProfile").all()) {
+                    await expect(locator).toHaveCSS("width", "80px");
+                    await expect(locator).toHaveCSS("margin-inline-end", "0px");
+                }
+                // --icon-width should be applied
+                for (const locator of await page.locator(".mx_EventTile .mx_EventTile_avatar > .mx_BaseAvatar").all()) {
+                    await expect(locator).toHaveCSS("width", "14px");
+                }
+                // var(--MessageTimestamp-width) should be applied
+                for (const locator of await page.locator(".mx_EventTile > a").all()) {
+                    await expect(locator).toHaveCSS("min-width", "46px");
+                }
+                // Record alignment of collapsed GELS and messages on messagePanel
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "collapsed-gels-and-messages-irc-layout.png",
+                    {
+                        // Exclude timestamp from snapshot of mx_MainSplit
+                        mask: [page.locator(".mx_MessageTimestamp")],
+                    },
+                );
+
+                // 2. Alignment of expanded GELS and messages
+                // Click "expand" link button
+                await page.locator(".mx_GenericEventListSummary").getByRole("button", { name: "Expand" }).click();
+                // Check inline start spacing of info line on expanded GELS
+                // See: _EventTile.pcss
+                // --EventTile_irc_line_info-margin-inline-start
+                // = 80 + 14 + 1 * 5
+                await expect(
+                    page.locator(".mx_EventTile[data-layout=irc].mx_EventTile_info:first-of-type .mx_EventTile_line"),
+                ).toHaveCSS("margin-inline-start", "99px");
+                // Record alignment of expanded GELS and messages on messagePanel
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "expanded-gels-and-messages-irc-layout.png",
+                    {
+                        // Exclude timestamp from snapshot of mx_MainSplit
+                        mask: [page.locator(".mx_MessageTimestamp")],
+                    },
+                );
+
+                // 3. Alignment of expanded GELS and placeholder of deleted message
+                // Delete the second (last) message
+                const lastTile = page.locator(".mx_RoomView_MessageList > .mx_EventTile_last");
+                await lastTile.hover();
+                await lastTile.getByRole("button", { name: "Options" }).click();
+                await page.getByRole("menuitem", { name: "Remove" }).click();
+                // Confirm deletion
+                await page.locator(".mx_Dialog_buttons").getByRole("button", { name: "Remove" }).click();
+                // Make sure the dialog was closed and the second (last) message was redacted
+                await expect(page.locator(".mx_Dialog")).not.toBeVisible();
+                await expect(
+                    page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_RedactedBody"),
+                ).toBeVisible();
+                await expect(
+                    page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_EventTile_receiptSent"),
+                ).toBeVisible();
+                // Record alignment of expanded GELS and placeholder of deleted message on messagePanel
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "expanded-gels-redaction-placeholder.png",
+                    {
+                        // Exclude timestamp from snapshot of mx_MainSplit
+                        mask: [page.locator(".mx_MessageTimestamp")],
+                    },
+                );
+
+                // 4. Alignment of expanded GELS, placeholder of deleted message, and emote
+                // Send a emote
+                await page
+                    .locator(".mx_RoomView_body")
+                    .getByRole("textbox", { name: "Send a message…" })
+                    .fill("/me says hello to Mr. Bot");
+                await page
+                    .locator(".mx_RoomView_body")
+                    .getByRole("textbox", { name: "Send a message…" })
+                    .press("Enter");
+                // Check inline start margin of its avatar
+                // Here --right-padding is for the avatar on the message line
+                // See: _IRCLayout.pcss
+                // .mx_IRCLayout .mx_EventTile_emote .mx_EventTile_avatar
+                // = calc(var(--name-width) + var(--icon-width) + 1 * var(--right-padding))
+                // = 80 + 14 + 1 * 5
+                await expect(page.locator(".mx_EventTile_emote .mx_EventTile_avatar")).toHaveCSS("margin-left", "99px");
+                // Make sure emote was sent
+                await expect(
+                    page.locator(".mx_EventTile_last.mx_EventTile_emote .mx_EventTile_receiptSent"),
+                ).toBeVisible();
+                // Record alignment of expanded GELS, placeholder of deleted message, and emote
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-emote-irc-layout.png", {
                     // Exclude timestamp from snapshot of mx_MainSplit
                     mask: [page.locator(".mx_MessageTimestamp")],
-                },
-            );
+                });
+            },
+        );
 
-            // 2. Alignment of expanded GELS and messages
-            // Click "expand" link button
-            await page.locator(".mx_GenericEventListSummary").getByRole("button", { name: "Expand" }).click();
-            // Check inline start spacing of info line on expanded GELS
-            // See: _EventTile.pcss
-            // --EventTile_irc_line_info-margin-inline-start
-            // = 80 + 14 + 1 * 5
-            await expect(
-                page.locator(".mx_EventTile[data-layout=irc].mx_EventTile_info:first-of-type .mx_EventTile_line"),
-            ).toHaveCSS("margin-inline-start", "99px");
-            // Record alignment of expanded GELS and messages on messagePanel
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-and-messages-irc-layout.png", {
-                // Exclude timestamp from snapshot of mx_MainSplit
-                mask: [page.locator(".mx_MessageTimestamp")],
-            });
-
-            // 3. Alignment of expanded GELS and placeholder of deleted message
-            // Delete the second (last) message
-            const lastTile = page.locator(".mx_RoomView_MessageList > .mx_EventTile_last");
-            await lastTile.hover();
-            await lastTile.getByRole("button", { name: "Options" }).click();
-            await page.getByRole("menuitem", { name: "Remove" }).click();
-            // Confirm deletion
-            await page.locator(".mx_Dialog_buttons").getByRole("button", { name: "Remove" }).click();
-            // Make sure the dialog was closed and the second (last) message was redacted
-            await expect(page.locator(".mx_Dialog")).not.toBeVisible();
-            await expect(page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_RedactedBody")).toBeVisible();
-            await expect(
-                page.locator(".mx_GenericEventListSummary .mx_EventTile_last .mx_EventTile_receiptSent"),
-            ).toBeVisible();
-            // Record alignment of expanded GELS and placeholder of deleted message on messagePanel
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-redaction-placeholder.png", {
-                // Exclude timestamp from snapshot of mx_MainSplit
-                mask: [page.locator(".mx_MessageTimestamp")],
-            });
-
-            // 4. Alignment of expanded GELS, placeholder of deleted message, and emote
-            // Send a emote
-            await page
-                .locator(".mx_RoomView_body")
-                .getByRole("textbox", { name: "Send a message…" })
-                .fill("/me says hello to Mr. Bot");
-            await page.locator(".mx_RoomView_body").getByRole("textbox", { name: "Send a message…" }).press("Enter");
-            // Check inline start margin of its avatar
-            // Here --right-padding is for the avatar on the message line
-            // See: _IRCLayout.pcss
-            // .mx_IRCLayout .mx_EventTile_emote .mx_EventTile_avatar
-            // = calc(var(--name-width) + var(--icon-width) + 1 * var(--right-padding))
-            // = 80 + 14 + 1 * 5
-            await expect(page.locator(".mx_EventTile_emote .mx_EventTile_avatar")).toHaveCSS("margin-left", "99px");
-            // Make sure emote was sent
-            await expect(page.locator(".mx_EventTile_last.mx_EventTile_emote .mx_EventTile_receiptSent")).toBeVisible();
-            // Record alignment of expanded GELS, placeholder of deleted message, and emote
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("expanded-gels-emote-irc-layout.png", {
-                // Exclude timestamp from snapshot of mx_MainSplit
-                mask: [page.locator(".mx_MessageTimestamp")],
-            });
-        });
-
-        test("should render EventTiles on IRC, modern (group), and bubble layout", async ({ page, app, room }) => {
-            const screenshotOptions = {
-                // Hide because flaky - See https://github.com/vector-im/element-web/issues/24957
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
+        test(
+            "should render EventTiles on IRC, modern (group), and bubble layout",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                const screenshotOptions = {
+                    // Hide because flaky - See https://github.com/vector-im/element-web/issues/24957
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                    css: `
                     .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
                         display: none !important;
                     }
                 `,
-            };
+                };
 
-            await sendEvent(app.client, room.roomId);
-            await sendEvent(app.client, room.roomId); // check continuation
-            await sendEvent(app.client, room.roomId); // check the last EventTile
+                await sendEvent(app.client, room.roomId);
+                await sendEvent(app.client, room.roomId); // check continuation
+                await sendEvent(app.client, room.roomId); // check the last EventTile
 
-            await page.goto(`/#/room/${room.roomId}`);
-            const composer = app.getComposerField();
-            // Send a plain text message
-            await composer.fill("Hello");
-            await composer.press("Enter");
-            // Send a big emoji
-            await composer.fill("🏀");
-            await composer.press("Enter");
-            // Send an inline emoji
-            await composer.fill("This message has an inline emoji 👒");
-            await composer.press("Enter");
+                await page.goto(`/#/room/${room.roomId}`);
+                const composer = app.getComposerField();
+                // Send a plain text message
+                await composer.fill("Hello");
+                await composer.press("Enter");
+                // Send a big emoji
+                await composer.fill("🏀");
+                await composer.press("Enter");
+                // Send an inline emoji
+                await composer.fill("This message has an inline emoji 👒");
+                await composer.press("Enter");
 
-            await expect(page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒")).toBeVisible();
+                await expect(
+                    page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
+                ).toBeVisible();
 
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
-            // IRC layout
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                // IRC layout
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
 
-            // Wait until configuration is finished
-            await expect(
-                page
-                    .locator(".mx_GenericEventListSummary_summary")
-                    .getByText(`${OLD_NAME} created and configured the room.`),
-            ).toBeVisible();
+                // Wait until configuration is finished
+                await expect(
+                    page
+                        .locator(".mx_GenericEventListSummary_summary")
+                        .getByText(`${OLD_NAME} created and configured the room.`),
+                ).toBeVisible();
 
-            await app.timeline.scrollToBottom();
-            await expect(
-                page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
-            ).toBeInViewport();
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "event-tiles-irc-layout.png",
-                screenshotOptions,
-            );
+                await app.timeline.scrollToBottom();
+                await expect(
+                    page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
+                ).toBeInViewport();
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "event-tiles-irc-layout.png",
+                    screenshotOptions,
+                );
 
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
-            // Group/modern layout
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                // Group/modern layout
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
 
-            // Check that the last EventTile is rendered
-            await app.timeline.scrollToBottom();
-            await expect(
-                page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
-            ).toBeInViewport();
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "event-tiles-modern-layout.png",
-                screenshotOptions,
-            );
+                // Check that the last EventTile is rendered
+                await app.timeline.scrollToBottom();
+                await expect(
+                    page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
+                ).toBeInViewport();
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "event-tiles-modern-layout.png",
+                    screenshotOptions,
+                );
 
-            // Check the same thing for compact layout
-            await app.settings.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
+                // Check the same thing for compact layout
+                await app.settings.setValue("useCompactLayout", null, SettingLevel.DEVICE, true);
 
-            // Check that the last EventTile is rendered
-            await app.timeline.scrollToBottom();
-            await expect(
-                page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
-            ).toBeInViewport();
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "event-tiles-compact-modern-layout.png",
-                screenshotOptions,
-            );
+                // Check that the last EventTile is rendered
+                await app.timeline.scrollToBottom();
+                await expect(
+                    page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
+                ).toBeInViewport();
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "event-tiles-compact-modern-layout.png",
+                    screenshotOptions,
+                );
 
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
-            // Message bubble layout
-            ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+                // Message bubble layout
+                ////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
 
-            await app.timeline.scrollToBottom();
-            await expect(
-                page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
-            ).toBeInViewport();
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "event-tiles-bubble-layout.png",
-                screenshotOptions,
-            );
-        });
+                await app.timeline.scrollToBottom();
+                await expect(
+                    page.locator(".mx_RoomView").getByText("This message has an inline emoji 👒"),
+                ).toBeInViewport();
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "event-tiles-bubble-layout.png",
+                    screenshotOptions,
+                );
+            },
+        );
 
-        test("should set inline start padding to a hidden event line", async ({ page, app, room }) => {
-            test.skip(
-                true,
-                "Disabled due to screenshot test being flaky - https://github.com/element-hq/element-web/issues/26890",
-            );
-            await sendEvent(app.client, room.roomId);
-            await page.goto(`/#/room/${room.roomId}`);
-            await app.settings.setValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true);
-            await expect(
-                page
-                    .locator(".mx_GenericEventListSummary_summary")
-                    .getByText(`${OLD_NAME} created and configured the room.`),
-            ).toBeVisible();
+        test(
+            "should set inline start padding to a hidden event line",
+            { tag: "@screenshot" },
+            async ({ page, app, room }) => {
+                test.skip(
+                    true,
+                    "Disabled due to screenshot test being flaky - https://github.com/element-hq/element-web/issues/26890",
+                );
+                await sendEvent(app.client, room.roomId);
+                await page.goto(`/#/room/${room.roomId}`);
+                await app.settings.setValue("showHiddenEventsInTimeline", null, SettingLevel.DEVICE, true);
+                await expect(
+                    page
+                        .locator(".mx_GenericEventListSummary_summary")
+                        .getByText(`${OLD_NAME} created and configured the room.`),
+                ).toBeVisible();
 
-            // Edit message
-            await messageEdit(page);
+                // Edit message
+                await messageEdit(page);
 
-            // Click timestamp to highlight hidden event line
-            await page.locator(".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp").click();
+                // Click timestamp to highlight hidden event line
+                await page.locator(".mx_RoomView_body .mx_EventTile_info .mx_MessageTimestamp").click();
 
-            // should not add inline start padding to a hidden event line on IRC layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
-            await expect(
-                page.locator(".mx_EventTile[data-layout=irc].mx_EventTile_info .mx_EventTile_line").first(),
-            ).toHaveCSS("padding-inline-start", "0px");
+                // should not add inline start padding to a hidden event line on IRC layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+                await expect(
+                    page.locator(".mx_EventTile[data-layout=irc].mx_EventTile_info .mx_EventTile_line").first(),
+                ).toHaveCSS("padding-inline-start", "0px");
 
-            // Exclude timestamp and read marker from snapshot
-            const screenshotOptions = {
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
+                // Exclude timestamp and read marker from snapshot
+                const screenshotOptions = {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                    css: `
                     .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
                         display: none !important;
                     }
                 `,
-            };
+                };
 
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "hidden-event-line-zero-padding-irc-layout.png",
-                screenshotOptions,
-            );
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "hidden-event-line-zero-padding-irc-layout.png",
+                    screenshotOptions,
+                );
 
-            // should add inline start padding to a hidden event line on modern layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
-            // calc(var(--EventTile_group_line-spacing-inline-start) + 20px) = 64 + 20 = 84px
-            await expect(
-                page.locator(".mx_EventTile[data-layout=group].mx_EventTile_info .mx_EventTile_line").first(),
-            ).toHaveCSS("padding-inline-start", "84px");
+                // should add inline start padding to a hidden event line on modern layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
+                // calc(var(--EventTile_group_line-spacing-inline-start) + 20px) = 64 + 20 = 84px
+                await expect(
+                    page.locator(".mx_EventTile[data-layout=group].mx_EventTile_info .mx_EventTile_line").first(),
+                ).toHaveCSS("padding-inline-start", "84px");
 
-            await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
-                "hidden-event-line-padding-modern-layout.png",
-                screenshotOptions,
-            );
-        });
+                await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
+                    "hidden-event-line-padding-modern-layout.png",
+                    screenshotOptions,
+                );
+            },
+        );
 
-        test("should click view source event toggle", async ({ page, app, room }) => {
+        test("should click view source event toggle", { tag: "@screenshot" }, async ({ page, app, room }) => {
             // This test checks:
             // 1. clickability of top left of view source event toggle
             // 2. clickability of view source toggle on IRC layout
@@ -712,89 +742,97 @@ test.describe("Timeline", () => {
             ).toBeVisible();
         });
 
-        test("should render url previews", async ({ page, app, room, axe, checkA11y, context }) => {
-            axe.disableRules("color-contrast");
+        test(
+            "should render url previews",
+            { tag: "@screenshot" },
+            async ({ page, app, room, axe, checkA11y, 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,
-                        },
-                    });
-                },
-            );
+                // 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<any>[] = [
-                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?*"),
-            ];
+                const requestPromises: Promise<any>[] = [
+                    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 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(page.locator(".mx_LinkPreviewWidget").getByText("Element Call")).toBeVisible();
+                await Promise.all(requestPromises);
 
-            await checkA11y();
+                await checkA11y();
 
-            await app.timeline.scrollToBottom();
-            await expect(page.locator(".mx_EventTile_last")).toMatchScreenshot("url-preview.png", {
-                // Exclude timestamp and read marker from snapshot
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
+                await app.timeline.scrollToBottom();
+                await expect(page.locator(".mx_EventTile_last")).toMatchScreenshot("url-preview.png", {
+                    // Exclude timestamp and read marker from snapshot
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                    css: `
                     .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
                         display: none !important;
                     }
                 `,
-            });
-        });
+                });
+            },
+        );
 
         test.describe("on search results panel", () => {
-            test("should highlight search result words regardless of formatting", async ({ page, app, room }) => {
-                await sendEvent(app.client, room.roomId);
-                await sendEvent(app.client, room.roomId, true);
-                await page.goto(`/#/room/${room.roomId}`);
+            test(
+                "should highlight search result words regardless of formatting",
+                { tag: "@screenshot" },
+                async ({ page, app, room }) => {
+                    await sendEvent(app.client, room.roomId);
+                    await sendEvent(app.client, room.roomId, true);
+                    await page.goto(`/#/room/${room.roomId}`);
 
-                await app.toggleRoomInfoPanel();
+                    await app.toggleRoomInfoPanel();
 
-                await page.locator(".mx_RoomSummaryCard_search").getByRole("searchbox").fill("Message");
-                await page.locator(".mx_RoomSummaryCard_search").getByRole("searchbox").press("Enter");
+                    await page.locator(".mx_RoomSummaryCard_search").getByRole("searchbox").fill("Message");
+                    await page.locator(".mx_RoomSummaryCard_search").getByRole("searchbox").press("Enter");
 
-                await expect(page.locator(".mx_RoomSearchAuxPanel")).toMatchScreenshot("search-aux-panel.png");
+                    await expect(page.locator(".mx_RoomSearchAuxPanel")).toMatchScreenshot("search-aux-panel.png");
 
-                for (const locator of await page
-                    .locator(".mx_EventTile:not(.mx_EventTile_contextual) .mx_EventTile_searchHighlight")
-                    .all()) {
-                    await expect(locator).toBeVisible();
-                }
-                await expect(page.locator(".mx_RoomView_searchResultsPanel")).toMatchScreenshot(
-                    "highlighted-search-results.png",
-                );
-            });
+                    for (const locator of await page
+                        .locator(".mx_EventTile:not(.mx_EventTile_contextual) .mx_EventTile_searchHighlight")
+                        .all()) {
+                        await expect(locator).toBeVisible();
+                    }
+                    await expect(page.locator(".mx_RoomView_searchResultsPanel")).toMatchScreenshot(
+                        "highlighted-search-results.png",
+                    );
+                },
+            );
 
-            test("should render a fully opaque textual event", async ({ page, app, room }) => {
+            test("should render a fully opaque textual event", { tag: "@screenshot" }, async ({ page, app, room }) => {
                 const stringToSearch = "Message"; // Same with string sent with sendEvent()
 
                 await sendEvent(app.client, room.roomId);
@@ -918,7 +956,7 @@ test.describe("Timeline", () => {
             ).toHaveCount(4);
         });
 
-        test("should display a reply chain", async ({ page, app, room, homeserver }) => {
+        test("should display a reply chain", { tag: "@screenshot" }, async ({ page, app, room, homeserver }) => {
             const reply2 = "Reply again";
 
             await page.goto(`/#/room/${room.roomId}`);
@@ -1031,122 +1069,121 @@ test.describe("Timeline", () => {
             );
         });
 
-        test("should send, reply, and display long strings without overflowing", async ({
-            page,
-            app,
-            room,
-            homeserver,
-        }) => {
-            // Max 256 characters for display name
-            const LONG_STRING =
-                "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut " +
-                "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
-                "aliquip";
+        test(
+            "should send, reply, and display long strings without overflowing",
+            { tag: "@screenshot" },
+            async ({ page, app, room, homeserver }) => {
+                // Max 256 characters for display name
+                const LONG_STRING =
+                    "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut " +
+                    "et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " +
+                    "aliquip";
 
-            const newDisplayName = `${LONG_STRING} 2`;
+                const newDisplayName = `${LONG_STRING} 2`;
 
-            // Set the display name to "LONG_STRING 2" in order to avoid screenshot tests from failing
-            // due to the generated random mxid being displayed inside the GELS summary.
-            // Note that we set it here as the test was failing on CI (but not locally!) if the name
-            // was changed afterwards. This is quite concerning, but maybe better than just disabling the
-            // whole test?
-            // https://github.com/element-hq/element-web/issues/27109
-            await app.client.setDisplayName(newDisplayName);
+                // Set the display name to "LONG_STRING 2" in order to avoid screenshot tests from failing
+                // due to the generated random mxid being displayed inside the GELS summary.
+                // Note that we set it here as the test was failing on CI (but not locally!) if the name
+                // was changed afterwards. This is quite concerning, but maybe better than just disabling the
+                // whole test?
+                // https://github.com/element-hq/element-web/issues/27109
+                await app.client.setDisplayName(newDisplayName);
 
-            // Create a bot with a long display name
-            const bot = new Bot(page, homeserver, {
-                displayName: LONG_STRING,
-                autoAcceptInvites: false,
-            });
-            await bot.prepareClient();
+                // Create a bot with a long display name
+                const bot = new Bot(page, homeserver, {
+                    displayName: LONG_STRING,
+                    autoAcceptInvites: false,
+                });
+                await bot.prepareClient();
 
-            // Create another room with a long name, invite the bot, and open the room
-            const testRoomId = await app.client.createRoom({ name: LONG_STRING });
-            await app.client.inviteUser(testRoomId, bot.credentials.userId);
-            await bot.joinRoom(testRoomId);
-            await page.goto(`/#/room/${testRoomId}`);
+                // Create another room with a long name, invite the bot, and open the room
+                const testRoomId = await app.client.createRoom({ name: LONG_STRING });
+                await app.client.inviteUser(testRoomId, bot.credentials.userId);
+                await bot.joinRoom(testRoomId);
+                await page.goto(`/#/room/${testRoomId}`);
 
-            // Wait until configuration is finished
-            await expect(
-                page
-                    .locator(".mx_GenericEventListSummary_summary")
-                    .getByText(newDisplayName + " created and configured the room."),
-            ).toBeVisible();
+                // Wait until configuration is finished
+                await expect(
+                    page
+                        .locator(".mx_GenericEventListSummary_summary")
+                        .getByText(newDisplayName + " created and configured the room."),
+                ).toBeVisible();
 
-            // Have the bot send a long message
-            await bot.sendMessage(testRoomId, {
-                body: LONG_STRING,
-                msgtype: "m.text",
-            });
+                // Have the bot send a long message
+                await bot.sendMessage(testRoomId, {
+                    body: LONG_STRING,
+                    msgtype: "m.text",
+                });
 
-            // Wait until the message is rendered
-            await expect(
-                page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText(LONG_STRING),
-            ).toBeVisible();
+                // Wait until the message is rendered
+                await expect(
+                    page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText(LONG_STRING),
+                ).toBeVisible();
 
-            // Reply to the message
-            await clickButtonReply(page);
-            await app.getComposerField().fill(reply);
-            await app.getComposerField().press("Enter");
+                // Reply to the message
+                await clickButtonReply(page);
+                await app.getComposerField().fill(reply);
+                await app.getComposerField().press("Enter");
 
-            // Make sure the reply tile is rendered
-            const eventTileLine = page.locator(".mx_EventTile_last .mx_EventTile_line");
-            await expect(eventTileLine.locator(".mx_ReplyTile .mx_MTextBody").getByText(LONG_STRING)).toBeVisible();
+                // Make sure the reply tile is rendered
+                const eventTileLine = page.locator(".mx_EventTile_last .mx_EventTile_line");
+                await expect(eventTileLine.locator(".mx_ReplyTile .mx_MTextBody").getByText(LONG_STRING)).toBeVisible();
 
-            await expect(eventTileLine.getByText(reply)).toHaveCount(1);
+                await expect(eventTileLine.getByText(reply)).toHaveCount(1);
 
-            // Change the viewport size
-            await page.setViewportSize({ width: 1600, height: 1200 });
+                // Change the viewport size
+                await page.setViewportSize({ width: 1600, height: 1200 });
 
-            // Exclude timestamp and read marker from snapshot
-            const screenshotOptions = {
-                mask: [page.locator(".mx_MessageTimestamp")],
-                css: `
+                // Exclude timestamp and read marker from snapshot
+                const screenshotOptions = {
+                    mask: [page.locator(".mx_MessageTimestamp")],
+                    css: `
                     .mx_TopUnreadMessagesBar, .mx_MessagePanel_myReadMarker {
                         display: none !important;
                     }
                 `,
-            };
+                };
 
-            // Make sure the strings do not overflow on IRC layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
-            // Scroll to the bottom to take a snapshot of the whole viewport
-            await app.timeline.scrollToBottom();
-            // Assert that both avatar in the introduction and the last message are visible at the same time
-            await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
-            const lastEventTileIrc = page.locator(".mx_EventTile_last[data-layout='irc']");
-            await expect(lastEventTileIrc.locator(".mx_MTextBody").first()).toBeVisible();
-            await expect(lastEventTileIrc.locator(".mx_EventTile_receiptSent")).toBeVisible(); // rendered at the bottom of EventTile
-            // Take a snapshot in IRC layout
-            await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
-                "long-strings-with-reply-irc-layout.png",
-                screenshotOptions,
-            );
+                // Make sure the strings do not overflow on IRC layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC);
+                // Scroll to the bottom to take a snapshot of the whole viewport
+                await app.timeline.scrollToBottom();
+                // Assert that both avatar in the introduction and the last message are visible at the same time
+                await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
+                const lastEventTileIrc = page.locator(".mx_EventTile_last[data-layout='irc']");
+                await expect(lastEventTileIrc.locator(".mx_MTextBody").first()).toBeVisible();
+                await expect(lastEventTileIrc.locator(".mx_EventTile_receiptSent")).toBeVisible(); // rendered at the bottom of EventTile
+                // Take a snapshot in IRC layout
+                await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
+                    "long-strings-with-reply-irc-layout.png",
+                    screenshotOptions,
+                );
 
-            // Make sure the strings do not overflow on modern layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
-            await app.timeline.scrollToBottom(); // Scroll again in case
-            await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
-            const lastEventTileGroup = page.locator(".mx_EventTile_last[data-layout='group']");
-            await expect(lastEventTileGroup.locator(".mx_MTextBody").first()).toBeVisible();
-            await expect(lastEventTileGroup.locator(".mx_EventTile_receiptSent")).toBeVisible();
-            await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
-                "long-strings-with-reply-modern-layout.png",
-                screenshotOptions,
-            );
+                // Make sure the strings do not overflow on modern layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Group);
+                await app.timeline.scrollToBottom(); // Scroll again in case
+                await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
+                const lastEventTileGroup = page.locator(".mx_EventTile_last[data-layout='group']");
+                await expect(lastEventTileGroup.locator(".mx_MTextBody").first()).toBeVisible();
+                await expect(lastEventTileGroup.locator(".mx_EventTile_receiptSent")).toBeVisible();
+                await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
+                    "long-strings-with-reply-modern-layout.png",
+                    screenshotOptions,
+                );
 
-            // Make sure the strings do not overflow on bubble layout
-            await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
-            await app.timeline.scrollToBottom(); // Scroll again in case
-            await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
-            const lastEventTileBubble = page.locator(".mx_EventTile_last[data-layout='bubble']");
-            await expect(lastEventTileBubble.locator(".mx_MTextBody").first()).toBeVisible();
-            await expect(lastEventTileBubble.locator(".mx_EventTile_receiptSent")).toBeVisible();
-            await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
-                "long-strings-with-reply-bubble-layout.png",
-                screenshotOptions,
-            );
-        });
+                // Make sure the strings do not overflow on bubble layout
+                await app.settings.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble);
+                await app.timeline.scrollToBottom(); // Scroll again in case
+                await expect(page.locator(".mx_NewRoomIntro .mx_BaseAvatar")).toBeVisible();
+                const lastEventTileBubble = page.locator(".mx_EventTile_last[data-layout='bubble']");
+                await expect(lastEventTileBubble.locator(".mx_MTextBody").first()).toBeVisible();
+                await expect(lastEventTileBubble.locator(".mx_EventTile_receiptSent")).toBeVisible();
+                await expect(page.locator(".mx_ScrollPanel")).toMatchScreenshot(
+                    "long-strings-with-reply-bubble-layout.png",
+                    screenshotOptions,
+                );
+            },
+        );
 
         async function testImageRendering(page: Page, app: ElementAppPage, room: { roomId: string }) {
             await app.viewRoomById(room.roomId);
@@ -1176,7 +1213,7 @@ test.describe("Timeline", () => {
             );
         }
 
-        test("should render images in the timeline", async ({ page, app, room, context }) => {
+        test("should render images in the timeline", { tag: "@screenshot" }, async ({ page, app, room, context }) => {
             await testImageRendering(page, app, room);
         });
 
@@ -1188,50 +1225,54 @@ test.describe("Timeline", () => {
         // In practice, this means this test will *always* succeed because it ends up relying on fallback behaviour tested
         // above (unless of course the above tests are also broken).
         test.describe("MSC3916 - Authenticated Media", () => {
-            test("should render authenticated images in the timeline", async ({ page, app, room, context }) => {
-                // Note: we have to use `context` instead of `page` for routing, otherwise we'll miss Service Worker events.
-                // See https://playwright.dev/docs/service-workers-experimental#network-events-and-routing
+            test(
+                "should render authenticated images in the timeline",
+                { tag: "@screenshot" },
+                async ({ page, app, room, context }) => {
+                    // Note: we have to use `context` instead of `page` for routing, otherwise we'll miss Service Worker events.
+                    // See https://playwright.dev/docs/service-workers-experimental#network-events-and-routing
 
-                // Install our mocks and preventative measures
-                await context.route("**/_matrix/client/versions", async (route) => {
-                    // Force enable MSC3916/Matrix 1.11, which may require the service worker's internal cache to be cleared later.
-                    const json = await (await route.fetch()).json();
-                    if (!json["versions"]) json["versions"] = [];
-                    json["versions"].push("v1.11");
-                    await route.fulfill({ json });
-                });
-                await context.route("**/_matrix/media/*/download/**", async (route) => {
-                    // should not be called. We don't use `abort` so that it's clearer in the logs what happened.
-                    await route.fulfill({
-                        status: 500,
-                        json: { errcode: "M_UNKNOWN", error: "Unexpected route called." },
+                    // Install our mocks and preventative measures
+                    await context.route("**/_matrix/client/versions", async (route) => {
+                        // Force enable MSC3916/Matrix 1.11, which may require the service worker's internal cache to be cleared later.
+                        const json = await (await route.fetch()).json();
+                        if (!json["versions"]) json["versions"] = [];
+                        json["versions"].push("v1.11");
+                        await route.fulfill({ json });
                     });
-                });
-                await context.route("**/_matrix/media/*/thumbnail/**", async (route) => {
-                    // should not be called. We don't use `abort` so that it's clearer in the logs what happened.
-                    await route.fulfill({
-                        status: 500,
-                        json: { errcode: "M_UNKNOWN", error: "Unexpected route called." },
+                    await context.route("**/_matrix/media/*/download/**", async (route) => {
+                        // should not be called. We don't use `abort` so that it's clearer in the logs what happened.
+                        await route.fulfill({
+                            status: 500,
+                            json: { errcode: "M_UNKNOWN", error: "Unexpected route called." },
+                        });
                     });
-                });
-                await context.route("**/_matrix/client/v1/download/**", async (route) => {
-                    expect(route.request().headers()["Authorization"]).toBeDefined();
-                    // we can't use route.continue() because no configured homeserver supports MSC3916 yet
-                    await route.fulfill({
-                        body: NEW_AVATAR,
+                    await context.route("**/_matrix/media/*/thumbnail/**", async (route) => {
+                        // should not be called. We don't use `abort` so that it's clearer in the logs what happened.
+                        await route.fulfill({
+                            status: 500,
+                            json: { errcode: "M_UNKNOWN", error: "Unexpected route called." },
+                        });
                     });
-                });
-                await context.route("**/_matrix/client/v1/thumbnail/**", async (route) => {
-                    expect(route.request().headers()["Authorization"]).toBeDefined();
-                    // we can't use route.continue() because no configured homeserver supports MSC3916 yet
-                    await route.fulfill({
-                        body: NEW_AVATAR,
+                    await context.route("**/_matrix/client/v1/download/**", async (route) => {
+                        expect(route.request().headers()["Authorization"]).toBeDefined();
+                        // we can't use route.continue() because no configured homeserver supports MSC3916 yet
+                        await route.fulfill({
+                            body: NEW_AVATAR,
+                        });
+                    });
+                    await context.route("**/_matrix/client/v1/thumbnail/**", async (route) => {
+                        expect(route.request().headers()["Authorization"]).toBeDefined();
+                        // we can't use route.continue() because no configured homeserver supports MSC3916 yet
+                        await route.fulfill({
+                            body: NEW_AVATAR,
+                        });
                     });
-                });
 
-                // We check the same screenshot because there should be no user-visible impact to using authentication.
-                await testImageRendering(page, app, room);
-            });
+                    // We check the same screenshot because there should be no user-visible impact to using authentication.
+                    await testImageRendering(page, app, room);
+                },
+            );
         });
     });
 });
diff --git a/playwright/e2e/user-menu/user-menu.spec.ts b/playwright/e2e/user-menu/user-menu.spec.ts
index 0ad21dbded..268da00f30 100644
--- a/playwright/e2e/user-menu/user-menu.spec.ts
+++ b/playwright/e2e/user-menu/user-menu.spec.ts
@@ -11,7 +11,7 @@ import { test, expect } from "../../element-web-test";
 test.describe("User Menu", () => {
     test.use({ displayName: "Jeff" });
 
-    test("should contain our name & userId", async ({ page, user }) => {
+    test("should contain our name & userId", { tag: "@screenshot" }, async ({ page, user }) => {
         await page.getByRole("button", { name: "User menu", exact: true }).click();
         const menu = page.getByRole("menu");
 
diff --git a/playwright/e2e/user-onboarding/user-onboarding-new.spec.ts b/playwright/e2e/user-onboarding/user-onboarding-new.spec.ts
index f561eb9615..b89fa3ac70 100644
--- a/playwright/e2e/user-onboarding/user-onboarding-new.spec.ts
+++ b/playwright/e2e/user-onboarding/user-onboarding-new.spec.ts
@@ -26,7 +26,7 @@ test.describe("User Onboarding (new user)", () => {
         await expect(page.locator(".mx_UserOnboardingList")).toBeVisible();
     });
 
-    test("page is shown and preference exists", async ({ page, app }) => {
+    test("page is shown and preference exists", { tag: "@screenshot" }, async ({ page, app }) => {
         await expect(page.locator(".mx_UserOnboardingPage")).toMatchScreenshot(
             "User-Onboarding-new-user-page-is-shown-and-preference-exists-1.png",
         );
@@ -34,7 +34,7 @@ test.describe("User Onboarding (new user)", () => {
         await expect(page.getByText("Show shortcut to welcome checklist above the room list")).toBeVisible();
     });
 
-    test("app download dialog", async ({ page }) => {
+    test("app download dialog", { tag: "@screenshot" }, async ({ page }) => {
         await page.getByRole("button", { name: "Download apps" }).click();
         await expect(
             page.getByRole("dialog").getByRole("heading", { level: 1, name: "Download Element" }),
diff --git a/playwright/e2e/user-view/user-view.spec.ts b/playwright/e2e/user-view/user-view.spec.ts
index 218c63fe74..ff8e9684e9 100644
--- a/playwright/e2e/user-view/user-view.spec.ts
+++ b/playwright/e2e/user-view/user-view.spec.ts
@@ -14,7 +14,7 @@ test.describe("UserView", () => {
         botCreateOpts: { displayName: "Usman" },
     });
 
-    test("should render the user view as expected", async ({ page, homeserver, user, bot }) => {
+    test("should render the user view as expected", { tag: "@screenshot" }, async ({ page, homeserver, user, bot }) => {
         await page.goto(`/#/user/${bot.credentials.userId}`);
 
         const rightPanel = page.locator("#mx_RightPanel");
diff --git a/playwright/e2e/widgets/layout.spec.ts b/playwright/e2e/widgets/layout.spec.ts
index 41cfece6e8..c80ea44078 100644
--- a/playwright/e2e/widgets/layout.spec.ts
+++ b/playwright/e2e/widgets/layout.spec.ts
@@ -70,7 +70,7 @@ test.describe("Widget Layout", () => {
         await app.viewRoomByName(ROOM_NAME);
     });
 
-    test("should be set properly", async ({ page }) => {
+    test("should be set properly", { tag: "@screenshot" }, async ({ page }) => {
         await expect(page.locator(".mx_AppsDrawer")).toMatchScreenshot("apps-drawer.png");
     });
 
diff --git a/playwright/element-web-test.ts b/playwright/element-web-test.ts
index 8d5229a510..76e57e33f7 100644
--- a/playwright/element-web-test.ts
+++ b/playwright/element-web-test.ts
@@ -314,6 +314,10 @@ export const expect = baseExpect.extend({
         const testInfo = test.info();
         if (!testInfo) throw new Error(`toMatchScreenshot() must be called during the test`);
 
+        if (!testInfo.tags.includes("@screenshot")) {
+            throw new Error("toMatchScreenshot() must be used in a test tagged with @screenshot");
+        }
+
         const page = "page" in receiver ? receiver.page() : receiver;
 
         let css = `