Skip to content

Commit 52b42c0

Browse files
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
1 parent bb8b4d7 commit 52b42c0

File tree

14 files changed

+489
-1095
lines changed

14 files changed

+489
-1095
lines changed

playwright/e2e/crypto/dehydration.spec.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
66
Please see LICENSE files in the repository root for full details.
77
*/
88

9-
import { type Locator, type Page } from "@playwright/test";
10-
119
import { test, expect } from "../../element-web-test";
12-
import { viewRoomSummaryByName } from "../right-panel/utils";
1310
import { isDendrite } from "../../plugins/homeserver/dendrite";
1411
import { completeCreateSecretStorageDialog, createBot, logIntoElement } from "./utils.ts";
1512
import { type Client } from "../../pages/client.ts";
1613

17-
const ROOM_NAME = "Test room";
1814
const NAME = "Alice";
1915

20-
function getMemberTileByName(page: Page, name: string): Locator {
21-
return page.locator(`.mx_MemberTileView, [title="${name}"]`);
22-
}
23-
2416
test.use({
2517
displayName: NAME,
2618
synapseConfig: {
@@ -70,23 +62,6 @@ test.describe("Dehydration", () => {
7062
// device.
7163
const sessionsTab = await app.settings.openUserSettings("Sessions");
7264
await expect(sessionsTab.getByText("Dehydrated device")).not.toBeVisible();
73-
74-
await app.settings.closeDialog();
75-
76-
// now check that the user info right-panel shows the dehydrated device
77-
// as a feature rather than as a normal device
78-
await app.client.createRoom({ name: ROOM_NAME });
79-
80-
await viewRoomSummaryByName(page, app, ROOM_NAME);
81-
82-
await page.locator(".mx_RightPanel").getByRole("menuitem", { name: "People" }).click();
83-
await expect(page.locator(".mx_MemberListView")).toBeVisible();
84-
85-
await getMemberTileByName(page, NAME).click();
86-
await page.locator(".mx_UserInfo_devices .mx_UserInfo_expand").click();
87-
88-
await expect(page.locator(".mx_UserInfo_devices").getByText("Offline device enabled")).toBeVisible();
89-
await expect(page.locator(".mx_UserInfo_devices").getByText("Dehydrated device")).not.toBeVisible();
9065
});
9166

9267
test("Reset recovery key during login re-creates dehydrated device", async ({

playwright/e2e/crypto/event-shields.spec.ts

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
logIntoElement,
1818
logOutOfElement,
1919
verify,
20+
waitForDevices,
2021
} from "./utils";
2122
import { bootstrapCrossSigningForClient } from "../../pages/client.ts";
2223
import { type ElementAppPage } from "../../pages/ElementAppPage.ts";
@@ -144,25 +145,8 @@ test.describe("Cryptography", function () {
144145
// bob deletes his second device
145146
await bobSecondDevice.evaluate((cli) => cli.logout(true));
146147

147-
// 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.
148-
async function awaitOneDevice(iterations = 1) {
149-
const rightPanel = page.locator(".mx_RightPanel");
150-
await rightPanel.getByTestId("base-card-back-button").click();
151-
await rightPanel.getByText("Bob").click();
152-
const sessionCountText = await rightPanel
153-
.locator(".mx_UserInfo_devices")
154-
.getByText(" session", { exact: false })
155-
.textContent();
156-
// cf https://github.com/vector-im/element-web/issues/26279: Element-R uses the wrong text here
157-
if (sessionCountText != "1 session" && sessionCountText != "1 verified session") {
158-
if (iterations >= 10) {
159-
throw new Error(`Bob still has ${sessionCountText} after 10 iterations`);
160-
}
161-
await awaitOneDevice(iterations + 1);
162-
}
163-
}
164-
165-
await awaitOneDevice();
148+
// wait for the logout to propagate.
149+
await waitForDevices(app, bob.credentials.userId, 1);
166150

167151
// close and reopen the room, to get the shield to update.
168152
await app.viewRoomByName("Bob");
@@ -285,11 +269,7 @@ test.describe("Cryptography", function () {
285269
// Workaround for https://github.com/element-hq/element-web/issues/28640:
286270
// make sure that Alice has seen Bob's identity before she goes offline. We do this by opening
287271
// his user info.
288-
await app.toggleRoomInfoPanel();
289-
const rightPanel = page.locator(".mx_RightPanel");
290-
await rightPanel.getByRole("menuitem", { name: "People" }).click();
291-
await rightPanel.getByRole("button", { name: bob.credentials!.userId }).click();
292-
await expect(rightPanel.locator(".mx_UserInfo_devices")).toContainText("1 session");
272+
await waitForDevices(app, bob.credentials.userId, 1);
293273

294274
// Our app is blocked from syncing while Bob sends his messages.
295275
await app.client.network.goOffline();

playwright/e2e/crypto/user-verification.spec.ts

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ Please see LICENSE files in the repository root for full details.
88

99
import { type Preset, type Visibility } from "matrix-js-sdk/src/matrix";
1010

11-
import type { Page } from "@playwright/test";
1211
import { test, expect } from "../../element-web-test";
13-
import { doTwoWaySasVerification, awaitVerifier } from "./utils";
12+
import { doTwoWaySasVerification, awaitVerifier, waitForDevices } from "./utils";
1413
import { type Client } from "../../pages/client";
1514

1615
test.describe("User verification", () => {
@@ -33,13 +32,17 @@ test.describe("User verification", () => {
3332
});
3433

3534
test("can receive a verification request when there is no existing DM", async ({
35+
app,
3636
page,
3737
bot: bob,
3838
user: aliceCredentials,
3939
toasts,
4040
room: { roomId: dmRoomId },
4141
}) => {
42-
await waitForDeviceKeys(page);
42+
await waitForDevices(app, bob.credentials.userId, 1);
43+
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
44+
const avatar = page.getByRole("button", { name: "Avatar" });
45+
await avatar.click();
4346

4447
// once Alice has joined, Bob starts the verification
4548
const bobVerificationRequest = await bob.evaluateHandle(
@@ -84,13 +87,17 @@ test.describe("User verification", () => {
8487
});
8588

8689
test("can abort emoji verification when emoji mismatch", async ({
90+
app,
8791
page,
8892
bot: bob,
8993
user: aliceCredentials,
9094
toasts,
9195
room: { roomId: dmRoomId },
9296
}) => {
93-
await waitForDeviceKeys(page);
97+
await waitForDevices(app, bob.credentials.userId, 1);
98+
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
99+
const avatar = page.getByRole("button", { name: "Avatar" });
100+
await avatar.click();
94101

95102
// once Alice has joined, Bob starts the verification
96103
const bobVerificationRequest = await bob.evaluateHandle(
@@ -154,15 +161,3 @@ async function createDMRoom(client: Client, userId: string): Promise<string> {
154161
],
155162
});
156163
}
157-
158-
/**
159-
* Wait until we get the other user's device keys.
160-
* In newer rust-crypto versions, the verification request will be ignored if we
161-
* don't have the sender's device keys.
162-
*/
163-
async function waitForDeviceKeys(page: Page): Promise<void> {
164-
await expect(page.getByRole("button", { name: "Avatar" })).toBeVisible();
165-
const avatar = await page.getByRole("button", { name: "Avatar" });
166-
await avatar.click();
167-
await expect(page.getByText("1 session")).toBeVisible();
168-
}

playwright/e2e/crypto/utils.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,3 +502,31 @@ export async function deleteCachedSecrets(page: Page) {
502502
});
503503
await page.reload();
504504
}
505+
506+
/**
507+
* Wait until the given user has a given number of devices.
508+
* This function will check the device keys ten times and if
509+
* the expected number of devices were not found by then, an
510+
* error is thrown.
511+
*/
512+
export async function waitForDevices(
513+
app: ElementAppPage,
514+
userId: string,
515+
expectedNumberOfDevices: number,
516+
): Promise<void> {
517+
const result = await app.client.evaluate(
518+
async (cli, { userId, expectedNumberOfDevices }) => {
519+
for (let i = 0; i < 10; ++i) {
520+
const userDeviceMap = await cli.getCrypto()?.getUserDeviceInfo([userId], true);
521+
const deviceMap = userDeviceMap?.get(userId);
522+
if (deviceMap.size === expectedNumberOfDevices) return true;
523+
await new Promise((r) => setTimeout(r, 500));
524+
}
525+
return false;
526+
},
527+
{ userId, expectedNumberOfDevices },
528+
);
529+
if (!result) {
530+
throw new Error(`User ${userId} did not have ${expectedNumberOfDevices} devices within ten iterations!`);
531+
}
532+
}

playwright/e2e/user-view/user-view.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ test.describe("UserView", () => {
1919

2020
const rightPanel = page.locator("#mx_RightPanel");
2121
await expect(rightPanel.getByRole("heading", { name: bot.credentials.displayName, exact: true })).toBeVisible();
22-
await expect(rightPanel.getByText("1 session")).toBeVisible();
2322
await expect(rightPanel).toMatchScreenshot("user-info.png", {
2423
mask: [page.locator(".mx_UserInfo_profile_mxid")],
2524
css: `
Loading

res/css/views/right_panel/_UserInfo.pcss

Lines changed: 22 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ Please see LICENSE files in the repository root for full details.
3737
padding: var(--cpd-space-2x) 0 var(--cpd-space-4x);
3838
margin: 0 var(--cpd-space-4x);
3939

40-
.mx_UserInfo_container_verifyButton {
41-
margin-top: $spacing-8;
42-
}
43-
4440
& + .mx_UserInfo_container {
4541
border-top: 1px solid $separator;
4642
}
@@ -180,6 +176,28 @@ Please see LICENSE files in the repository root for full details.
180176
opacity: 1;
181177
}
182178

179+
.mx_UserInfo_verification {
180+
margin-top: var(--cpd-space-4x);
181+
height: 36px;
182+
183+
.mx_UserInfo_verified_badge {
184+
min-width: 68px;
185+
height: 20px;
186+
187+
.mx_UserInfo_verified_icon {
188+
flex-shrink: 0;
189+
}
190+
191+
.mx_UserInfo_verified_label {
192+
margin: 0;
193+
}
194+
}
195+
196+
.mx_UserInfo_verification_unavailable {
197+
color: var(--cpd-color-text-secondary);
198+
}
199+
}
200+
183201
.mx_UserInfo_memberDetails {
184202
.mx_UserInfo_profileField {
185203
display: flex;
@@ -226,45 +244,6 @@ Please see LICENSE files in the repository root for full details.
226244
flex: 1 1 0;
227245
}
228246

229-
.mx_UserInfo_devices {
230-
.mx_UserInfo_device {
231-
display: flex;
232-
margin: $spacing-8 0;
233-
234-
&.mx_UserInfo_device_verified {
235-
.mx_UserInfo_device_trusted {
236-
color: $accent;
237-
}
238-
}
239-
&.mx_UserInfo_device_unverified {
240-
.mx_UserInfo_device_trusted {
241-
color: $alert;
242-
}
243-
}
244-
245-
.mx_UserInfo_device_name {
246-
flex: 1;
247-
margin: 0 5px;
248-
word-break: break-word;
249-
}
250-
}
251-
252-
/* both for icon in expand button and device item */
253-
.mx_E2EIcon {
254-
/* don't squeeze */
255-
flex: 0 0 auto;
256-
margin: 0;
257-
width: 12px;
258-
height: 12px;
259-
}
260-
261-
.mx_UserInfo_expand {
262-
column-gap: 5px; /* cf: mx_UserInfo_device_name */
263-
margin-bottom: 11px;
264-
align-items: initial; /* Cancel the default property */
265-
}
266-
}
267-
268247
&.mx_UserInfo_smallAvatar {
269248
.mx_UserInfo_avatar {
270249
.mx_UserInfo_avatar_transition {

0 commit comments

Comments
 (0)