diff --git a/electron/package.json b/electron/package.json index afac7c5ccc..423e639bc0 100644 --- a/electron/package.json +++ b/electron/package.json @@ -51,6 +51,7 @@ "files": [ "main.js", "preload.js", + "loginManager.js", "sqlite-inspection.js", "package.json", "node_modules/**/*" diff --git a/tests/unit/electron-main.test.ts b/tests/unit/electron-main.test.ts index eb50c65680..8ccacc7ddd 100644 --- a/tests/unit/electron-main.test.ts +++ b/tests/unit/electron-main.test.ts @@ -13,7 +13,7 @@ import { describe, it } from "node:test"; import assert from "node:assert/strict"; -import { mkdtempSync, rmSync } from "node:fs"; +import { mkdtempSync, rmSync, readFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { createRequire } from "node:module"; @@ -38,6 +38,44 @@ function raceDelays(firstMs, secondMs) { }); } +// ─── Packaging manifest completeness (#3292 regression) ────── +// +// The packaged Electron app (electron-builder asar) only ships the files +// listed in `build.files`. When #3292 added `electron/loginManager.js` and a +// `require("./loginManager")` in main.js without adding it to `build.files`, +// the packaged app crashed at startup with "Cannot find module './loginManager'" +// — caught only by the CI smoke test on Linux/macOS. This guard asserts that +// every local `require("./x")` in the Electron entry points is shipped. + +describe("Electron packaging manifest completeness (#3292)", () => { + const electronDir = join(import.meta.dirname, "../../electron"); + const pkg = JSON.parse(readFileSync(join(electronDir, "package.json"), "utf8")); + const files: string[] = pkg.build?.files ?? []; + + function localRequires(entry: string): string[] { + const src = readFileSync(join(electronDir, entry), "utf8"); + const out = new Set(); + for (const m of src.matchAll(/require\(["'](\.\/[A-Za-z0-9_./-]+)["']\)/g)) { + // normalize "./loginManager" -> "loginManager.js" (entry points require sibling .js) + let rel = m[1].replace(/^\.\//, ""); + if (!/\.[a-z]+$/.test(rel)) rel += ".js"; + out.add(rel); + } + return [...out]; + } + + for (const entry of ["main.js", "preload.js"]) { + it(`ships every local require() of ${entry} in build.files`, () => { + for (const dep of localRequires(entry)) { + assert.ok( + files.includes(dep), + `electron/${dep} is require()d by ${entry} but missing from package.json build.files — the packaged app will crash with "Cannot find module"` + ); + } + }); + } +}); + // ─── URL Validation Tests ──────────────────────────────────── describe("Electron URL Validation", () => {