Florian Duros 13913ba8b2
Add Recovery section in the new user settings Encryption tab (#28673)
* Refine `SettingsSection` & `SettingsTab`

* Add encryption tab

* Add recovery section

* Add device verification

* Rename `Panel` into `State`

* Update & add tests to user settings common

* Add tests to `RecoveryPanel`

* Add tests to `ChangeRecoveryKey`

* Update CreateSecretStorageDialog-test snapshot

* Add tests to `EncryptionUserSettingsTab`

* Update existing screenshots of e2e tests

* Add new encryption tab ownership to `@element-hq/element-crypto-web-reviewers`

* Add e2e tests

* Fix monospace font and add figma link to hardcoded value

* Add unit to Icon

* Improve e2e doc

* Assert that the crypto module is defined

* Add classname doc

* Fix typo

* Use `good` state instead of default

* Rename `ChangeRecoveryKey.isSetupFlow` into `ChangeRecoveryKey.userHasKeyBackup`

* Move `deleteCachedSecrets` fixture in `recovery.spec.ts`

* Use one callback instead of two in `RecoveryPanel`

* Fix docs and naming of `utils.createBot`

* Fix typo in `RecoveryPanel`

* Add more doc to the state of the `EncryptionUserSettingsTab`

* Rename `verification_required` into `set_up_encryption`

* Update test

* ADd new license

* Update comments and doc

* Assert that `recoveryKey.encodedPrivateKey` is always defined

* Add comments to explain how the secrets could be uncached

* Use `matrixClient.secretStorage.getDefaultKeyId` instead of `matrixClient.getCrypto().checkKeyBackupAndEnable` to know if we need to set up a recovery key

* Update existing screenshot to add encryption tab.

* Update tests

* Use new labels when changing the recovery key

* Fix docs

* Don't reset key backup when creating a recovery key

* Fix doc
2025-01-15 12:44:20 +00:00

122 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright 2024 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, waitFor } from "jest-matrix-react";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event";
import { ChangeRecoveryKey } from "../../../../../../src/components/views/settings/encryption/ChangeRecoveryKey";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { copyPlaintext } from "../../../../../../src/utils/strings";
jest.mock("../../../../../../src/utils/strings", () => ({
copyPlaintext: jest.fn(),
}));
describe("<ChangeRecoveryKey />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
function renderComponent(userHasRecoveryKey = true, onFinish = jest.fn(), onCancelClick = jest.fn()) {
return render(
<ChangeRecoveryKey
userHasRecoveryKey={userHasRecoveryKey}
onFinish={onFinish}
onCancelClick={onCancelClick}
/>,
withClientContextRenderOptions(matrixClient),
);
}
describe("flow to setup a recovery key", () => {
it("should display information about the recovery key", async () => {
const user = userEvent.setup();
const onCancelClick = jest.fn();
const { asFragment } = renderComponent(false, jest.fn(), onCancelClick);
await waitFor(() =>
expect(
screen.getByText(
"Your key storage is protected by a recovery key. If you need a new recovery key after setup, you can recreate it by selecting Change recovery key.",
),
).toBeInTheDocument(),
);
expect(asFragment()).toMatchSnapshot();
await user.click(screen.getByRole("button", { name: "Cancel" }));
expect(onCancelClick).toHaveBeenCalled();
});
it("should display the recovery key", async () => {
const user = userEvent.setup();
const onCancelClick = jest.fn();
const { asFragment } = renderComponent(false, jest.fn(), onCancelClick);
await waitFor(() => user.click(screen.getByRole("button", { name: "Continue" })));
expect(screen.getByText("Save your recovery key somewhere safe")).toBeInTheDocument();
expect(screen.getByText("encoded private key")).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
// Test copy button
await user.click(screen.getByRole("button", { name: "Copy" }));
expect(copyPlaintext).toHaveBeenCalled();
await user.click(screen.getByRole("button", { name: "Cancel" }));
expect(onCancelClick).toHaveBeenCalled();
});
it("should ask the user to enter the recovery key", async () => {
const user = userEvent.setup();
const onFinish = jest.fn();
const { asFragment } = renderComponent(false, onFinish);
// Display the recovery key to save
await waitFor(() => user.click(screen.getByRole("button", { name: "Continue" })));
// Display the form to confirm the recovery key
await waitFor(() => user.click(screen.getByRole("button", { name: "Continue" })));
await waitFor(() => expect(screen.getByText("Enter your recovery key to confirm")).toBeInTheDocument());
expect(asFragment()).toMatchSnapshot();
// The finish button should be disabled by default
const finishButton = screen.getByRole("button", { name: "Finish set up" });
expect(finishButton).toHaveAttribute("aria-disabled", "true");
const input = screen.getByRole("textbox");
// If the user enters an incorrect recovery key, the finish button should be disabled
// and we display an error message
await userEvent.type(input, "wrong recovery key");
expect(finishButton).toHaveAttribute("aria-disabled", "true");
expect(screen.getByText("The recovery key you entered is not correct.")).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
await userEvent.clear(input);
// If the user enters the correct recovery key, the finish button should be enabled
await userEvent.type(input, "encoded private key");
await waitFor(() => expect(finishButton).not.toHaveAttribute("aria-disabled", "true"));
await user.click(finishButton);
expect(onFinish).toHaveBeenCalledWith();
});
});
describe("flow to change the recovery key", () => {
it("should display the recovery key", async () => {
const { asFragment } = renderComponent();
await waitFor(() => expect(screen.getByText("Change recovery key?")).toBeInTheDocument());
expect(screen.getByText("encoded private key")).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
});
});