diff --git a/test/unit-tests/components/views/settings/encryption/ChangeRecoveryKey-test.tsx b/test/unit-tests/components/views/settings/encryption/ChangeRecoveryKey-test.tsx
new file mode 100644
index 0000000000..5d67b4e6b3
--- /dev/null
+++ b/test/unit-tests/components/views/settings/encryption/ChangeRecoveryKey-test.tsx
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2024 New Vector Ltd.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
+ * 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("", () => {
+ let matrixClient: MatrixClient;
+
+ beforeEach(() => {
+ matrixClient = createTestClient();
+ });
+
+ function renderComponent(isSetupFlow = false, onFinish = jest.fn(), onCancelClick = jest.fn()) {
+ return render(
+ ,
+ 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(true, 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(true, 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(true, 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();
+ });
+ });
+});
diff --git a/test/unit-tests/components/views/settings/encryption/__snapshots__/ChangeRecoveryKey-test.tsx.snap b/test/unit-tests/components/views/settings/encryption/__snapshots__/ChangeRecoveryKey-test.tsx.snap
new file mode 100644
index 0000000000..ee18396e44
--- /dev/null
+++ b/test/unit-tests/components/views/settings/encryption/__snapshots__/ChangeRecoveryKey-test.tsx.snap
@@ -0,0 +1,725 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` flow to change the recovery key should display the recovery key 1`] = `
+
+
+
+
+
+
+
+
+ Change recovery key?
+
+
+ Get a new recovery key if you've lost your existing one. After changing your recovery key, your old one will no longer work.
+
+
+
+
+ Recovery key
+
+
+
+ encoded private key
+
+
+ Do not share this with anyone!
+
+
+
+
+
+
+
+`;
+
+exports[` flow to setup a recovery key should ask the user to enter the recovery key 1`] = `
+
+
+
+
+
+
+
+
+ Enter your recovery key to confirm
+
+
+ Enter the recovery key shown on the previous screen to finish setting up recovery.
+
+
+
+
+
+`;
+
+exports[` flow to setup a recovery key should ask the user to enter the recovery key 2`] = `
+
+
+
+
+
+
+
+
+ Enter your recovery key to confirm
+
+
+ Enter the recovery key shown on the previous screen to finish setting up recovery.
+
+
+
+
+
+`;
+
+exports[` flow to setup a recovery key should display information about the recovery key 1`] = `
+
+
+
+
+
+
+
+
+ Set up recovery
+
+
+ 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’.
+
+
+
+ After clicking continue, we’ll generate a recovery key for you.
+
+
+
+
+`;
+
+exports[` flow to setup a recovery key should display the recovery key 1`] = `
+
+
+
+
+
+
+
+
+ Save your recovery key somewhere safe
+
+
+ Write down this recovery key somewhere safe, like a password manager, encrypted note, or a physical safe.
+
+
+
+
+ Recovery key
+
+
+
+ encoded private key
+
+
+ Do not share this with anyone!
+
+