diff --git a/apps/desktop/src/build-config.test.ts b/apps/desktop/src/build-config.test.ts new file mode 100644 index 0000000000..2979254c3c --- /dev/null +++ b/apps/desktop/src/build-config.test.ts @@ -0,0 +1,35 @@ +/* +Copyright 2026 Element Creations 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 { expect, describe, it, beforeEach, vi } from "vitest"; +import { fs as memfs, vol } from "memfs"; + +import { getBuildConfig } from "./build-config.js"; + +vi.mock("node:fs", () => ({ default: memfs })); + +beforeEach(() => { + // Reset the state of the in-memory fs + vol.reset(); +}); + +describe("getBuildConfig", () => { + it("should read fields from package.json correctly", () => { + vol.fromJSON({ + "package.json": JSON.stringify({ + electron_appId: "app.id", + electron_protocol: "proto", + electron_windows_cert_sn: "subject.name", + }), + }); + + const config = getBuildConfig(); + expect(config.appId).toBe("app.id"); + expect(config.protocol).toBe("proto"); + expect(config.windowsCertSubjectName).toBe("subject.name"); + }); +}); diff --git a/apps/desktop/src/icon.test.ts b/apps/desktop/src/icon.test.ts new file mode 100644 index 0000000000..35df79e070 --- /dev/null +++ b/apps/desktop/src/icon.test.ts @@ -0,0 +1,44 @@ +/* +Copyright 2026 Element Creations 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 { expect, describe, it, beforeEach, vi } from "vitest"; +import { fs as memfs, vol } from "memfs"; +import path from "node:path"; + +import { getIconPath } from "./icon.js"; + +vi.mock("node:fs/promises", () => ({ default: memfs.promises })); + +beforeEach(() => { + // Reset the state of the in-memory fs + vol.reset(); +}); + +describe("getIconPath", () => { + beforeEach(() => { + vol.fromJSON( + { + "build/icon.png": "png", + "build/icon.ico": "ico", + }, + "../webapp", + ); + }); + + it("should use .ico on Windows", async () => { + vi.spyOn(process, "platform", "get").mockReturnValue("win32"); + await expect(getIconPath()).resolves.toEqual(path.resolve("../build/icon.ico")); + }); + it("should use .png on macOS", async () => { + vi.spyOn(process, "platform", "get").mockReturnValue("darwin"); + await expect(getIconPath()).resolves.toEqual(path.resolve("../build/icon.png")); + }); + it("should use .png on Linux", async () => { + vi.spyOn(process, "platform", "get").mockReturnValue("linux"); + await expect(getIconPath()).resolves.toEqual(path.resolve("../build/icon.png")); + }); +}); diff --git a/apps/desktop/src/utils.test.ts b/apps/desktop/src/utils.test.ts new file mode 100644 index 0000000000..aa099c6f65 --- /dev/null +++ b/apps/desktop/src/utils.test.ts @@ -0,0 +1,78 @@ +/* +Copyright 2026 Element Creations 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 { expect, describe, it, beforeEach, vi } from "vitest"; +import { fs as memfs, vol } from "memfs"; + +import { loadJsonFile, tryPaths, randomArray } from "./utils.js"; + +vi.mock("node:fs", () => ({ default: memfs })); +vi.mock("node:fs/promises", () => ({ default: memfs.promises })); + +beforeEach(() => { + // Reset the state of the in-memory fs + vol.reset(); +}); + +describe("randomArray", () => { + it("should return an array matching the requested size", async () => { + function toUnpaddedBase64Size(size: number): number { + return Math.ceil((4 * size) / 3); + } + + await expect(randomArray(100)).resolves.toHaveLength(toUnpaddedBase64Size(100)); + await expect(randomArray(32)).resolves.toHaveLength(toUnpaddedBase64Size(32)); + }); + + it("should return a unique random array", async () => { + const arr1 = await randomArray(60); + const arr2 = await randomArray(60); + expect(arr1).not.toEqual(arr2); + }); +}); + +describe("loadJsonFile", () => { + beforeEach(() => { + vol.fromJSON({ + "./file.json": JSON.stringify({ file1: true }), + "./nested/deep/file.json": JSON.stringify({ file2: true }), + }); + }); + + it("should load and parse a JSON file correctly", () => { + expect(loadJsonFile("file.json")).toStrictEqual({ file1: true }); + }); + + it("should use args as path segments", () => { + expect(loadJsonFile("nested", "deep", "file.json")).toStrictEqual({ file2: true }); + }); + + it("should return an empty object when file does not exist", () => { + expect(loadJsonFile("unknown-file.json")).toStrictEqual({}); + }); +}); + +describe("tryPaths", () => { + beforeEach(() => { + vol.fromNestedJSON({ + "./dirA/": {}, + "./dir/dirB/": {}, + }); + }); + + it("should find file relative to given root", async () => { + await expect(tryPaths("name", "dir", ["dirB"])).resolves.toEqual("dir/dirB/"); + }); + + it("should handle unknown paths", async () => { + await expect(tryPaths("name", ".", ["dirB", "dirA"])).resolves.toEqual("dirA/"); + }); + + it("should throw error if file does not exist", async () => { + await expect(tryPaths("name", "dir", ["a.json", "b.json"])).rejects.toThrow("Failed to find name path"); + }); +}); diff --git a/apps/desktop/src/utils.ts b/apps/desktop/src/utils.ts index 368211aba7..6adb91ffa8 100644 --- a/apps/desktop/src/utils.ts +++ b/apps/desktop/src/utils.ts @@ -10,6 +10,10 @@ import fs from "node:fs"; import path from "node:path"; import afs from "node:fs/promises"; +/** + * Returns a random array of a specified size in unpadded base64 + * @param size - the size of the underlying random array + */ export async function randomArray(size: number): Promise { return new Promise((resolve, reject) => { crypto.randomBytes(size, (err, buf) => { @@ -62,9 +66,9 @@ export async function tryPaths(name: string, root: string, rawPaths: string[]): return p + "/"; } catch {} } - console.log(`Couldn't find ${name} files in any of: `); + console.log(`Couldn't find '${name}' in any of: `); for (const p of paths) { console.log("\t" + path.resolve(p)); } - throw new Error(`Failed to find ${name} files`); + throw new Error(`Failed to find ${name} path`); } diff --git a/packages/vite-common/vite.config.ts b/packages/vite-common/vite.config.ts index b45cd45912..93cb0b90f1 100644 --- a/packages/vite-common/vite.config.ts +++ b/packages/vite-common/vite.config.ts @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import { defineConfig, type ViteUserConfig } from "vitest/config"; -import { type Reporter } from "vitest/reporters"; +import { type Reporter } from "vitest/node"; import { env } from "node:process"; const reporters: NonNullable["reporters"] = [["default"]];