const test = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs/promises"); const os = require("node:os"); const path = require("node:path"); const { ChromiumFallback } = require("../build/ChromiumFallback"); const waitFor = async (predicate) => { const deadline = Date.now() + 3000; while (Date.now() < deadline) { if (await predicate()) return; await new Promise((resolve) => setTimeout(resolve, 20)); } throw new Error("timed out waiting for subprocess fixture"); }; test("uses isolated profile and safe argv; stop kills only its process and cleans profile", async () => { const fixture = await fs.mkdtemp(path.join(os.tmpdir(), "queuecube-test-")); const argsFile = path.join(fixture, "argv.json"); const executable = path.join(fixture, "fake-chrome.js"); await fs.writeFile(executable, `#!/usr/bin/env node\nconst fs=require('fs'); fs.writeFileSync(${JSON.stringify(argsFile)}, JSON.stringify(process.argv.slice(2))); setInterval(()=>{},1000);\n`); await fs.chmod(executable, 0o700); const browser = new ChromiumFallback(() => undefined, executable); const url = "https://example.test/?x=1; touch /tmp/should-not-exist"; try { assert.equal(await browser.start(url, () => true), true); await waitFor(async () => { try { await fs.access(argsFile); return true; } catch { return false; } }); const args = JSON.parse(await fs.readFile(argsFile, "utf8")); assert.equal(args.at(-1), url); assert.ok(args.some((arg) => arg.startsWith("--user-data-dir="))); assert.ok(args.includes("--kiosk")); assert.ok(!args.some((arg) => /no-sandbox|disable-web-security|ignore-certificate/i.test(arg))); const profilePath = args.find((arg) => arg.startsWith("--user-data-dir=")).split("=", 2)[1]; await browser.stop(); assert.equal(browser.isRunning, false); await assert.rejects(fs.access(profilePath)); } finally { await browser.stop(); await fs.rm(fixture, { recursive: true, force: true }); } }); test("discovers chromium-browser when that is the installed executable", async () => { const fixture = await fs.mkdtemp(path.join(os.tmpdir(), "queuecube-browser-path-test-")); const argsFile = path.join(fixture, "argv.json"); const executable = path.join(fixture, "chromium-browser"); const previousPath = process.env.PATH; const previousOverride = process.env.QUEUECUBE_CHROME_BIN; await fs.writeFile(executable, `#!/usr/bin/env node\nconst fs=require('fs'); fs.writeFileSync(${JSON.stringify(argsFile)}, JSON.stringify(process.argv.slice(2))); setInterval(()=>{},1000);\n`); await fs.chmod(executable, 0o700); await fs.symlink(process.execPath, path.join(fixture, "node")); try { process.env.PATH = fixture; delete process.env.QUEUECUBE_CHROME_BIN; const browser = new ChromiumFallback(); assert.equal(await browser.start("https://example.test/browser-discovery", () => true), true); await waitFor(async () => { try { await fs.access(argsFile); return true; } catch { return false; } }); assert.equal(JSON.parse(await fs.readFile(argsFile, "utf8")).at(-1), "https://example.test/browser-discovery"); await browser.stop(); } finally { process.env.PATH = previousPath; if (previousOverride === undefined) delete process.env.QUEUECUBE_CHROME_BIN; else process.env.QUEUECUBE_CHROME_BIN = previousOverride; await fs.rm(fixture, { recursive: true, force: true }); } }); test("canceled startup cleans its profile without spawning", async () => { const browser = new ChromiumFallback(() => undefined, "/bin/false"); assert.equal(await browser.start("https://example.test/", () => false), false); assert.equal(browser.isRunning, false); }); test("cancellation during process startup stops the late process and removes its profile", async () => { const fixture = await fs.mkdtemp(path.join(os.tmpdir(), "queuecube-race-test-")); const executable = path.join(fixture, "fake-chrome.js"); await fs.writeFile(executable, "#!/usr/bin/env node\nsetInterval(()=>{},1000);\n"); await fs.chmod(executable, 0o700); const browser = new ChromiumFallback(() => undefined, executable); const cleanedProfiles = []; const removeProfile = browser.removeProfile.bind(browser); browser.removeProfile = async (profile) => { cleanedProfiles.push(profile); await removeProfile(profile); }; let checks = 0; try { assert.equal(await browser.start("https://example.test/race", () => ++checks === 1), false); assert.equal(checks, 2, "the request became stale after process startup"); await waitFor(async () => { if (!cleanedProfiles.length) return false; try { await fs.access(cleanedProfiles[0]); return false; } catch { return true; } }); assert.equal(browser.isRunning, false); } finally { await browser.stop(); await fs.rm(fixture, { recursive: true, force: true }); } }); test("missing browser executable reports startup failure and clears ownership", async () => { const browser = new ChromiumFallback(() => undefined, "/definitely/missing/queuecube-chrome"); await assert.rejects(browser.start("https://example.test/", () => true), /ENOENT/); assert.equal(browser.isRunning, false); });