2023-05-05 08:13:50 +00:00
|
|
|
/*
|
2024-09-09 13:57:16 +00:00
|
|
|
Copyright 2024 New Vector Ltd.
|
2023-05-05 08:13:50 +00:00
|
|
|
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
|
|
|
2024-09-09 13:57:16 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2023-05-05 08:13:50 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
import React from "react";
|
2024-10-14 16:11:58 +00:00
|
|
|
import { fireEvent, render, screen, within } from "jest-matrix-react";
|
2023-05-05 08:13:50 +00:00
|
|
|
import { mocked } from "jest-mock";
|
|
|
|
|
2023-07-28 09:24:28 +00:00
|
|
|
import {
|
|
|
|
flushPromises,
|
|
|
|
getMockClientWithEventEmitter,
|
|
|
|
mockClientMethodsCrypto,
|
|
|
|
mockClientMethodsUser,
|
|
|
|
} from "../../../test-utils";
|
2023-05-05 08:13:50 +00:00
|
|
|
import SecureBackupPanel from "../../../../src/components/views/settings/SecureBackupPanel";
|
|
|
|
import { accessSecretStorage } from "../../../../src/SecurityManager";
|
|
|
|
|
|
|
|
jest.mock("../../../../src/SecurityManager", () => ({
|
|
|
|
accessSecretStorage: jest.fn(),
|
|
|
|
}));
|
|
|
|
|
|
|
|
describe("<SecureBackupPanel />", () => {
|
|
|
|
const userId = "@alice:server.org";
|
|
|
|
const client = getMockClientWithEventEmitter({
|
|
|
|
...mockClientMethodsUser(userId),
|
2023-07-28 09:24:28 +00:00
|
|
|
...mockClientMethodsCrypto(),
|
2023-05-05 08:13:50 +00:00
|
|
|
getKeyBackupVersion: jest.fn().mockReturnValue("1"),
|
|
|
|
getClientWellKnown: jest.fn(),
|
|
|
|
});
|
|
|
|
|
|
|
|
const getComponent = () => render(<SecureBackupPanel />);
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2023-09-21 12:19:38 +00:00
|
|
|
client.getKeyBackupVersion.mockResolvedValue({
|
|
|
|
version: "1",
|
|
|
|
algorithm: "test",
|
|
|
|
auth_data: {
|
|
|
|
public_key: "1234",
|
2023-05-05 08:13:50 +00:00
|
|
|
},
|
|
|
|
});
|
2023-09-22 10:57:11 +00:00
|
|
|
Object.assign(client.getCrypto()!, {
|
|
|
|
isKeyBackupTrusted: jest.fn().mockResolvedValue({
|
|
|
|
trusted: false,
|
|
|
|
matchesDecryptionKey: false,
|
|
|
|
}),
|
|
|
|
getActiveSessionBackupVersion: jest.fn().mockResolvedValue(null),
|
|
|
|
deleteKeyBackupVersion: jest.fn().mockResolvedValue(undefined),
|
2023-09-21 12:19:38 +00:00
|
|
|
});
|
2023-05-05 08:13:50 +00:00
|
|
|
|
2023-05-23 12:01:27 +00:00
|
|
|
mocked(client.secretStorage.hasKey).mockClear().mockResolvedValue(false);
|
2023-05-05 08:13:50 +00:00
|
|
|
client.getKeyBackupVersion.mockClear();
|
|
|
|
|
|
|
|
mocked(accessSecretStorage).mockClear().mockResolvedValue();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("displays a loader while checking keybackup", async () => {
|
|
|
|
getComponent();
|
|
|
|
expect(screen.getByRole("progressbar")).toBeInTheDocument();
|
|
|
|
await flushPromises();
|
|
|
|
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
2023-09-21 12:19:38 +00:00
|
|
|
it("handles error fetching backup", async () => {
|
|
|
|
// getKeyBackupVersion can fail for various reasons
|
|
|
|
client.getKeyBackupVersion.mockImplementation(async () => {
|
|
|
|
throw new Error("beep beep");
|
|
|
|
});
|
|
|
|
const renderResult = getComponent();
|
|
|
|
await renderResult.findByText("Unable to load key backup status");
|
|
|
|
expect(renderResult.container).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("handles absence of backup", async () => {
|
|
|
|
client.getKeyBackupVersion.mockResolvedValue(null);
|
2023-05-05 08:13:50 +00:00
|
|
|
getComponent();
|
2023-09-21 12:19:38 +00:00
|
|
|
// flush getKeyBackupVersion promise
|
2023-05-05 08:13:50 +00:00
|
|
|
await flushPromises();
|
|
|
|
expect(screen.getByText("Back up your keys before signing out to avoid losing them.")).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("suggests connecting session to key backup when backup exists", async () => {
|
|
|
|
const { container } = getComponent();
|
|
|
|
// flush checkKeyBackup promise
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
expect(container).toMatchSnapshot();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("displays when session is connected to key backup", async () => {
|
2023-09-22 10:57:11 +00:00
|
|
|
mocked(client.getCrypto()!).getActiveSessionBackupVersion.mockResolvedValue("1");
|
2023-05-05 08:13:50 +00:00
|
|
|
getComponent();
|
|
|
|
// flush checkKeyBackup promise
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
expect(screen.getByText("✅ This session is backing up your keys.")).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("asks for confirmation before deleting a backup", async () => {
|
|
|
|
getComponent();
|
|
|
|
// flush checkKeyBackup promise
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
fireEvent.click(screen.getByText("Delete Backup"));
|
|
|
|
|
|
|
|
const dialog = await screen.findByRole("dialog");
|
|
|
|
|
|
|
|
expect(
|
|
|
|
within(dialog).getByText(
|
|
|
|
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.",
|
|
|
|
),
|
|
|
|
).toBeInTheDocument();
|
|
|
|
|
|
|
|
fireEvent.click(within(dialog).getByText("Cancel"));
|
|
|
|
|
2023-09-22 10:57:11 +00:00
|
|
|
expect(client.getCrypto()!.deleteKeyBackupVersion).not.toHaveBeenCalled();
|
2023-05-05 08:13:50 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it("deletes backup after confirmation", async () => {
|
2023-09-21 12:19:38 +00:00
|
|
|
client.getKeyBackupVersion
|
2023-05-05 08:13:50 +00:00
|
|
|
.mockResolvedValueOnce({
|
2023-09-21 12:19:38 +00:00
|
|
|
version: "1",
|
|
|
|
algorithm: "test",
|
|
|
|
auth_data: {
|
|
|
|
public_key: "1234",
|
2023-05-05 08:13:50 +00:00
|
|
|
},
|
|
|
|
})
|
|
|
|
.mockResolvedValue(null);
|
|
|
|
getComponent();
|
|
|
|
// flush checkKeyBackup promise
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
fireEvent.click(screen.getByText("Delete Backup"));
|
|
|
|
|
|
|
|
const dialog = await screen.findByRole("dialog");
|
|
|
|
|
|
|
|
expect(
|
|
|
|
within(dialog).getByText(
|
|
|
|
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.",
|
|
|
|
),
|
|
|
|
).toBeInTheDocument();
|
|
|
|
|
|
|
|
fireEvent.click(within(dialog).getByTestId("dialog-primary-button"));
|
|
|
|
|
2023-09-22 10:57:11 +00:00
|
|
|
expect(client.getCrypto()!.deleteKeyBackupVersion).toHaveBeenCalledWith("1");
|
2023-05-05 08:13:50 +00:00
|
|
|
|
|
|
|
// delete request
|
|
|
|
await flushPromises();
|
|
|
|
// refresh backup info
|
|
|
|
await flushPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("resets secret storage", async () => {
|
2023-05-23 12:01:27 +00:00
|
|
|
mocked(client.secretStorage.hasKey).mockClear().mockResolvedValue(true);
|
2023-05-05 08:13:50 +00:00
|
|
|
getComponent();
|
|
|
|
// flush checkKeyBackup promise
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
client.getKeyBackupVersion.mockClear();
|
2023-09-22 10:57:11 +00:00
|
|
|
mocked(client.getCrypto()!).isKeyBackupTrusted.mockClear();
|
2023-05-05 08:13:50 +00:00
|
|
|
|
|
|
|
fireEvent.click(screen.getByText("Reset"));
|
|
|
|
|
|
|
|
// enter loading state
|
|
|
|
expect(accessSecretStorage).toHaveBeenCalled();
|
|
|
|
await flushPromises();
|
|
|
|
|
|
|
|
// backup status refreshed
|
|
|
|
expect(client.getKeyBackupVersion).toHaveBeenCalled();
|
2023-09-22 10:57:11 +00:00
|
|
|
expect(client.getCrypto()!.isKeyBackupTrusted).toHaveBeenCalled();
|
2023-05-05 08:13:50 +00:00
|
|
|
});
|
|
|
|
});
|