blob: bd9a9ef642a00abe13cec9afba7f9c5fb077bebd [file]
import { strict as assert } from 'node:assert';
import { EventEmitter } from 'node:events';
import { describe, it } from 'node:test';
import { closeElectronApplication } from './electron-lifecycle.mjs';
// Bounded teardown is harness behavior, not product behavior: it drives a fake
// process handle and never launches Electron. It lives here beside
// fixture-window.test.mjs rather than in the Playwright suite, which would pay
// a real Electron boot slot to run assertions that need none.
class FakeElectronProcess extends EventEmitter {
exitCode = null;
signalCode = null;
killedWith = undefined;
kill(signal) {
this.killedWith = signal;
queueMicrotask(() => {
this.signalCode = signal;
this.emit('exit');
});
return true;
}
}
/** An app whose graceful close never settles, so every test exercises the deadline. */
function wedgedApp(child) {
return {
close: () => new Promise(() => {}),
process: () => child,
};
}
describe('closeElectronApplication', () => {
it('force-kills Electron when graceful teardown does not settle', async () => {
const child = new FakeElectronProcess();
let terminatedTree = false;
const settled = await Promise.race([
closeElectronApplication(wedgedApp(child), 0, async (target, signal) => {
assert.equal(target, child);
assert.equal(signal, 'SIGKILL');
terminatedTree = true;
child.signalCode = signal;
child.emit('exit');
return true;
}).then(() => true),
new Promise((resolve) => setTimeout(() => resolve(false), 25)),
]);
assert.equal(settled, true);
assert.equal(terminatedTree, true);
assert.equal(child.killedWith, undefined);
});
it('falls back to a direct SIGKILL when the group kill misses the root', async () => {
// The tree terminator signals the child's process group; on a child that is
// not a group leader the group signal reports ESRCH and the terminator
// returns as if the tree were gone while the root lives on. The bounded
// close must then land a kill on the root itself — measured for real with
// the smoke gate's visible window, which also ignores SIGTERM.
const child = new FakeElectronProcess();
await closeElectronApplication(wedgedApp(child), 0, async () => true);
assert.equal(child.killedWith, 'SIGKILL');
assert.equal(child.signalCode, 'SIGKILL');
});
});