Add new verification section to user profile (#29200)
* Create new verification section * Remove old code and use new VerificationSection * Add styling and translation * Fix tests * Remove dead code * Fix broken test * Remove imports * Remove console.log * Update snapshots * Fix broken tests * Fix lint * Make badge expand with content * Remove unused code
This commit is contained in:
@@ -6,21 +6,13 @@ 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.
|
||||
*/
|
||||
|
||||
import { type Locator, type Page } from "@playwright/test";
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { viewRoomSummaryByName } from "../right-panel/utils";
|
||||
import { isDendrite } from "../../plugins/homeserver/dendrite";
|
||||
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
|
||||
import { type Client } from "../../pages/client.ts";
|
||||
|
||||
const ROOM_NAME = "Test room";
|
||||
const NAME = "Alice";
|
||||
|
||||
function getMemberTileByName(page: Page, name: string): Locator {
|
||||
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
|
||||
}
|
||||
|
||||
test.use({
|
||||
displayName: NAME,
|
||||
synapseConfig: {
|
||||
@@ -70,23 +62,6 @@ test.describe("Dehydration", () => {
|
||||
// device.
|
||||
const sessionsTab = await app.settings.openUserSettings("Sessions");
|
||||
await expect(sessionsTab.getByText("Dehydrated device")).not.toBeVisible();
|
||||
|
||||
await app.settings.closeDialog();
|
||||
|
||||
// now check that the user info right-panel shows the dehydrated device
|
||||
// as a feature rather than as a normal device
|
||||
await app.client.createRoom({ name: ROOM_NAME });
|
||||
|
||||
await viewRoomSummaryByName(page, app, ROOM_NAME);
|
||||
|
||||
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
|
||||
await expect(page.locator(".mx_MemberListView")).toBeVisible();
|
||||
|
||||
await getMemberTileByName(page, NAME).click();
|
||||
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
|
||||
|
||||
await expect(page.locator(".mx_UserInfo_devices").getByText("Offline device enabled")).toBeVisible();
|
||||
await expect(page.locator(".mx_UserInfo_devices").getByText("Dehydrated device")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("Reset recovery key during login re-creates dehydrated device", async ({
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
logIntoElement,
|
||||
logOutOfElement,
|
||||
verify,
|
||||
waitForDevices,
|
||||
} from "./utils";
|
||||
import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
|
||||
import { type ElementAppPage } from "../../pages/ElementAppPage.ts";
|
||||
@@ -144,25 +145,8 @@ test.describe("Cryptography", function () {
|
||||
// bob deletes his second device
|
||||
await bobSecondDevice.evaluate((cli) => cli.logout(true));
|
||||
|
||||
// wait for the logout to propagate. Workaround for https://github.com/vector-im/element-web/issues/26263 by repeatedly closing and reopening Bob's user info.
|
||||
async function awaitOneDevice(iterations = 1) {
|
||||
const rightPanel = page.locator(".mx_RightPanel");
|
||||
await rightPanel.getByTestId("base-card-back-button").click();
|
||||
await rightPanel.getByText("Bob").click();
|
||||
const sessionCountText = await rightPanel
|
||||
.locator(".mx_UserInfo_devices")
|
||||
.getByText(" session", { exact: false })
|
||||
.textContent();
|
||||
// cf https://github.com/vector-im/element-web/issues/26279: Element-R uses the wrong text here
|
||||
if (sessionCountText != "1 session" && sessionCountText != "1 verified session") {
|
||||
if (iterations >= 10) {
|
||||
throw new Error(`Bob still has ${sessionCountText} after 10 iterations`);
|
||||
}
|
||||
await awaitOneDevice(iterations + 1);
|
||||
}
|
||||
}
|
||||
|
||||
await awaitOneDevice();
|
||||
// wait for the logout to propagate.
|
||||
await waitForDevices(app, bob.credentials.userId, 1);
|
||||
|
||||
// close and reopen the room, to get the shield to update.
|
||||
await app.viewRoomByName("Bob");
|
||||
@@ -285,11 +269,7 @@ test.describe("Cryptography", function () {
|
||||
// Workaround for https://github.com/element-hq/element-web/issues/28640:
|
||||
// make sure that Alice has seen Bob's identity before she goes offline. We do this by opening
|
||||
// his user info.
|
||||
await app.toggleRoomInfoPanel();
|
||||
const rightPanel = page.locator(".mx_RightPanel");
|
||||
await rightPanel.getByRole("menuitem", { name: "People" }).click();
|
||||
await rightPanel.getByRole("button", { name: bob.credentials!.userId }).click();
|
||||
await expect(rightPanel.locator(".mx_UserInfo_devices")).toContainText("1 session");
|
||||
await waitForDevices(app, bob.credentials.userId, 1);
|
||||
|
||||
// Our app is blocked from syncing while Bob sends his messages.
|
||||
await app.client.network.goOffline();
|
||||
|
||||
@@ -8,9 +8,8 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import type { Page } from "@playwright/test";
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
|
||||
import { doTwoWaySasVerification, awaitVerifier, waitForDevices } from "./utils";
|
||||
import { type Client } from "../../pages/client";
|
||||
|
||||
test.describe("User verification", () => {
|
||||
@@ -33,13 +32,17 @@ test.describe("User verification", () => {
|
||||
});
|
||||
|
||||
test("can receive a verification request when there is no existing DM", async ({
|
||||
app,
|
||||
page,
|
||||
bot: bob,
|
||||
user: aliceCredentials,
|
||||
toasts,
|
||||
room: { roomId: dmRoomId },
|
||||
}) => {
|
||||
await waitForDeviceKeys(page);
|
||||
await waitForDevices(app, bob.credentials.userId, 1);
|
||||
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
|
||||
const avatar = page.getByRole("button", { name: "Avatar" });
|
||||
await avatar.click();
|
||||
|
||||
// once Alice has joined, Bob starts the verification
|
||||
const bobVerificationRequest = await bob.evaluateHandle(
|
||||
@@ -84,13 +87,17 @@ test.describe("User verification", () => {
|
||||
});
|
||||
|
||||
test("can abort emoji verification when emoji mismatch", async ({
|
||||
app,
|
||||
page,
|
||||
bot: bob,
|
||||
user: aliceCredentials,
|
||||
toasts,
|
||||
room: { roomId: dmRoomId },
|
||||
}) => {
|
||||
await waitForDeviceKeys(page);
|
||||
await waitForDevices(app, bob.credentials.userId, 1);
|
||||
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
|
||||
const avatar = page.getByRole("button", { name: "Avatar" });
|
||||
await avatar.click();
|
||||
|
||||
// once Alice has joined, Bob starts the verification
|
||||
const bobVerificationRequest = await bob.evaluateHandle(
|
||||
@@ -154,15 +161,3 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until we get the other user's device keys.
|
||||
* In newer rust-crypto versions, the verification request will be ignored if we
|
||||
* don't have the sender's device keys.
|
||||
*/
|
||||
async function waitForDeviceKeys(page: Page): Promise<void> {
|
||||
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
|
||||
const avatar = await page.getByRole("button", { name: "Avatar" });
|
||||
await avatar.click();
|
||||
await expect(page.getByText("1 session")).toBeVisible();
|
||||
}
|
||||
|
||||
@@ -502,3 +502,31 @@ export async function deleteCachedSecrets(page: Page) {
|
||||
});
|
||||
await page.reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until the given user has a given number of devices.
|
||||
* This function will check the device keys ten times and if
|
||||
* the expected number of devices were not found by then, an
|
||||
* error is thrown.
|
||||
*/
|
||||
export async function waitForDevices(
|
||||
app: ElementAppPage,
|
||||
userId: string,
|
||||
expectedNumberOfDevices: number,
|
||||
): Promise<void> {
|
||||
const result = await app.client.evaluate(
|
||||
async (cli, { userId, expectedNumberOfDevices }) => {
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const userDeviceMap = await cli.getCrypto()?.getUserDeviceInfo([userId], true);
|
||||
const deviceMap = userDeviceMap?.get(userId);
|
||||
if (deviceMap.size === expectedNumberOfDevices) return true;
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
}
|
||||
return false;
|
||||
},
|
||||
{ userId, expectedNumberOfDevices },
|
||||
);
|
||||
if (!result) {
|
||||
throw new Error(`User ${userId} did not have ${expectedNumberOfDevices} devices within ten iterations!`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ test.describe("UserView", () => {
|
||||
|
||||
const rightPanel = page.locator("#mx_RightPanel");
|
||||
await expect(rightPanel.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
|
||||
await expect(rightPanel.getByText("1 session")).toBeVisible();
|
||||
await expect(rightPanel).toMatchScreenshot("user-info.png", {
|
||||
mask: [page.locator(".mx_UserInfo_profile_mxid")],
|
||||
css: `
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user