From fcb4bb3cec7844813b1b0db0ecbfb9dfc9525c9d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 12 Jul 2017 16:09:21 +0200 Subject: [PATCH] wip: start handling async errors Ref https://github.com/facebook/jest/issues/2059 --- .../__tests__/promise_it.test.js | 40 +++++++++++++++++++ .../jest-cli/src/reporters/coverage_worker.js | 4 +- .../src/__tests__/queue_runner.test.js | 26 ++++++++++++ packages/jest-jasmine2/src/jasmine/Env.js | 22 ++++++++++ packages/jest-jasmine2/src/queue_runner.js | 8 +++- packages/jest-runner/src/test_worker.js | 4 +- 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/integration_tests/jasmine_async/__tests__/promise_it.test.js b/integration_tests/jasmine_async/__tests__/promise_it.test.js index 8b67dd95be37..2050b72f69db 100644 --- a/integration_tests/jasmine_async/__tests__/promise_it.test.js +++ b/integration_tests/jasmine_async/__tests__/promise_it.test.js @@ -26,6 +26,10 @@ describe('promise it', () => { done(); }); + it('works with async done', done => { + setTimeout(done, 1); + }); + it('is bound to context object', () => { return new Promise(resolve => { if (this.someContextValue !== 'value') { @@ -46,6 +50,42 @@ describe('promise it', () => { done.fail(new Error('done.fail was called')); }); + it('works with done(error)', done => { + done(new Error('done was called with error')); + }) + + it('fails if failed expectation with done', done => { + expect(true).toEqual(false); + done(); + }) + + it('fails if failed expectation with done - async', done => { + setTimeout(() => { + expect(true).toEqual(false) + done() + }, 1) + }) + + it('fails with thrown error with done - sync', done => { + throw new Error('sync fail'); + done(); + }) + + it('fails with thrown error with done - async', done => { + setTimeout(() => { + throw new Error('async fail'); + done(); + }, 1); + }) + + // I wish it was possible to catch this but I do not see a way. + // Currently both jest and mocha will pass this test. + it.skip('fails with thrown error - async', () => { + setTimeout(() => { + throw new Error('async fail - no done'); + }, 1); + }) + it('fails a sync test', () => { expect('sync').toBe('failed'); }); diff --git a/packages/jest-cli/src/reporters/coverage_worker.js b/packages/jest-cli/src/reporters/coverage_worker.js index 25ef877458ba..41d7e11253cd 100644 --- a/packages/jest-cli/src/reporters/coverage_worker.js +++ b/packages/jest-cli/src/reporters/coverage_worker.js @@ -39,8 +39,8 @@ function formatCoverageError(error, filename: Path): SerializableError { // Make sure uncaught errors are logged before we exit. process.on('uncaughtException', err => { - console.error(err.stack); - process.exit(1); + // console.error(err.stack); + // process.exit(1); }); module.exports = ( diff --git a/packages/jest-jasmine2/src/__tests__/queue_runner.test.js b/packages/jest-jasmine2/src/__tests__/queue_runner.test.js index 897365aced16..165f312e2efb 100644 --- a/packages/jest-jasmine2/src/__tests__/queue_runner.test.js +++ b/packages/jest-jasmine2/src/__tests__/queue_runner.test.js @@ -130,4 +130,30 @@ describe('queueRunner', () => { expect(options.fail).toHaveBeenCalledWith('miserably', 'failed'); }); + + it('calls `fail` when done(error) is invoked', async () => { + const error = new Error('I am an error') + const fail = jest.fn(); + const fnOne = jest.fn(next => next(error)); + const fnTwo = jest.fn(next => next()); + const options = { + clearTimeout, + fail, + onException: () => {}, + queueableFns: [ + { + fn: fnOne, + }, + { + fn: fnTwo, + }, + ], + setTimeout, + }; + await queueRunner(options); + expect(fnOne).toHaveBeenCalled(); + expect(fail).toHaveBeenCalledWith(error); + // Even if `fail` is called, the queue keeps running. + expect(fnTwo).toHaveBeenCalled(); + }); }); diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 2a0ad062806e..09acfd446ce9 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -95,6 +95,7 @@ module.exports = function(j$) { }; const clearResourcesForRunnable = function(id) { + console.log('clear resource', id) spyRegistry.clearSpies(); delete runnableResources[id]; }; @@ -187,6 +188,24 @@ module.exports = function(j$) { } } + const uncaught = (err) => { + let name + let current + if (currentSpec) { + current = currentSpec + name = current.getFullName() + current.onException(err) + // TODO: how to cancel the current running spec + } else { + // TODO: Handle top level failures + } + console.error('caught in ' + name) + } + + console.log('START') + process.on('uncaughtException', uncaught) + process.on('unhandledRejection', uncaught) + reporter.jasmineStarted({ totalSpecsDefined, }); @@ -215,6 +234,9 @@ module.exports = function(j$) { reporter.jasmineDone({ failedExpectations: topSuite.result.failedExpectations, }); + + process.removeListener('uncaughtException', uncaught) + process.removeListener('unhandledRejection', uncaught) }; this.addReporter = function(reporterToAdd) { diff --git a/packages/jest-jasmine2/src/queue_runner.js b/packages/jest-jasmine2/src/queue_runner.js index 57309120300a..1bb3acc48fa6 100644 --- a/packages/jest-jasmine2/src/queue_runner.js +++ b/packages/jest-jasmine2/src/queue_runner.js @@ -27,7 +27,13 @@ type QueueableFn = { async function queueRunner(options: Options) { const mapper = ({fn, timeout}) => { const promise = new Promise(resolve => { - const next = () => resolve(); + const next = function(err) { + if (err) { + options.fail.apply(null, arguments); + } + resolve(); + }; + next.fail = function() { options.fail.apply(null, arguments); resolve(); diff --git a/packages/jest-runner/src/test_worker.js b/packages/jest-runner/src/test_worker.js index 79baeefad59d..aee9ba0916d3 100644 --- a/packages/jest-runner/src/test_worker.js +++ b/packages/jest-runner/src/test_worker.js @@ -14,8 +14,8 @@ import type {RawModuleMap} from 'types/HasteMap'; // Make sure uncaught errors are logged before we exit. process.on('uncaughtException', err => { - console.error(err.stack); - process.exit(1); + // console.error(err.stack); + // process.exit(1); }); import HasteMap from 'jest-haste-map';