Skip to content

Commit 716b6ec

Browse files
stabilize watch helper using run-completion sync
1 parent 6695fba commit 716b6ec

2 files changed

Lines changed: 94 additions & 26 deletions

File tree

test/integration/helpers.js

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -432,32 +432,92 @@ async function runMochaWatchAsync(args, opts, change) {
432432
}
433433
opts = {
434434
sleepMs: 2000,
435+
expectedRuns: 2,
435436
stdio: ["pipe", "pipe", "inherit"],
436437
...opts,
437438
fork: process.platform === "win32",
438439
};
440+
const { sleepMs, expectedRuns } = opts;
441+
const isJSON = args.some(
442+
(arg, i) =>
443+
arg === "--reporter=json" ||
444+
(arg === "--reporter" && args[i + 1] === "json"),
445+
);
446+
439447
const [mochaProcess, resultPromise] = invokeMochaAsync(
440448
[...args, "--watch"],
441449
opts,
442450
);
443-
await sleep(opts.sleepMs);
444-
await change(mochaProcess);
445-
await sleep(opts.sleepMs);
446451

447-
if (
448-
!(mochaProcess.connected
449-
? mochaProcess.send("SIGINT")
450-
: mochaProcess.kill("SIGINT"))
451-
) {
452-
throw new Error("failed to send signal to subprocess");
453-
}
452+
const RUN_DONE_MARKER = '"stats":';
453+
let buf = "";
454+
let runsDone = 0;
455+
let notifyProgress = null;
456+
457+
const onStdout = (data) => {
458+
buf += data.toString();
459+
let advanced = false;
460+
let idx;
461+
while ((idx = buf.indexOf(RUN_DONE_MARKER)) !== -1) {
462+
runsDone += 1;
463+
buf = buf.slice(idx + RUN_DONE_MARKER.length);
464+
advanced = true;
465+
}
466+
if (advanced && notifyProgress) notifyProgress();
467+
};
468+
469+
const waitForRuns = (target, timeoutMs) =>
470+
new Promise((resolve) => {
471+
if (runsDone >= target) return resolve(true);
472+
const timer = setTimeout(() => {
473+
notifyProgress = null;
474+
debug("watch run wait timed out at %d/%d", runsDone, target);
475+
resolve(false);
476+
}, timeoutMs);
477+
notifyProgress = () => {
478+
if (runsDone >= target) {
479+
clearTimeout(timer);
480+
notifyProgress = null;
481+
resolve(true);
482+
}
483+
};
484+
});
454485

455-
const res = await resultPromise;
486+
mochaProcess.stdout.on("data", onStdout);
456487

457-
// we kill the process with `SIGINT`, so it will always appear as "failed" to our
458-
// custom assertions (a non-zero exit code 130). just change it to 0.
459-
res.code = 0;
460-
return res;
488+
try {
489+
if (isJSON) {
490+
await waitForRuns(1, sleepMs * 3);
491+
} else {
492+
await sleep(sleepMs);
493+
}
494+
495+
const baseline = runsDone;
496+
await change(mochaProcess);
497+
498+
if (isJSON && expectedRuns > baseline) {
499+
await waitForRuns(expectedRuns, sleepMs * 3);
500+
} else {
501+
await sleep(sleepMs);
502+
}
503+
504+
if (
505+
!(mochaProcess.connected
506+
? mochaProcess.send("SIGINT")
507+
: mochaProcess.kill("SIGINT"))
508+
) {
509+
throw new Error("failed to send signal to subprocess");
510+
}
511+
512+
const res = await resultPromise;
513+
514+
// we kill the process with `SIGINT`, so it will always appear as "failed" to our
515+
// custom assertions (a non-zero exit code 130). just change it to 0.
516+
res.code = 0;
517+
return res;
518+
} finally {
519+
mochaProcess.stdout.removeListener("data", onStdout);
520+
}
461521
}
462522

463523
/**

test/integration/options/watch.spec.js

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,13 @@ describe("--watch", function () {
5353

5454
replaceFileContents(testFile, "done();", "done((;");
5555

56-
return runMochaWatchJSONAsync([testFile], tempDir, () => {
57-
replaceFileContents(testFile, "done((;", "done();");
58-
}).then((results) => {
56+
return runMochaWatchJSONAsync(
57+
[testFile],
58+
{ cwd: tempDir, expectedRuns: 1 },
59+
() => {
60+
replaceFileContents(testFile, "done((;", "done();");
61+
},
62+
).then((results) => {
5963
expect(results, "to have length", 1);
6064
});
6165
});
@@ -78,9 +82,13 @@ describe("--watch", function () {
7882

7983
replaceFileContents(testFile, "done();", "done((;");
8084

81-
return runMochaWatchJSONAsync([testFile], tempDir, () => {
82-
replaceFileContents(testFile, "done((;", "done();");
83-
}).then((results) => {
85+
return runMochaWatchJSONAsync(
86+
[testFile],
87+
{ cwd: tempDir, expectedRuns: 1 },
88+
() => {
89+
replaceFileContents(testFile, "done((;", "done();");
90+
},
91+
).then((results) => {
8492
expect(results, "to have length", 1);
8593
});
8694
});
@@ -131,7 +139,7 @@ describe("--watch", function () {
131139

132140
return runMochaWatchJSONAsync(
133141
[testFile, "--watch-files", "lib"],
134-
tempDir,
142+
{ cwd: tempDir, expectedRuns: 4 },
135143
async () => {
136144
fs.mkdirSync(libPath);
137145
await sleep(1000);
@@ -287,7 +295,7 @@ describe("--watch", function () {
287295

288296
return runMochaWatchJSONAsync(
289297
[testFile, "--watch-files", "dir/*.xyz"],
290-
tempDir,
298+
{ cwd: tempDir, expectedRuns: 1 },
291299
() => {
292300
touchFile(watchedFile);
293301
},
@@ -373,7 +381,7 @@ describe("--watch", function () {
373381

374382
return runMochaWatchJSONAsync(
375383
[testFile, "--extension", "xyz,js"],
376-
tempDir,
384+
{ cwd: tempDir, expectedRuns: 1 },
377385
() => {
378386
touchFile(gitFile);
379387
touchFile(nodeModulesFile);
@@ -398,7 +406,7 @@ describe("--watch", function () {
398406
"--watch-ignore",
399407
"dir/*ignore*",
400408
],
401-
tempDir,
409+
{ cwd: tempDir, expectedRuns: 1 },
402410
() => {
403411
touchFile(watchedFile);
404412
},
@@ -417,7 +425,7 @@ describe("--watch", function () {
417425

418426
return runMochaWatchJSONAsync(
419427
[testFile, "--watch-files", "dir/[ab].js"],
420-
tempDir,
428+
{ cwd: tempDir, expectedRuns: 1 },
421429
() => {
422430
touchFile(watchedFile);
423431
},

0 commit comments

Comments
 (0)