/*
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, screen, within } from "@testing-library/react";
import { mocked } from "jest-mock";
import {
flushPromises,
getMockClientWithEventEmitter,
mockClientMethodsCrypto,
mockClientMethodsUser,
} from "../../../test-utils";
import SecureBackupPanel from "../../../../src/components/views/settings/SecureBackupPanel";
import { accessSecretStorage } from "../../../../src/SecurityManager";
jest.mock("../../../../src/SecurityManager", () => ({
accessSecretStorage: jest.fn(),
}));
describe("", () => {
const userId = "@alice:server.org";
const client = getMockClientWithEventEmitter({
...mockClientMethodsUser(userId),
...mockClientMethodsCrypto(),
getKeyBackupVersion: jest.fn().mockReturnValue("1"),
getClientWellKnown: jest.fn(),
});
const getComponent = () => render();
beforeEach(() => {
client.getKeyBackupVersion.mockResolvedValue({
version: "1",
algorithm: "test",
auth_data: {
public_key: "1234",
},
});
Object.assign(client.getCrypto()!, {
isKeyBackupTrusted: jest.fn().mockResolvedValue({
trusted: false,
matchesDecryptionKey: false,
}),
getActiveSessionBackupVersion: jest.fn().mockResolvedValue(null),
deleteKeyBackupVersion: jest.fn().mockResolvedValue(undefined),
});
mocked(client.secretStorage.hasKey).mockClear().mockResolvedValue(false);
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();
});
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);
getComponent();
// flush getKeyBackupVersion promise
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 () => {
mocked(client.getCrypto()!).getActiveSessionBackupVersion.mockResolvedValue("1");
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"));
expect(client.getCrypto()!.deleteKeyBackupVersion).not.toHaveBeenCalled();
});
it("deletes backup after confirmation", async () => {
client.getKeyBackupVersion
.mockResolvedValueOnce({
version: "1",
algorithm: "test",
auth_data: {
public_key: "1234",
},
})
.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"));
expect(client.getCrypto()!.deleteKeyBackupVersion).toHaveBeenCalledWith("1");
// delete request
await flushPromises();
// refresh backup info
await flushPromises();
});
it("resets secret storage", async () => {
mocked(client.secretStorage.hasKey).mockClear().mockResolvedValue(true);
getComponent();
// flush checkKeyBackup promise
await flushPromises();
client.getKeyBackupVersion.mockClear();
mocked(client.getCrypto()!).isKeyBackupTrusted.mockClear();
fireEvent.click(screen.getByText("Reset"));
// enter loading state
expect(accessSecretStorage).toHaveBeenCalled();
await flushPromises();
// backup status refreshed
expect(client.getKeyBackupVersion).toHaveBeenCalled();
expect(client.getCrypto()!.isKeyBackupTrusted).toHaveBeenCalled();
});
});