/* Copyright 2024 New Vector Ltd. Copyright 2020-2022 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, { ComponentProps } from "react"; import { SecretStorage, MatrixClient } from "matrix-js-sdk/src/matrix"; import { act, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Mocked } from "jest-mock"; import { getMockClientWithEventEmitter, mockPlatformPeg } from "../../../test-utils"; import AccessSecretStorageDialog from "../../../../src/components/views/dialogs/security/AccessSecretStorageDialog"; const securityKey = "EsTc WKmb ivvk jLS7 Y1NH 5CcQ mP1E JJwj B3Fd pFWm t4Dp dbyu"; describe("AccessSecretStorageDialog", () => { let mockClient: Mocked; const defaultProps: ComponentProps = { keyInfo: {} as any, onFinished: jest.fn(), checkPrivateKey: jest.fn(), }; const renderComponent = (props = {}): void => { render(); }; const enterSecurityKey = (placeholder = "Security Key"): void => { act(() => { fireEvent.change(screen.getByPlaceholderText(placeholder), { target: { value: securityKey, }, }); // wait for debounce jest.advanceTimersByTime(250); }); }; const submitDialog = async (): Promise => { await userEvent.click(screen.getByText("Continue"), { delay: null }); }; beforeAll(() => { jest.useFakeTimers(); mockPlatformPeg(); }); afterAll(() => { jest.useRealTimers(); jest.restoreAllMocks(); }); beforeEach(() => { mockClient = getMockClientWithEventEmitter({ keyBackupKeyFromRecoveryKey: jest.fn(), checkSecretStorageKey: jest.fn(), isValidRecoveryKey: jest.fn(), }); }); it("Closes the dialog when the form is submitted with a valid key", async () => { mockClient.checkSecretStorageKey.mockResolvedValue(true); mockClient.isValidRecoveryKey.mockReturnValue(true); const onFinished = jest.fn(); const checkPrivateKey = jest.fn().mockResolvedValue(true); renderComponent({ onFinished, checkPrivateKey }); // check that the input field is focused expect(screen.getByPlaceholderText("Security Key")).toHaveFocus(); await enterSecurityKey(); await submitDialog(); expect(screen.getByText("Looks good!")).toBeInTheDocument(); expect(checkPrivateKey).toHaveBeenCalledWith({ recoveryKey: securityKey }); expect(onFinished).toHaveBeenCalledWith({ recoveryKey: securityKey }); }); it("Notifies the user if they input an invalid Security Key", async () => { const onFinished = jest.fn(); const checkPrivateKey = jest.fn().mockResolvedValue(true); renderComponent({ onFinished, checkPrivateKey }); mockClient.keyBackupKeyFromRecoveryKey.mockImplementation(() => { throw new Error("that's no key"); }); await enterSecurityKey(); await submitDialog(); expect(screen.getByText("Continue")).toBeDisabled(); expect(screen.getByText("Invalid Security Key")).toBeInTheDocument(); }); it("Notifies the user if they input an invalid passphrase", async function () { const keyInfo = { name: "test", algorithm: "test", iv: "test", mac: "1:2:3:4", passphrase: { // this type is weird in js-sdk // cast 'm.pbkdf2' to itself algorithm: "m.pbkdf2" as SecretStorage.PassphraseInfo["algorithm"], iterations: 2, salt: "nonempty", }, }; const checkPrivateKey = jest.fn().mockResolvedValue(false); renderComponent({ checkPrivateKey, keyInfo }); mockClient.isValidRecoveryKey.mockReturnValue(false); await enterSecurityKey("Security Phrase"); expect(screen.getByPlaceholderText("Security Phrase")).toHaveValue(securityKey); await submitDialog(); await expect( screen.findByText( "👎 Unable to access secret storage. Please verify that you entered the correct Security Phrase.", ), ).resolves.toBeInTheDocument(); expect(screen.getByPlaceholderText("Security Phrase")).toHaveFocus(); }); });