From 53f83124a04e021fffa03c525d170c33f45ba98d Mon Sep 17 00:00:00 2001 From: Florian D Date: Thu, 6 Feb 2025 11:54:20 +0100 Subject: [PATCH] Add `Forgot recovery key?` button to encryption tab (#29202) * feat(crypto settings): add "Forgot recovery key?" button to encryption tab * test(crypto settings): add tests for `RecoveryPanelOutOfSync` * test(crypto settings): update encryption tab test * test(crypto settings): update and add e2e test --- .../encryption-tab.spec.ts | 18 +++ .../out-of-sync-recovery-linux.png | Bin 22347 -> 25453 bytes res/css/_components.pcss | 1 + .../encryption/_RecoveryPanelOutOfSync.pcss | 11 ++ .../encryption/RecoveryPanelOutOfSync.tsx | 33 ++-- .../tabs/user/EncryptionUserSettingsTab.tsx | 7 +- src/i18n/strings/en_EN.json | 3 +- .../RecoveryPanelOutOfSync-test.tsx | 51 ++++++ .../RecoveryPanelOutOfSync-test.tsx.snap | 75 +++++++++ .../user/EncryptionUserSettingsTab-test.tsx | 16 +- .../EncryptionUserSettingsTab-test.tsx.snap | 153 ++++++++++-------- 11 files changed, 273 insertions(+), 95 deletions(-) create mode 100644 res/css/views/settings/encryption/_RecoveryPanelOutOfSync.pcss create mode 100644 test/unit-tests/components/views/settings/encryption/RecoveryPanelOutOfSync-test.tsx create mode 100644 test/unit-tests/components/views/settings/encryption/__snapshots__/RecoveryPanelOutOfSync-test.tsx.snap diff --git a/playwright/e2e/settings/encryption-user-tab/encryption-tab.spec.ts b/playwright/e2e/settings/encryption-user-tab/encryption-tab.spec.ts index 6bffd793f6..a279961814 100644 --- a/playwright/e2e/settings/encryption-user-tab/encryption-tab.spec.ts +++ b/playwright/e2e/settings/encryption-user-tab/encryption-tab.spec.ts @@ -93,4 +93,22 @@ test.describe("Encryption tab", () => { await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); }, ); + + test("should display the reset identity panel when the user clicks on 'Forgot recovery key?'", async ({ + page, + app, + util, + }) => { + await verifySession(app, "new passphrase"); + // We need to delete the cached secrets + await deleteCachedSecrets(page); + + // The "Key storage is out sync" section is displayed and the user click on the "Forgot recovery key?" button + await util.openEncryptionTab(); + const dialog = util.getEncryptionTabContent(); + await dialog.getByRole("button", { name: "Forgot recovery key?" }).click(); + + // The user is prompted to reset their identity + await expect(dialog.getByText("Forgot your recovery key? You’ll need to reset your identity.")).toBeVisible(); + }); }); diff --git a/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/out-of-sync-recovery-linux.png b/playwright/snapshots/settings/encryption-user-tab/encryption-tab.spec.ts/out-of-sync-recovery-linux.png index e6664a5f79b1e1a536f38cba312a836273a9f8ed..f1a9152e89ae10a08f85cf5c7387de359aba64b3 100644 GIT binary patch literal 25453 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 literal 22347 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 cgJ7~PL0@|;eY}SRfhxrCy4kfd9mgmC13nM2!T void; + /** + * Callback for when the user clicks on the "Forgot recovery key?" button. + */ + onForgotRecoveryKey: () => void; } /** @@ -28,7 +32,7 @@ interface RecoveryPanelOutOfSyncProps { * It prompts the user to enter their recovery key so that the secrets can be loaded from 4S into * the client. */ -export function RecoveryPanelOutOfSync({ onFinish }: RecoveryPanelOutOfSyncProps): JSX.Element { +export function RecoveryPanelOutOfSync({ onForgotRecoveryKey, onFinish }: RecoveryPanelOutOfSyncProps): JSX.Element { return ( - +
+ + +
); } diff --git a/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx b/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx index f164342e27..0b4b27e9d0 100644 --- a/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/EncryptionUserSettingsTab.tsx @@ -71,7 +71,12 @@ export function EncryptionUserSettingsTab({ initialState = "loading" }: Encrypti content = ; break; case "secrets_not_cached": - content = ; + content = ( + setState("reset_identity_forgot")} + /> + ); break; case "main": content = ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3896d05e88..c9adce2cc7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2496,7 +2496,8 @@ "description": "Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices.", "enter_key_error": "The recovery key you entered is not correct.", "enter_recovery_key": "Enter recovery key", - "key_storage_warning": "Your key storage is out of sync. Click the button below to fix the problem.", + "forgot_recovery_key": "Forgot recovery key?", + "key_storage_warning": "Your key storage is out of sync. Click one of the buttons below to fix the problem.", "save_key_description": "Do not share this with anyone!", "save_key_title": "Recovery key", "set_up_recovery": "Set up recovery", diff --git a/test/unit-tests/components/views/settings/encryption/RecoveryPanelOutOfSync-test.tsx b/test/unit-tests/components/views/settings/encryption/RecoveryPanelOutOfSync-test.tsx new file mode 100644 index 0000000000..36e35dbe83 --- /dev/null +++ b/test/unit-tests/components/views/settings/encryption/RecoveryPanelOutOfSync-test.tsx @@ -0,0 +1,51 @@ +/* + * 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 React from "react"; +import { render, screen } from "jest-matrix-react"; +import userEvent from "@testing-library/user-event"; +import { mocked } from "jest-mock"; + +import { RecoveryPanelOutOfSync } from "../../../../../../src/components/views/settings/encryption/RecoveryPanelOutOfSync"; +import { accessSecretStorage } from "../../../../../../src/SecurityManager"; + +jest.mock("../../../../../../src/SecurityManager", () => ({ + accessSecretStorage: jest.fn(), +})); + +describe("", () => { + function renderComponent(onFinish = jest.fn(), onForgotRecoveryKey = jest.fn()) { + return render(); + } + + it("should render", () => { + const { asFragment } = renderComponent(); + expect(asFragment()).toMatchSnapshot(); + }); + + it("should call onForgotRecoveryKey when the 'Forgot recovery key?' is clicked", async () => { + const user = userEvent.setup(); + + const onForgotRecoveryKey = jest.fn(); + renderComponent(jest.fn(), onForgotRecoveryKey); + + await user.click(screen.getByRole("button", { name: "Forgot recovery key?" })); + expect(onForgotRecoveryKey).toHaveBeenCalled(); + }); + + it("should access to 4S and call onFinish when 'Enter recovery key' is clicked", async () => { + const user = userEvent.setup(); + mocked(accessSecretStorage).mockClear().mockResolvedValue(); + + const onFinish = jest.fn(); + renderComponent(onFinish); + + await user.click(screen.getByRole("button", { name: "Enter recovery key" })); + expect(accessSecretStorage).toHaveBeenCalled(); + expect(onFinish).toHaveBeenCalled(); + }); +}); diff --git a/test/unit-tests/components/views/settings/encryption/__snapshots__/RecoveryPanelOutOfSync-test.tsx.snap b/test/unit-tests/components/views/settings/encryption/__snapshots__/RecoveryPanelOutOfSync-test.tsx.snap new file mode 100644 index 0000000000..16cac376fa --- /dev/null +++ b/test/unit-tests/components/views/settings/encryption/__snapshots__/RecoveryPanelOutOfSync-test.tsx.snap @@ -0,0 +1,75 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should render 1`] = ` + +
+
+

+ Recovery +

+
+ Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices. + + + + + Your key storage is out of sync. Click one of the buttons below to fix the problem. + +
+
+
+ + +
+
+
+`; diff --git a/test/unit-tests/components/views/settings/tabs/user/EncryptionUserSettingsTab-test.tsx b/test/unit-tests/components/views/settings/tabs/user/EncryptionUserSettingsTab-test.tsx index fdf8f7106d..9137140c79 100644 --- a/test/unit-tests/components/views/settings/tabs/user/EncryptionUserSettingsTab-test.tsx +++ b/test/unit-tests/components/views/settings/tabs/user/EncryptionUserSettingsTab-test.tsx @@ -10,7 +10,6 @@ import { render, screen } from "jest-matrix-react"; import { type MatrixClient } from "matrix-js-sdk/src/matrix"; import { waitFor } from "@testing-library/dom"; import userEvent from "@testing-library/user-event"; -import { mocked } from "jest-mock"; import { EncryptionUserSettingsTab, @@ -18,11 +17,6 @@ import { } from "../../../../../../../src/components/views/settings/tabs/user/EncryptionUserSettingsTab"; import { createTestClient, withClientContextRenderOptions } from "../../../../../../test-utils"; import Modal from "../../../../../../../src/Modal"; -import { accessSecretStorage } from "../../../../../../../src/SecurityManager"; - -jest.mock("../../../../../../../src/SecurityManager", () => ({ - accessSecretStorage: jest.fn(), -})); describe("", () => { let matrixClient: MatrixClient; @@ -42,8 +36,6 @@ describe("", () => { userSigningKey: true, }, }); - - mocked(accessSecretStorage).mockClear().mockResolvedValue(); }); function renderComponent(props: { initialState?: State } = {}) { @@ -79,7 +71,7 @@ describe("", () => { await waitFor(() => expect(screen.getByText("Recovery")).toBeInTheDocument()); }); - it("should ask to enter the recovery key when secrets are not cached", async () => { + it("should display the recovery out of sync panel when secrets are not cached", async () => { // Secrets are not cached jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({ privateKeysInSecretStorage: true, @@ -97,8 +89,10 @@ describe("", () => { await waitFor(() => screen.getByRole("button", { name: "Enter recovery key" })); expect(asFragment()).toMatchSnapshot(); - await user.click(screen.getByRole("button", { name: "Enter recovery key" })); - expect(accessSecretStorage).toHaveBeenCalled(); + await user.click(screen.getByRole("button", { name: "Forgot recovery key?" })); + expect( + screen.getByRole("heading", { name: "Forgot your recovery key? You’ll need to reset your identity." }), + ).toBeVisible(); }); it("should display the change recovery key panel when the user clicks on the change recovery button", async () => { diff --git a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/EncryptionUserSettingsTab-test.tsx.snap b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/EncryptionUserSettingsTab-test.tsx.snap index 5856e6fda3..2e507dd67a 100644 --- a/test/unit-tests/components/views/settings/tabs/user/__snapshots__/EncryptionUserSettingsTab-test.tsx.snap +++ b/test/unit-tests/components/views/settings/tabs/user/__snapshots__/EncryptionUserSettingsTab-test.tsx.snap @@ -1,75 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should ask to enter the recovery key when secrets are not cached 1`] = ` - -
-
-
-
-

- Recovery -

-
- Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices. - - - - - Your key storage is out of sync. Click the button below to fix the problem. - -
-
- -
-
-
-
-`; - exports[` should display a verify button when the encryption is not set up 1`] = `
should display the change recovery key pa `; +exports[` should display the recovery out of sync panel when secrets are not cached 1`] = ` + +
+
+
+
+

+ Recovery +

+
+ Recover your cryptographic identity and message history with a recovery key if you’ve lost all your existing devices. + + + + + Your key storage is out of sync. Click one of the buttons below to fix the problem. + +
+
+
+ + +
+
+
+
+
+`; + exports[` should display the reset identity panel when the user clicks on the reset cryptographic identity panel 1`] = `