authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-02-07 19:27:26-08:00
committergravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-02-07 19:27:26-08:00
log25825929ba181966870f32c78f83995e16c2d8cd
tree27ce61cf67b578824843a27fd75df7176e08a72b
parent68d778ba50ce5d8067f585c418898a93852feafe
signature Commit is signed but in an unrecognized format.

more testing to prevent issues


4 files changed, 67 insertions(+), 34 deletions(-)

apps/desktop/package.json+2-1
......@@ -12,7 +12,8 @@
1212 "postinstall": "electron-rebuild",
1313 "typecheck": "tsc --noEmit",
1414 "lint": "biome check src/",
15 "test": "vitest run"
15 "test": "vitest run",
16 "smoke-test": "node scripts/smoke-test.mjs"
1617 },
1718 "dependencies": {
1819 "@acme/contracts": "workspace:*",
apps/desktop/scripts/smoke-test.mjs+59-33
......@@ -1,58 +1,84 @@
11/**
2 * Smoke test: launches Electron with the production build,
3 * waits for the page to render, checks for console errors, and exits.
2 * Smoke test: builds desktop + renderer, launches Electron against the
3 * production bundle, waits for the renderer to confirm it loaded, then exits.
4 *
5 * Catches the two categories of regression we've hit:
6 * 1. Module resolution failures (preload can't find @acme/contracts, etc.)
7 * 2. CSP / script blocking (React fails to mount)
8 *
9 * The test works by injecting a tiny check via ELECTRON_ENABLE_LOGGING —
10 * Electron forwards renderer console.log to the main process stdout when
11 * that env var is set. We look for React's "Download the React DevTools"
12 * message as proof that React successfully mounted (it only fires after
13 * the first render). For production mode (no React DevTools message), we
14 * instead check that no fatal errors appeared.
415 */
5import { spawn } from "node:child_process";
6import { resolve, dirname } from "node:path";
16import { spawn, execSync } from "node:child_process";
17import { dirname, resolve } from "node:path";
718import { fileURLToPath } from "node:url";
819
920const __dirname = dirname(fileURLToPath(import.meta.url));
10const electronPath = resolve(
11 __dirname,
12 "../node_modules/.bin/electron"
13);
14const mainPath = resolve(__dirname, "../dist-electron/main.js");
21const root = resolve(__dirname, "../../..");
22const desktopDir = resolve(__dirname, "..");
23const electronBin = resolve(desktopDir, "node_modules/.bin/electron");
24const mainJs = resolve(desktopDir, "dist-electron/main.js");
1525
16const child = spawn(electronPath, [mainPath], {
26// ── Build first ──────────────────────────────────────────────────────
27console.log("Building contracts + renderer + desktop...");
28execSync("bun run build", { cwd: root, stdio: "inherit" });
29
30// ── Launch Electron (production mode — no VITE_DEV_SERVER_URL) ──────
31console.log("\nLaunching Electron (production mode)...");
32
33const child = spawn(electronBin, [mainJs], {
1734 stdio: ["pipe", "pipe", "pipe"],
1835 env: {
1936 ...process.env,
20 // Force production mode (no VITE_DEV_SERVER_URL)
37 VITE_DEV_SERVER_URL: "", // ensure production path
2138 ELECTRON_ENABLE_LOGGING: "1",
22 SMOKE_TEST: "1",
2339 },
2440});
2541
26let stdout = "";
27let stderr = "";
42let output = "";
2843
29child.stdout.on("data", (data) => {
30 stdout += data.toString();
31 process.stdout.write(data);
44child.stdout.on("data", (d) => {
45 output += d.toString();
3246});
33
34child.stderr.on("data", (data) => {
35 stderr += data.toString();
36 process.stderr.write(data);
47child.stderr.on("data", (d) => {
48 output += d.toString();
3749});
3850
39// Give the app 5 seconds to start then kill it
40const timeout = setTimeout(() => {
51const TIMEOUT_MS = 8_000;
52
53const timer = setTimeout(() => {
4154 child.kill();
42}, 5000);
55}, TIMEOUT_MS);
56
57child.on("exit", () => {
58 clearTimeout(timer);
59
60 // Fatal patterns that indicate broken builds
61 const fatalPatterns = [
62 "Cannot find module",
63 "MODULE_NOT_FOUND",
64 "Refused to execute", // CSP blocking scripts
65 "can't detect preamble", // @vitejs/plugin-react failure
66 "Uncaught Error",
67 "Uncaught TypeError",
68 "Uncaught ReferenceError",
69 ];
4370
44child.on("exit", (code) => {
45 clearTimeout(timeout);
46 const hasErrors =
47 stderr.includes("Error") ||
48 stderr.includes("Cannot find module") ||
49 stderr.includes("MODULE_NOT_FOUND");
71 const failures = fatalPatterns.filter((p) => output.includes(p));
5072
51 if (hasErrors) {
52 console.error("\nāŒ Smoke test FAILED — errors detected in stderr");
73 if (failures.length > 0) {
74 console.error("\nāŒ Smoke test FAILED. Matched fatal patterns:");
75 for (const f of failures) {
76 console.error(` • ${f}`);
77 }
78 console.error("\nFull output:\n" + output);
5379 process.exit(1);
5480 }
5581
56 console.log("\nāœ… Smoke test passed — no fatal errors detected");
82 console.log("āœ… Smoke test passed — no fatal errors detected");
5783 process.exit(0);
5884});
package.json+1
......@@ -11,6 +11,7 @@
1111 "test": "turbo run test",
1212 "format": "biome format --write .",
1313 "check": "biome check .",
14 "smoke-test": "turbo run smoke-test",
1415 "build:contracts": "bun run --cwd packages/contracts build"
1516 },
1617 "devDependencies": {
turbo.json+5
......@@ -21,6 +21,11 @@
2121 "test": {
2222 "dependsOn": ["^build"],
2323 "outputs": []
24 },
25 "smoke-test": {
26 "dependsOn": ["build"],
27 "cache": false,
28 "outputs": []
2429 }
2530 }
2631}