Merge matrix-react-sdk into element-web
Merge remote-tracking branch 'repomerge/t3chguy/repomerge' into t3chguy/repo-merge Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
commit
f0ee7f7905
3265 changed files with 484599 additions and 699 deletions
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render, screen, waitFor } from "jest-matrix-react";
|
||||
import React from "react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import CreateKeyBackupDialog from "../../../../../../src/async-components/views/dialogs/security/CreateKeyBackupDialog";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg";
|
||||
|
||||
jest.mock("../../../../../../src/SecurityManager", () => ({
|
||||
accessSecretStorage: jest.fn().mockResolvedValue(undefined),
|
||||
withSecretStorageKeyCache: jest.fn().mockImplementation((fn) => fn()),
|
||||
}));
|
||||
|
||||
describe("CreateKeyBackupDialog", () => {
|
||||
beforeEach(() => {
|
||||
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => createTestClient();
|
||||
});
|
||||
|
||||
it("should display the spinner when creating backup", () => {
|
||||
const { asFragment } = render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
|
||||
|
||||
// Check if the spinner is displayed
|
||||
expect(screen.getByTestId("spinner")).toBeDefined();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display an error message when backup creation failed", async () => {
|
||||
const matrixClient = createTestClient();
|
||||
mocked(matrixClient.hasSecretStorageKey).mockResolvedValue(true);
|
||||
mocked(matrixClient.getCrypto()!.resetKeyBackup).mockImplementation(() => {
|
||||
throw new Error("failed");
|
||||
});
|
||||
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient;
|
||||
|
||||
const { asFragment } = render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
|
||||
|
||||
// Check if the error message is displayed
|
||||
await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display an error message when there is no Crypto available", async () => {
|
||||
const matrixClient = createTestClient();
|
||||
mocked(matrixClient.hasSecretStorageKey).mockResolvedValue(true);
|
||||
mocked(matrixClient.getCrypto).mockReturnValue(undefined);
|
||||
MatrixClientPeg.safeGet = MatrixClientPeg.get = () => matrixClient;
|
||||
|
||||
render(<CreateKeyBackupDialog onFinished={jest.fn()} />);
|
||||
|
||||
// Check if the error message is displayed
|
||||
await waitFor(() => expect(screen.getByText("Unable to create key backup")).toBeDefined());
|
||||
});
|
||||
|
||||
it("should display the success dialog when the key backup is finished", async () => {
|
||||
const onFinished = jest.fn();
|
||||
const { asFragment } = render(<CreateKeyBackupDialog onFinished={onFinished} />);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByText("Your keys are being backed up (the first backup could take a few minutes)."),
|
||||
).toBeDefined(),
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
|
||||
// Click on the OK button
|
||||
screen.getByRole("button", { name: "OK" }).click();
|
||||
expect(onFinished).toHaveBeenCalledWith(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render, RenderResult, screen } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import { Crypto, MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
|
||||
import { defer, IDeferred, sleep } from "matrix-js-sdk/src/utils";
|
||||
import { BackupTrustInfo, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import {
|
||||
filterConsole,
|
||||
flushPromises,
|
||||
getMockClientWithEventEmitter,
|
||||
mockClientMethodsCrypto,
|
||||
mockClientMethodsServer,
|
||||
} from "../../../../../test-utils";
|
||||
import CreateSecretStorageDialog from "../../../../../../src/async-components/views/dialogs/security/CreateSecretStorageDialog";
|
||||
import Modal from "../../../../../../src/Modal";
|
||||
import RestoreKeyBackupDialog from "../../../../../../src/components/views/dialogs/security/RestoreKeyBackupDialog";
|
||||
|
||||
describe("CreateSecretStorageDialog", () => {
|
||||
let mockClient: MockedObject<MatrixClient>;
|
||||
let mockCrypto: MockedObject<Crypto.CryptoApi>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
...mockClientMethodsServer(),
|
||||
...mockClientMethodsCrypto(),
|
||||
uploadDeviceSigningKeys: jest.fn().mockImplementation(async () => {
|
||||
await sleep(0); // CreateSecretStorageDialog doesn't expect this to resolve immediately
|
||||
throw new MatrixError({ flows: [] });
|
||||
}),
|
||||
});
|
||||
|
||||
mockCrypto = mocked(mockClient.getCrypto()!);
|
||||
Object.assign(mockCrypto, {
|
||||
isKeyBackupTrusted: jest.fn(),
|
||||
isDehydrationSupported: jest.fn(() => false),
|
||||
bootstrapCrossSigning: jest.fn(),
|
||||
bootstrapSecretStorage: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
function renderComponent(
|
||||
props: Partial<React.ComponentProps<typeof CreateSecretStorageDialog>> = {},
|
||||
): RenderResult {
|
||||
const onFinished = jest.fn();
|
||||
return render(<CreateSecretStorageDialog onFinished={onFinished} {...props} />);
|
||||
}
|
||||
|
||||
it("shows a loading spinner initially", async () => {
|
||||
const { container } = renderComponent();
|
||||
expect(screen.getByTestId("spinner")).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
describe("when there is an error fetching the backup version", () => {
|
||||
filterConsole("Error fetching backup data from server");
|
||||
|
||||
it("shows an error", async () => {
|
||||
mockClient.getKeyBackupVersion.mockImplementation(async () => {
|
||||
throw new Error("bleh bleh");
|
||||
});
|
||||
|
||||
const result = renderComponent();
|
||||
// XXX the error message is... misleading.
|
||||
await result.findByText("Unable to query secret storage status");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it("shows 'Generate a Security Key' text if no key backup is present", async () => {
|
||||
const result = renderComponent();
|
||||
await flushPromises();
|
||||
expect(result.container).toMatchSnapshot();
|
||||
result.getByText("Generate a Security Key");
|
||||
});
|
||||
|
||||
describe("when canUploadKeysWithPasswordOnly", () => {
|
||||
// spy on Modal.createDialog
|
||||
let modalSpy: jest.SpyInstance;
|
||||
|
||||
// deferred which should be resolved to indicate that the created dialog has completed
|
||||
let restoreDialogFinishedDefer: IDeferred<[done?: boolean]>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
|
||||
mockClient.uploadDeviceSigningKeys.mockImplementation(async () => {
|
||||
await sleep(0);
|
||||
throw new MatrixError({
|
||||
flows: [{ stages: ["m.login.password"] }],
|
||||
});
|
||||
});
|
||||
|
||||
restoreDialogFinishedDefer = defer<[done?: boolean]>();
|
||||
modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue({
|
||||
finished: restoreDialogFinishedDefer.promise,
|
||||
close: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("prompts for a password and then shows RestoreKeyBackupDialog", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
keyCallback: expect.any(Function),
|
||||
showSummary: false,
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
});
|
||||
|
||||
it("calls bootstrapSecretStorage once keys are restored if the backup is now trusted", async () => {
|
||||
mockClient.isCryptoEnabled.mockReturnValue(true);
|
||||
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
|
||||
// While we restore the key backup, its signature becomes accepted
|
||||
mockCrypto.isKeyBackupTrusted.mockResolvedValue({ trusted: true } as BackupTrustInfo);
|
||||
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
await flushPromises();
|
||||
|
||||
// XXX no idea why this is a sensible thing to do. I just work here.
|
||||
expect(mockCrypto.bootstrapCrossSigning).toHaveBeenCalled();
|
||||
expect(mockCrypto.bootstrapSecretStorage).toHaveBeenCalled();
|
||||
|
||||
await result.findByText("Your keys are now being backed up from this device.");
|
||||
});
|
||||
|
||||
describe("when there is an error fetching the backup version after RestoreKeyBackupDialog", () => {
|
||||
filterConsole("Error fetching backup data from server");
|
||||
|
||||
it("handles the error sensibly", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText(/Enter your account password to confirm the upgrade/);
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
await userEvent.type(result.getByPlaceholderText("Password"), "my pass");
|
||||
result.getByRole("button", { name: "Next" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalled();
|
||||
|
||||
mockClient.getKeyBackupVersion.mockImplementation(async () => {
|
||||
throw new Error("bleh bleh");
|
||||
});
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
await result.findByText("Unable to query secret storage status");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when backup is present but not trusted", () => {
|
||||
beforeEach(() => {
|
||||
mockClient.getKeyBackupVersion.mockResolvedValue({} as KeyBackupInfo);
|
||||
});
|
||||
|
||||
it("shows migrate text, then 'RestoreKeyBackupDialog' if 'Restore' is clicked", async () => {
|
||||
const result = renderComponent();
|
||||
await result.findByText("Restore your key backup to upgrade your encryption");
|
||||
expect(result.container).toMatchSnapshot();
|
||||
|
||||
// before we click "Restore", set up a spy on createDialog
|
||||
const restoreDialogFinishedDefer = defer<[done?: boolean]>();
|
||||
const modalSpy = jest.spyOn(Modal, "createDialog").mockReturnValue({
|
||||
finished: restoreDialogFinishedDefer.promise,
|
||||
close: jest.fn(),
|
||||
});
|
||||
|
||||
result.getByRole("button", { name: "Restore" }).click();
|
||||
|
||||
expect(modalSpy).toHaveBeenCalledWith(
|
||||
RestoreKeyBackupDialog,
|
||||
{
|
||||
keyCallback: expect.any(Function),
|
||||
showSummary: false,
|
||||
},
|
||||
undefined,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
|
||||
// simulate RestoreKeyBackupDialog completing, to run that code path
|
||||
restoreDialogFinishedDefer.resolve([]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 { screen, fireEvent, render, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Crypto, IMegolmSessionData } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import * as MegolmExportEncryption from "../../../../../../src/utils/MegolmExportEncryption";
|
||||
import ExportE2eKeysDialog from "../../../../../../src/async-components/views/dialogs/security/ExportE2eKeysDialog";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
|
||||
describe("ExportE2eKeysDialog", () => {
|
||||
it("renders", () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const { asFragment } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should have disabled submit button initially", () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
||||
expect(screen.getByText("Enter passphrase")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should complain about weak passphrases", async () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
|
||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
const input = screen.getByLabelText("Enter passphrase");
|
||||
await userEvent.type(input, "password");
|
||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
||||
await expect(screen.findByText("This is a top-10 common password")).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should complain if passphrases don't match", async () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
|
||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
await userEvent.type(screen.getByLabelText("Enter passphrase"), "ThisIsAMoreSecurePW123$$");
|
||||
await userEvent.type(screen.getByLabelText("Confirm passphrase"), "ThisIsAMoreSecurePW124$$");
|
||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
||||
await expect(screen.findByText("Passphrases must match")).resolves.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should export if everything is fine", async () => {
|
||||
// Given a client able to export keys
|
||||
const cli = createTestClient();
|
||||
const keys: IMegolmSessionData[] = [];
|
||||
const passphrase = "ThisIsAMoreSecurePW123$$";
|
||||
const exportRoomKeysAsJson = jest.fn().mockResolvedValue(JSON.stringify(keys));
|
||||
cli.getCrypto = () => {
|
||||
return {
|
||||
exportRoomKeysAsJson,
|
||||
} as unknown as Crypto.CryptoApi;
|
||||
};
|
||||
|
||||
// Mock the result of encrypting the sessions. If we don't do this, the
|
||||
// encryption process fails, possibly because we didn't initialise
|
||||
// something.
|
||||
jest.spyOn(MegolmExportEncryption, "encryptMegolmKeyFile").mockResolvedValue(new ArrayBuffer(3));
|
||||
|
||||
// When we tell the dialog to export
|
||||
const { container } = render(<ExportE2eKeysDialog matrixClient={cli} onFinished={jest.fn()} />);
|
||||
await userEvent.type(screen.getByLabelText("Enter passphrase"), passphrase);
|
||||
await userEvent.type(screen.getByLabelText("Confirm passphrase"), passphrase);
|
||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
||||
|
||||
// Then it exports keys and encrypts them
|
||||
await waitFor(() => expect(exportRoomKeysAsJson).toHaveBeenCalled());
|
||||
await waitFor(() =>
|
||||
expect(MegolmExportEncryption.encryptMegolmKeyFile).toHaveBeenCalledWith(JSON.stringify(keys), passphrase),
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 { fireEvent, render, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { Crypto } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import ImportE2eKeysDialog from "../../../../../../src/async-components/views/dialogs/security/ImportE2eKeysDialog";
|
||||
import * as MegolmExportEncryption from "../../../../../../src/utils/MegolmExportEncryption";
|
||||
import { createTestClient } from "../../../../../test-utils";
|
||||
|
||||
describe("ImportE2eKeysDialog", () => {
|
||||
it("renders", () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const { asFragment } = render(<ImportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should have disabled submit button initially", () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const { container } = render(<ImportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
expect(container.querySelector("[type=submit]")!).toBeDisabled();
|
||||
});
|
||||
|
||||
it("should enable submit once file is uploaded and passphrase typed in", () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const file = new File(["test"], "file.txt", { type: "text/plain" });
|
||||
|
||||
const { container } = render(<ImportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
fireEvent.change(container.querySelector("[type=file]")!, {
|
||||
target: { files: [file] },
|
||||
});
|
||||
fireEvent.change(container.querySelector("[type=password]")!, {
|
||||
target: { value: "passphrase" },
|
||||
});
|
||||
expect(container.querySelector("[type=submit]")!).toBeEnabled();
|
||||
});
|
||||
|
||||
it("should enable submit once file is uploaded and passphrase pasted in", async () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const file = new File(["test"], "file.txt", { type: "text/plain" });
|
||||
|
||||
const { container } = render(<ImportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
fireEvent.change(container.querySelector("[type=file]")!, {
|
||||
target: { files: [file] },
|
||||
});
|
||||
await userEvent.click(container.querySelector("[type=password]")!);
|
||||
await userEvent.paste("passphrase");
|
||||
expect(container.querySelector("[type=submit]")!).toBeEnabled();
|
||||
});
|
||||
|
||||
it("should import exported keys on submit", async () => {
|
||||
const cli = createTestClient();
|
||||
const onFinished = jest.fn();
|
||||
const file = new File(["test"], "file.txt", { type: "text/plain" });
|
||||
const importRoomKeysAsJson = jest.fn();
|
||||
cli.getCrypto = () => {
|
||||
return {
|
||||
importRoomKeysAsJson,
|
||||
} as unknown as Crypto.CryptoApi;
|
||||
};
|
||||
|
||||
// Mock the result of decrypting the sessions, to avoid needing to
|
||||
// create encrypted input data.
|
||||
jest.spyOn(MegolmExportEncryption, "decryptMegolmKeyFile").mockResolvedValue("[]");
|
||||
|
||||
const { container } = render(<ImportE2eKeysDialog matrixClient={cli} onFinished={onFinished} />);
|
||||
fireEvent.change(container.querySelector("[type=file]")!, {
|
||||
target: { files: [file] },
|
||||
});
|
||||
await userEvent.click(container.querySelector("[type=password]")!);
|
||||
await userEvent.paste("passphrase");
|
||||
fireEvent.click(container.querySelector("[type=submit]")!);
|
||||
|
||||
await waitFor(() => expect(importRoomKeysAsJson).toHaveBeenCalled());
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { screen, render, waitFor } from "jest-matrix-react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
// Needed to be able to mock decodeRecoveryKey
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import * as recoveryKeyModule from "matrix-js-sdk/src/crypto-api/recovery-key";
|
||||
|
||||
import RestoreKeyBackupDialog from "../../../../../../src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx";
|
||||
import { stubClient } from "../../../../../test-utils";
|
||||
|
||||
describe("<RestoreKeyBackupDialog />", () => {
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockReturnValue(new Uint8Array(32));
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display an error when recovery key is invalid", async () => {
|
||||
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockImplementation(() => {
|
||||
throw new Error("Invalid recovery key");
|
||||
});
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "invalid key");
|
||||
await waitFor(() => expect(screen.getByText("👎 Not a valid Security Key")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not raise an error when recovery is valid", async () => {
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "valid key");
|
||||
await waitFor(() => expect(screen.getByText("👍 This looks like a valid Security Key!")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,168 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CreateKeyBackupDialog should display an error message when backup creation failed 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Starting backup…
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
Unable to create key backup
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`CreateKeyBackupDialog should display the spinner when creating backup 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Starting backup…
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`CreateKeyBackupDialog should display the success dialog when the key backup is finished 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Success!
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
Your keys are being backed up (the first backup could take a few minutes).
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
OK
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,551 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CreateSecretStorageDialog shows 'Generate a Security Key' text if no key backup is present 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title mx_CreateSecretStorageDialog_centeredTitle"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Set up Secure Backup
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p
|
||||
class="mx_CreateSecretStorageDialog_centeredBody"
|
||||
>
|
||||
Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.
|
||||
</p>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_primaryContainer"
|
||||
role="radiogroup"
|
||||
>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked mx_StyledRadioButton_outlined"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
name="keyPassphrase"
|
||||
type="radio"
|
||||
value="key"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_optionTitle"
|
||||
>
|
||||
<span
|
||||
class="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup"
|
||||
/>
|
||||
Generate a Security Key
|
||||
</div>
|
||||
<div>
|
||||
We'll generate a Security Key for you to store somewhere safe, like a password manager or a safe.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_outlined"
|
||||
>
|
||||
<input
|
||||
name="keyPassphrase"
|
||||
type="radio"
|
||||
value="passphrase"
|
||||
/>
|
||||
<div>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_content"
|
||||
>
|
||||
<div
|
||||
class="mx_CreateSecretStorageDialog_optionTitle"
|
||||
>
|
||||
<span
|
||||
class="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase"
|
||||
/>
|
||||
Enter a Security Phrase
|
||||
</div>
|
||||
<div>
|
||||
Use a secret phrase only you know, and optionally save a Security Key to use for backup.
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_StyledRadioButton_spacer"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog shows a loading spinner initially 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
/>
|
||||
<div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when backup is present but not trusted shows migrate text, then 'RestoreKeyBackupDialog' if 'Restore' is clicked 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Restore your key backup to upgrade your encryption
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Restore
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly calls bootstrapSecretStorage once keys are restored if the backup is now trusted 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly prompts for a password and then shows RestoreKeyBackupDialog 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when canUploadKeysWithPasswordOnly when there is an error fetching the backup version after RestoreKeyBackupDialog handles the error sensibly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Upgrade your encryption
|
||||
</h1>
|
||||
</div>
|
||||
<div>
|
||||
<form>
|
||||
<p>
|
||||
Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.
|
||||
</p>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
Enter your account password to confirm the upgrade:
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_CreateSecretStorageDialog_password"
|
||||
label="Password"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_CreateSecretStorageDialog_password"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
class="danger"
|
||||
type="button"
|
||||
>
|
||||
Skip
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`CreateSecretStorageDialog when there is an error fetching the backup version shows an error 1`] = `
|
||||
<div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_CreateSecretStorageDialog"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
/>
|
||||
<div>
|
||||
<div>
|
||||
<p>
|
||||
Unable to query secret storage status
|
||||
</p>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,112 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ExportE2eKeysDialog renders 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_exportE2eKeysDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Export room keys
|
||||
</h1>
|
||||
</div>
|
||||
<form>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
>
|
||||
<p>
|
||||
This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.
|
||||
</p>
|
||||
<p>
|
||||
The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a unique passphrase below, which will only be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.
|
||||
</p>
|
||||
<div
|
||||
class="error"
|
||||
/>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputTable"
|
||||
>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputRow"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_input mx_PassphraseField"
|
||||
>
|
||||
<input
|
||||
autocomplete="new-password"
|
||||
id="mx_Field_1"
|
||||
label="Enter passphrase"
|
||||
placeholder="Enter passphrase"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_1"
|
||||
>
|
||||
Enter passphrase
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputRow"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
autocomplete="new-password"
|
||||
id="mx_Field_2"
|
||||
label="Confirm passphrase"
|
||||
placeholder="Confirm passphrase"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_2"
|
||||
>
|
||||
Confirm passphrase
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<input
|
||||
class="mx_Dialog_primary"
|
||||
type="submit"
|
||||
value="Export"
|
||||
/>
|
||||
<button>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,113 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ImportE2eKeysDialog renders 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_importE2eKeysDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Import room keys
|
||||
</h1>
|
||||
</div>
|
||||
<form>
|
||||
<div
|
||||
class="mx_Dialog_content"
|
||||
>
|
||||
<p>
|
||||
This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.
|
||||
</p>
|
||||
<p>
|
||||
The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.
|
||||
</p>
|
||||
<div
|
||||
class="error"
|
||||
/>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputTable"
|
||||
>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputRow"
|
||||
>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputLabel"
|
||||
>
|
||||
<label
|
||||
for="importFile"
|
||||
>
|
||||
File to import
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputCell"
|
||||
>
|
||||
<input
|
||||
id="importFile"
|
||||
type="file"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_E2eKeysDialog_inputRow"
|
||||
>
|
||||
<div
|
||||
class="mx_Field mx_Field_input"
|
||||
>
|
||||
<input
|
||||
id="mx_Field_1"
|
||||
label="Enter passphrase"
|
||||
placeholder="Enter passphrase"
|
||||
size="64"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
<label
|
||||
for="mx_Field_1"
|
||||
>
|
||||
Enter passphrase
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<input
|
||||
class="mx_Dialog_primary"
|
||||
disabled=""
|
||||
type="submit"
|
||||
value="Import"
|
||||
/>
|
||||
<button>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -0,0 +1,298 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should display an error when recovery key is invalid 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<strong>
|
||||
Warning
|
||||
</strong>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value="invalid key"
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
>
|
||||
👎 Not a valid Security Key
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should not raise an error when recovery is valid 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<strong>
|
||||
Warning
|
||||
</strong>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value="valid key"
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
>
|
||||
👍 This looks like a valid Security Key!
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should render 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<strong>
|
||||
Warning
|
||||
</strong>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
/>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
Loading…
Add table
Add a link
Reference in a new issue