Skip to content

Commit 527741b

Browse files
committed
fix: begin running tests immediately instead of waiting for watcher
1 parent 65a91b5 commit 527741b

File tree

2 files changed

+45
-41
lines changed

2 files changed

+45
-41
lines changed

lib/cli/watch-run.js

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const isPathInside = require('is-path-inside');
99
const {minimatch} = require('minimatch');
1010
const Context = require('../context');
1111
const collectFiles = require('./collect-files');
12+
const fs = require('node:fs');
1213

1314
/**
1415
* @typedef {import('chokidar').FSWatcher} FSWatcher
@@ -385,6 +386,10 @@ function createPathMatcher(allowed, ignored, basePath) {
385386
return matcher;
386387
}
387388

389+
// grab Date.now before anything like sinon.createFakeTimers can monkeypatch it
390+
// eslint-disable-next-line no-restricted-globals
391+
const dateNow = Date.now;
392+
388393
/**
389394
* Bootstraps a Chokidar watcher. Handles keyboard input & signals
390395
* @param {Mocha} mocha - Mocha instance
@@ -434,26 +439,41 @@ const createWatcher = (
434439
ignored: matcher.ignore
435440
});
436441

437-
const rerunner = createRerunner(mocha, watcher, {
442+
const rerunner = createRerunner(mocha, watcher, matcher, {
438443
beforeRun
439444
});
440445

441-
watcher.on('ready', async () => {
446+
const startTime = dateNow();
447+
let ready = false;
448+
watcher.on('ready', () => {
442449
debug('watcher ready');
443-
if (!globalFixtureContext) {
444-
debug('triggering global setup');
445-
globalFixtureContext = await mocha.runGlobalSetup();
446-
}
447-
rerunner.run();
450+
ready = true;
448451
});
449452

450-
watcher.on('all', (_event, filePath) => {
451-
// only allow file paths that match the allowed patterns
452-
if (matcher.allow(filePath)) {
453-
rerunner.scheduleRun();
453+
watcher.on('all', async function handleEvent(event, filePath, stat) {
454+
if (exiting || !matcher.allow(filePath)) return;
455+
if (event === 'unlink' || (stat ? stat.mtime > startTime : ready)) {
456+
// we don't want to accidentally trigger a run before globalFixtureContext
457+
// has been created. If it hasn't then it's okay to do nothing here because
458+
// the code that creates globalFixtureContext will run the tests afterward
459+
if (globalFixtureContext) {
460+
rerunner.scheduleRun();
461+
}
462+
return;
463+
}
464+
if (!ready && !stat) {
465+
fs.stat(filePath, (err, stat) => {
466+
if (stat) handleEvent(event, filePath, stat);
467+
});
454468
}
455469
});
456470

471+
debug('triggering global setup');
472+
mocha.runGlobalSetup().then(context => {
473+
globalFixtureContext = context;
474+
rerunner.run();
475+
});
476+
457477
hideCursor();
458478
process.on('exit', () => {
459479
showCursor();
@@ -509,13 +529,14 @@ const createWatcher = (
509529
*
510530
* @param {Mocha} mocha - Mocha instance
511531
* @param {FSWatcher} watcher - Chokidar `FSWatcher` instance
532+
* @param {PathMatcher} matcher - `PathMatcher` instance
512533
* @param {Object} [opts] - Options!
513534
* @param {BeforeWatchRun} [opts.beforeRun] - Function to call before `mocha.run()`
514535
* @returns {Rerunner}
515536
* @ignore
516537
* @private
517538
*/
518-
const createRerunner = (mocha, watcher, {beforeRun} = {}) => {
539+
const createRerunner = (mocha, watcher, matcher, {beforeRun} = {}) => {
519540
// Set to a `Runner` when mocha is running. Set to `null` when mocha is not
520541
// running.
521542
let runner = null;
@@ -529,7 +550,7 @@ const createRerunner = (mocha, watcher, {beforeRun} = {}) => {
529550
runner = mocha.run(() => {
530551
debug('finished watch run');
531552
runner = null;
532-
blastCache(watcher);
553+
blastCache(matcher);
533554
if (rerunScheduled) {
534555
rerun();
535556
} else {
@@ -566,25 +587,6 @@ const createRerunner = (mocha, watcher, {beforeRun} = {}) => {
566587
};
567588
};
568589

569-
/**
570-
* Return the list of absolute paths watched by a Chokidar watcher.
571-
*
572-
* @param watcher - Instance of a Chokidar watcher
573-
* @return {string[]} - List of absolute paths
574-
* @ignore
575-
* @private
576-
*/
577-
const getWatchedFiles = watcher => {
578-
const watchedDirs = watcher.getWatched();
579-
return Object.keys(watchedDirs).reduce(
580-
(acc, dir) => [
581-
...acc,
582-
...watchedDirs[dir].map(file => path.join(dir, file))
583-
],
584-
[]
585-
);
586-
};
587-
588590
/**
589591
* Hide the cursor.
590592
* @ignore
@@ -613,14 +615,17 @@ const eraseLine = () => {
613615

614616
/**
615617
* Blast all of the watched files out of `require.cache`
616-
* @param {FSWatcher} watcher - Chokidar FSWatcher
618+
* @param {PathMatcher} matcher - `PathMatcher` instance
617619
* @ignore
618620
* @private
619621
*/
620-
const blastCache = watcher => {
621-
const files = getWatchedFiles(watcher);
622-
files.forEach(file => {
623-
delete require.cache[file];
624-
});
625-
debug('deleted %d file(s) from the require cache', files.length);
622+
const blastCache = matcher => {
623+
let deletedCount = 0;
624+
for (const key in require.cache) {
625+
if (matcher.allow(key)) {
626+
deletedCount++;
627+
delete require.cache[key];
628+
}
629+
}
630+
debug('deleted %d file(s) from the require cache', deletedCount);
626631
};

test/integration/helpers.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,6 @@ async function runMochaWatchJSONAsync(args, opts, change) {
492492
);
493493
}
494494

495-
const touchRef = new Date();
496-
497495
/**
498496
* Synchronously touch a file. Creates
499497
* the file and all its parent directories if necessary.
@@ -503,6 +501,7 @@ const touchRef = new Date();
503501
function touchFile(filepath) {
504502
fs.mkdirSync(path.dirname(filepath), { recursive: true });
505503
try {
504+
const touchRef = new Date();
506505
fs.utimesSync(filepath, touchRef, touchRef);
507506
} catch (err) {
508507
const fd = fs.openSync(filepath, 'a');

0 commit comments

Comments
 (0)