From fc7d4cf33c4af3f525e7c8d91230cfe886853688 Mon Sep 17 00:00:00 2001 From: indieisaconcept Date: Sun, 26 Jul 2020 12:07:58 +1000 Subject: [PATCH] Ensure root level hooks are called when running in watch mode ## Rationale Tests with root level hooks are ignored when running in watch mode. This occurs on initial & subsequent runs. ## Changes - extended `suite.clone` to clone hooks This change was necessary to ensure that when a suite is cloned during the setup phase for watch that hooks are available on the newly cloned suite. Note the use of `suite.clone` appears to be only used in to `lib/cli/run-watch`. I did consider making the cloning of hooks optional but opted not to as this `suite.clone` is internal `mocha` & not publicly referenced in the API documentation. Tests for `suite.clone` have been amended to reflect the above change. - Extended integration tests for watch to take into consideration hooks ## Refs - mochajs/mocha/issues/4347 --- lib/suite.js | 7 ++++ .../fixtures/options/watch/hook.fixture.js | 7 ++++ test/integration/options/watch.spec.js | 37 +++++++++++++++++++ test/unit/suite.spec.js | 16 ++++---- 4 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 test/integration/fixtures/options/watch/hook.fixture.js diff --git a/lib/suite.js b/lib/suite.js index e9d45d94c4..15f31f758a 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -119,6 +119,13 @@ Suite.prototype.clone = function() { debug('clone'); suite.ctx = this.ctx; suite.root = this.root; + + // Required to ensure hooks are available during watch + suite._beforeEach = this._beforeEach; + suite._afterEach = this._afterEach; + suite._beforeAll = this._beforeAll; + suite._afterAll = this._afterAll; + suite.timeout(this.timeout()); suite.retries(this.retries()); suite.slow(this.slow()); diff --git a/test/integration/fixtures/options/watch/hook.fixture.js b/test/integration/fixtures/options/watch/hook.fixture.js new file mode 100644 index 0000000000..3463761f3a --- /dev/null +++ b/test/integration/fixtures/options/watch/hook.fixture.js @@ -0,0 +1,7 @@ +module.exports = { + mochaHooks: { + [""]: function() { + throw new Error(" Hook Error"); + }, + }, +}; diff --git a/test/integration/options/watch.spec.js b/test/integration/options/watch.spec.js index 55ea345ab7..8db5b8dc46 100644 --- a/test/integration/options/watch.spec.js +++ b/test/integration/options/watch.spec.js @@ -275,6 +275,43 @@ describe('--watch', function() { expect(results[1].tests, 'to have length', 2); }); }); + + describe('with required hooks', function() { + /** + * Helper for setting up hook tests + * + * @param {string} hookName name of hook to test + * @return {function} + */ + function setupHookTest(hookName) { + return function() { + const testFile = path.join(tempDir, 'test.js'); + const hookFile = path.join(tempDir, 'hook.js'); + + copyFixture('__default__', testFile); + copyFixture('options/watch/hook', hookFile); + + replaceFileContents(hookFile, '', hookName); + + return runMochaWatch( + [testFile, '--require', hookFile], + tempDir, + () => { + touchFile(testFile); + } + ).then(results => { + expect(results.length, 'to equal', 2); + expect(results[0].failures, 'to have length', 1); + expect(results[1].failures, 'to have length', 1); + }); + }; + } + + it('mochaHooks.beforeAll runs as expected', setupHookTest('beforeAll')); + it('mochaHooks.beforeEach runs as expected', setupHookTest('beforeEach')); + it('mochaHooks.afterAll runs as expected', setupHookTest('afterAll')); + it('mochaHooks.afterEaxch runs as expected', setupHookTest('afterEach')); + }); }); }); diff --git a/test/unit/suite.spec.js b/test/unit/suite.spec.js index 06752daba2..872464680d 100644 --- a/test/unit/suite.spec.js +++ b/test/unit/suite.spec.js @@ -54,20 +54,20 @@ describe('Suite', function() { expect(this.suite.clone().tests, 'to be empty'); }); - it('should not copy the values from the _beforeEach array', function() { - expect(this.suite.clone()._beforeEach, 'to be empty'); + it('should copy the values from the _beforeEach array', function() { + expect(this.suite.clone()._beforeEach, 'to equal', [2]); }); - it('should not copy the values from the _beforeAll array', function() { - expect(this.suite.clone()._beforeAll, 'to be empty'); + it('should copy the values from the _beforeAll array', function() { + expect(this.suite.clone()._beforeAll, 'to equal', [3]); }); - it('should not copy the values from the _afterEach array', function() { - expect(this.suite.clone()._afterEach, 'to be empty'); + it('should copy the values from the _afterEach array', function() { + expect(this.suite.clone()._afterEach, 'to equal', [4]); }); - it('should not copy the values from the _afterAll array', function() { - expect(this.suite.clone()._afterAll, 'to be empty'); + it('should copy the values from the _afterAll array', function() { + expect(this.suite.clone()._afterAll, 'to equal', [5]); }); it('should copy the root property', function() {