Show 'Verify this device' toast even if there are no encrypted rooms yet (#32891)

* Show 'Verify this device' toast even if there are no encrypted rooms yet

* Close verify toast in more tests
This commit is contained in:
Andy Balaam 2026-04-24 12:39:59 +01:00 committed by GitHub
parent 6c7c90c15d
commit 76b65b14de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 219 additions and 144 deletions

View File

@ -15,6 +15,8 @@ test.describe("Landmark navigation tests", () => {
});
test("without any rooms", async ({ page, homeserver, app, user }) => {
await app.closeVerifyToast();
// sometimes the space button doesn't appear right away
await expect(page.locator(".mx_SpaceButton_active")).toBeVisible();
@ -118,6 +120,7 @@ test.describe("Landmark navigation tests", () => {
},
);
await app.closeVerifyToast();
await app.viewRoomByName("Bob");
// confirm the room was loaded
await expect(page.getByText("Bob joined the room")).toBeVisible();

View File

@ -144,6 +144,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
};
test.beforeEach(async ({ page, app, user }) => {
await app.closeVerifyToast();
await app.client.createRoom({ name: "Test Room" });
await app.viewRoomByName("Test Room");

View File

@ -172,6 +172,7 @@ test.describe("Cryptography", function () {
"creating a DM should work, being e2e-encrypted / user verification",
{ tag: "@screenshot" },
async ({ page, app, bot: bob, user: aliceCredentials }) => {
await app.closeVerifyToast();
await app.client.bootstrapCrossSigning(aliceCredentials);
await startDMWithBob(page, bob);
// send first message

View File

@ -124,6 +124,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
await infoDialog.getByRole("button", { name: "Got it" }).click();
// There should be no toast (other than the notifications one)
await toasts.rejectToast("Verify this device");
await toasts.rejectToast("Notifications");
await toasts.assertNoToasts();

View File

@ -28,12 +28,16 @@ test.describe("History sharing", function () {
// we then invite Bob, and ensure Bob can see the content.
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await aliceElementApp.closeKeyStorageToast();
// 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);
await bobElementApp.closeKeyStorageToast();
await aliceElementApp.closeNotificationToast();
// Create the room and send a message
await createRoom(alicePage, "TestRoom", true);
@ -84,6 +88,7 @@ test.describe("History sharing", function () {
// 5. Charlie can't see the message.
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await aliceElementApp.closeKeyStorageToast();
await createRoom(alicePage, "TestRoom", true);
// Register a second user, and open it in a second instance of the app
@ -91,6 +96,7 @@ test.describe("History sharing", function () {
const bobPage = await createNewInstance(browser, bobCredentials, {}, labsFlags);
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
await bobElementApp.closeKeyStorageToast();
// ... and a third
const charlieCredentials = await homeserver.registerUser(
@ -101,6 +107,7 @@ test.describe("History sharing", function () {
const charliePage = await createNewInstance(browser, charlieCredentials, {}, labsFlags);
const charlieElementApp = new ElementAppPage(charliePage);
await charlieElementApp.client.bootstrapCrossSigning(charlieCredentials);
await charlieElementApp.closeKeyStorageToast();
// Alice invites Bob, and Bob accepts
const roomId = await aliceElementApp.getCurrentRoomIdFromUrl();

View File

@ -31,15 +31,6 @@ test.describe("Key storage out of sync toast", () => {
await logIntoElementAndVerify(page, credentials, recoveryKey.encodedPrivateKey);
await deleteCachedSecrets(page);
// We won't be prompted for crypto setup unless we have an e2e room, so make one
await page
.getByRole("navigation", { name: "Room list" })
.getByRole("button", { name: "New conversation" })
.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();
});
test("should prompt for recovery key if 'enter recovery key' pressed", { tag: "@screenshot" }, async ({ page }) => {

View File

@ -19,6 +19,7 @@ test.describe("Devtools", () => {
const profileSettings = userSettings.locator(".mx_UserProfileSettings");
await profileSettings.getByAltText("Upload").setInputFiles(getSampleFilePath("riot.png"));
await app.closeDialog();
await app.closeVerifyToast();
// Create an initial room.
const createRoomDialog = await app.openCreateRoomDialog();

View File

@ -91,6 +91,7 @@ test.describe("Invite dialog", function () {
"should support inviting a user to Direct Messages",
{ tag: "@screenshot" },
async ({ page, app, user, bot }) => {
await app.closeVerifyToast();
await page
.getByRole("navigation", { name: "Room list" })
.getByRole("button", { name: "New conversation" })

View File

@ -19,6 +19,8 @@ test.describe("Create Knock Room", () => {
});
test("should create a knock room", async ({ page, app, user }) => {
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog();
await dialog.getByRole("textbox", { name: "Name" }).fill("Cybersecurity");
await dialog.getByRole("button", { name: "Room visibility" }).click();
@ -37,6 +39,8 @@ test.describe("Create Knock Room", () => {
});
test("should create a room and change a join rule to knock", async ({ page, app, user }) => {
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog();
await dialog.getByRole("textbox", { name: "Name" }).fill("Cybersecurity");
await dialog.getByRole("button", { name: "Create room" }).click();
@ -59,6 +63,8 @@ test.describe("Create Knock Room", () => {
});
test("should create a public knock room", async ({ page, app, user }) => {
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog();
await dialog.getByRole("textbox", { name: "Name" }).fill("Cybersecurity");
await dialog.getByRole("button", { name: "Room visibility" }).click();

View File

@ -14,6 +14,7 @@ test.describe("Collapsible Room list", () => {
});
test.beforeEach(async ({ page, app, user }) => {
await app.closeVerifyToast();
await app.closeNotificationToast();
for (let i = 0; i < 10; i++) {
await app.client.createRoom({ name: `room${i}` });

View File

@ -59,6 +59,7 @@ test.describe("Room list custom sections", () => {
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
await app.closeNotificationToast();
await app.closeVerifyToast();
// Focus the user menu to avoid hover decoration
await page.getByRole("button", { name: "User menu" }).focus();

View File

@ -31,7 +31,8 @@ test.describe("Room list filters and sort", () => {
}
test.beforeEach(async ({ page, app, bot, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
});

View File

@ -14,7 +14,8 @@ test.describe("Header section of the room list", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
});

View File

@ -14,7 +14,8 @@ test.describe("Room list panel", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
// Populate the room list

View File

@ -14,7 +14,8 @@ test.describe("Search section of the room list", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
});

View File

@ -19,7 +19,8 @@ test.describe("Room list sections", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
// focus the user menu to avoid to have hover decoration

View File

@ -22,7 +22,8 @@ test.describe("Room list", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// The notification toast is displayed above the search section
// The toasts are displayed above the search section
await app.closeVerifyToast();
await app.closeNotificationToast();
// focus the user menu to avoid to have hover decoration

View File

@ -88,6 +88,7 @@ test.describe("Message rendering", () => {
room: async ({ user, app }, use) => {
const roomId = await app.client.createRoom({ name: "Test room" });
await use({ roomId });
await app.closeVerifyToast();
},
});
@ -218,6 +219,7 @@ test.describe("Message url previews", () => {
room: async ({ user, app }, use) => {
const roomId = await app.client.createRoom({ name: "Test room" });
await use({ roomId });
await app.closeVerifyToast();
},
});
test("should render a basic preview", { tag: "@screenshot" }, async ({ page, user, app, room, axe }) => {

View File

@ -65,6 +65,7 @@ test.describe("Room Directory", () => {
room_alias_name: "test1234",
});
await app.closeVerifyToast();
await page.getByRole("button", { name: "Explore rooms" }).click();
const dialog = page.locator(".mx_SpotlightDialog");

View File

@ -22,6 +22,7 @@ test.describe("Create Room", () => {
"should create a public room with name, topic & address set",
{ tag: "@screenshot" },
async ({ page, user, app, axe }) => {
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog();
// Fill name & topic
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
@ -50,6 +51,8 @@ test.describe("Create Room", () => {
);
test("should allow us to start a chat and show encryption state", async ({ page, user, app }) => {
await app.closeVerifyToast();
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "Start chat" }).click();
@ -69,6 +72,7 @@ test.describe("Create Room", () => {
test("should create a video room", { tag: "@screenshot" }, async ({ page, user, app }) => {
await app.settings.setValue("feature_video_rooms", null, SettingLevel.DEVICE, true);
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog("New video room");
// Fill name & topic
@ -103,6 +107,7 @@ test.describe("Create Room", () => {
});
test("should disallow creating public rooms", { tag: "@screenshot" }, async ({ page, user, app, axe }) => {
await app.closeVerifyToast();
const dialog = await app.openCreateRoomDialog();
// Fill name & topic
await dialog.getByRole("textbox", { name: "Name" }).fill(name);
@ -128,7 +133,9 @@ test.describe("Create Room", () => {
test.describe("when the encrypted state labs flag is turned off", () => {
test.use({ labsFlags: [] });
test("creates a room without encrypted state", { tag: "@screenshot" }, async ({ page, user: _user }) => {
test("creates a room without encrypted state", { tag: "@screenshot" }, async ({ page, user: _user, app }) => {
await app.closeVerifyToast();
// When we start to create a room
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
@ -157,7 +164,9 @@ test.describe("Create Room", () => {
test(
"creates a room with encrypted state if we check the box",
{ tag: "@screenshot" },
async ({ page, user: _user }) => {
async ({ page, user: _user, app }) => {
await app.closeVerifyToast();
// Given we check the Encrypted State checkbox
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
@ -184,7 +193,9 @@ test.describe("Create Room", () => {
test(
"creates a room without encrypted state if we don't check the box",
{ tag: "@screenshot" },
async ({ page, user: _user }) => {
async ({ page, user: _user, app }) => {
await app.closeVerifyToast();
// Given we did not check the Encrypted State checkbox
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();

View File

@ -19,6 +19,7 @@ test.describe("Room Status Bar", () => {
const roomId = await app.client.createRoom({
name: "A room",
});
await app.closeVerifyToast();
await app.closeNotificationToast();
await app.viewRoomById(roomId);
await use({ roomId });
@ -139,6 +140,7 @@ test.describe("Room Status Bar", () => {
"should show an error when creating a local room fails",
{ tag: "@screenshot" },
async ({ page, app, user, bot }) => {
await app.closeVerifyToast();
await page
.getByRole("navigation", { name: "Room list" })
.getByRole("button", { name: "New conversation" })

View File

@ -14,6 +14,7 @@ test.describe("Appearance user settings tab", () => {
});
test("should be rendered properly", { tag: "@screenshot" }, async ({ page, user, app, axe }) => {
await app.closeVerifyToast();
const tab = await app.settings.openUserSettings("Appearance");
// Click "Show advanced" link button
@ -31,6 +32,7 @@ test.describe("Appearance user settings tab", () => {
"should support changing font size by using the font size dropdown",
{ tag: "@screenshot" },
async ({ page, app, user }) => {
await app.closeVerifyToast();
await app.settings.openUserSettings("Appearance");
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
@ -46,6 +48,7 @@ test.describe("Appearance user settings tab", () => {
);
test("should support enabling system font", async ({ page, app, user }) => {
await app.closeVerifyToast();
await app.settings.openUserSettings("Appearance");
const tab = page.getByTestId("mx_AppearanceUserSettingsTab");
@ -63,7 +66,10 @@ test.describe("Appearance user settings tab", () => {
"should keep same font and emoji when switching theme",
{ tag: "@screenshot" },
async ({ page, app, user, util }) => {
await app.closeVerifyToast();
const roomId = await util.createAndDisplayRoom();
await app.client.sendMessage(roomId, { body: "Message with 🦡", msgtype: "m.text" });
await app.settings.openUserSettings("Appearance");

View File

@ -17,6 +17,8 @@ test.describe("Appearance user settings tab", () => {
test.beforeEach(async ({ app, user, util }) => {
// Disable the default theme for consistency in case ThemeWatcher automatically chooses it
await util.disableSystemTheme();
await app.closeVerifyToast();
await util.openAppearanceTab();
});
@ -102,6 +104,7 @@ test.describe("Appearance user settings tab", () => {
await expect(page).toMatchScreenshot("window-custom-theme.png");
await page.reload();
await app.closeVerifyToast();
await util.openAppearanceTab();
// Assert that the custom theme is still selected after reloading the page

View File

@ -85,6 +85,7 @@ test.describe("Encryption tab", () => {
// Fill the recovery key
await util.enterRecoveryKey(recoveryKey);
await dialog.getByRole("heading", { name: "Key storage" }).scrollIntoViewIfNeeded();
await expect(dialog).toMatchScreenshot("default-tab.png", {
mask: [dialog.getByTestId("deviceId"), dialog.getByTestId("sessionKey")],
});

View File

@ -6,10 +6,13 @@
*/
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({
@ -23,21 +26,13 @@ test.describe("Other people's devices section in Encryption tab", () => {
browser,
user: aliceCredentials,
}, testInfo) => {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await prepForEncryption(aliceElementApp, aliceCredentials);
// Create a second browser instance.
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
const bobPage = await createNewInstance(browser, bobCredentials, {});
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
// Alice sends a message, which Bob should be able to decrypt
await sendMessageInCurrentRoom(alicePage, "Decryptable");
@ -52,7 +47,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
user: aliceCredentials,
util,
}, testInfo) => {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await prepForEncryption(aliceElementApp, aliceCredentials);
// Enable blacklist toggle.
const dialog = await util.openEncryptionTab();
@ -65,18 +60,10 @@ test.describe("Other people's devices section in Encryption tab", () => {
await aliceElementApp.settings.closeDialog();
// Create a second browser instance.
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
const bobPage = await createNewInstance(browser, bobCredentials, {});
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
// Alice sends a message, which Bob should not be able to decrypt
await sendMessageInCurrentRoom(alicePage, "Undecryptable");
@ -95,7 +82,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
user: aliceCredentials,
util,
}, testInfo) => {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await prepForEncryption(aliceElementApp, aliceCredentials);
// Enable blacklist toggle.
const dialog = await util.openEncryptionTab();
@ -108,21 +95,11 @@ test.describe("Other people's devices section in Encryption tab", () => {
await aliceElementApp.settings.closeDialog();
// Create a second browser instance.
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
const bobPage = await createNewInstance(browser, bobCredentials, {});
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
const { bobCredentials, bobPage, bobElementApp } = await newBrowser(homeserver, testInfo, browser);
// Create the room and invite bob
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite and dismisses the warnings.
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
await bobPage.getByRole("button", { name: "Dismiss" }).click(); // enable notifications
await bobPage.getByRole("button", { name: "Dismiss" }).click(); // enable key storage
await bobPage.getByRole("button", { name: "Yes, dismiss" }).click(); // enable key storage x2
await inviteBobToNewRoom(alicePage, aliceElementApp, bobCredentials, bobPage);
await bobElementApp.closeNotificationToast();
// Perform verification.
await verifyApp("alice", aliceElementApp, "bob", bobElementApp);
@ -139,21 +116,13 @@ test.describe("Other people's devices section in Encryption tab", () => {
browser,
user: aliceCredentials,
}, testInfo) => {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await prepForEncryption(aliceElementApp, aliceCredentials);
// Create a second browser instance.
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
const bobPage = await createNewInstance(browser, bobCredentials, {});
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
// Alice creates the room and invite Bob.
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite.
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
// 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");
@ -168,10 +137,6 @@ test.describe("Other people's devices section in Encryption tab", () => {
),
).toBeVisible();
// Alice dismisses key storage warnings, as they now hide the "New conversation" button.
await alicePage.getByRole("button", { name: "Dismiss" }).click(); // enable key storage
await alicePage.getByRole("button", { name: "Yes, dismiss" }).click(); // enable key storage x2
// 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
@ -194,7 +159,7 @@ test.describe("Other people's devices section in Encryption tab", () => {
user: aliceCredentials,
util,
}, testInfo) => {
await aliceElementApp.client.bootstrapCrossSigning(aliceCredentials);
await prepForEncryption(aliceElementApp, aliceCredentials);
// Enable blacklist toggle.
let dialog = await util.openEncryptionTab();
@ -207,18 +172,10 @@ test.describe("Other people's devices section in Encryption tab", () => {
await aliceElementApp.settings.closeDialog();
// Create a second browser instance.
const bobCredentials = await homeserver.registerUser(`user_${testInfo.testId}_bob`, "password", "bob");
const bobPage = await createNewInstance(browser, bobCredentials, {});
const bobElementApp = new ElementAppPage(bobPage);
await bobElementApp.client.bootstrapCrossSigning(bobCredentials);
const { bobCredentials, bobPage } = await newBrowser(homeserver, testInfo, browser);
// Alice creates the room and invite Bob.
await createRoom(alicePage, "TestRoom", true);
await aliceElementApp.inviteUserToCurrentRoom(bobCredentials.userId, { confirmUnknownUser: true });
// Bob accepts the invite.
await bobPage.getByRole("option", { name: "TestRoom" }).click();
await bobPage.getByRole("button", { name: "Accept" }).click();
// 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");
@ -229,10 +186,6 @@ test.describe("Other people's devices section in Encryption tab", () => {
await sendMessageInCurrentRoom(alicePage, "Decryptable");
await expect(bobPage.getByText("Decryptable")).toBeVisible();
// Alice dismisses key storage warnings, as they now hide the "New conversation" button.
await alicePage.getByRole("button", { name: "Dismiss" }).click(); // enable key storage
await alicePage.getByRole("button", { name: "Yes, dismiss" }).click(); // enable key storage x2
// 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
@ -251,3 +204,32 @@ test.describe("Other people's devices section in Encryption tab", () => {
).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();
}

View File

@ -26,7 +26,8 @@ test.describe("Security user settings tab", () => {
});
test.beforeEach(async ({ page, app, user }) => {
// Dismiss "Notification" toast
// Dismiss toasts
await app.closeVerifyToast();
await app.closeNotificationToast();
await page.locator(".mx_Toast_buttons").getByRole("button", { name: "Yes" }).click(); // Allow analytics
});

View File

@ -72,6 +72,7 @@ test.describe("Sliding Sync", () => {
// Load the user fixture for all tests
test.beforeEach(async ({ app, user }) => {
await app.closeVerifyToast();
await app.closeNotificationToast();
});

View File

@ -68,6 +68,7 @@ test.describe("Spaces", () => {
"should allow user to create public space",
{ tag: ["@screenshot", "@no-webkit"] },
async ({ page, app, user }) => {
await app.closeVerifyToast();
const contextMenu = await openSpaceCreateMenu(page);
await expect(contextMenu).toMatchScreenshot("space-create-menu.png");
@ -104,6 +105,7 @@ test.describe("Spaces", () => {
);
test("should allow user to create private space", { tag: "@screenshot" }, async ({ page, app, user }) => {
await app.closeVerifyToast();
const menu = await openSpaceCreateMenu(page);
await menu.getByRole("button", { name: "Private" }).click();
@ -150,6 +152,7 @@ test.describe("Spaces", () => {
name: "Sample Room",
});
await app.closeVerifyToast();
const menu = await openSpaceCreateMenu(page);
await menu.getByRole("button", { name: "Private" }).click();
@ -184,6 +187,7 @@ test.describe("Spaces", () => {
name: "A Room that will not be selected",
});
await app.closeVerifyToast();
const menu = await openSpaceCreateMenu(page);
await menu.getByRole("button", { name: "Private" }).click();
@ -283,6 +287,8 @@ test.describe("Spaces", () => {
"should render subspaces in the space panel only when expanded",
{ tag: "@screenshot" },
async ({ page, app, user, axe }) => {
await app.closeVerifyToast();
axe.disableRules([
// Disable this check as it triggers on nested roving tab index elements which are in practice fine
"nested-interactive",
@ -404,6 +410,7 @@ test.describe("Spaces", () => {
});
test("should disallow creating public rooms", { tag: "@screenshot" }, async ({ page, user, app }) => {
await app.closeVerifyToast();
const menu = await openSpaceCreateMenu(page);
await menu
.locator('.mx_SpaceBasicSettings_avatarContainer input[type="file"]')

View File

@ -24,7 +24,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test(
"should have the button correctly aligned and displayed in the space panel when expanded",
{ tag: "@screenshot" },
async ({ util }) => {
async ({ util, app }) => {
await app.closeVerifyToast();
// Open the space panel
await util.expandSpacePanel();
// The buttons in the space panel should be aligned when expanded
@ -32,7 +34,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
},
);
test("should not show indicator when there is no thread", { tag: "@screenshot" }, async ({ room1, util }) => {
test("should not show indicator when there is no thread", { tag: "@screenshot" }, async ({ room1, util, app }) => {
await app.closeVerifyToast();
// No indicator should be shown
await util.assertNoTacIndicator();
@ -43,7 +47,14 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
await util.assertNoTacIndicator();
});
test("should show a notification indicator when there is a message in a thread", async ({ room1, util, msg }) => {
test("should show a notification indicator when there is a message in a thread", async ({
room1,
util,
msg,
app,
}) => {
await app.closeVerifyToast();
await util.goTo(room1);
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
@ -56,7 +67,10 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
util,
msg,
user,
app,
}) => {
await app.closeVerifyToast();
await util.goTo(room1);
await util.receiveMessages(room1, [
"Msg1",
@ -77,7 +91,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
test(
"should show the rooms with unread threads",
{ tag: "@screenshot" },
async ({ room1, room2, util, msg, user }) => {
async ({ room1, room2, util, msg, user, app }) => {
await app.closeVerifyToast();
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user);
// The indicator should be shown
@ -95,30 +111,38 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
},
);
test("should update with a thread is read", { tag: "@screenshot" }, async ({ room1, room2, util, msg, user }) => {
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user);
test(
"should update with a thread is read",
{ tag: "@screenshot" },
async ({ room1, room2, util, msg, user, app }) => {
await app.closeVerifyToast();
// Click on the first room in TAC
await util.openTac();
await util.clickRoomInTac(room2.name);
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user);
// Verify that the thread panel is opened after a click on the room in the TAC
await util.assertThreadPanelIsOpened();
// Click on the first room in TAC
await util.openTac();
await util.clickRoomInTac(room2.name);
// Open a thread and mark it as read
// The room 2 doesn't have a mention anymore in its unread, so the highest notification level is notification
await util.openThread("Msg1");
await util.assertNotificationTac();
await util.openTac();
await util.assertRoomsInTac([
{ room: room1.name, notificationLevel: "notification" },
{ room: room2.name, notificationLevel: "notification" },
]);
await expect(util.getTacPanel()).toMatchScreenshot("tac-panel-notification-unread.png");
});
// Verify that the thread panel is opened after a click on the room in the TAC
await util.assertThreadPanelIsOpened();
// Open a thread and mark it as read
// The room 2 doesn't have a mention anymore in its unread, so the highest notification level is notification
await util.openThread("Msg1");
await util.assertNotificationTac();
await util.openTac();
await util.assertRoomsInTac([
{ room: room1.name, notificationLevel: "notification" },
{ room: room2.name, notificationLevel: "notification" },
]);
await expect(util.getTacPanel()).toMatchScreenshot("tac-panel-notification-unread.png");
},
);
test("should order by recency after notification level", async ({ room1, room2, util, msg, user, app }) => {
await app.closeVerifyToast();
test("should order by recency after notification level", async ({ room1, room2, util, msg, user }) => {
await util.goTo(room2);
await util.populateThreads(room1, room2, msg, user, false);
@ -129,7 +153,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
]);
});
test("should block the Spotlight to open when the TAC is opened", async ({ util, page }) => {
test("should block the Spotlight to open when the TAC is opened", async ({ util, page, app }) => {
await app.closeVerifyToast();
const toggleSpotlight = () => page.keyboard.press(`${CommandOrControl}+k`);
// Sanity check
@ -144,7 +170,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
await expect(page.locator(".mx_SpotlightDialog")).not.toBeVisible();
});
test("should have the correct hover state", { tag: "@screenshot" }, async ({ util, page }) => {
test("should have the correct hover state", { tag: "@screenshot" }, async ({ util, page, app }) => {
await app.closeVerifyToast();
await util.hoverTacButton();
await expect(util.getSpacePanel()).toMatchScreenshot("tac-hovered.png");
@ -154,7 +182,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
await expect(util.getSpacePanel()).toMatchScreenshot("tac-hovered-expanded.png");
});
test("should mark all threads as read", { tag: "@screenshot" }, async ({ room1, room2, util, msg, page }) => {
test("should mark all threads as read", { tag: "@screenshot" }, async ({ room1, room2, util, msg, page, app }) => {
await app.closeVerifyToast();
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
await util.assertNotificationTac();
@ -167,7 +197,9 @@ test.describe("Threads Activity Centre", { tag: "@no-firefox" }, () => {
await util.assertNoTacIndicator();
});
test("should focus the thread tab when clicking an item in the TAC", async ({ room1, room2, util, msg }) => {
test("should focus the thread tab when clicking an item in the TAC", async ({ room1, room2, util, msg, app }) => {
await app.closeVerifyToast();
await util.receiveMessages(room1, ["Msg1", msg.threadedOff("Msg1", "Resp1")]);
await util.openTac();

View File

@ -36,6 +36,8 @@ test.describe("Media preview settings", () => {
});
test("should be able to hide avatars of inviters", { tag: "@screenshot" }, async ({ page, app, room, user }) => {
await app.closeVerifyToast();
let settings = await app.settings.openUserSettings("Preferences");
await settings.getByLabel("Hide avatars of room and inviter").click();
await app.closeDialog();

View File

@ -788,6 +788,7 @@ test.describe("Timeline", () => {
await sendEvent(app.client, room.roomId);
await sendEvent(app.client, room.roomId, true);
await page.goto(`/#/room/${room.roomId}`);
await app.closeVerifyToast();
await app.toggleRoomInfoPanel();
@ -813,6 +814,7 @@ test.describe("Timeline", () => {
await sendEvent(app.client, room.roomId);
await page.goto(`/#/room/${room.roomId}`);
await app.closeVerifyToast();
// Open a room setting dialog
await app.toggleRoomInfoPanel();

View File

@ -14,6 +14,7 @@ test.describe("Analytics Toast", () => {
});
test("should not show an analytics toast if config has nothing about posthog", async ({ user, toasts }) => {
await toasts.rejectToast("Verify this device");
await toasts.rejectToast("Notifications");
await toasts.assertNoToasts();
});
@ -29,6 +30,7 @@ test.describe("Analytics Toast", () => {
});
test.beforeEach(async ({ user, toasts }) => {
await toasts.rejectToast("Verify this device");
await toasts.rejectToast("Notifications");
});

View File

@ -21,6 +21,7 @@ test.describe("PSTN", () => {
});
test("should render dialpad as expected", { tag: "@screenshot" }, async ({ page, user, toasts }) => {
await toasts.rejectToast("Verify this device");
await toasts.rejectToast("Notifications");
await toasts.assertNoToasts();

View File

@ -259,15 +259,30 @@ export class ElementAppPage {
}
}
async closeToast(title: string, button: string): Promise<void> {
await this.page.locator(".mx_Toast_toast", { hasText: title }).getByRole("button", { name: button }).click();
}
/**
* Close the notification toast
* Dismiss the "Notifications" toast.
*/
public closeNotificationToast(): Promise<void> {
// Dismiss "Notification" toast
return this.page
.locator(".mx_Toast_toast", { hasText: "Notifications" })
.getByRole("button", { name: "Dismiss" })
.click();
public async closeNotificationToast(): Promise<void> {
await this.closeToast("Notifications", "Dismiss");
}
/**
* Dismiss the "Turn on key storage" toast.
*/
public async closeKeyStorageToast() {
await this.closeToast("Turn on key storage", "Dismiss");
await this.page.getByRole("button", { name: "Yes, dismiss" }).click();
}
/**
* Dismiss the "Verify this device" toast by clicking "Later".
*/
public async closeVerifyToast() {
await this.closeToast("Verify this device", "Later");
}
/**

View File

@ -24,7 +24,6 @@ import {
showToast as showSetupEncryptionToast,
} from "../toasts/SetupEncryptionToast";
import { isSecretStorageBeingAccessed } from "../SecurityManager";
import { asyncSomeParallel } from "../utils/arrays";
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
@ -271,10 +270,12 @@ export class DeviceListenerCurrentDevice {
if (newState === "ok" || this.dismissedThisDeviceToast) {
hideSetupEncryptionToast();
} else if (await this.shouldShowSetupEncryptionToast()) {
} else if (!isSecretStorageBeingAccessed()) {
showSetupEncryptionToast(newState);
} else {
logSpan.info("Not yet ready, but shouldShowSetupEncryptionToast==false");
// If we're in the middle of a secret storage operation, we're likely
// modifying the state involved here, so don't add new toasts to setup.
logSpan.info("Device is not yet ready, but secret storage is being accessed, so not showing toast.");
}
}
@ -386,23 +387,6 @@ export class DeviceListenerCurrentDevice {
return this.keyBackupInfo;
}
/**
* Is the user in at least one encrypted room?
*/
private async shouldShowSetupEncryptionToast(): Promise<boolean> {
// If we're in the middle of a secret storage operation, we're likely
// modifying the state involved here, so don't add new toasts to setup.
if (isSecretStorageBeingAccessed()) return false;
// Show setup toasts once the user is in at least one encrypted room.
const cryptoApi = this.client.getCrypto();
if (!cryptoApi) return false;
return await asyncSomeParallel(this.client.getRooms(), ({ roomId }) =>
cryptoApi.isEncryptionEnabledInRoom(roomId),
);
}
/**
* Is key backup enabled? Use a cached answer if we have one.
*/

View File

@ -348,11 +348,11 @@ describe("DeviceListener", () => {
expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled();
});
it("does not show any toasts when no rooms are encrypted", async () => {
it("shows toasts even when no rooms are encrypted", async () => {
jest.spyOn(mockClient.getCrypto()!, "isEncryptionEnabledInRoom").mockResolvedValue(false);
await createAndStart();
expect(SetupEncryptionToast.showToast).not.toHaveBeenCalled();
expect(SetupEncryptionToast.showToast).toHaveBeenCalled();
});
it("shows verify session toast when account has cross signing", async () => {