Skip to content

Commit d0d0b82

Browse files
authored
Tag screenshot tests to speed up test:playwright:screenshot (element-hq#28623)
* Tag screenshot tests to speed up test:playwright:screenshot Signed-off-by: Michael Telatynski <[email protected]> * Add more tags Signed-off-by: Michael Telatynski <[email protected]> --------- Signed-off-by: Michael Telatynski <[email protected]>
1 parent d0e19d3 commit d0d0b82

38 files changed

+1508
-1368
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"test:playwright:open": "yarn test:playwright --ui",
6565
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
6666
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
67-
"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",
67+
"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",
6868
"coverage": "yarn test --coverage",
6969
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
7070
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",

playwright/e2e/app-loading/feature-detection.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
88

99
import { test, expect } from "../../element-web-test";
1010

11-
test(`shows error page if browser lacks Intl support`, async ({ page }) => {
11+
test(`shows error page if browser lacks Intl support`, { tag: "@screenshot" }, async ({ page }) => {
1212
await page.addInitScript({ content: `delete window.Intl;` });
1313
await page.goto("/");
1414

@@ -21,7 +21,7 @@ test(`shows error page if browser lacks Intl support`, async ({ page }) => {
2121
await expect(page).toMatchScreenshot("unsupported-browser.png");
2222
});
2323

24-
test(`shows error page if browser lacks WebAssembly support`, async ({ page }) => {
24+
test(`shows error page if browser lacks WebAssembly support`, { tag: "@screenshot" }, async ({ page }) => {
2525
await page.addInitScript({ content: `delete window.WebAssembly;` });
2626
await page.goto("/");
2727

playwright/e2e/audio-player/audio-player.spec.ts

+82-70
Original file line numberDiff line numberDiff line change
@@ -134,18 +134,22 @@ test.describe("Audio player", () => {
134134
).toBeVisible();
135135
});
136136

137-
test("should be correctly rendered - light theme", async ({ page, app }) => {
137+
test("should be correctly rendered - light theme", { tag: "@screenshot" }, async ({ page, app }) => {
138138
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
139139
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme)");
140140
});
141141

142-
test("should be correctly rendered - light theme with monospace font", async ({ page, app }) => {
143-
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
142+
test(
143+
"should be correctly rendered - light theme with monospace font",
144+
{ tag: "@screenshot" },
145+
async ({ page, app }) => {
146+
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");
144147

145-
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
146-
});
148+
await takeSnapshots(page, app, "Selected EventTile of audio player (light theme, monospace font)", true); // Enable monospace
149+
},
150+
);
147151

148-
test("should be correctly rendered - high contrast theme", async ({ page, app }) => {
152+
test("should be correctly rendered - high contrast theme", { tag: "@screenshot" }, async ({ page, app }) => {
149153
// Disable system theme in case ThemeWatcher enables the theme automatically,
150154
// so that the high contrast theme can be enabled
151155
await app.settings.setValue("use_system_theme", null, SettingLevel.DEVICE, false);
@@ -161,7 +165,7 @@ test.describe("Audio player", () => {
161165
await takeSnapshots(page, app, "Selected EventTile of audio player (high contrast)");
162166
});
163167

164-
test("should be correctly rendered - dark theme", async ({ page, app }) => {
168+
test("should be correctly rendered - dark theme", { tag: "@screenshot" }, async ({ page, app }) => {
165169
// Enable dark theme
166170
await app.settings.setValue("theme", null, SettingLevel.ACCOUNT, "dark");
167171

@@ -207,93 +211,101 @@ test.describe("Audio player", () => {
207211
expect(download.suggestedFilename()).toBe("1sec.ogg");
208212
});
209213

210-
test("should support replying to audio file with another audio file", async ({ page, app }) => {
211-
await uploadFile(page, "playwright/sample-files/1sec.ogg");
214+
test(
215+
"should support replying to audio file with another audio file",
216+
{ tag: "@screenshot" },
217+
async ({ page, app }) => {
218+
await uploadFile(page, "playwright/sample-files/1sec.ogg");
212219

213-
// Assert the audio player is rendered
214-
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
220+
// Assert the audio player is rendered
221+
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
215222

216-
// Find and click "Reply" button on MessageActionBar
217-
const tile = page.locator(".mx_EventTile_last");
218-
await tile.hover();
219-
await tile.getByRole("button", { name: "Reply", exact: true }).click();
223+
// Find and click "Reply" button on MessageActionBar
224+
const tile = page.locator(".mx_EventTile_last");
225+
await tile.hover();
226+
await tile.getByRole("button", { name: "Reply", exact: true }).click();
220227

221-
// Reply to the player with another audio file
222-
await uploadFile(page, "playwright/sample-files/1sec.ogg");
228+
// Reply to the player with another audio file
229+
await uploadFile(page, "playwright/sample-files/1sec.ogg");
223230

224-
// Assert that the audio player is rendered
225-
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
231+
// Assert that the audio player is rendered
232+
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
226233

227-
// Assert that replied audio file is rendered as file button inside ReplyChain
228-
const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
229-
// Assert that the file button has file name
230-
await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
234+
// Assert that replied audio file is rendered as file button inside ReplyChain
235+
const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
236+
// Assert that the file button has file name
237+
await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
231238

232-
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
233-
});
239+
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
240+
},
241+
);
234242

235-
test("should support creating a reply chain with multiple audio files", async ({ page, app, user }) => {
236-
// Note: "mx_ReplyChain" element is used not only for replies which
237-
// create a reply chain, but also for a single reply without a replied
238-
// message. This test checks whether a reply chain which consists of
239-
// multiple audio file replies is rendered properly.
243+
test(
244+
"should support creating a reply chain with multiple audio files",
245+
{ tag: "@screenshot" },
246+
async ({ page, app, user }) => {
247+
// Note: "mx_ReplyChain" element is used not only for replies which
248+
// create a reply chain, but also for a single reply without a replied
249+
// message. This test checks whether a reply chain which consists of
250+
// multiple audio file replies is rendered properly.
240251

241-
const tile = page.locator(".mx_EventTile_last");
252+
const tile = page.locator(".mx_EventTile_last");
242253

243-
// Find and click "Reply" button
244-
const clickButtonReply = async () => {
245-
await tile.scrollIntoViewIfNeeded();
246-
await tile.hover();
247-
await tile.getByRole("button", { name: "Reply", exact: true }).click();
248-
};
254+
// Find and click "Reply" button
255+
const clickButtonReply = async () => {
256+
await tile.scrollIntoViewIfNeeded();
257+
await tile.hover();
258+
await tile.getByRole("button", { name: "Reply", exact: true }).click();
259+
};
249260

250-
await uploadFile(page, "playwright/sample-files/upload-first.ogg");
261+
await uploadFile(page, "playwright/sample-files/upload-first.ogg");
251262

252-
// Assert that the audio player is rendered
253-
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
263+
// Assert that the audio player is rendered
264+
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
254265

255-
await clickButtonReply();
266+
await clickButtonReply();
256267

257-
// Reply to the player with another audio file
258-
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
268+
// Reply to the player with another audio file
269+
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
259270

260-
// Assert that the audio player is rendered
261-
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
271+
// Assert that the audio player is rendered
272+
await expect(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();
262273

263-
await clickButtonReply();
274+
await clickButtonReply();
264275

265-
// Reply to the player with yet another audio file to create a reply chain
266-
await uploadFile(page, "playwright/sample-files/upload-third.ogg");
276+
// Reply to the player with yet another audio file to create a reply chain
277+
await uploadFile(page, "playwright/sample-files/upload-third.ogg");
267278

268-
// Assert that the audio player is rendered
269-
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
279+
// Assert that the audio player is rendered
280+
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
270281

271-
// Assert that there are two "mx_ReplyChain" elements
272-
await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
282+
// Assert that there are two "mx_ReplyChain" elements
283+
await expect(tile.locator(".mx_ReplyChain")).toHaveCount(2);
273284

274-
// Assert that one line contains the user name
275-
await expect(tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName)).toBeVisible();
285+
// Assert that one line contains the user name
286+
await expect(tile.locator(".mx_ReplyChain .mx_ReplyTile_sender").getByText(user.displayName)).toBeVisible();
276287

277-
// Assert that the other line contains the file button
278-
await expect(tile.locator(".mx_ReplyChain .mx_MFileBody")).toBeVisible();
288+
// Assert that the other line contains the file button
289+
await expect(tile.locator(".mx_ReplyChain .mx_MFileBody")).toBeVisible();
279290

280-
// Click "In reply to"
281-
await tile.locator(".mx_ReplyChain .mx_ReplyChain_show", { hasText: "In reply to" }).click();
291+
// Click "In reply to"
292+
await tile.locator(".mx_ReplyChain .mx_ReplyChain_show", { hasText: "In reply to" }).click();
282293

283-
const replyChain = tile.locator(".mx_ReplyChain:first-of-type");
284-
// Assert that "In reply to" has disappeared
285-
await expect(replyChain.getByText("In reply to")).not.toBeVisible();
294+
const replyChain = tile.locator(".mx_ReplyChain:first-of-type");
295+
// Assert that "In reply to" has disappeared
296+
await expect(replyChain.getByText("In reply to")).not.toBeVisible();
286297

287-
// Assert that the file button contains the name of the file sent at first
288-
await expect(
289-
replyChain
290-
.locator(".mx_MFileBody_info[role='button']")
291-
.locator(".mx_MFileBody_info_filename", { hasText: "upload-first.ogg" }),
292-
).toBeVisible();
298+
// Assert that the file button contains the name of the file sent at first
299+
await expect(
300+
replyChain
301+
.locator(".mx_MFileBody_info[role='button']")
302+
.locator(".mx_MFileBody_info_filename", { hasText: "upload-first.ogg" }),
303+
).toBeVisible();
293304

294-
// Take snapshots
295-
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
296-
});
305+
// Take snapshots
306+
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply chain");
307+
},
308+
);
297309

298310
test("should be rendered, play, and support replying on a thread", async ({ page, app }) => {
299311
await uploadFile(page, "playwright/sample-files/1sec-long-name-audio-file.ogg");

playwright/e2e/chat-export/html-export.spec.ts

+42-38
Original file line numberDiff line numberDiff line change
@@ -89,43 +89,47 @@ test.describe("HTML Export", () => {
8989
},
9090
});
9191

92-
test("should export html successfully and match screenshot", async ({ page, app, room }) => {
93-
// Set a fixed time rather than masking off the line with the time in it: we don't need to worry
94-
// about the width changing and we can actually test this line looks correct.
95-
page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
96-
97-
// Send a bunch of messages to populate the room
98-
for (let i = 1; i < 10; i++) {
99-
const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
100-
if (i == 1) {
101-
await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
92+
test(
93+
"should export html successfully and match screenshot",
94+
{ tag: "@screenshot" },
95+
async ({ page, app, room }) => {
96+
// Set a fixed time rather than masking off the line with the time in it: we don't need to worry
97+
// about the width changing and we can actually test this line looks correct.
98+
page.clock.setSystemTime(new Date("2024-01-01T00:00:00Z"));
99+
100+
// Send a bunch of messages to populate the room
101+
for (let i = 1; i < 10; i++) {
102+
const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
103+
if (i == 1) {
104+
await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
105+
}
102106
}
103-
}
104-
105-
// Wait for all the messages to be displayed
106-
await expect(
107-
page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText("Testing 9"),
108-
).toBeVisible();
109-
110-
await app.toggleRoomInfoPanel();
111-
await page.getByRole("menuitem", { name: "Export Chat" }).click();
112-
113-
const downloadPromise = page.waitForEvent("download");
114-
await page.getByRole("button", { name: "Export", exact: true }).click();
115-
const download = await downloadPromise;
116-
117-
const dirPath = path.join(os.tmpdir(), "html-export-test");
118-
const zipPath = `${dirPath}.zip`;
119-
await download.saveAs(zipPath);
120-
121-
const zip = await extractZipFileToPath(zipPath, dirPath);
122-
await page.goto(`file://${dirPath}/${Object.keys(zip.files)[0]}/messages.html`);
123-
await expect(page).toMatchScreenshot("html-export.png", {
124-
mask: [
125-
// We need to mask the whole thing because the width of the time part changes
126-
page.locator(".mx_TimelineSeparator"),
127-
page.locator(".mx_MessageTimestamp"),
128-
],
129-
});
130-
});
107+
108+
// Wait for all the messages to be displayed
109+
await expect(
110+
page.locator(".mx_EventTile_last .mx_MTextBody .mx_EventTile_body").getByText("Testing 9"),
111+
).toBeVisible();
112+
113+
await app.toggleRoomInfoPanel();
114+
await page.getByRole("menuitem", { name: "Export Chat" }).click();
115+
116+
const downloadPromise = page.waitForEvent("download");
117+
await page.getByRole("button", { name: "Export", exact: true }).click();
118+
const download = await downloadPromise;
119+
120+
const dirPath = path.join(os.tmpdir(), "html-export-test");
121+
const zipPath = `${dirPath}.zip`;
122+
await download.saveAs(zipPath);
123+
124+
const zip = await extractZipFileToPath(zipPath, dirPath);
125+
await page.goto(`file://${dirPath}/${Object.keys(zip.files)[0]}/messages.html`);
126+
await expect(page).toMatchScreenshot("html-export.png", {
127+
mask: [
128+
// We need to mask the whole thing because the width of the time part changes
129+
page.locator(".mx_TimelineSeparator"),
130+
page.locator(".mx_MessageTimestamp"),
131+
],
132+
});
133+
},
134+
);
131135
});

playwright/e2e/crypto/crypto.spec.ts

+23-24
Original file line numberDiff line numberDiff line change
@@ -204,30 +204,29 @@ test.describe("Cryptography", function () {
204204
await expect(page.locator(".mx_Dialog")).toHaveCount(1);
205205
});
206206

207-
test("creating a DM should work, being e2e-encrypted / user verification", async ({
208-
page,
209-
app,
210-
bot: bob,
211-
user: aliceCredentials,
212-
}) => {
213-
await app.client.bootstrapCrossSigning(aliceCredentials);
214-
await startDMWithBob(page, bob);
215-
// send first message
216-
await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
217-
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
218-
await checkDMRoom(page);
219-
const bobRoomId = await bobJoin(page, bob);
220-
await testMessages(page, bob, bobRoomId);
221-
await verify(app, bob);
222-
223-
// Assert that verified icon is rendered
224-
await page.getByTestId("base-card-back-button").click();
225-
await page.getByLabel("Room info").nth(1).click();
226-
await expect(page.locator('.mx_RoomSummaryCard_badges [data-kind="green"]')).toContainText("Encrypted");
227-
228-
// Take a snapshot of RoomSummaryCard with a verified E2EE icon
229-
await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
230-
});
207+
test(
208+
"creating a DM should work, being e2e-encrypted / user verification",
209+
{ tag: "@screenshot" },
210+
async ({ page, app, bot: bob, user: aliceCredentials }) => {
211+
await app.client.bootstrapCrossSigning(aliceCredentials);
212+
await startDMWithBob(page, bob);
213+
// send first message
214+
await page.getByRole("textbox", { name: "Send a message…" }).fill("Hey!");
215+
await page.getByRole("textbox", { name: "Send a message…" }).press("Enter");
216+
await checkDMRoom(page);
217+
const bobRoomId = await bobJoin(page, bob);
218+
await testMessages(page, bob, bobRoomId);
219+
await verify(app, bob);
220+
221+
// Assert that verified icon is rendered
222+
await page.getByTestId("base-card-back-button").click();
223+
await page.getByLabel("Room info").nth(1).click();
224+
await expect(page.locator('.mx_RoomSummaryCard_badges [data-kind="green"]')).toContainText("Encrypted");
225+
226+
// Take a snapshot of RoomSummaryCard with a verified E2EE icon
227+
await expect(page.locator(".mx_RightPanel")).toMatchScreenshot("RoomSummaryCard-with-verified-e2ee.png");
228+
},
229+
);
231230

232231
test("should allow verification when there is no existing DM", async ({
233232
page,

0 commit comments

Comments
 (0)