Skip to content

Commit

Permalink
properly dispose Mocha instance in watch mode; closes #4495
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher Hiller <boneskull@boneskull.com>
  • Loading branch information
boneskull committed Nov 2, 2020
1 parent c3ced39 commit 014a1b9
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 9 deletions.
6 changes: 6 additions & 0 deletions lib/cli/watch-run.js
Expand Up @@ -41,6 +41,9 @@ exports.watchParallelRun = (
// I don't know why we're cloning the root suite.
const rootSuite = mocha.suite.clone();

// ensure we aren't leaking event listeners
mocha.dispose();

// this `require` is needed because the require cache has been cleared. the dynamic
// exports set via the below call to `mocha.ui()` won't work properly if a
// test depends on this module (see `required-tokens.spec.js`).
Expand Down Expand Up @@ -100,6 +103,9 @@ exports.watchRun = (mocha, {watchFiles, watchIgnore}, fileCollectParams) => {
// I don't know why we're cloning the root suite.
const rootSuite = mocha.suite.clone();

// ensure we aren't leaking event listeners
mocha.dispose();

// this `require` is needed because the require cache has been cleared. the dynamic
// exports set via the below call to `mocha.ui()` won't work properly if a
// test depends on this module (see `required-tokens.spec.js`).
Expand Down
23 changes: 14 additions & 9 deletions test/integration/helpers.js
Expand Up @@ -415,8 +415,12 @@ async function runMochaWatchAsync(args, opts, change) {
if (typeof opts === 'string') {
opts = {cwd: opts};
}
opts = {sleepMs: 2000, ...opts, fork: process.platform === 'win32'};
opts.stdio = ['pipe', 'pipe', 'inherit'];
opts = {
sleepMs: 2000,
stdio: ['pipe', 'pipe', 'inherit'],
...opts,
fork: process.platform === 'win32'
};
const [mochaProcess, resultPromise] = invokeMochaAsync(
[...args, '--watch'],
opts
Expand Down Expand Up @@ -539,24 +543,25 @@ function sleep(time) {
module.exports = {
DEFAULT_FIXTURE,
SPLIT_DOT_REPORTER_REGEXP,
copyFixture,

createTempDir,
escapeRegExp,
getSummary,
invokeMocha,
invokeMochaAsync,
invokeNode,
getSummary,
replaceFileContents,
resolveFixturePath,
toJSONResult,
escapeRegExp,
runMocha,
runMochaJSON,
runMochaAsync,
runMochaJSON,
runMochaJSONAsync,
runMochaWatchAsync,
runMochaWatchJSONAsync,
copyFixture,
touchFile,
replaceFileContents
sleep,
toJSONResult,
touchFile
};

/**
Expand Down
30 changes: 30 additions & 0 deletions test/integration/options/watch.spec.js
Expand Up @@ -5,6 +5,8 @@ const path = require('path');
const {
copyFixture,
runMochaWatchJSONAsync,
sleep,
runMochaWatchAsync,
touchFile,
replaceFileContents,
createTempDir,
Expand Down Expand Up @@ -343,5 +345,33 @@ describe('--watch', function() {
it('mochaHooks.afterAll runs as expected', setupHookTest('afterAll'));
it('mochaHooks.afterEach runs as expected', setupHookTest('afterEach'));
});

it('should not leak event listeners', function() {
this.timeout(20000);
const testFile = path.join(tempDir, 'test.js');
copyFixture(DEFAULT_FIXTURE, testFile);

return expect(
runMochaWatchAsync(
[testFile],
{cwd: tempDir, stdio: 'pipe'},
async () => {
// we want to cause _n + 1_ reruns, which should cause the warning
// to occur if the listeners aren't properly destroyed
const iterations = new Array(process.getMaxListeners() + 1);
// eslint-disable-next-line no-unused-vars
for await (const _ of iterations) {
touchFile(testFile);
await sleep(1000);
}
}
),
'when fulfilled',
'to satisfy',
{
output: expect.it('not to match', /MaxListenersExceededWarning/)
}
);
});
});
});

0 comments on commit 014a1b9

Please sign in to comment.