Skip to content

Commit

Permalink
Tag screenshot tests to speed up test:playwright:screenshot (#28623)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
t3chguy authored Dec 4, 2024
1 parent d0e19d3 commit d0d0b82
Show file tree
Hide file tree
Showing 38 changed files with 1,508 additions and 1,368 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions playwright/e2e/app-loading/feature-detection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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("/");

Expand All @@ -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("/");

Expand Down
152 changes: 82 additions & 70 deletions playwright/e2e/audio-player/audio-player.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");

Expand Down Expand Up @@ -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();
// 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");
// 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 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();
// 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");
});
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.
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.

const tile = page.locator(".mx_EventTile_last");
const tile = page.locator(".mx_EventTile_last");

// Find and click "Reply" button
const clickButtonReply = async () => {
await tile.scrollIntoViewIfNeeded();
await tile.hover();
await tile.getByRole("button", { name: "Reply", exact: true }).click();
};
// Find and click "Reply" button
const clickButtonReply = async () => {
await tile.scrollIntoViewIfNeeded();
await tile.hover();
await tile.getByRole("button", { name: "Reply", exact: true }).click();
};

await uploadFile(page, "playwright/sample-files/upload-first.ogg");
await uploadFile(page, "playwright/sample-files/upload-first.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(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();

await clickButtonReply();
await clickButtonReply();

// Reply to the player with another audio file
await uploadFile(page, "playwright/sample-files/upload-second.ogg");
// Reply to the player with another audio file
await uploadFile(page, "playwright/sample-files/upload-second.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(page.locator(".mx_EventTile_last .mx_AudioPlayer_container")).toBeVisible();

await clickButtonReply();
await clickButtonReply();

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

// Assert that the audio player is rendered
await expect(tile.locator(".mx_AudioPlayer_container")).toBeVisible();
// 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 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 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();
// 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();
// 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();
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();
// 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");
});
// 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");
Expand Down
80 changes: 42 additions & 38 deletions playwright/e2e/chat-export/html-export.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

// 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, "🙃");
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, "🙃");
}
}
}

// 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();

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 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"),
],
});
});

// 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();

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 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"),
],
});
},
);
});
47 changes: 23 additions & 24 deletions playwright/e2e/crypto/crypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// 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");
});
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");

// 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,
Expand Down
Loading

0 comments on commit d0d0b82

Please sign in to comment.