mv element.io @types __mocks__/ debian docker module_system/ playwright res src test webapp Dockerfile .dockerignore .eslintignore .stylelintrc.cjs babel.config.cjs recorder-worklet-loader.cjs .modernizr.json components.json config.json config.sample.json package.json project.json tsconfig.json tsconfig.module_system.json jest.config.ts playwright.config.ts webpack.config.ts build_config.sample.yaml apps/web/

mkdir apps/web/scripts
mv scripts/{cleanup.sh,ci_package.sh,copy-res.ts,deploy.py,package.sh} apps/web/scripts

And a couple of gitignore tweaks

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-02-24 15:43:58 +00:00
parent e7509c92a1
commit 91a3cb03c1
3408 changed files with 28 additions and 32 deletions
@@ -0,0 +1,99 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { render, screen, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { AdvancedPanel } from "../../../../../../src/components/views/settings/encryption/AdvancedPanel";
import SettingsStore from "../../../../../../src/settings/SettingsStore";
import { SettingLevel } from "../../../../../../src/settings/SettingLevel";
describe("<AdvancedPanel />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
async function renderAdvancedPanel(onResetIdentityClick = jest.fn()) {
const renderResult = render(
<AdvancedPanel onResetIdentityClick={onResetIdentityClick} />,
withClientContextRenderOptions(matrixClient),
);
// Wait for the device keys to be displayed
await waitFor(() => expect(screen.getByText("ed25519")).toBeInTheDocument());
return renderResult;
}
describe("<EncryptionDetails />", () => {
it("should display a spinner when loading the device keys", async () => {
jest.spyOn(matrixClient.getCrypto()!, "getOwnDeviceKeys").mockImplementation(() => new Promise(() => {}));
render(<AdvancedPanel onResetIdentityClick={jest.fn()} />, withClientContextRenderOptions(matrixClient));
expect(screen.getByTestId("encryptionDetails")).toMatchSnapshot();
});
it("should display the device keys", async () => {
await renderAdvancedPanel();
// session id
expect(screen.getByText("ABCDEFGHI")).toBeInTheDocument();
// session key
expect(screen.getByText("ed25519")).toBeInTheDocument();
expect(screen.getByTestId("encryptionDetails")).toMatchSnapshot();
});
it("should call the onResetIdentityClick callback when the reset cryptographic identity button is clicked", async () => {
const user = userEvent.setup();
const onResetIdentityClick = jest.fn();
await renderAdvancedPanel(onResetIdentityClick);
const resetIdentityButton = screen.getByRole("button", { name: "Reset cryptographic identity" });
await user.click(resetIdentityButton);
expect(onResetIdentityClick).toHaveBeenCalled();
});
});
describe("<OtherSettings />", () => {
it("should display the blacklist of unverified devices settings", async () => {
const user = userEvent.setup();
jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true);
jest.spyOn(SettingsStore, "canSetValue").mockReturnValue(true);
jest.spyOn(SettingsStore, "setValue");
await renderAdvancedPanel();
expect(screen.getByTestId("otherSettings")).toMatchSnapshot();
const checkbox = screen.getByRole("switch", {
name: "In encrypted rooms, only send messages to verified users",
});
expect(checkbox).toBeChecked();
await user.click(checkbox);
expect(SettingsStore.setValue).toHaveBeenCalledWith(
"blacklistUnverifiedDevices",
null,
SettingLevel.DEVICE,
false,
);
});
it("should not display the section when the user can not set the value", async () => {
jest.spyOn(SettingsStore, "canSetValue").mockReturnValue(false);
jest.spyOn(SettingsStore, "setValue");
await renderAdvancedPanel();
expect(screen.queryByTestId("otherSettings")).toBeNull();
});
});
});
@@ -0,0 +1,254 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import { ChangeRecoveryKey } from "../../../../../../src/components/views/settings/encryption/ChangeRecoveryKey";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { copyPlaintext } from "../../../../../../src/utils/strings";
import Modal from "../../../../../../src/Modal";
import ErrorDialog from "../../../../../../src/components/views/dialogs/ErrorDialog";
import { DeviceListener } from "../../../../../../src/device-listener";
jest.mock("../../../../../../src/utils/strings", () => ({
copyPlaintext: jest.fn(),
}));
afterEach(() => {
jest.restoreAllMocks();
});
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 set up 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 () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(false);
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.getByTitle("Enter recovery key");
// 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();
const setAccountDataSpy = jest.spyOn(matrixClient, "setAccountData");
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(setAccountDataSpy).toHaveBeenCalledWith("io.element.recovery", { enabled: true });
expect(onFinish).toHaveBeenCalledWith();
});
it("should reset key backup if needed", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(true);
const user = userEvent.setup();
const onFinish = jest.fn();
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());
const finishButton = screen.getByRole("button", { name: "Finish set up" });
const input = screen.getByTitle("Enter recovery key");
// If the user enters the correct recovery key, the finish button should be enabled
await userEvent.type(input, "encoded private key");
await user.click(finishButton);
expect(matrixClient.getCrypto()!.resetKeyBackup).toHaveBeenCalled();
});
it("should not reset key backup if not needed", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(false);
const user = userEvent.setup();
const onFinish = jest.fn();
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());
const finishButton = screen.getByRole("button", { name: "Finish set up" });
const input = screen.getByTitle("Enter recovery key");
// If the user enters the correct recovery key, the finish button should be enabled
await userEvent.type(input, "encoded private key");
await user.click(finishButton);
expect(matrixClient.getCrypto()!.resetKeyBackup).not.toHaveBeenCalled();
});
it("should display errors from bootstrapSecretStorage", async () => {
const consoleErrorSpy = jest.spyOn(console, "error").mockReturnValue(undefined);
mocked(matrixClient.getCrypto()!).bootstrapSecretStorage.mockRejectedValue(new Error("can't bootstrap"));
const user = userEvent.setup();
const { getByRole, getByText, getByTitle } = renderComponent(false);
// Display the recovery key to save
await waitFor(() => user.click(getByRole("button", { name: "Continue" })));
// Display the form to confirm the recovery key
await waitFor(() => user.click(getByRole("button", { name: "Continue" })));
await waitFor(() => expect(getByText("Enter your recovery key to confirm")).toBeInTheDocument());
const finishButton = getByRole("button", { name: "Finish set up" });
const input = getByTitle("Enter recovery key");
await userEvent.type(input, "encoded private key");
await waitFor(() => {
expect(finishButton).not.toBeDisabled();
});
jest.spyOn(Modal, "createDialog");
await user.click(finishButton);
expect(Modal.createDialog).toHaveBeenCalledWith(ErrorDialog, {
title: "Failed to set up secret storage",
description: "Error: can't bootstrap",
});
await waitFor(() => user.click(getByRole("button", { name: "OK" })));
expect(consoleErrorSpy).toHaveBeenCalledWith(
"Failed to set up secret storage:",
new Error("can't bootstrap"),
);
});
it("should disallow repeated attempts to change the recovery key", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(false);
const mockFn = mocked(matrixClient.getCrypto()!).bootstrapSecretStorage.mockImplementation(() => {
// Pretend to do some work.
return new Promise((r) => setTimeout(r, 200));
});
const user = userEvent.setup();
const { getByRole, getByText, getByTitle } = renderComponent(false);
// Display the recovery key to save
await waitFor(() => user.click(getByRole("button", { name: "Continue" })));
// Display the form to confirm the recovery key
await waitFor(() => user.click(getByRole("button", { name: "Continue" })));
await waitFor(() => expect(getByText("Enter your recovery key to confirm")).toBeInTheDocument());
const finishButton = getByRole("button", { name: "Finish set up" });
const input = getByTitle("Enter recovery key");
await userEvent.type(input, "encoded private key");
await waitFor(() => {
expect(finishButton).not.toBeDisabled();
});
await user.click(finishButton);
await user.click(finishButton);
await user.click(finishButton);
await waitFor(() => expect(mockFn).toHaveBeenCalledTimes(1));
});
});
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();
});
});
});
@@ -0,0 +1,95 @@
/*
* 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, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { mocked } from "jest-mock";
import type { MatrixClient } from "matrix-js-sdk/src/matrix";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { DeleteKeyStoragePanel } from "../../../../../../src/components/views/settings/encryption/DeleteKeyStoragePanel";
import { useKeyStoragePanelViewModel } from "../../../../../../src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel";
jest.mock("../../../../../../src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel", () => ({
useKeyStoragePanelViewModel: jest
.fn()
.mockReturnValue({ setEnabled: jest.fn(), isEnabled: true, loading: false, busy: false }),
}));
describe("<DeleteKeyStoragePanel />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
afterEach(() => {
jest.restoreAllMocks();
});
it("should match snapshot", async () => {
const { asFragment } = render(
<DeleteKeyStoragePanel onFinish={() => {}} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
});
it("should call onFinished when cancel pressed", async () => {
const user = userEvent.setup();
const onFinish = jest.fn();
render(<DeleteKeyStoragePanel onFinish={onFinish} />, withClientContextRenderOptions(matrixClient));
await user.click(screen.getByRole("button", { name: "Cancel" }));
expect(onFinish).toHaveBeenCalled();
});
it("should call disable key storage when confirm pressed", async () => {
const setEnabled = jest.fn();
mocked(useKeyStoragePanelViewModel).mockReturnValue({
setEnabled,
isEnabled: true,
loading: false,
busy: false,
});
const user = userEvent.setup();
const onFinish = jest.fn();
render(<DeleteKeyStoragePanel onFinish={onFinish} />, withClientContextRenderOptions(matrixClient));
await user.click(screen.getByRole("button", { name: "Delete key storage" }));
expect(setEnabled).toHaveBeenCalledWith(false);
});
it("should wait with button disabled while setEnabled runs", async () => {
const setEnabledDefer = Promise.withResolvers<void>();
mocked(useKeyStoragePanelViewModel).mockReturnValue({
setEnabled: jest.fn().mockReturnValue(setEnabledDefer.promise),
isEnabled: true,
loading: false,
busy: false,
});
const user = userEvent.setup();
const onFinish = jest.fn();
render(<DeleteKeyStoragePanel onFinish={onFinish} />, withClientContextRenderOptions(matrixClient));
await user.click(screen.getByRole("button", { name: "Delete key storage" }));
expect(onFinish).not.toHaveBeenCalled();
expect(screen.getByRole("button", { name: "Delete key storage" })).toHaveAttribute("aria-disabled", "true");
setEnabledDefer.resolve();
await waitFor(() => expect(onFinish).toHaveBeenCalled());
});
});
@@ -0,0 +1,23 @@
/*
* 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 } from "jest-matrix-react";
import KeyIcon from "@vector-im/compound-design-tokens/assets/web/icons/key-solid";
import { EncryptionCard } from "../../../../../../src/components/views/settings/encryption/EncryptionCard";
describe("<EncryptionCard />", () => {
it("should render", () => {
const { asFragment } = render(
<EncryptionCard Icon={KeyIcon} title="My title" description="My description">
Encryption card children
</EncryptionCard>,
);
expect(asFragment()).toMatchSnapshot();
});
});
@@ -0,0 +1,73 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { render, screen } from "jest-matrix-react";
import { waitFor } from "@testing-library/dom";
import userEvent from "@testing-library/user-event";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
import { RecoveryPanel } from "../../../../../../src/components/views/settings/encryption/RecoveryPanel";
describe("<RecoveryPanel />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
function renderRecoverPanel(onChangeRecoveryKeyClick = jest.fn()) {
return render(
<RecoveryPanel onChangeRecoveryKeyClick={onChangeRecoveryKeyClick} />,
withClientContextRenderOptions(matrixClient),
);
}
it("should be in loading state when checking the recovery key and the cached keys", () => {
jest.spyOn(matrixClient.secretStorage, "getDefaultKeyId").mockImplementation(() => new Promise(() => {}));
const { asFragment } = renderRecoverPanel();
expect(screen.getByLabelText("Loading…")).toBeInTheDocument();
expect(asFragment()).toMatchSnapshot();
});
it("should ask to set up a recovery key when there is no recovery key", async () => {
const user = userEvent.setup();
const onChangeRecoveryKeyClick = jest.fn();
const { asFragment } = renderRecoverPanel(onChangeRecoveryKeyClick);
await waitFor(() => screen.getByRole("button", { name: "Set up recovery" }));
expect(asFragment()).toMatchSnapshot();
await user.click(screen.getByRole("button", { name: "Set up recovery" }));
expect(onChangeRecoveryKeyClick).toHaveBeenCalledWith(true);
});
it("should allow to change the recovery key when everything is good", async () => {
jest.spyOn(matrixClient.secretStorage, "getDefaultKeyId").mockResolvedValue("default key");
jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({
privateKeysInSecretStorage: true,
publicKeysOnDevice: true,
privateKeysCachedLocally: {
masterKey: true,
selfSigningKey: true,
userSigningKey: true,
},
});
const user = userEvent.setup();
const onChangeRecoveryKeyClick = jest.fn();
const { asFragment } = renderRecoverPanel(onChangeRecoveryKeyClick);
await waitFor(() => screen.getByRole("button", { name: "Change recovery key" }));
expect(asFragment()).toMatchSnapshot();
await user.click(screen.getByRole("button", { name: "Change recovery key" }));
expect(onChangeRecoveryKeyClick).toHaveBeenCalledWith(false);
});
});
@@ -0,0 +1,135 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { RecoveryPanelOutOfSync } from "../../../../../../src/components/views/settings/encryption/RecoveryPanelOutOfSync";
import { AccessCancelledError, accessSecretStorage } from "../../../../../../src/SecurityManager";
import { DeviceListener } from "../../../../../../src/device-listener";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
jest.mock("../../../../../../src/SecurityManager", () => {
const originalModule = jest.requireActual("../../../../../../src/SecurityManager");
return {
...originalModule,
accessSecretStorage: jest.fn(),
};
});
describe("<RecoveyPanelOutOfSync />", () => {
let matrixClient: MatrixClient;
function renderComponent(
onFinish = jest.fn(),
onForgotRecoveryKey = jest.fn(),
onAccessSecretStorageFailed = jest.fn(),
) {
matrixClient = createTestClient();
return render(
<RecoveryPanelOutOfSync
onFinish={onFinish}
onForgotRecoveryKey={onForgotRecoveryKey}
onAccessSecretStorageFailed={onAccessSecretStorageFailed}
/>,
withClientContextRenderOptions(matrixClient),
);
}
afterEach(() => {
jest.clearAllMocks();
});
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 () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(false);
const user = userEvent.setup();
mocked(accessSecretStorage).mockImplementation(async (func = async (): Promise<void> => {}) => {
return await func();
});
const onFinish = jest.fn();
renderComponent(onFinish);
await user.click(screen.getByRole("button", { name: "Enter recovery key" }));
expect(accessSecretStorage).toHaveBeenCalled();
expect(onFinish).toHaveBeenCalled();
expect(matrixClient.getCrypto()!.resetKeyBackup).not.toHaveBeenCalled();
});
it("should reset key backup if needed", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(true);
const user = userEvent.setup();
mocked(accessSecretStorage).mockImplementation(async (func = async (): Promise<void> => {}) => {
return await func();
});
const onFinish = jest.fn();
renderComponent(onFinish);
await user.click(screen.getByRole("button", { name: "Enter recovery key" }));
expect(accessSecretStorage).toHaveBeenCalled();
expect(onFinish).toHaveBeenCalled();
expect(matrixClient.getCrypto()!.resetKeyBackup).toHaveBeenCalled();
});
it("should call onAccessSecretStorageFailed on failure", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(true);
const user = userEvent.setup();
mocked(accessSecretStorage).mockImplementation(async (func = async (): Promise<void> => {}) => {
throw new Error("Error");
});
const onAccessSecretStorageFailed = jest.fn();
renderComponent(jest.fn(), jest.fn(), onAccessSecretStorageFailed);
await user.click(screen.getByRole("button", { name: "Enter recovery key" }));
expect(accessSecretStorage).toHaveBeenCalled();
expect(onAccessSecretStorageFailed).toHaveBeenCalled();
});
it("should not call onAccessSecretStorageFailed when cancelled", async () => {
jest.spyOn(DeviceListener.sharedInstance(), "keyStorageOutOfSyncNeedsBackupReset").mockResolvedValue(true);
const user = userEvent.setup();
mocked(accessSecretStorage).mockImplementation(async (func = async (): Promise<void> => {}) => {
throw new AccessCancelledError();
});
const onFinish = jest.fn();
const onAccessSecretStorageFailed = jest.fn();
renderComponent(onFinish, jest.fn(), onAccessSecretStorageFailed);
await user.click(screen.getByRole("button", { name: "Enter recovery key" }));
expect(accessSecretStorage).toHaveBeenCalled();
expect(onFinish).not.toHaveBeenCalled();
expect(onAccessSecretStorageFailed).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,66 @@
/*
* 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 { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
import { render, screen } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import { ResetIdentityPanel } from "../../../../../../src/components/views/settings/encryption/ResetIdentityPanel";
import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils";
describe("<ResetIdentityPanel />", () => {
let matrixClient: MatrixClient;
beforeEach(() => {
matrixClient = createTestClient();
});
it("should reset the encryption when the continue button is clicked", async () => {
const user = userEvent.setup();
const onReset = jest.fn();
const { asFragment } = render(
<ResetIdentityPanel variant="compromised" onReset={onReset} onCancelClick={jest.fn()} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
// We need to pause the reset so that we can check that it's providing
// feedback to the user that something is happening.
const { promise: resetEncryptionPromise, resolve: resolveResetEncryption } = Promise.withResolvers<void>();
jest.spyOn(matrixClient.getCrypto()!, "resetEncryption").mockReturnValue(resetEncryptionPromise);
const continueButton = screen.getByRole("button", { name: "Continue" });
await user.click(continueButton);
expect(asFragment()).toMatchSnapshot();
resolveResetEncryption!();
await sleep(0);
expect(matrixClient.getCrypto()!.resetEncryption).toHaveBeenCalled();
expect(onReset).toHaveBeenCalled();
});
it("should display the 'forgot recovery key' variant correctly", async () => {
const onReset = jest.fn();
const { asFragment } = render(
<ResetIdentityPanel variant="forgot" onReset={onReset} onCancelClick={jest.fn()} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
});
it("should display the 'sync failed' variant correctly", async () => {
const onReset = jest.fn();
const { asFragment } = render(
<ResetIdentityPanel variant="sync_failed" onReset={onReset} onCancelClick={jest.fn()} />,
withClientContextRenderOptions(matrixClient),
);
expect(asFragment()).toMatchSnapshot();
});
});
@@ -0,0 +1,254 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<AdvancedPanel /> <EncryptionDetails /> should display a spinner when loading the device keys 1`] = `
<div
class="mx_EncryptionDetails"
data-testid="encryptionDetails"
>
<div
class="mx_EncryptionDetails_session"
>
<h3
class="mx_EncryptionDetails_session_title"
>
Encryption details
</h3>
<div>
<span>
Session ID:
</span>
<span
data-testid="deviceId"
>
ABCDEFGHI
</span>
</div>
<div>
<span>
Session key:
</span>
<span
data-testid="sessionKey"
>
<svg
aria-label="Loading…"
class="_icon_11k6c_18"
fill="currentColor"
height="1em"
style="width: 20px; height: 20px;"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M12 4.031a8 8 0 1 0 8 8 1 1 0 0 1 2 0c0 5.523-4.477 10-10 10s-10-4.477-10-10 4.477-10 10-10a1 1 0 1 1 0 2"
fill-rule="evenodd"
/>
</svg>
</span>
</div>
</div>
<div
class="mx_EncryptionDetails_buttons"
>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 15.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-3.6-3.6a.95.95 0 0 1-.275-.7q0-.425.275-.7.274-.275.712-.288t.713.263L11 12.15V5q0-.424.287-.713A.97.97 0 0 1 12 4q.424 0 .713.287Q13 4.576 13 5v7.15l1.875-1.875q.274-.274.713-.263.437.014.712.288a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-3.6 3.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063M6 20q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 18v-2q0-.424.287-.713A.97.97 0 0 1 5 15q.424 0 .713.287Q6 15.576 6 16v2h12v-2q0-.424.288-.713A.97.97 0 0 1 19 15q.424 0 .712.287.288.288.288.713v2q0 .824-.587 1.413A1.93 1.93 0 0 1 18 20z"
/>
</svg>
Export keys
</button>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 16a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 15V7.85L9.125 9.725q-.3.3-.7.3T7.7 9.7a.93.93 0 0 1-.288-.713A.98.98 0 0 1 7.7 8.3l3.6-3.6q.15-.15.325-.213.175-.062.375-.062t.375.062a.9.9 0 0 1 .325.213l3.6 3.6q.3.3.287.712a.98.98 0 0 1-.287.688q-.3.3-.713.313a.93.93 0 0 1-.712-.288L13 7.85V15q0 .424-.287.713A.97.97 0 0 1 12 16m-6 4q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 18v-2q0-.424.287-.713A.97.97 0 0 1 5 15q.424 0 .713.287Q6 15.576 6 16v2h12v-2q0-.424.288-.713A.97.97 0 0 1 19 15q.424 0 .712.287.288.288.288.713v2q0 .824-.587 1.413A1.93 1.93 0 0 1 18 20z"
/>
</svg>
Import keys
</button>
</div>
<button
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="tertiary"
data-size="sm"
role="button"
tabindex="0"
>
Reset cryptographic identity
</button>
</div>
`;
exports[`<AdvancedPanel /> <EncryptionDetails /> should display the device keys 1`] = `
<div
class="mx_EncryptionDetails"
data-testid="encryptionDetails"
>
<div
class="mx_EncryptionDetails_session"
>
<h3
class="mx_EncryptionDetails_session_title"
>
Encryption details
</h3>
<div>
<span>
Session ID:
</span>
<span
data-testid="deviceId"
>
ABCDEFGHI
</span>
</div>
<div>
<span>
Session key:
</span>
<span
data-testid="sessionKey"
>
ed25519
</span>
</div>
</div>
<div
class="mx_EncryptionDetails_buttons"
>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 15.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-3.6-3.6a.95.95 0 0 1-.275-.7q0-.425.275-.7.274-.275.712-.288t.713.263L11 12.15V5q0-.424.287-.713A.97.97 0 0 1 12 4q.424 0 .713.287Q13 4.576 13 5v7.15l1.875-1.875q.274-.274.713-.263.437.014.712.288a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-3.6 3.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063M6 20q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 18v-2q0-.424.287-.713A.97.97 0 0 1 5 15q.424 0 .713.287Q6 15.576 6 16v2h12v-2q0-.424.288-.713A.97.97 0 0 1 19 15q.424 0 .712.287.288.288.288.713v2q0 .824-.587 1.413A1.93 1.93 0 0 1 18 20z"
/>
</svg>
Export keys
</button>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 16a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 15V7.85L9.125 9.725q-.3.3-.7.3T7.7 9.7a.93.93 0 0 1-.288-.713A.98.98 0 0 1 7.7 8.3l3.6-3.6q.15-.15.325-.213.175-.062.375-.062t.375.062a.9.9 0 0 1 .325.213l3.6 3.6q.3.3.287.712a.98.98 0 0 1-.287.688q-.3.3-.713.313a.93.93 0 0 1-.712-.288L13 7.85V15q0 .424-.287.713A.97.97 0 0 1 12 16m-6 4q-.824 0-1.412-.587A1.93 1.93 0 0 1 4 18v-2q0-.424.287-.713A.97.97 0 0 1 5 15q.424 0 .713.287Q6 15.576 6 16v2h12v-2q0-.424.288-.713A.97.97 0 0 1 19 15q.424 0 .712.287.288.288.288.713v2q0 .824-.587 1.413A1.93 1.93 0 0 1 18 20z"
/>
</svg>
Import keys
</button>
</div>
<button
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="tertiary"
data-size="sm"
role="button"
tabindex="0"
>
Reset cryptographic identity
</button>
</div>
`;
exports[`<AdvancedPanel /> <OtherSettings /> should display the blacklist of unverified devices settings 1`] = `
<form
class="_root_19upo_16 mx_OtherSettings"
data-testid="otherSettings"
>
<h3
class="mx_OtherSettings_title"
>
Other peoples devices
</h3>
<div
class="_inline-field_19upo_32"
>
<div
class="_inline-field-control_19upo_44"
>
<div
class="_container_udcm8_10"
>
<input
aria-describedby="radix-_r_7_"
checked=""
class="_input_udcm8_24"
id="radix-_r_6_"
name="neverSendEncrypted"
role="switch"
title=""
type="checkbox"
/>
<div
class="_ui_udcm8_34"
/>
</div>
</div>
<div
class="_inline-field-body_19upo_38"
>
<label
class="_label_19upo_59"
for="radix-_r_6_"
>
In encrypted rooms, only send messages to verified users
</label>
<span
class="_message_19upo_85 _help-message_19upo_91"
id="radix-_r_7_"
>
Warning: users who have not explicitly verified with you (e.g. using emoji) will not receive your encrypted messages. Also, unverified devices of verified users will not receive your encrypted messages. Changes require an application restart to take effect.
</span>
</div>
</div>
</form>
`;
@@ -0,0 +1,782 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<ChangeRecoveryKey /> flow to change the recovery key should display the recovery key 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Change recovery key
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard mx_ChangeRecoveryKey"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Change recovery key?
</h2>
<span>
Write down this new recovery key somewhere safe. Then click Continue to confirm the change.
</span>
</div>
<div
class="mx_KeyPanel"
>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60"
>
Recovery key
</span>
<div>
<span
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50 mx_KeyPanel_key"
data-testid="recoveryKey"
>
encoded private key
</span>
<span
class="_typography_6v6n8_153 _font-body-sm-regular_6v6n8_31"
>
Do not share this with anyone!
</span>
</div>
<button
aria-label="Copy"
class="_icon-button_1215g_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</button>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
class="_button_13vu4_8"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
exports[`<ChangeRecoveryKey /> flow to set up a recovery key should ask the user to enter the recovery key 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Set up recovery
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard mx_ChangeRecoveryKey"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Enter your recovery key to confirm
</h2>
<span>
Enter the recovery key shown on the previous screen to finish setting up recovery.
</span>
</div>
<form
class="_root_19upo_16 mx_KeyForm"
>
<div
class="_field_19upo_26"
>
<label
class="_label_19upo_59"
for="radix-_r_0_"
>
Enter recovery key
</label>
<div
class="_container_1s836_8 mx_KeyForm_password mx_no_textinput"
id="_r_1_"
>
<input
class="_control_sqdq4_10 _control_1s836_13"
id="radix-_r_0_"
name="recoveryKey"
required=""
title="Enter recovery key"
type="password"
/>
<button
aria-controls="_r_1_"
aria-labelledby="_r_2_"
class="_action_1s836_24"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m16.1 13.3-1.45-1.45q.225-1.175-.675-2.2t-2.325-.8L10.2 7.4q.424-.2.863-.3A4.2 4.2 0 0 1 12 7q1.875 0 3.188 1.312Q16.5 9.625 16.5 11.5q0 .5-.1.938t-.3.862m3.2 3.15-1.45-1.4a11 11 0 0 0 1.688-1.588A9 9 0 0 0 20.8 11.5q-1.25-2.524-3.588-4.013Q14.875 6 12 6q-.724 0-1.425.1a10 10 0 0 0-1.375.3L7.65 4.85A11.1 11.1 0 0 1 12 4q3.575 0 6.425 1.887T22.7 10.8a.8.8 0 0 1 .1.313q.025.188.025.387a2 2 0 0 1-.125.7 10.9 10.9 0 0 1-3.4 4.25m-.2 5.45-3.5-3.45q-.874.274-1.762.413Q12.95 19 12 19q-3.575 0-6.425-1.887T1.3 12.2a.8.8 0 0 1-.1-.312 3 3 0 0 1 0-.763.8.8 0 0 1 .1-.3Q1.825 9.7 2.55 8.75A13.3 13.3 0 0 1 4.15 7L2.075 4.9a.93.93 0 0 1-.275-.688q0-.412.3-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275l17 17q.275.275.288.688a.93.93 0 0 1-.288.712.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275M5.55 8.4q-.725.65-1.325 1.425A9 9 0 0 0 3.2 11.5q1.25 2.524 3.588 4.012T12 17q.5 0 .975-.062.475-.063.975-.138l-.9-.95q-.274.075-.525.113A3.5 3.5 0 0 1 12 16q-1.875 0-3.187-1.312Q7.5 13.375 7.5 11.5q0-.274.038-.525.037-.25.112-.525z"
/>
</svg>
</button>
</div>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="true"
class="_button_13vu4_8"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Finish set up
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</form>
</div>
</DocumentFragment>
`;
exports[`<ChangeRecoveryKey /> flow to set up a recovery key should ask the user to enter the recovery key 2`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Set up recovery
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard mx_ChangeRecoveryKey"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Enter your recovery key to confirm
</h2>
<span>
Enter the recovery key shown on the previous screen to finish setting up recovery.
</span>
</div>
<form
class="_root_19upo_16 mx_KeyForm"
>
<div
class="_field_19upo_26"
data-invalid="true"
>
<label
class="_label_19upo_59"
data-invalid="true"
for="radix-_r_0_"
>
Enter recovery key
</label>
<div
class="_container_1s836_8 mx_KeyForm_password mx_no_textinput"
id="_r_1_"
>
<input
aria-describedby="radix-_r_8_"
aria-invalid="true"
class="_control_sqdq4_10 _control_1s836_13"
data-invalid="true"
id="radix-_r_0_"
name="recoveryKey"
required=""
title="Enter recovery key"
type="password"
/>
<button
aria-controls="_r_1_"
aria-labelledby="_r_2_"
class="_action_1s836_24"
type="button"
>
<svg
aria-hidden="true"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m16.1 13.3-1.45-1.45q.225-1.175-.675-2.2t-2.325-.8L10.2 7.4q.424-.2.863-.3A4.2 4.2 0 0 1 12 7q1.875 0 3.188 1.312Q16.5 9.625 16.5 11.5q0 .5-.1.938t-.3.862m3.2 3.15-1.45-1.4a11 11 0 0 0 1.688-1.588A9 9 0 0 0 20.8 11.5q-1.25-2.524-3.588-4.013Q14.875 6 12 6q-.724 0-1.425.1a10 10 0 0 0-1.375.3L7.65 4.85A11.1 11.1 0 0 1 12 4q3.575 0 6.425 1.887T22.7 10.8a.8.8 0 0 1 .1.313q.025.188.025.387a2 2 0 0 1-.125.7 10.9 10.9 0 0 1-3.4 4.25m-.2 5.45-3.5-3.45q-.874.274-1.762.413Q12.95 19 12 19q-3.575 0-6.425-1.887T1.3 12.2a.8.8 0 0 1-.1-.312 3 3 0 0 1 0-.763.8.8 0 0 1 .1-.3Q1.825 9.7 2.55 8.75A13.3 13.3 0 0 1 4.15 7L2.075 4.9a.93.93 0 0 1-.275-.688q0-.412.3-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275l17 17q.275.275.288.688a.93.93 0 0 1-.288.712.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275M5.55 8.4q-.725.65-1.325 1.425A9 9 0 0 0 3.2 11.5q1.25 2.524 3.588 4.012T12 17q.5 0 .975-.062.475-.063.975-.138l-.9-.95q-.274.075-.525.113A3.5 3.5 0 0 1 12 16q-1.875 0-3.187-1.312Q7.5 13.375 7.5 11.5q0-.274.038-.525.037-.25.112-.525z"
/>
</svg>
</button>
</div>
<span
class="_message_19upo_85 _error-message_19upo_95"
id="radix-_r_8_"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
The recovery key you entered is not correct.
</span>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="true"
class="_button_13vu4_8"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Finish set up
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</form>
</div>
</DocumentFragment>
`;
exports[`<ChangeRecoveryKey /> flow to set up a recovery key should display information about the recovery key 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Set up recovery
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard mx_ChangeRecoveryKey"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Set up recovery
</h2>
<span>
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.
</span>
</div>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60 mx_InformationPanel_description"
>
After clicking continue, well generate a recovery key for you.
</span>
<div
class="mx_EncryptionCard_buttons"
>
<button
class="_button_13vu4_8"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
exports[`<ChangeRecoveryKey /> flow to set up a recovery key should display the recovery key 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Set up recovery
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard mx_ChangeRecoveryKey"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Save your recovery key somewhere safe
</h2>
<span>
Write down this recovery key somewhere safe, like a password manager, encrypted note, or a physical safe.
</span>
</div>
<div
class="mx_KeyPanel"
>
<span
class="_typography_6v6n8_153 _font-body-md-medium_6v6n8_60"
>
Recovery key
</span>
<div>
<span
class="_typography_6v6n8_153 _font-body-md-regular_6v6n8_50 mx_KeyPanel_key"
data-testid="recoveryKey"
>
encoded private key
</span>
<span
class="_typography_6v6n8_153 _font-body-sm-regular_6v6n8_31"
>
Do not share this with anyone!
</span>
</div>
<button
aria-label="Copy"
class="_icon-button_1215g_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 5H5v9h1a1 1 0 1 1 0 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1a1 1 0 1 1-2 0z"
/>
<path
d="M8 10a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2zm2 0v9h9v-9z"
/>
</svg>
</div>
</button>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
class="_button_13vu4_8"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,157 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<DeleteKeyStoragePanel /> should match snapshot 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Delete key storage
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8 _destructive_1r8kr_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Are you sure you want to turn off key storage and delete it?
</h2>
</div>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
Deleting key storage will remove your cryptographic identity and message keys from the server and turn off the following security features:
<ul
class="_visual-list_1mgc1_8"
>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-destructive_1nc1y_26"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414"
/>
</svg>
You will not have encrypted message history on new devices
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-destructive_1nc1y_26"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414"
/>
</svg>
You will lose access to your encrypted messages if you are signed out of Element everywhere
</li>
</ul>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Delete key storage
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,39 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<EncryptionCard /> should render 1`] = `
<DocumentFragment>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.475 16.9A5.86 5.86 0 0 1 7 18q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q2.026 0 3.537 1.15Q12.05 8.3 12.65 10h7.95q.2 0 .387.075.189.075.338.225l1.025 1.025a1 1 0 0 1 .2.288q.075.162.075.362t-.062.375a1.1 1.1 0 0 1-.188.325l-2.25 2.575a.97.97 0 0 1-1.038.313 1 1 0 0 1-.337-.188L17 14l-1.3 1.3q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L13 14h-.35a5.8 5.8 0 0 1-2.175 2.9m-4.887-3.487Q6.175 14 7 14q.824 0 1.412-.588Q9 12.826 9 12t-.588-1.412A1.93 1.93 0 0 0 7 10q-.824 0-1.412.588A1.93 1.93 0 0 0 5 12q0 .825.588 1.412"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
My title
</h2>
<span>
My description
</span>
</div>
Encryption card children
</div>
</DocumentFragment>
`;
@@ -0,0 +1,119 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<RecoveryPanel /> should allow to change the recovery key when everything is good 1`] = `
<DocumentFragment>
<div
class="mx_SettingsSection mx_SettingsSection_newUi"
data-testid="recoveryPanel"
>
<div
class="mx_SettingsSection_header"
>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93 mx_SettingsHeader"
>
Recovery
</h2>
Recover your cryptographic identity and message history with a recovery key if youve lost all your existing devices.
</div>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 5 12q0-.825.588-1.412A1.93 1.93 0 0 1 7 10q.824 0 1.412.588Q9 11.175 9 12t-.588 1.412A1.93 1.93 0 0 1 7 14m0 4q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q1.676 0 3.037.825A6.2 6.2 0 0 1 12.2 9h8.375q.2 0 .387.075.188.075.338.225l2 2q.15.15.212.325.063.175.063.375t-.062.375a.9.9 0 0 1-.213.325l-3.175 3.175a1 1 0 0 1-.3.2q-.175.075-.35.1a.8.8 0 0 1-.35-.025.9.9 0 0 1-.325-.175L17.5 15l-1.425 1.075a.95.95 0 0 1-.887.15.9.9 0 0 1-.288-.15L13.375 15H12.2a6.2 6.2 0 0 1-2.162 2.175Q8.675 18 7 18m0-2q1.4 0 2.463-.85A4.03 4.03 0 0 0 10.875 13H14l1.45 1.025L17.5 12.5l1.775 1.375L21.15 12l-1-1h-9.275a4.03 4.03 0 0 0-1.412-2.15Q8.4 8 7 8 5.35 8 4.175 9.175T3 12t1.175 2.825T7 16"
/>
</svg>
Change recovery key
</button>
</div>
</DocumentFragment>
`;
exports[`<RecoveryPanel /> should ask to set up a recovery key when there is no recovery key 1`] = `
<DocumentFragment>
<div
class="mx_SettingsSection mx_SettingsSection_newUi"
data-testid="recoveryPanel"
>
<div
class="mx_SettingsSection_header"
>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93 mx_SettingsHeader mx_SettingsHeader_recommended"
>
Recovery
</h2>
Recover your cryptographic identity and message history with a recovery key if youve lost all your existing devices.
</div>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="primary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 5 12q0-.825.588-1.412A1.93 1.93 0 0 1 7 10q.824 0 1.412.588Q9 11.175 9 12t-.588 1.412A1.93 1.93 0 0 1 7 14m0 4q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q1.676 0 3.037.825A6.2 6.2 0 0 1 12.2 9h8.375q.2 0 .387.075.188.075.338.225l2 2q.15.15.212.325.063.175.063.375t-.062.375a.9.9 0 0 1-.213.325l-3.175 3.175a1 1 0 0 1-.3.2q-.175.075-.35.1a.8.8 0 0 1-.35-.025.9.9 0 0 1-.325-.175L17.5 15l-1.425 1.075a.95.95 0 0 1-.887.15.9.9 0 0 1-.288-.15L13.375 15H12.2a6.2 6.2 0 0 1-2.162 2.175Q8.675 18 7 18m0-2q1.4 0 2.463-.85A4.03 4.03 0 0 0 10.875 13H14l1.45 1.025L17.5 12.5l1.775 1.375L21.15 12l-1-1h-9.275a4.03 4.03 0 0 0-1.412-2.15Q8.4 8 7 8 5.35 8 4.175 9.175T3 12t1.175 2.825T7 16"
/>
</svg>
Set up recovery
</button>
</div>
</DocumentFragment>
`;
exports[`<RecoveryPanel /> should be in loading state when checking the recovery key and the cached keys 1`] = `
<DocumentFragment>
<div
class="mx_SettingsSection mx_SettingsSection_newUi"
data-testid="recoveryPanel"
>
<div
class="mx_SettingsSection_header"
>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93 mx_SettingsHeader"
>
Recovery
</h2>
Recover your cryptographic identity and message history with a recovery key if youve lost all your existing devices.
</div>
<svg
aria-label="Loading…"
class="_icon_11k6c_18"
fill="currentColor"
height="1em"
style="width: 20px; height: 20px;"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M12 4.031a8 8 0 1 0 8 8 1 1 0 0 1 2 0c0 5.523-4.477 10-10 10s-10-4.477-10-10 4.477-10 10-10a1 1 0 1 1 0 2"
fill-rule="evenodd"
/>
</svg>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,75 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<RecoveyPanelOutOfSync /> should render 1`] = `
<DocumentFragment>
<div
class="mx_SettingsSection mx_SettingsSection_newUi"
data-testid="recoveryPanel"
>
<div
class="mx_SettingsSection_header"
>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93 mx_SettingsHeader"
>
Recovery
</h2>
<div
class="mx_SettingsSubheader"
>
Recover your cryptographic identity and message history with a recovery key if youve lost all your existing devices.
<span
class="mx_SettingsSubheader_error"
>
<svg
fill="currentColor"
height="20px"
viewBox="0 0 24 24"
width="20px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
Your key storage is out of sync. Click one of the buttons below to fix the problem.
</span>
</div>
</div>
<div
class="mx_RecoveryPanelOutOfSync"
>
<button
class="_button_13vu4_8"
data-kind="secondary"
data-size="sm"
role="button"
tabindex="0"
>
Forgot recovery key?
</button>
<button
class="_button_13vu4_8 _has-icon_13vu4_60"
data-kind="primary"
data-size="sm"
role="button"
tabindex="0"
>
<svg
aria-hidden="true"
fill="currentColor"
height="20"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 5 12q0-.825.588-1.412A1.93 1.93 0 0 1 7 10q.824 0 1.412.588Q9 11.175 9 12t-.588 1.412A1.93 1.93 0 0 1 7 14m0 4q-2.5 0-4.25-1.75T1 12t1.75-4.25T7 6q1.676 0 3.037.825A6.2 6.2 0 0 1 12.2 9h8.375q.2 0 .387.075.188.075.338.225l2 2q.15.15.212.325.063.175.063.375t-.062.375a.9.9 0 0 1-.213.325l-3.175 3.175a1 1 0 0 1-.3.2q-.175.075-.35.1a.8.8 0 0 1-.35-.025.9.9 0 0 1-.325-.175L17.5 15l-1.425 1.075a.95.95 0 0 1-.887.15.9.9 0 0 1-.288-.15L13.375 15H12.2a6.2 6.2 0 0 1-2.162 2.175Q8.675 18 7 18m0-2q1.4 0 2.463-.85A4.03 4.03 0 0 0 10.875 13H14l1.45 1.025L17.5 12.5l1.775 1.375L21.15 12l-1-1h-9.275a4.03 4.03 0 0 0-1.412-2.15Q8.4 8 7 8 5.35 8 4.175 9.175T3 12t1.175 2.825T7 16"
/>
</svg>
Enter recovery key
</button>
</div>
</div>
</DocumentFragment>
`;
@@ -0,0 +1,755 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`<ResetIdentityPanel /> should display the 'forgot recovery key' variant correctly 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Reset encryption
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8 _destructive_1r8kr_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Forgot your recovery key? Youll need to reset your identity.
</h2>
</div>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<ul
class="_visual-list_1mgc1_8"
>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-success_1nc1y_22"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
Your account details, contacts, preferences, and chat list will be kept
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will lose any message history thats stored only on the server
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will need to verify all your existing devices and contacts again
</li>
</ul>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
exports[`<ResetIdentityPanel /> should display the 'sync failed' variant correctly 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Reset encryption
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8 _destructive_1r8kr_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Failed to sync key storage. You need to reset your identity.
</h2>
</div>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<ul
class="_visual-list_1mgc1_8"
>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-success_1nc1y_22"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
Your account details, contacts, preferences, and chat list will be kept
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will lose any message history thats stored only on the server
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will need to verify all your existing devices and contacts again
</li>
</ul>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
exports[`<ResetIdentityPanel /> should reset the encryption when the continue button is clicked 1`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Reset encryption
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8 _destructive_1r8kr_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Are you sure you want to reset your identity?
</h2>
</div>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<ul
class="_visual-list_1mgc1_8"
>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-success_1nc1y_22"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
Your account details, contacts, preferences, and chat list will be kept
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will lose any message history thats stored only on the server
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will need to verify all your existing devices and contacts again
</li>
</ul>
<span>
Only do this if you believe your account has been compromised.
</span>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="false"
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
Continue
</button>
<button
class="_button_13vu4_8"
data-kind="tertiary"
data-size="lg"
role="button"
tabindex="0"
>
Cancel
</button>
</div>
</div>
</DocumentFragment>
`;
exports[`<ResetIdentityPanel /> should reset the encryption when the continue button is clicked 2`] = `
<DocumentFragment>
<nav
class="_breadcrumb_1xygz_8"
>
<button
aria-label="Back"
class="_icon-button_1215g_8"
data-kind="secondary"
role="button"
style="--cpd-icon-button-size: 28px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m13.3 17.3-4.6-4.6a.9.9 0 0 1-.213-.325A1.1 1.1 0 0 1 8.425 12q0-.2.062-.375A.9.9 0 0 1 8.7 11.3l4.6-4.6a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L10.8 12l3.9 3.9a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275"
/>
</svg>
</div>
</button>
<ol
class="_pages_1xygz_17"
>
<li>
<a
class="_link_1v5rz_8"
data-kind="primary"
data-size="small"
rel="noreferrer noopener"
role="button"
tabindex="0"
>
Encryption
</a>
</li>
<li>
<span
aria-current="page"
class="_last-page_1xygz_30"
>
Reset encryption
</span>
</li>
</ol>
</nav>
<div
class="mx_EncryptionCard"
>
<div
class="mx_EncryptionCard_header"
>
<div
class="_content_1r8kr_8 _destructive_1r8kr_34"
data-size="large"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
/>
</svg>
</div>
<h2
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
>
Are you sure you want to reset your identity?
</h2>
</div>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<ul
class="_visual-list_1mgc1_8"
>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17 _visual-list-item-icon-success_1nc1y_22"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
Your account details, contacts, preferences, and chat list will be kept
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will lose any message history thats stored only on the server
</li>
<li
class="_visual-list-item_1nc1y_8"
>
<svg
aria-hidden="true"
class="_visual-list-item-icon_1nc1y_17"
fill="currentColor"
height="24px"
viewBox="0 0 24 24"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.288 7.288A.97.97 0 0 1 12 7q.424 0 .713.287Q13 7.576 13 8t-.287.713A.97.97 0 0 1 12 9a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 8q0-.424.287-.713m.001 4.001A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713v4q0 .424-.287.712A.97.97 0 0 1 12 17a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 16v-4q0-.424.287-.713"
/>
<path
clip-rule="evenodd"
d="M22 12c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2s10 4.477 10 10m-2 0a8 8 0 1 1-16 0 8 8 0 0 1 16 0"
fill-rule="evenodd"
/>
</svg>
You will need to verify all your existing devices and contacts again
</li>
</ul>
<span>
Only do this if you believe your account has been compromised.
</span>
</div>
<div
class="mx_EncryptionCard_buttons"
>
<button
aria-disabled="true"
class="_button_13vu4_8 _destructive_13vu4_110"
data-kind="primary"
data-size="lg"
role="button"
tabindex="0"
>
<svg
class="_icon_11k6c_18"
fill="currentColor"
height="1em"
style="width: 20px; height: 20px;"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M12 4.031a8 8 0 1 0 8 8 1 1 0 0 1 2 0c0 5.523-4.477 10-10 10s-10-4.477-10-10 4.477-10 10-10a1 1 0 1 1 0 2"
fill-rule="evenodd"
/>
</svg>
Reset in progress...
</button>
<div
class="_flex_4dswl_9 mx_EncryptionCard_emphasisedContent"
style="--mx-flex-display: flex; --mx-flex-direction: column; --mx-flex-align: normal; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<span
class="mx_ResetIdentityPanel_warning"
>
Do not close this window until the reset is finished
</span>
</div>
</div>
</div>
</DocumentFragment>
`;