76b65b14de
* Show 'Verify this device' toast even if there are no encrypted rooms yet * Close verify toast in more tests
236 lines
9.9 KiB
TypeScript
236 lines
9.9 KiB
TypeScript
/*
|
|
* Copyright 2026 Element Creations 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 { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
|
import { type Page, type Browser, type TestInfo } from "@playwright/test";
|
|
|
|
import { test, expect } from "./index";
|
|
import { ElementAppPage } from "../../../pages/ElementAppPage";
|
|
import { createRoom, sendMessageInCurrentRoom, verifyApp } from "../../crypto/utils";
|
|
import { type CredentialsOptionalAccessToken } from "../../../pages/bot";
|
|
|
|
test.describe("Other people's devices section in Encryption tab", () => {
|
|
test.use({
|
|
displayName: "alice",
|
|
});
|
|
|
|
test("unverified devices should be able to decrypt while global blacklist is not toggled", async ({
|
|
page: alicePage,
|
|
app: aliceElementApp,
|
|
homeserver,
|
|
browser,
|
|
user: aliceCredentials,
|
|
}, testInfo) => {
|
|
await prepForEncryption(aliceElementApp, aliceCredentials);
|
|
|
|
// Create a second browser instance.
|
|
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
|
|
|
|
// Create the room and invite bob
|
|
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
|
|
|
|
// Alice sends a message, which Bob should be able to decrypt
|
|
await sendMessageInCurrentRoom(alicePage, "Decryptable");
|
|
await expect(bobPage.getByText("Decryptable")).toBeVisible();
|
|
});
|
|
|
|
test("unverified devices should not be able to decrypt while global blacklist is toggled", async ({
|
|
page: alicePage,
|
|
app: aliceElementApp,
|
|
homeserver,
|
|
browser,
|
|
user: aliceCredentials,
|
|
util,
|
|
}, testInfo) => {
|
|
await prepForEncryption(aliceElementApp, aliceCredentials);
|
|
|
|
// Enable blacklist toggle.
|
|
const dialog = await util.openEncryptionTab();
|
|
const blacklistToggle = dialog.getByRole("switch", {
|
|
name: "In encrypted rooms, only send messages to verified users",
|
|
});
|
|
await blacklistToggle.scrollIntoViewIfNeeded();
|
|
await expect(blacklistToggle).toBeVisible();
|
|
await blacklistToggle.click();
|
|
await aliceElementApp.settings.closeDialog();
|
|
|
|
// Create a second browser instance.
|
|
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
|
|
|
|
// Create the room and invite bob
|
|
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
|
|
|
|
// Alice sends a message, which Bob should not be able to decrypt
|
|
await sendMessageInCurrentRoom(alicePage, "Undecryptable");
|
|
await expect(
|
|
bobPage.getByText(
|
|
"The sender has blocked you from receiving this message because your device is unverified",
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("verified devices should be able to decrypt while global blacklist is toggled", async ({
|
|
page: alicePage,
|
|
app: aliceElementApp,
|
|
homeserver,
|
|
browser,
|
|
user: aliceCredentials,
|
|
util,
|
|
}, testInfo) => {
|
|
await prepForEncryption(aliceElementApp, aliceCredentials);
|
|
|
|
// Enable blacklist toggle.
|
|
const dialog = await util.openEncryptionTab();
|
|
const blacklistToggle = dialog.getByRole("switch", {
|
|
name: "In encrypted rooms, only send messages to verified users",
|
|
});
|
|
await blacklistToggle.scrollIntoViewIfNeeded();
|
|
await expect(blacklistToggle).toBeVisible();
|
|
await blacklistToggle.click();
|
|
await aliceElementApp.settings.closeDialog();
|
|
|
|
// Create a second browser instance.
|
|
const { bobCredentials, bobPage, bobElementApp } = await newBrowser(homeserver, testInfo, browser);
|
|
|
|
// Create the room and invite bob
|
|
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
|
|
await bobElementApp.closeNotificationToast();
|
|
|
|
// Perform verification.
|
|
await verifyApp("alice", aliceElementApp, "bob", bobElementApp);
|
|
|
|
// Alice sends a message, which Bob should be able to decrypt
|
|
await sendMessageInCurrentRoom(alicePage, "Decryptable");
|
|
await expect(bobPage.getByText("Decryptable")).toBeVisible();
|
|
});
|
|
|
|
test("setting per-room unverified blacklist toggle does not affect other rooms", async ({
|
|
page: alicePage,
|
|
app: aliceElementApp,
|
|
homeserver,
|
|
browser,
|
|
user: aliceCredentials,
|
|
}, testInfo) => {
|
|
await prepForEncryption(aliceElementApp, aliceCredentials);
|
|
|
|
// Create a second browser instance.
|
|
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
|
|
|
|
// Alice creates the room and invites Bob.
|
|
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
|
|
|
|
// Alice configures her client to blacklist unverified users in this room.
|
|
const dialog = await aliceElementApp.settings.openRoomSettings("Security & Privacy");
|
|
await dialog.getByRole("switch", { name: "Only send messages to verified users." }).click();
|
|
await aliceElementApp.settings.closeDialog();
|
|
|
|
// Alice sends a message which Bob should not be able to decrypt.
|
|
await sendMessageInCurrentRoom(alicePage, "Undecryptable");
|
|
await expect(
|
|
bobPage.getByText(
|
|
"The sender has blocked you from receiving this message because your device is unverified",
|
|
),
|
|
).toBeVisible();
|
|
|
|
// Alice creates a second room and invites Bob.
|
|
await createRoom(alicePage, "TestRoom2", true);
|
|
await aliceElementApp.toggleRoomInfoPanel(); // should not be necessary, called in body of below
|
|
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
|
|
|
|
// Bob accepts the invite.
|
|
await bobPage.getByRole("option", { name: "TestRoom2" }).click();
|
|
await bobPage.getByRole("button", { name: "Accept" }).click();
|
|
|
|
// Alice sends a message in the new room, which Bob should be able to decrypt.
|
|
await sendMessageInCurrentRoom(alicePage, "Decryptable");
|
|
await expect(bobPage.getByText("Decryptable")).toBeVisible();
|
|
});
|
|
|
|
test("setting per-room unverified blacklist toggle overrides global toggle", async ({
|
|
page: alicePage,
|
|
app: aliceElementApp,
|
|
homeserver,
|
|
browser,
|
|
user: aliceCredentials,
|
|
util,
|
|
}, testInfo) => {
|
|
await prepForEncryption(aliceElementApp, aliceCredentials);
|
|
|
|
// Enable blacklist toggle.
|
|
let dialog = await util.openEncryptionTab();
|
|
const blacklistToggle = dialog.getByRole("switch", {
|
|
name: "In encrypted rooms, only send messages to verified users",
|
|
});
|
|
await blacklistToggle.scrollIntoViewIfNeeded();
|
|
await expect(blacklistToggle).toBeVisible();
|
|
await blacklistToggle.click();
|
|
await aliceElementApp.settings.closeDialog();
|
|
|
|
// Create a second browser instance.
|
|
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
|
|
|
|
// Alice creates the room and invites Bob.
|
|
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
|
|
|
|
// Alice configures her client to allow sending to unverified users in this room.
|
|
dialog = await aliceElementApp.settings.openRoomSettings("Security & Privacy");
|
|
await dialog.getByRole("switch", { name: "Only send messages to verified users." }).click();
|
|
await aliceElementApp.settings.closeDialog();
|
|
|
|
// Alice sends a message which Bob should be able to decrypt.
|
|
await sendMessageInCurrentRoom(alicePage, "Decryptable");
|
|
await expect(bobPage.getByText("Decryptable")).toBeVisible();
|
|
|
|
// Alice creates a second room and invites Bob.
|
|
await createRoom(alicePage, "TestRoom2", true);
|
|
await aliceElementApp.toggleRoomInfoPanel(); // should not be necessary, called in body of below
|
|
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId);
|
|
|
|
// Bob accepts the invite.
|
|
await bobPage.getByRole("option", { name: "TestRoom2" }).click();
|
|
await bobPage.getByRole("button", { name: "Accept" }).click();
|
|
|
|
// Alice sends a message in the new room, which Bob should not be able to decrypt.
|
|
await sendMessageInCurrentRoom(alicePage, "Undecryptable");
|
|
await expect(
|
|
bobPage.getByText(
|
|
"The sender has blocked you from receiving this message because your device is unverified",
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
});
|
|
|
|
async function inviteBobToNewRoom(
|
|
alicePage: Page,
|
|
aliceElementApp: ElementAppPage,
|
|
bobCredentials: CredentialsOptionalAccessToken,
|
|
bobPage: Page,
|
|
) {
|
|
await createRoom(alicePage, "TestRoom", true);
|
|
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
|
|
await bobPage.getByRole("option", { name: "TestRoom" }).click();
|
|
await bobPage.getByRole("button", { name: "Accept" }).click();
|
|
}
|
|
|
|
async function newBrowser(
|
|
homeserver: StartedHomeserverContainer,
|
|
testInfo: TestInfo,
|
|
browser: Browser,
|
|
): Promise<{ bobCredentials: CredentialsOptionalAccessToken; bobPage: Page; bobElementApp: ElementAppPage }> {
|
|
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
|
|
const bobPage = await createNewInstance(browser, bobCredentials, {});
|
|
const bobElementApp = new ElementAppPage(bobPage);
|
|
await prepForEncryption(bobElementApp, bobCredentials);
|
|
return { bobCredentials, bobPage, bobElementApp };
|
|
}
|
|
|
|
async function prepForEncryption(app: ElementAppPage, credentials: CredentialsOptionalAccessToken): Promise<void> {
|
|
await app.client.bootstrapCrossSigning(credentials);
|
|
await app.closeKeyStorageToast();
|
|
}
|