Merge branch 'develop' of https://github.com/vector-im/element-web into t3chguy/playwright-testcontainers
# Conflicts: # playwright/e2e/crypto/backups.spec.tspull/28860/head
commit
ac53bab10c
|
@ -253,7 +253,6 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||||
|
|
||||||
// Find and click "Reply" button
|
// Find and click "Reply" button
|
||||||
const clickButtonReply = async () => {
|
const clickButtonReply = async () => {
|
||||||
await tile.scrollIntoViewIfNeeded();
|
|
||||||
await tile.hover();
|
await tile.hover();
|
||||||
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
await tile.getByRole("button", { name: "Reply", exact: true }).click();
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,9 +79,8 @@ test.describe("Composer", () => {
|
||||||
// Enter some more text, then send the message
|
// Enter some more text, then send the message
|
||||||
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
await page.getByRole("textbox").pressSequentially("this is the spoiler text ");
|
||||||
await page.getByRole("button", { name: "Send message" }).click();
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
// Check that a spoiler item has appeared in the timeline and locator the spoiler command text
|
// Check that a spoiler item has appeared in the timeline and contains the spoiler text
|
||||||
await expect(page.locator("button.mx_EventTile_spoiler")).toBeVisible();
|
await expect(page.locator("button.mx_EventTile_spoiler")).toHaveText("this is the spoiler text");
|
||||||
await expect(page.getByText("this is the spoiler text")).toBeVisible();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { type Page } from "@playwright/test";
|
||||||
import { test, expect } from "../../element-web-test";
|
import { test, expect } from "../../element-web-test";
|
||||||
import { registerAccountMas } from "../oidc";
|
import { registerAccountMas } from "../oidc";
|
||||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||||
|
import { TestClientServerAPI } from "../csAPI";
|
||||||
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
|
import { masHomeserver } from "../../plugins/homeserver/synapse/masHomeserver.ts";
|
||||||
|
|
||||||
async function expectBackupVersionToBe(page: Page, version: string) {
|
async function expectBackupVersionToBe(page: Page, version: string) {
|
||||||
|
@ -21,6 +22,9 @@ async function expectBackupVersionToBe(page: Page, version: string) {
|
||||||
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
|
await expect(page.locator(".mx_SecureBackupPanel_statusList tr:nth-child(6) td")).toHaveText(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These tests register an account with MAS because then we go through the "normal" registration flow
|
||||||
|
// and crypto gets set up. Using the 'user' fixture create a a user an synthesizes an existing login,
|
||||||
|
// which is faster but leaves us without crypto set up.
|
||||||
test.describe("Encryption state after registration", () => {
|
test.describe("Encryption state after registration", () => {
|
||||||
test.use(masHomeserver);
|
test.use(masHomeserver);
|
||||||
test.skip(isDendrite, "does not yet support MAS");
|
test.skip(isDendrite, "does not yet support MAS");
|
||||||
|
@ -48,6 +52,60 @@ test.describe("Encryption state after registration", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("Key backup reset from elsewhere", () => {
|
||||||
|
test.use(masHomeserver);
|
||||||
|
test.skip(isDendrite, "does not yet support MAS");
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Key backup is disabled when reset from elsewhere",
|
||||||
|
async ({ page, mailhogClient, request, homeserver }) => {
|
||||||
|
const testUsername = "alice";
|
||||||
|
const testPassword = "Pa$sW0rD!";
|
||||||
|
|
||||||
|
// there's a delay before keys are uploaded so the error doesn't appear immediately: use a fake
|
||||||
|
// clock so we can skip the delay
|
||||||
|
await page.clock.install();
|
||||||
|
|
||||||
|
await page.goto("/#/login");
|
||||||
|
await page.getByRole("button", { name: "Continue" }).click();
|
||||||
|
await registerAccountMas(page, mailhogClient, testUsername, "alice@email.com", testPassword);
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "Add room" }).click();
|
||||||
|
await page.getByRole("menuitem", { name: "New room" }).click();
|
||||||
|
await page.getByRole("textbox", { name: "Name" }).fill("test room");
|
||||||
|
await page.getByRole("button", { name: "Create room" }).click();
|
||||||
|
|
||||||
|
// @ts-ignore - this runs in the browser scope where mxMatrixClientPeg is a thing. Here, it is not.
|
||||||
|
const accessToken = await page.evaluate(() => mxMatrixClientPeg.get().getAccessToken());
|
||||||
|
|
||||||
|
const csAPI = new TestClientServerAPI(request, homeserver, accessToken);
|
||||||
|
|
||||||
|
const backupInfo = await csAPI.getCurrentBackupInfo();
|
||||||
|
|
||||||
|
await csAPI.deleteBackupVersion(backupInfo.version);
|
||||||
|
|
||||||
|
await page.getByRole("textbox", { name: "Send an encrypted message…" }).fill("/discardsession");
|
||||||
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole("textbox", { name: "Send an encrypted message…" })
|
||||||
|
.fill("Message with broken key backup");
|
||||||
|
await page.getByRole("button", { name: "Send message" }).click();
|
||||||
|
|
||||||
|
// Should be the message we sent plus the room creation event
|
||||||
|
await expect(page.locator(".mx_EventTile")).toHaveCount(2);
|
||||||
|
await expect(
|
||||||
|
page.locator(".mx_RoomView_MessageList > .mx_EventTile_last .mx_EventTile_receiptSent"),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Wait for it to try uploading the key
|
||||||
|
await page.clock.fastForward(20000);
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { level: 1, name: "New Recovery Method" })).toBeVisible();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test.describe("Backups", () => {
|
test.describe("Backups", () => {
|
||||||
test.use({
|
test.use({
|
||||||
displayName: "Hanako",
|
displayName: "Hanako",
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { APIRequestContext } from "playwright-core";
|
||||||
|
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||||
|
|
||||||
|
import { HomeserverInstance } from "../plugins/homeserver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small subset of the Client-Server API used to manipulate the state of the
|
||||||
|
* account on the homeserver independently of the client under test.
|
||||||
|
*/
|
||||||
|
export class TestClientServerAPI {
|
||||||
|
public constructor(
|
||||||
|
private request: APIRequestContext,
|
||||||
|
private homeserver: HomeserverInstance,
|
||||||
|
private accessToken: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async getCurrentBackupInfo(): Promise<KeyBackupInfo | null> {
|
||||||
|
const res = await this.request.get(`${this.homeserver.config.baseUrl}/_matrix/client/v3/room_keys/version`, {
|
||||||
|
headers: { Authorization: `Bearer ${this.accessToken}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
return await res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the API directly to delete the given backup version
|
||||||
|
* @param version The version to delete
|
||||||
|
*/
|
||||||
|
public async deleteBackupVersion(version: string): Promise<void> {
|
||||||
|
const res = await this.request.delete(
|
||||||
|
`${this.homeserver.config.baseUrl}/_matrix/client/v3/room_keys/version/${version}`,
|
||||||
|
{
|
||||||
|
headers: { Authorization: `Bearer ${this.accessToken}` },
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Failed to delete backup version: ${res.status}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,7 +36,9 @@ test.describe("Soft logout", () => {
|
||||||
|
|
||||||
// back to the welcome page
|
// back to the welcome page
|
||||||
await expect(page).toHaveURL(/\/#\/home/);
|
await expect(page).toHaveURL(/\/#\/home/);
|
||||||
await expect(page.getByRole("heading", { name: `Welcome ${user.userId}`, exact: true })).toBeVisible();
|
await expect(
|
||||||
|
page.getByRole("heading", { name: "Now, let's help you get started", exact: true }),
|
||||||
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({
|
test("still shows the soft-logout page when the page is reloaded after a soft-logout", async ({
|
||||||
|
|
|
@ -11,6 +11,8 @@ Please see LICENSE files in the repository root for full details.
|
||||||
import { customEvent, many, test } from ".";
|
import { customEvent, many, test } from ".";
|
||||||
|
|
||||||
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
test.describe("Read receipts", { tag: "@mergequeue" }, () => {
|
||||||
|
test.slow();
|
||||||
|
|
||||||
test.describe("Ignored events", () => {
|
test.describe("Ignored events", () => {
|
||||||
test("If all events after receipt are unimportant, the room is read", async ({
|
test("If all events after receipt are unimportant, the room is read", async ({
|
||||||
roomAlpha: room1,
|
roomAlpha: room1,
|
||||||
|
|
|
@ -37,7 +37,9 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||||
// Change the role of Alice to Moderator (50)
|
// Change the role of Alice to Moderator (50)
|
||||||
await combobox.selectOption("Moderator");
|
await combobox.selectOption("Moderator");
|
||||||
await expect(combobox).toHaveValue("50");
|
await expect(combobox).toHaveValue("50");
|
||||||
|
const respPromise = page.waitForRequest("**/state/**");
|
||||||
await applyButton.click();
|
await applyButton.click();
|
||||||
|
await respPromise;
|
||||||
|
|
||||||
// Reload and check Alice is still Moderator (50)
|
// Reload and check Alice is still Moderator (50)
|
||||||
await page.reload();
|
await page.reload();
|
||||||
|
|
|
@ -125,7 +125,7 @@ test.describe("Spotlight", () => {
|
||||||
await expect(resultLocator).toHaveCount(1);
|
await expect(resultLocator).toHaveCount(1);
|
||||||
await expect(resultLocator.first()).toContainText(room1Name);
|
await expect(resultLocator.first()).toContainText(room1Name);
|
||||||
await resultLocator.first().click();
|
await resultLocator.first().click();
|
||||||
expect(page.url()).toContain(room1Id);
|
await expect(page).toHaveURL(new RegExp(`#/room/${room1Id}`));
|
||||||
await expect(roomHeaderName(page)).toContainText(room1Name);
|
await expect(roomHeaderName(page)).toContainText(room1Name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ test.describe("Spotlight", () => {
|
||||||
await expect(resultLocator.first()).toContainText(room1Name);
|
await expect(resultLocator.first()).toContainText(room1Name);
|
||||||
await expect(resultLocator.first()).toContainText("View");
|
await expect(resultLocator.first()).toContainText("View");
|
||||||
await resultLocator.first().click();
|
await resultLocator.first().click();
|
||||||
expect(page.url()).toContain(room1Id);
|
await expect(page).toHaveURL(new RegExp(`#/room/${room1Id}`));
|
||||||
await expect(roomHeaderName(page)).toContainText(room1Name);
|
await expect(roomHeaderName(page)).toContainText(room1Name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ test.describe("Spotlight", () => {
|
||||||
await expect(resultLocator.first()).toContainText(room2Name);
|
await expect(resultLocator.first()).toContainText(room2Name);
|
||||||
await expect(resultLocator.first()).toContainText("Join");
|
await expect(resultLocator.first()).toContainText("Join");
|
||||||
await resultLocator.first().click();
|
await resultLocator.first().click();
|
||||||
expect(page.url()).toContain(room2Id);
|
await expect(page).toHaveURL(new RegExp(`#/room/${room2Id}`));
|
||||||
await expect(page.locator(".mx_RoomView_MessageList")).toHaveCount(1);
|
await expect(page.locator(".mx_RoomView_MessageList")).toHaveCount(1);
|
||||||
await expect(roomHeaderName(page)).toContainText(room2Name);
|
await expect(roomHeaderName(page)).toContainText(room2Name);
|
||||||
});
|
});
|
||||||
|
@ -168,7 +168,7 @@ test.describe("Spotlight", () => {
|
||||||
await expect(resultLocator.first()).toContainText(room3Name);
|
await expect(resultLocator.first()).toContainText(room3Name);
|
||||||
await expect(resultLocator.first()).toContainText("View");
|
await expect(resultLocator.first()).toContainText("View");
|
||||||
await resultLocator.first().click();
|
await resultLocator.first().click();
|
||||||
expect(page.url()).toContain(room3Id);
|
await expect(page).toHaveURL(new RegExp(`#/room/${room3Id}`));
|
||||||
await page.getByRole("button", { name: "Join the discussion" }).click();
|
await page.getByRole("button", { name: "Join the discussion" }).click();
|
||||||
await expect(roomHeaderName(page)).toHaveText(room3Name);
|
await expect(roomHeaderName(page)).toHaveText(room3Name);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue