125 lines
4.0 KiB
JavaScript
125 lines
4.0 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { EventEmitter } = require('node:events');
|
|
const path = require('node:path');
|
|
const { createRestartController, createWatchedPaths, formatChangeMessage } = require('../scripts/dev-runner.cjs');
|
|
|
|
function createDeferred() {
|
|
let resolve;
|
|
let reject;
|
|
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
resolve = resolvePromise;
|
|
reject = rejectPromise;
|
|
});
|
|
return { promise, resolve, reject };
|
|
}
|
|
|
|
function createHarness(stopChild) {
|
|
const started = [];
|
|
const stopped = [];
|
|
const unexpectedExits = [];
|
|
let nextPid = 1;
|
|
const controller = createRestartController({
|
|
startChild() {
|
|
const child = new EventEmitter();
|
|
child.pid = nextPid;
|
|
nextPid += 1;
|
|
started.push(child);
|
|
return child;
|
|
},
|
|
stopChild(child) {
|
|
stopped.push(child);
|
|
return stopChild(child);
|
|
},
|
|
onUnexpectedExit(code, signal) {
|
|
unexpectedExits.push({ code, signal });
|
|
}
|
|
});
|
|
return { controller, started, stopped, unexpectedExits };
|
|
}
|
|
|
|
test('three changes during an open kill produce exactly one replacement Electron tree', async () => {
|
|
const kill = createDeferred();
|
|
const harness = createHarness(() => kill.promise);
|
|
const original = harness.controller.start();
|
|
const firstRestart = harness.controller.restart();
|
|
|
|
const pendingChanges = [
|
|
harness.controller.restart(),
|
|
harness.controller.restart(),
|
|
harness.controller.restart()
|
|
];
|
|
|
|
assert.equal(harness.stopped.length, 1);
|
|
assert.strictEqual(harness.stopped[0], original);
|
|
assert.strictEqual(harness.controller.start(), original);
|
|
assert.equal(harness.started.length, 1);
|
|
|
|
kill.resolve();
|
|
await Promise.all([firstRestart, ...pendingChanges]);
|
|
|
|
assert.equal(harness.stopped.length, 1);
|
|
assert.equal(harness.started.length, 2);
|
|
assert.notStrictEqual(harness.started[1], original);
|
|
});
|
|
|
|
test('a failed kill cannot start Electron over the still-running tree', async () => {
|
|
const harness = createHarness(async () => {
|
|
throw new Error('taskkill failed');
|
|
});
|
|
const original = harness.controller.start();
|
|
|
|
await assert.rejects(harness.controller.restart(), /taskkill failed/u);
|
|
|
|
assert.equal(harness.stopped.length, 1);
|
|
assert.equal(harness.started.length, 1);
|
|
assert.strictEqual(harness.controller.start(), original);
|
|
});
|
|
|
|
test('shutdown during an open restart kill prevents its completion from starting Electron', async () => {
|
|
const kill = createDeferred();
|
|
const harness = createHarness(() => kill.promise);
|
|
harness.controller.start();
|
|
const restart = harness.controller.restart();
|
|
const shutdown = harness.controller.shutdown();
|
|
|
|
assert.equal(harness.stopped.length, 1);
|
|
assert.equal(harness.started.length, 1);
|
|
|
|
kill.resolve();
|
|
await Promise.all([restart, shutdown]);
|
|
|
|
assert.equal(harness.stopped.length, 1);
|
|
assert.equal(harness.started.length, 1);
|
|
assert.equal(harness.controller.start(), null);
|
|
});
|
|
|
|
test('an old child exit cannot clear the replacement Electron child', async () => {
|
|
const harness = createHarness(async () => {});
|
|
const original = harness.controller.start();
|
|
|
|
await harness.controller.restart();
|
|
const replacement = harness.started[1];
|
|
original.emit('exit', 0, 'SIGTERM');
|
|
|
|
assert.equal(harness.started.length, 2);
|
|
assert.strictEqual(harness.controller.start(), replacement);
|
|
assert.deepEqual(harness.unexpectedExits, []);
|
|
});
|
|
|
|
test('watch paths still cover main, preloads, lib and renderer', () => {
|
|
const projectRoot = path.resolve('C:\\project');
|
|
|
|
assert.deepEqual([...createWatchedPaths(projectRoot)], [
|
|
path.join(projectRoot, 'main.js'),
|
|
path.join(projectRoot, 'preload.js'),
|
|
path.join(projectRoot, 'preload-drop-target.js'),
|
|
path.join(projectRoot, 'lib'),
|
|
path.join(projectRoot, 'renderer')
|
|
]);
|
|
});
|
|
|
|
test('change log identifies every watched file without calling it a renderer change', () => {
|
|
assert.equal(formatChangeMessage('C:\\project\\lib\\hosters.js'), '[hotdev] change detected: C:\\project\\lib\\hosters.js\n');
|
|
});
|