Add Advanced section to the user settings encryption tab (#28804)

* Make the encryption card more configurable:
- Change the icon
- Can set the destructive props

* Update compound

* Add advanced section

* Add the `Never send encrypted messages to unverified devices` settings

* - Add commercial license
- Remove generic type

* Rename EncryptionDetails css classes

* Use same uiAuthCallback

* Use h3 for title

* Add tests to `AdvancedPanel`

* Add tests to `EncryptionUserSettingsTab`

* Add tests to `ResetIdentityPanel`

* Get only the recovery section in recovery tests

* Add e2e test
This commit is contained in:
Florian Duros
2025-01-24 09:33:16 +01:00
committed by GitHub
parent a0044d6b5f
commit ac565dca80
31 changed files with 1296 additions and 71 deletions

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2025 New Vector 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 { test, expect } from "./index";
import { checkDeviceIsCrossSigned } from "../../crypto/utils";
import { bootstrapCrossSigningForClient } from "../../../pages/client";
test.describe("Advanced section in Encryption tab", () => {
test.beforeEach(async ({ page, app, homeserver, credentials, util }) => {
const clientHandle = await app.client.prepareClient();
// Reset cross signing in order to have a verified session
await bootstrapCrossSigningForClient(clientHandle, credentials, true);
});
test("should show the encryption details", { tag: "@screenshot" }, async ({ page, app, util }) => {
await util.openEncryptionTab();
const section = util.getEncryptionDetailsSection();
const deviceId = await page.evaluate(() => window.mxMatrixClientPeg.get().getDeviceId());
await expect(section.getByText(deviceId)).toBeVisible();
await expect(section).toMatchScreenshot("encryption-details.png", {
mask: [section.getByTestId("deviceId"), section.getByTestId("sessionKey")],
});
});
test("should show the import room keys dialog", async ({ page, app, util }) => {
await util.openEncryptionTab();
const section = util.getEncryptionDetailsSection();
await section.getByRole("button", { name: "Import keys" }).click();
await expect(page.getByRole("heading", { name: "Import room keys" })).toBeVisible();
});
test("should show the export room keys dialog", async ({ page, app, util }) => {
await util.openEncryptionTab();
const section = util.getEncryptionDetailsSection();
await section.getByRole("button", { name: "Export keys" }).click();
await expect(page.getByRole("heading", { name: "Export room keys" })).toBeVisible();
});
test(
"should reset the cryptographic identity",
{ tag: "@screenshot" },
async ({ page, app, credentials, util }) => {
const tab = await util.openEncryptionTab();
const section = util.getEncryptionDetailsSection();
await section.getByRole("button", { name: "Reset cryptographic identity" }).click();
await expect(util.getEncryptionTabContent()).toMatchScreenshot("reset-cryptographic-identity.png");
await tab.getByRole("button", { name: "Continue" }).click();
// Fill password dialog and validate
const dialog = page.locator(".mx_InteractiveAuthDialog");
await dialog.getByRole("textbox", { name: "Password" }).fill(credentials.password);
await dialog.getByRole("button", { name: "Continue" }).click();
await expect(section.getByRole("button", { name: "Reset cryptographic identity" })).toBeVisible();
// After resetting the identity, the user should set up a new recovery key
await expect(
util.getEncryptionRecoverySection().getByRole("button", { name: "Set up recovery" }),
).toBeVisible();
await checkDeviceIsCrossSigned(app);
},
);
});

View File

@@ -18,6 +18,8 @@ export { expect };
export const test = base.extend<{
util: Helpers;
}>({
displayName: "Alice",
util: async ({ page, app, bot }, use) => {
await use(new Helpers(page, app));
},
@@ -67,6 +69,20 @@ class Helpers {
return this.page.getByTestId("encryptionTab");
}
/**
* Get the recovery section
*/
getEncryptionRecoverySection() {
return this.page.getByTestId("recoveryPanel");
}
/**
* Get the encryption details section
*/
getEncryptionDetailsSection() {
return this.page.getByTestId("encryptionDetails");
}
/**
* Set the default key id of the secret storage to `null`
*/
@@ -92,6 +108,6 @@ class Helpers {
const clipboardContent = await this.app.getClipboard();
await dialog.getByRole("textbox").fill(clipboardContent);
await dialog.getByRole("button", { name: confirmButtonLabel }).click();
await expect(dialog).toMatchScreenshot("default-recovery.png");
await expect(this.getEncryptionRecoverySection()).toMatchScreenshot("default-recovery.png");
}
}

View File

@@ -32,15 +32,19 @@ test.describe("Recovery section in Encryption tab", () => {
test("should verify the device", { tag: "@screenshot" }, async ({ page, app, util }) => {
const dialog = await util.openEncryptionTab();
const content = util.getEncryptionTabContent();
// The user's device is in an unverified state, therefore the only option available to them here is to verify it
const verifyButton = dialog.getByRole("button", { name: "Verify this device" });
await expect(verifyButton).toBeVisible();
await expect(util.getEncryptionTabContent()).toMatchScreenshot("verify-device-encryption-tab.png");
await expect(content).toMatchScreenshot("verify-device-encryption-tab.png");
await verifyButton.click();
await util.verifyDevice(recoveryKey);
await expect(util.getEncryptionTabContent()).toMatchScreenshot("default-recovery.png");
await expect(content).toMatchScreenshot("default-tab.png", {
mask: [content.getByTestId("deviceId"), content.getByTestId("sessionKey")],
});
// Check that our device is now cross-signed
await checkDeviceIsCrossSigned(app);
@@ -61,7 +65,7 @@ test.describe("Recovery section in Encryption tab", () => {
// The user can only change the recovery key
const changeButton = dialog.getByRole("button", { name: "Change recovery key" });
await expect(changeButton).toBeVisible();
await expect(util.getEncryptionTabContent()).toMatchScreenshot("default-recovery.png");
await expect(util.getEncryptionRecoverySection()).toMatchScreenshot("default-recovery.png");
await changeButton.click();
// Display the new recovery key and click on the copy button
@@ -89,7 +93,7 @@ test.describe("Recovery section in Encryption tab", () => {
const dialog = await util.openEncryptionTab();
const setupButton = dialog.getByRole("button", { name: "Set up recovery" });
await expect(setupButton).toBeVisible();
await expect(util.getEncryptionTabContent()).toMatchScreenshot("set-up-recovery.png");
await expect(util.getEncryptionRecoverySection()).toMatchScreenshot("set-up-recovery.png");
await setupButton.click();
// Display an informative panel about the recovery key
@@ -137,12 +141,12 @@ test.describe("Recovery section in Encryption tab", () => {
const dialog = util.getEncryptionTabContent();
const enterKeyButton = dialog.getByRole("button", { name: "Enter recovery key" });
await expect(enterKeyButton).toBeVisible();
await expect(dialog).toMatchScreenshot("out-of-sync-recovery.png");
await expect(util.getEncryptionRecoverySection()).toMatchScreenshot("out-of-sync-recovery.png");
await enterKeyButton.click();
// Fill the recovery key
await util.enterRecoveryKey(recoveryKey);
await expect(dialog).toMatchScreenshot("default-recovery.png");
await expect(util.getEncryptionRecoverySection()).toMatchScreenshot("default-recovery.png");
// Check that our device is now cross-signed
await checkDeviceIsCrossSigned(app);

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB