mirror of
https://github.com/vector-im/element-web.git
synced 2025-11-08 20:21:42 +01:00
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 https://github.com/element-hq/element-modules/pull/80. * Playwright: use `routeConfigJson` from common lib https://github.com/element-hq/element-modules/pull/81 added a utility function for building and routing `config.json`; we should use it. * Playwright test for history sharing on invite Fixes https://github.com/element-hq/element-meta/issues/2920 * Avoid use of CSS in playwright locators
This commit is contained in:
parent
0fcc4d15c8
commit
4a0e8d661f
@ -186,7 +186,7 @@
|
|||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.5",
|
||||||
"@casualbot/jest-sonar-reporter": "2.2.7",
|
"@casualbot/jest-sonar-reporter": "2.2.7",
|
||||||
"@element-hq/element-call-embedded": "0.16.0",
|
"@element-hq/element-call-embedded": "0.16.0",
|
||||||
"@element-hq/element-web-playwright-common": "^1.4.6",
|
"@element-hq/element-web-playwright-common": "^2.0.0",
|
||||||
"@peculiar/webcrypto": "^1.4.3",
|
"@peculiar/webcrypto": "^1.4.3",
|
||||||
"@playwright/test": "^1.50.1",
|
"@playwright/test": "^1.50.1",
|
||||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||||
|
|||||||
59
playwright/e2e/crypto/history-sharing.spec.ts
Normal file
59
playwright/e2e/crypto/history-sharing.spec.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createNewInstance } from "@element-hq/element-web-playwright-common";
|
||||||
|
|
||||||
|
import { expect, test } from "../../element-web-test";
|
||||||
|
import { ElementAppPage } from "../../pages/ElementAppPage";
|
||||||
|
import { createRoom, sendMessageInCurrentRoom } from "./utils";
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
displayName: "Alice",
|
||||||
|
labsFlags: ["feature_share_history_on_invite"],
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Tests for MSC4268: encrypted history sharing */
|
||||||
|
test.describe("History sharing", function () {
|
||||||
|
test(
|
||||||
|
"We should share history when sending invites",
|
||||||
|
{ tag: "@screenshot" },
|
||||||
|
async (
|
||||||
|
{ labsFlags, browser, page: alicePage, user: aliceCredentials, app: aliceElementApp, homeserver },
|
||||||
|
testInfo,
|
||||||
|
) => {
|
||||||
|
// In this test, Alice creates an encrypted room and sends an event;
|
||||||
|
// we then invite Bob, and ensure Bob can see the content.
|
||||||
|
|
||||||
|
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
|
||||||
|
|
||||||
|
// Register a second user, and open it in a second instance of the app
|
||||||
|
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "Bob");
|
||||||
|
const bobPage = await createNewInstance(browser, bobCredentials, {}, labsFlags);
|
||||||
|
const bobElementApp = new ElementAppPage(bobPage);
|
||||||
|
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
|
||||||
|
|
||||||
|
// Create the room and send a message
|
||||||
|
await createRoom(alicePage, "TestRoom", true);
|
||||||
|
await sendMessageInCurrentRoom(alicePage, "A message from Alice");
|
||||||
|
|
||||||
|
// Send the invite to Bob
|
||||||
|
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
|
||||||
|
|
||||||
|
// Bob accepts the invite
|
||||||
|
await bobPage.getByRole("option", { name: "TestRoom" }).click();
|
||||||
|
await bobPage.getByRole("button", { name: "Accept" }).click();
|
||||||
|
|
||||||
|
// Bob should now be able to decrypt the event
|
||||||
|
await expect(bobPage.getByText("A message from Alice")).toBeVisible();
|
||||||
|
|
||||||
|
const mask = [bobPage.locator(".mx_MessageTimestamp")];
|
||||||
|
await expect(bobPage.locator(".mx_RoomView_body")).toMatchScreenshot("shared-history-invite-accepted.png", {
|
||||||
|
mask,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -27,8 +27,7 @@ test.describe("Create Knock Room", () => {
|
|||||||
|
|
||||||
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
||||||
|
|
||||||
const urlHash = await page.evaluate(() => window.location.hash);
|
const roomId = await app.getCurrentRoomIdFromUrl();
|
||||||
const roomId = urlHash.replace("#/room/", "");
|
|
||||||
|
|
||||||
// Room should have a knock join rule
|
// Room should have a knock join rule
|
||||||
await waitForRoom(page, app.client, roomId, (room) => {
|
await waitForRoom(page, app.client, roomId, (room) => {
|
||||||
@ -44,8 +43,7 @@ test.describe("Create Knock Room", () => {
|
|||||||
|
|
||||||
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
||||||
|
|
||||||
const urlHash = await page.evaluate(() => window.location.hash);
|
const roomId = await app.getCurrentRoomIdFromUrl();
|
||||||
const roomId = urlHash.replace("#/room/", "");
|
|
||||||
|
|
||||||
await app.settings.openRoomSettings("Security & Privacy");
|
await app.settings.openRoomSettings("Security & Privacy");
|
||||||
|
|
||||||
@ -70,8 +68,7 @@ test.describe("Create Knock Room", () => {
|
|||||||
|
|
||||||
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
await expect(page.locator(".mx_RoomHeader").getByText("Cybersecurity")).toBeVisible();
|
||||||
|
|
||||||
const urlHash = await page.evaluate(() => window.location.hash);
|
const roomId = await app.getCurrentRoomIdFromUrl();
|
||||||
const roomId = urlHash.replace("#/room/", "");
|
|
||||||
|
|
||||||
// Room should have a knock join rule
|
// Room should have a knock join rule
|
||||||
await waitForRoom(page, app.client, roomId, (room) => {
|
await waitForRoom(page, app.client, roomId, (room) => {
|
||||||
|
|||||||
@ -51,6 +51,7 @@ export async function doTokenRegistration(
|
|||||||
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
await expect(page.getByRole("heading", { name: "Welcome Alice", exact: true })).toBeVisible();
|
||||||
|
|
||||||
return page.evaluate(() => ({
|
return page.evaluate(() => ({
|
||||||
|
homeserverBaseUrl: window.mxMatrixClientPeg.get().getHomeserverUrl(),
|
||||||
accessToken: window.mxMatrixClientPeg.get().getAccessToken(),
|
accessToken: window.mxMatrixClientPeg.get().getAccessToken(),
|
||||||
userId: window.mxMatrixClientPeg.get().getUserId(),
|
userId: window.mxMatrixClientPeg.get().getUserId(),
|
||||||
deviceId: window.mxMatrixClientPeg.get().getDeviceId(),
|
deviceId: window.mxMatrixClientPeg.get().getDeviceId(),
|
||||||
|
|||||||
@ -6,9 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type Config, CONFIG_JSON } from "@element-hq/element-web-playwright-common";
|
import { type Config } from "@element-hq/element-web-playwright-common";
|
||||||
import { type Browser, type Page } from "@playwright/test";
|
import { type Browser, type Page } from "@playwright/test";
|
||||||
import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/HomeserverContainer";
|
import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/HomeserverContainer";
|
||||||
|
import { routeConfigJson } from "@element-hq/element-web-playwright-common";
|
||||||
|
|
||||||
import { test, expect } from "../../element-web-test.ts";
|
import { test, expect } from "../../element-web-test.ts";
|
||||||
import { logInAccountMas, registerAccountMas } from ".";
|
import { logInAccountMas, registerAccountMas } from ".";
|
||||||
@ -242,17 +243,6 @@ async function verifyUsingOtherDevice(deviceToVerifyPage: Page, alreadyVerifiedD
|
|||||||
*/
|
*/
|
||||||
async function newContext(browser: Browser, config: Partial<Partial<Config>>, homeserver: StartedHomeserverContainer) {
|
async function newContext(browser: Browser, config: Partial<Partial<Config>>, homeserver: StartedHomeserverContainer) {
|
||||||
const otherContext = await browser.newContext();
|
const otherContext = await browser.newContext();
|
||||||
await otherContext.route(`http://localhost:8080/config.json*`, async (route) => {
|
await routeConfigJson(otherContext, homeserver.baseUrl, config);
|
||||||
const json = {
|
|
||||||
...CONFIG_JSON,
|
|
||||||
...config,
|
|
||||||
default_server_config: {
|
|
||||||
"m.homeserver": {
|
|
||||||
base_url: homeserver.baseUrl,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await route.fulfill({ json });
|
|
||||||
});
|
|
||||||
return otherContext;
|
return otherContext;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,20 @@ export class ElementAppPage {
|
|||||||
return await this.page.evaluate(() => navigator.clipboard.readText());
|
return await this.page.evaluate(() => navigator.clipboard.readText());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the room ID from the current URL.
|
||||||
|
*
|
||||||
|
* @returns The room ID.
|
||||||
|
* @throws if the current URL does not contain a room ID.
|
||||||
|
*/
|
||||||
|
public async getCurrentRoomIdFromUrl(): Promise<string> {
|
||||||
|
const urlHash = await this.page.evaluate(() => window.location.hash);
|
||||||
|
if (!urlHash.startsWith("#/room/")) {
|
||||||
|
throw new Error("URL hash suggests we are not in a room");
|
||||||
|
}
|
||||||
|
return urlHash.replace("#/room/", "");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the given room by name. The room must be visible in the
|
* Opens the given room by name. The room must be visible in the
|
||||||
* room list and the room may contain unread messages.
|
* room list and the room may contain unread messages.
|
||||||
@ -197,6 +211,21 @@ export class ElementAppPage {
|
|||||||
return memberlist;
|
return memberlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the room info panel, and use it to send an invite to the given user.
|
||||||
|
*
|
||||||
|
* @param userId - The user to invite to the room.
|
||||||
|
*/
|
||||||
|
public async inviteUserToCurrentRoom(userId: string): Promise<void> {
|
||||||
|
await this.toggleRoomInfoPanel(); // TODO skip this if the room info panel is already open
|
||||||
|
await this.page.getByLabel("Right panel").getByRole("menuitem", { name: "Invite" }).click();
|
||||||
|
|
||||||
|
const input = this.page.getByRole("dialog").getByTestId("invite-dialog-input");
|
||||||
|
await input.fill(userId);
|
||||||
|
await input.press("Enter");
|
||||||
|
await this.page.getByRole("dialog").getByRole("button", { name: "Invite" }).click();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a locator for the tooltip associated with an element
|
* Get a locator for the tooltip associated with an element
|
||||||
* @param e The element with the tooltip
|
* @param e The element with the tooltip
|
||||||
|
|||||||
@ -6,7 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type ClientServerApi } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
|
import { type ClientServerApi, type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
|
||||||
|
export { type Credentials } from "@element-hq/element-web-playwright-common/lib/utils/api.js";
|
||||||
|
|
||||||
export interface HomeserverInstance {
|
export interface HomeserverInstance {
|
||||||
readonly baseUrl: string;
|
readonly baseUrl: string;
|
||||||
@ -37,14 +38,4 @@ export interface HomeserverInstance {
|
|||||||
setThreepid(userId: string, medium: string, address: string): Promise<void>;
|
setThreepid(userId: string, medium: string, address: string): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Credentials {
|
|
||||||
accessToken: string;
|
|
||||||
userId: string;
|
|
||||||
deviceId: string;
|
|
||||||
homeServer: string;
|
|
||||||
password: string | null; // null for password-less users
|
|
||||||
displayName?: string;
|
|
||||||
username: string; // the localpart of the userId
|
|
||||||
}
|
|
||||||
|
|
||||||
export type HomeserverType = "synapse" | "dendrite" | "pinecone";
|
export type HomeserverType = "synapse" | "dendrite" | "pinecone";
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@ -1738,10 +1738,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.4.1.tgz#a46526d58985190f9989bf1686ea872687d3c6e1"
|
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.4.1.tgz#a46526d58985190f9989bf1686ea872687d3c6e1"
|
||||||
integrity sha512-A8yaQtX7QoKThzzZVU+VYOFhpiNyppEMuIQijK48RvhVp1nwmy0cTD6u/6Yn64saNwJjtna+Oy+Qzo/TfwwhxQ==
|
integrity sha512-A8yaQtX7QoKThzzZVU+VYOFhpiNyppEMuIQijK48RvhVp1nwmy0cTD6u/6Yn64saNwJjtna+Oy+Qzo/TfwwhxQ==
|
||||||
|
|
||||||
"@element-hq/element-web-playwright-common@^1.4.6":
|
"@element-hq/element-web-playwright-common@^2.0.0":
|
||||||
version "1.4.6"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-1.4.6.tgz#a94d5d4ea94627aec430dd904c43f509a2e6c4b2"
|
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-2.0.0.tgz#30cf741a33c69540b4bc434f5349d0fe900bc611"
|
||||||
integrity sha512-LJ4V6e6NrF2ikNCsxR93PFwDfcRUTY3b2reXwlFJeo44pj8vTYFxkuJwokibFx6+x1zkXWAIMh/0saTMRUXdSA==
|
integrity sha512-axrWlPzP/OljYq53cefo9hha0SGDDu4HeM+sgevgbZSFSms8LsdMsMCyRyUcVBiGYb1xdKmM3RUsWOw5//eE+A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@axe-core/playwright" "^4.10.1"
|
"@axe-core/playwright" "^4.10.1"
|
||||||
"@testcontainers/postgresql" "^11.0.0"
|
"@testcontainers/postgresql" "^11.0.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user