Skip to content

Commit 4a0e8d6

Browse files
authored
Playwright test for history sharing on invite (#30948)
* Playwright: `getCurrentRoomIdFromUrl` Helper function to fish a room ID out of the URL * Playwright: use updated `Credentials` class from common lib Pick up the extended `Credentials` interface that was added in element-hq/element-modules#80. * Playwright: use `routeConfigJson` from common lib element-hq/element-modules#81 added a utility function for building and routing `config.json`; we should use it. * Playwright test for history sharing on invite Fixes element-hq/element-meta#2920 * Avoid use of CSS in playwright locators
1 parent 0fcc4d1 commit 4a0e8d6

File tree

9 files changed

+102
-35
lines changed

9 files changed

+102
-35
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
"@babel/runtime": "^7.12.5",
187187
"@casualbot/jest-sonar-reporter": "2.2.7",
188188
"@element-hq/element-call-embedded": "0.16.0",
189-
"@element-hq/element-web-playwright-common": "^1.4.6",
189+
"@element-hq/element-web-playwright-common": "^2.0.0",
190190
"@peculiar/webcrypto": "^1.4.3",
191191
"@playwright/test": "^1.50.1",
192192
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Copyright 2025 New Vector Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
import { createNewInstance } from "@element-hq/element-web-playwright-common";
9+
10+
import { expect, test } from "../../element-web-test";
11+
import { ElementAppPage } from "../../pages/ElementAppPage";
12+
import { createRoom, sendMessageInCurrentRoom } from "./utils";
13+
14+
test.use({
15+
displayName: "Alice",
16+
labsFlags: ["feature_share_history_on_invite"],
17+
});
18+
19+
/** Tests for MSC4268: encrypted history sharing */
20+
test.describe("History sharing", function () {
21+
test(
22+
"We should share history when sending invites",
23+
{ tag: "@screenshot" },
24+
async (
25+
{ labsFlags, browser, page: alicePage, user: aliceCredentials, app: aliceElementApp, homeserver },
26+
testInfo,
27+
) => {
28+
// In this test, Alice creates an encrypted room and sends an event;
29+
// we then invite Bob, and ensure Bob can see the content.
30+
31+
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
32+
33+
// Register a second user, and open it in a second instance of the app
34+
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "Bob");
35+
const bobPage = await createNewInstance(browser, bobCredentials, {}, labsFlags);
36+
const bobElementApp = new ElementAppPage(bobPage);
37+
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
38+
39+
// Create the room and send a message
40+
await createRoom(alicePage, "TestRoom", true);
41+
await sendMessageInCurrentRoom(alicePage, "A message from Alice");
42+
43+
// Send the invite to Bob
44+
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
45+
46+
// Bob accepts the invite
47+
await bobPage.getByRole("option", { name: "TestRoom" }).click();
48+
await bobPage.getByRole("button", { name: "Accept" }).click();
49+
50+
// Bob should now be able to decrypt the event
51+
await expect(bobPage.getByText("A message from Alice")).toBeVisible();
52+
53+
const mask = [bobPage.locator(".mx_MessageTimestamp")];
54+
await expect(bobPage.locator(".mx_RoomView_body")).toMatchScreenshot("shared-history-invite-accepted.png", {
55+
mask,
56+
});
57+
},
58+
);
59+
});

playwright/e2e/knock/create-knock-room.spec.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ test.describe("Create Knock Room", () => {
2727

2828
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
2929

30-
const urlHash = await page.evaluate(() => window.location.hash);
31-
const roomId = urlHash.replace("#/room/", "");
30+
const roomId = await app.getCurrentRoomIdFromUrl();
3231

3332
// Room should have a knock join rule
3433
await waitForRoom(page, app.client, roomId, (room) => {
@@ -44,8 +43,7 @@ test.describe("Create Knock Room", () => {
4443

4544
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
4645

47-
const urlHash = await page.evaluate(() => window.location.hash);
48-
const roomId = urlHash.replace("#/room/", "");
46+
const roomId = await app.getCurrentRoomIdFromUrl();
4947

5048
await app.settings.openRoomSettings("Security & Privacy");
5149

@@ -70,8 +68,7 @@ test.describe("Create Knock Room", () => {
7068

7169
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
7270

73-
const urlHash = await page.evaluate(() => window.location.hash);
74-
const roomId = urlHash.replace("#/room/", "");
71+
const roomId = await app.getCurrentRoomIdFromUrl();
7572

7673
// Room should have a knock join rule
7774
await waitForRoom(page, app.client, roomId, (room) => {

playwright/e2e/login/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export async function doTokenRegistration(
5151
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
5252

5353
return page.evaluate(() => ({
54+
homeserverBaseUrl: window.mxMatrixClientPeg.get().getHomeserverUrl(),
5455
accessToken: window.mxMatrixClientPeg.get().getAccessToken(),
5556
userId: window.mxMatrixClientPeg.get().getUserId(),
5657
deviceId: window.mxMatrixClientPeg.get().getDeviceId(),

playwright/e2e/oidc/oidc-native.spec.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9-
import { type Config, CONFIG_JSON } from "@element-hq/element-web-playwright-common";
9+
import { type Config } from "@element-hq/element-web-playwright-common";
1010
import { type Browser, type Page } from "@playwright/test";
1111
import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/HomeserverContainer";
12+
import { routeConfigJson } from "@element-hq/element-web-playwright-common";
1213

1314
import { test, expect } from "../../element-web-test.ts";
1415
import { logInAccountMas, registerAccountMas } from ".";
@@ -242,17 +243,6 @@ async function verifyUsingOtherDevice(deviceToVerifyPage: Page, alreadyVerifiedD
242243
*/
243244
async function newContext(browser: Browser, config: Partial<Partial<Config>>, homeserver: StartedHomeserverContainer) {
244245
const otherContext = await browser.newContext();
245-
await otherContext.route(`http://localhost:8080/config.json*`, async (route) => {
246-
const json = {
247-
...CONFIG_JSON,
248-
...config,
249-
default_server_config: {
250-
"m.homeserver": {
251-
base_url: homeserver.baseUrl,
252-
},
253-
},
254-
};
255-
await route.fulfill({ json });
256-
});
246+
await routeConfigJson(otherContext, homeserver.baseUrl, config);
257247
return otherContext;
258248
}

playwright/pages/ElementAppPage.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ export class ElementAppPage {
6969
return await this.page.evaluate(() => navigator.clipboard.readText());
7070
}
7171

72+
/**
73+
* Get the room ID from the current URL.
74+
*
75+
* @returns The room ID.
76+
* @throws if the current URL does not contain a room ID.
77+
*/
78+
public async getCurrentRoomIdFromUrl(): Promise<string> {
79+
const urlHash = await this.page.evaluate(() => window.location.hash);
80+
if (!urlHash.startsWith("#/room/")) {
81+
throw new Error("URL hash suggests we are not in a room");
82+
}
83+
return urlHash.replace("#/room/", "");
84+
}
85+
7286
/**
7387
* Opens the given room by name. The room must be visible in the
7488
* room list and the room may contain unread messages.
@@ -197,6 +211,21 @@ export class ElementAppPage {
197211
return memberlist;
198212
}
199213

214+
/**
215+
* Open the room info panel, and use it to send an invite to the given user.
216+
*
217+
* @param userId - The user to invite to the room.
218+
*/
219+
public async inviteUserToCurrentRoom(userId: string): Promise<void> {
220+
await this.toggleRoomInfoPanel(); // TODO skip this if the room info panel is already open
221+
await this.page.getByLabel("Right panel").getByRole("menuitem", { name: "Invite" }).click();
222+
223+
const input = this.page.getByRole("dialog").getByTestId("invite-dialog-input");
224+
await input.fill(userId);
225+
await input.press("Enter");
226+
await this.page.getByRole("dialog").getByRole("button", { name: "Invite" }).click();
227+
}
228+
200229
/**
201230
* Get a locator for the tooltip associated with an element
202231
* @param e The element with the tooltip

playwright/plugins/homeserver/index.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9-
import { type ClientServerApi } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
9+
import { type ClientServerApi, type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
10+
export { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
1011

1112
export interface HomeserverInstance {
1213
readonly baseUrl: string;
@@ -37,14 +38,4 @@ export interface HomeserverInstance {
3738
setThreepid(userId: string, medium: string, address: string): Promise<void>;
3839
}
3940

40-
export interface Credentials {
41-
accessToken: string;
42-
userId: string;
43-
deviceId: string;
44-
homeServer: string;
45-
password: string | null; // null for password-less users
46-
displayName?: string;
47-
username: string; // the localpart of the userId
48-
}
49-
5041
export type HomeserverType = "synapse" | "dendrite" | "pinecone";
48.8 KB
Loading

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,10 +1738,10 @@
17381738
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.4.1.tgz#a46526d58985190f9989bf1686ea872687d3c6e1"
17391739
integrity sha512-A8yaQtX7QoKThzzZVU+VYOFhpiNyppEMuIQijK48RvhVp1nwmy0cTD6u/6Yn64saNwJjtna+Oy+Qzo/TfwwhxQ==
17401740

1741-
"@element-hq/element-web-playwright-common@^1.4.6":
1742-
version "1.4.6"
1743-
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-1.4.6.tgz#a94d5d4ea94627aec430dd904c43f509a2e6c4b2"
1744-
integrity sha512-LJ4V6e6NrF2ikNCsxR93PFwDfcRUTY3b2reXwlFJeo44pj8vTYFxkuJwokibFx6+x1zkXWAIMh/0saTMRUXdSA==
1741+
"@element-hq/element-web-playwright-common@^2.0.0":
1742+
version "2.0.0"
1743+
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-2.0.0.tgz#30cf741a33c69540b4bc434f5349d0fe900bc611"
1744+
integrity sha512-axrWlPzP/OljYq53cefo9hha0SGDDu4HeM+sgevgbZSFSms8LsdMsMCyRyUcVBiGYb1xdKmM3RUsWOw5//eE+A==
17451745
dependencies:
17461746
"@axe-core/playwright" "^4.10.1"
17471747
"@testcontainers/postgresql" "^11.0.0"

0 commit comments

Comments
 (0)