diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index 1809fa91c8c..ad238cd65ff 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -151,6 +151,8 @@ export function createTestClient(): MatrixClient { }, }), isCrossSigningReady: jest.fn().mockResolvedValue(false), + getSessionBackupPrivateKey: jest.fn().mockResolvedValue(null), + isSecretStorageReady: jest.fn().mockResolvedValue(false), }), getPushActionsForEvent: jest.fn(), diff --git a/test/unit-tests/components/views/dialogs/devtools/Crypto-test.tsx b/test/unit-tests/components/views/dialogs/devtools/Crypto-test.tsx new file mode 100644 index 00000000000..9086be63e88 --- /dev/null +++ b/test/unit-tests/components/views/dialogs/devtools/Crypto-test.tsx @@ -0,0 +1,94 @@ +/* + * 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 { MatrixClient } from "matrix-js-sdk/src/matrix"; +import { render, screen, waitFor } from "jest-matrix-react"; +import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api"; + +import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils"; +import { Crypto } from "../../../../../../src/components/views/dialogs/devtools/Crypto"; + +describe("", () => { + let matrixClient: MatrixClient; + beforeEach(() => { + matrixClient = createTestClient(); + }); + + function renderComponent() { + return render(, withClientContextRenderOptions(matrixClient)); + } + + it("should display message if crypto is not available", async () => { + jest.spyOn(matrixClient, "getCrypto").mockReturnValue(undefined); + renderComponent(); + expect(screen.getByText("Cryptographic module is not available")).toBeInTheDocument(); + }); + + describe("", () => { + it("should display loading spinner while loading", async () => { + jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockImplementation(() => new Promise(() => {})); + renderComponent(); + await waitFor(() => expect(screen.getByLabelText("Loading…")).toBeInTheDocument()); + }); + + it("should display when the key storage data are missing", async () => { + renderComponent(); + await waitFor(() => expect(screen.getByRole("table", { name: "Key Storage" })).toBeInTheDocument()); + expect(screen.getByRole("table", { name: "Key Storage" })).toMatchSnapshot(); + }); + + it("should display when the key storage data are available", async () => { + jest.spyOn(matrixClient.getCrypto()!, "getKeyBackupInfo").mockResolvedValue({ + algorithm: "m.megolm_backup.v1", + version: "1", + } as unknown as KeyBackupInfo); + jest.spyOn(matrixClient, "isKeyBackupKeyStored").mockResolvedValue({}); + jest.spyOn(matrixClient.getCrypto()!, "getSessionBackupPrivateKey").mockResolvedValue(new Uint8Array(32)); + jest.spyOn(matrixClient.getCrypto()!, "getActiveSessionBackupVersion").mockResolvedValue("2"); + jest.spyOn(matrixClient.secretStorage, "hasKey").mockResolvedValue(true); + jest.spyOn(matrixClient.getCrypto()!, "isSecretStorageReady").mockResolvedValue(true); + + renderComponent(); + await waitFor(() => expect(screen.getByRole("table", { name: "Key Storage" })).toBeInTheDocument()); + expect(screen.getByRole("table", { name: "Key Storage" })).toMatchSnapshot(); + }); + }); + + describe("", () => { + it("should display loading spinner while loading", async () => { + jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockImplementation( + () => new Promise(() => {}), + ); + renderComponent(); + await waitFor(() => expect(screen.getByLabelText("Loading…")).toBeInTheDocument()); + }); + + it("should display when the cross-signing data are missing", async () => { + renderComponent(); + await waitFor(() => expect(screen.getByRole("table", { name: "Cross-signing" })).toBeInTheDocument()); + expect(screen.getByRole("table", { name: "Cross-signing" })).toMatchSnapshot(); + }); + + it("should display when the cross-signing data are available", async () => { + jest.spyOn(matrixClient.getCrypto()!, "getCrossSigningStatus").mockResolvedValue({ + publicKeysOnDevice: true, + privateKeysInSecretStorage: true, + privateKeysCachedLocally: { + masterKey: true, + selfSigningKey: true, + userSigningKey: true, + }, + }); + jest.spyOn(matrixClient.getCrypto()!, "isCrossSigningReady").mockResolvedValue(true); + + renderComponent(); + await waitFor(() => expect(screen.getByRole("table", { name: "Cross-signing" })).toBeInTheDocument()); + expect(screen.getByRole("table", { name: "Cross-signing" })).toMatchSnapshot(); + }); + }); +}); diff --git a/test/unit-tests/components/views/dialogs/devtools/__snapshots__/Crypto-test.tsx.snap b/test/unit-tests/components/views/dialogs/devtools/__snapshots__/Crypto-test.tsx.snap new file mode 100644 index 00000000000..c1f3f56902c --- /dev/null +++ b/test/unit-tests/components/views/dialogs/devtools/__snapshots__/Crypto-test.tsx.snap @@ -0,0 +1,289 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should display when the cross-signing data are available 1`] = ` + + + Cross-signing + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Cross-signing status: + + Cross-signing is ready for use. +
+ Cross-signing public keys: + + in memory +
+ Cross-signing private keys: + + in secret storage +
+ Master private key: + + cached locally +
+ Self signing private key: + + cached locally +
+ User signing private key: + + cached locally +
+`; + +exports[` should display when the cross-signing data are missing 1`] = ` + + + Cross-signing + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Cross-signing status: + + Cross-signing is not set up. +
+ Cross-signing public keys: + + not found +
+ Cross-signing private keys: + + not found in storage +
+ Master private key: + + not found locally +
+ Self signing private key: + + not found locally +
+ User signing private key: + + not found locally +
+`; + +exports[` should display when the key storage data are available 1`] = ` + + + Key Storage + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Latest backup version on server: + + 1 (Algorithm: m.megolm_backup.v1) +
+ Backup key stored: + + in secret storage +
+ Active backup version: + + 2 +
+ Backup key cached: + + cached locally, well formed +
+ Secret storage public key: + + in account data +
+ Secret storage: + + ready +
+`; + +exports[` should display when the key storage data are missing 1`] = ` + + + Key Storage + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Latest backup version on server: + + Your keys are not being backed up from this session. +
+ Backup key stored: + + not stored +
+ Active backup version: + + None +
+ Backup key cached: + + not found locally, unexpected type +
+ Secret storage public key: + + not found +
+ Secret storage: + + not ready +
+`;