diff --git a/scripts/lib/platform-launch.js b/scripts/lib/platform-launch.js index ff2e8c0b5..40bed9ae6 100644 --- a/scripts/lib/platform-launch.js +++ b/scripts/lib/platform-launch.js @@ -45,9 +45,10 @@ function openerCommandFor(platform, url) { * * @param {string} url * @param {NodeJS.Platform} [platform] - injectable for tests; defaults to process.platform + * @param {typeof spawn} [spawnProcess] - injectable process launcher for tests * @returns {{ opened: boolean, reason: string }} */ -function openBrowser(url, platform = process.platform) { +function openBrowser(url, platform = process.platform, spawnProcess = spawn) { if (typeof url !== 'string' || url.length === 0) { return { opened: false, reason: 'invalid-url' }; } @@ -55,7 +56,7 @@ function openBrowser(url, platform = process.platform) { const [cmd, args] = openerCommandFor(platform, url); let child; try { - child = spawn(cmd, args, { + child = spawnProcess(cmd, args, { detached: true, stdio: 'ignore', }); diff --git a/tests/lib/platform-launch.test.js b/tests/lib/platform-launch.test.js index 482aaa006..d4f44bbe7 100644 --- a/tests/lib/platform-launch.test.js +++ b/tests/lib/platform-launch.test.js @@ -38,11 +38,27 @@ test('openBrowser: returns structured { opened, reason }', () => { assert.ok(r.reason.length > 0); }); -test('openBrowser: uses xdg-open on linux', () => { - // Spy by stubbing spawn via require cache (not possible without mocking module). - // Smoke-test: just ensure the function is callable. - const r = openBrowser('http://localhost:0', 'linux'); - // Either opened=true (xdg-open exists on runner) or opened=false with reason - assert.ok(['spawned', 'child-error:ENOENT', 'child-error:EACCES', 'spawn-threw:ENOENT'].includes(r.reason) - || r.opened === true || r.opened === false); +test('openBrowser: installs an error listener and detaches the launcher', () => { + const handlers = new Map(); + let unrefCalls = 0; + const child = { + on(event, listener) { + handlers.set(event, listener); + }, + unref() { + unrefCalls += 1; + }, + }; + + const result = openBrowser('http://localhost:0', 'linux', (command, args, options) => { + assert.equal(command, 'xdg-open'); + assert.deepEqual(args, ['http://localhost:0']); + assert.deepEqual(options, { detached: true, stdio: 'ignore' }); + return child; + }); + + assert.deepEqual(result, { opened: true, reason: 'spawned' }); + assert.equal(unrefCalls, 1); + assert.equal(typeof handlers.get('error'), 'function'); + assert.doesNotThrow(() => handlers.get('error')({ code: 'ENOENT' })); });