From fbcb002c732afb1cbf6974ebe5b09291bcc393fa Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 22:09:46 +0200 Subject: [PATCH 1/7] add stacktraces to async matchers --- CHANGELOG.md | 1 + .../expect-async-matcher.test.js.snap | 54 ++++++++---- .../__snapshots__/failures.test.js.snap | 83 ++++++++++++++++++- .../failures/__tests__/async_failures.test.js | 14 +++- packages/expect/src/index.js | 65 ++++++++++----- 5 files changed, 177 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2785cb7c69..2c56713eeeea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Features +* `[expect]` Add stack trace for async errors (TBD) * `[jest-runtime]` Prevent modules from marking themselves as their own parent ([#5235](https://github.com/facebook/jest/issues/5235)) * `[jest-mock]` Add support for auto-mocking generator functions diff --git a/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap b/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap index 53443ffd492a..b796ec647e71 100644 --- a/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap @@ -30,23 +30,49 @@ Object { ● fail with expected promise values - Error - Error: Expected value to have length: - 2 - Received: - 1 - received.length: - 1 + Expected value to have length: + 2 + Received: + 1 + received.length: + 1 + + 22 | + 23 | it('fail with expected promise values', async () => { + > 24 | await (expect(Promise.resolve([1])): any).resolves.toHaveLengthAsync( + | ^ + 25 | Promise.resolve(2) + 26 | ); + 27 | }); + + at packages/expect/build/index.js:165:22 + at __tests__/failure.test.js:24:54 + at __tests__/failure.test.js:11:191 + at __tests__/failure.test.js:11:437 + at __tests__/failure.test.js:11:99 ● fail with expected promise values and not - Error - Error: Expected value to not have length: - 2 - Received: - 1,2 - received.length: - 2 + Expected value to not have length: + 2 + Received: + 1,2 + received.length: + 2 + + 28 | + 29 | it('fail with expected promise values and not', async () => { + > 30 | await (expect(Promise.resolve([1, 2])).resolves.not: any).toHaveLengthAsync( + | ^ + 31 | Promise.resolve(2) + 32 | ); + 33 | }); + + at packages/expect/build/index.js:165:22 + at __tests__/failure.test.js:30:61 + at __tests__/failure.test.js:11:191 + at __tests__/failure.test.js:11:437 + at __tests__/failure.test.js:11:99 ", "summary": "Test Suites: 1 failed, 1 total diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index e2c73766829e..bde548d0006e 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -191,9 +191,12 @@ exports[`works with assertions in separate files 1`] = ` exports[`works with async failures 1`] = ` "FAIL __tests__/async_failures.test.js - ✕ something async + ✕ resolve, but fail + ✕ reject, but fail + ✕ expect reject + ✕ expect resolve - ● something async + ● resolve, but fail expect(received).toEqual(expected) @@ -211,8 +214,82 @@ exports[`works with async failures 1`] = ` - \\"baz\\": \\"bar\\", + \\"foo\\": \\"bar\\", } + + 10 | + 11 | test('resolve, but fail', () => { + > 12 | return expect(Promise.resolve({foo: 'bar'})).resolves.toEqual({baz: 'bar'}); + | ^ + 13 | }); + 14 | + 15 | test('reject, but fail', () => { + + at packages/expect/build/index.js:165:22 + at __tests__/async_failures.test.js:12:57 + + ● reject, but fail + + expect(received).toEqual(expected) + + Expected value to equal: + {\\"baz\\": \\"bar\\"} + Received: + {\\"foo\\": \\"bar\\"} + + Difference: + + - Expected + + Received + + Object { + - \\"baz\\": \\"bar\\", + + \\"foo\\": \\"bar\\", + } + + 14 | + 15 | test('reject, but fail', () => { + > 16 | return expect(Promise.reject({foo: 'bar'})).rejects.toEqual({baz: 'bar'}); + | ^ + 17 | }); + 18 | + 19 | test('expect reject', () => { + + at packages/expect/build/index.js:202:22 + at __tests__/async_failures.test.js:16:55 + + ● expect reject + + expect(received).rejects.toEqual() + + Expected received Promise to reject, instead it resolved to value + {\\"foo\\": \\"bar\\"} + + 18 | + 19 | test('expect reject', () => { + > 20 | return expect(Promise.resolve({foo: 'bar'})).rejects.toEqual({foo: 'bar'}); + | ^ + 21 | }); + 22 | + 23 | test('expect resolve', () => { + + at packages/expect/build/index.js:96:15 + at __tests__/async_failures.test.js:20:10 + + ● expect resolve + + expect(received).resolves.toEqual() + + Expected received Promise to resolve, instead it rejected to value + {\\"foo\\": \\"bar\\"} + + 22 | + 23 | test('expect resolve', () => { + > 24 | return expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'}); + | ^ + 25 | }); + 26 | - at packages/expect/build/index.js:160:61 + at packages/expect/build/index.js:96:15 + at __tests__/async_failures.test.js:24:10 " `; diff --git a/integration-tests/failures/__tests__/async_failures.test.js b/integration-tests/failures/__tests__/async_failures.test.js index 71b92a99f6ef..f050ebfdb604 100644 --- a/integration-tests/failures/__tests__/async_failures.test.js +++ b/integration-tests/failures/__tests__/async_failures.test.js @@ -8,6 +8,18 @@ */ 'use strict'; -test('something async', () => { +test('resolve, but fail', () => { return expect(Promise.resolve({foo: 'bar'})).resolves.toEqual({baz: 'bar'}); }); + +test('reject, but fail', () => { + return expect(Promise.reject({foo: 'bar'})).rejects.toEqual({baz: 'bar'}); +}); + +test('expect reject', () => { + return expect(Promise.resolve({foo: 'bar'})).rejects.toEqual({foo: 'bar'}); +}); + +test('expect resolve', () => { + return expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'}); +}); diff --git a/packages/expect/src/index.js b/packages/expect/src/index.js index f78dbfac79a3..b863c714ea4f 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.js @@ -88,6 +88,8 @@ const expect = (actual: any, ...rest): ExpectationObject => { resolves: {not: {}}, }; + const err = new JestAssertionError(); + Object.keys(allMatchers).forEach(name => { const matcher = allMatchers[name]; const promiseMatcher = getPromiseMatcher(name, matcher) || matcher; @@ -99,12 +101,14 @@ const expect = (actual: any, ...rest): ExpectationObject => { promiseMatcher, false, actual, + err, ); expectation.resolves.not[name] = makeResolveMatcher( name, promiseMatcher, true, actual, + err, ); expectation.rejects[name] = makeRejectMatcher( @@ -112,12 +116,14 @@ const expect = (actual: any, ...rest): ExpectationObject => { promiseMatcher, false, actual, + err, ); expectation.rejects.not[name] = makeRejectMatcher( name, promiseMatcher, true, actual, + err, ); }); @@ -136,6 +142,7 @@ const makeResolveMatcher = ( matcher: RawMatcherFn, isNot: boolean, actual: Promise, + outerErr: JestAssertionError, ): PromiseMatcherFn => (...args) => { const matcherStatement = `.resolves.${isNot ? 'not.' : ''}${matcherName}`; if (!isPromise(actual)) { @@ -147,17 +154,19 @@ const makeResolveMatcher = ( ); } + const innerErr = new JestAssertionError(); + return actual.then( - result => makeThrowingMatcher(matcher, isNot, result).apply(null, args), + result => + makeThrowingMatcher(matcher, isNot, result, innerErr).apply(null, args), reason => { - const err = new JestAssertionError( + outerErr.message = utils.matcherHint(matcherStatement, 'received', '') + - '\n\n' + - `Expected ${utils.RECEIVED_COLOR('received')} Promise to resolve, ` + - 'instead it rejected to value\n' + - ` ${utils.printReceived(reason)}`, - ); - return Promise.reject(err); + '\n\n' + + `Expected ${utils.RECEIVED_COLOR('received')} Promise to resolve, ` + + 'instead it rejected to value\n' + + ` ${utils.printReceived(reason)}`; + return Promise.reject(outerErr); }, ); }; @@ -167,6 +176,7 @@ const makeRejectMatcher = ( matcher: RawMatcherFn, isNot: boolean, actual: Promise, + outerErr: JestAssertionError, ): PromiseMatcherFn => (...args) => { const matcherStatement = `.rejects.${isNot ? 'not.' : ''}${matcherName}`; if (!isPromise(actual)) { @@ -178,18 +188,20 @@ const makeRejectMatcher = ( ); } + const innerErr = new JestAssertionError(); + return actual.then( result => { - const err = new JestAssertionError( + outerErr.message = utils.matcherHint(matcherStatement, 'received', '') + - '\n\n' + - `Expected ${utils.RECEIVED_COLOR('received')} Promise to reject, ` + - 'instead it resolved to value\n' + - ` ${utils.printReceived(result)}`, - ); - return Promise.reject(err); + '\n\n' + + `Expected ${utils.RECEIVED_COLOR('received')} Promise to reject, ` + + 'instead it resolved to value\n' + + ` ${utils.printReceived(result)}`; + return Promise.reject(outerErr); }, - reason => makeThrowingMatcher(matcher, isNot, reason).apply(null, args), + reason => + makeThrowingMatcher(matcher, isNot, reason, innerErr).apply(null, args), ); }; @@ -197,6 +209,7 @@ const makeThrowingMatcher = ( matcher: RawMatcherFn, isNot: boolean, actual: any, + err?: JestAssertionError, ): ThrowingMatcherFn => { return function throwingMatcher(...args): any { let throws = true; @@ -223,16 +236,24 @@ const makeThrowingMatcher = ( if ((result.pass && isNot) || (!result.pass && !isNot)) { // XOR const message = getMessage(result.message); - const error = new JestAssertionError(message); + let error; + + if (err) { + error = err; + error.message = message; + } else { + error = new JestAssertionError(message); + + // Try to remove this function from the stack trace frame. + // Guard for some environments (browsers) that do not support this feature. + if (Error.captureStackTrace) { + Error.captureStackTrace(error, throwingMatcher); + } + } // Passing the result of the matcher with the error so that a custom // reporter could access the actual and expected objects of the result // for example in order to display a custom visual diff error.matcherResult = result; - // Try to remove this function from the stack trace frame. - // Guard for some environments (browsers) that do not support this feature. - if (Error.captureStackTrace) { - Error.captureStackTrace(error, throwingMatcher); - } if (throws) { throw error; From 64f7aa62f0d5b9e66687d9e62831ef0e1c5fa607 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 22:21:08 +0200 Subject: [PATCH 2/7] add stack trace on spec timeout --- CHANGELOG.md | 1 + .../__snapshots__/failures.test.js.snap | 17 +++++++++++++++++ .../failures/__tests__/async_failures.test.js | 6 ++++++ packages/jest-jasmine2/src/jasmine/Env.js | 7 +++++++ packages/jest-jasmine2/src/queue_runner.js | 11 +++++------ 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c56713eeeea..aa10e9fccd0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features * `[expect]` Add stack trace for async errors (TBD) +* `[jest-jasmine2]` Add stack trace for timeouts (TBD) * `[jest-runtime]` Prevent modules from marking themselves as their own parent ([#5235](https://github.com/facebook/jest/issues/5235)) * `[jest-mock]` Add support for auto-mocking generator functions diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index bde548d0006e..4366990c19ff 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -195,6 +195,7 @@ exports[`works with async failures 1`] = ` ✕ reject, but fail ✕ expect reject ✕ expect resolve + ✕ timeout ● resolve, but fail @@ -287,10 +288,26 @@ exports[`works with async failures 1`] = ` | ^ 25 | }); 26 | + 27 | test('timeout', done => { at packages/expect/build/index.js:96:15 at __tests__/async_failures.test.js:24:10 + ● timeout + + Timeout - Async callback was not invoked within the 5ms timeout specified by jest.setTimeout. + + 25 | }); + 26 | + > 27 | test('timeout', done => { + | ^ + 28 | jest.setTimeout(5); + 29 | + 30 | setTimeout(done, 10); + + at packages/jest-jasmine2/build/jasmine/Env.js:445:20 + at __tests__/async_failures.test.js:27:1 + " `; diff --git a/integration-tests/failures/__tests__/async_failures.test.js b/integration-tests/failures/__tests__/async_failures.test.js index f050ebfdb604..5c828bd6fa1e 100644 --- a/integration-tests/failures/__tests__/async_failures.test.js +++ b/integration-tests/failures/__tests__/async_failures.test.js @@ -23,3 +23,9 @@ test('expect reject', () => { test('expect resolve', () => { return expect(Promise.reject({foo: 'bar'})).resolves.toEqual({foo: 'bar'}); }); + +test('timeout', done => { + jest.setTimeout(5); + + setTimeout(done, 10); +}); diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index 6adaab2e8113..d7cac885494d 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -398,6 +398,12 @@ export default function(j$) { } const specFactory = function(description, fn, suite, timeout) { + const timeoutError = new Error(); + + if (Error.captureStackTrace) { + Error.captureStackTrace(timeoutError, specFactory); + } + totalSpecsDefined++; const spec = new j$.Spec({ id: getNextSpecId(), @@ -420,6 +426,7 @@ export default function(j$) { timeout() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }, + timeoutError, }, throwOnExpectationFailure, }); diff --git a/packages/jest-jasmine2/src/queue_runner.js b/packages/jest-jasmine2/src/queue_runner.js index 062cfc0bdd51..166759fba83d 100644 --- a/packages/jest-jasmine2/src/queue_runner.js +++ b/packages/jest-jasmine2/src/queue_runner.js @@ -34,7 +34,7 @@ export default function queueRunner(options: Options) { onCancel(resolve); }); - const mapper = ({fn, timeout}) => { + const mapper = ({fn, timeout, timeoutError = new Error()}) => { let promise = new Promise(resolve => { const next = function(err) { if (err) { @@ -69,12 +69,11 @@ export default function queueRunner(options: Options) { options.clearTimeout, options.setTimeout, () => { - const error = new Error( + timeoutError.message = 'Timeout - Async callback was not invoked within the ' + - timeoutMs + - 'ms timeout specified by jest.setTimeout.', - ); - options.onException(error); + timeoutMs + + 'ms timeout specified by jest.setTimeout.'; + options.onException(timeoutError); }, ); }; From 656f477797c944b495aeedd5acce0ea7be482201 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 22:32:15 +0200 Subject: [PATCH 3/7] link to PR --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa10e9fccd0b..8d695d363f6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ ### Features -* `[expect]` Add stack trace for async errors (TBD) -* `[jest-jasmine2]` Add stack trace for timeouts (TBD) +* `[expect]` Add stack trace for async errors + ([#6008](https://github.com/facebook/jest/pull/6008)) +* `[jest-jasmine2]` Add stack trace for timeouts + ([#6008](https://github.com/facebook/jest/pull/6008)) * `[jest-runtime]` Prevent modules from marking themselves as their own parent ([#5235](https://github.com/facebook/jest/issues/5235)) * `[jest-mock]` Add support for auto-mocking generator functions From 4dd5b4d20440474ba7b8dc807d6a86786e147166 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 22:53:27 +0200 Subject: [PATCH 4/7] remove uninteresting frame from snapshot --- .../__tests__/__snapshots__/failures.test.js.snap | 4 ---- integration-tests/__tests__/failures.test.js | 7 ++++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index 4366990c19ff..c05ee9c6ab0a 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -224,7 +224,6 @@ exports[`works with async failures 1`] = ` 14 | 15 | test('reject, but fail', () => { - at packages/expect/build/index.js:165:22 at __tests__/async_failures.test.js:12:57 ● reject, but fail @@ -254,7 +253,6 @@ exports[`works with async failures 1`] = ` 18 | 19 | test('expect reject', () => { - at packages/expect/build/index.js:202:22 at __tests__/async_failures.test.js:16:55 ● expect reject @@ -272,7 +270,6 @@ exports[`works with async failures 1`] = ` 22 | 23 | test('expect resolve', () => { - at packages/expect/build/index.js:96:15 at __tests__/async_failures.test.js:20:10 ● expect resolve @@ -290,7 +287,6 @@ exports[`works with async failures 1`] = ` 26 | 27 | test('timeout', done => { - at packages/expect/build/index.js:96:15 at __tests__/async_failures.test.js:24:10 ● timeout diff --git a/integration-tests/__tests__/failures.test.js b/integration-tests/__tests__/failures.test.js index e8fd6d0e65bf..e5debe43b264 100644 --- a/integration-tests/__tests__/failures.test.js +++ b/integration-tests/__tests__/failures.test.js @@ -110,7 +110,12 @@ test('works with assertions in separate files', () => { test('works with async failures', () => { const {stderr} = runJest(dir, ['async_failures.test.js']); - expect(normalizeDots(extractSummary(stderr).rest)).toMatchSnapshot(); + const rest = extractSummary(stderr) + .rest.split('\n') + .filter(line => line.indexOf('packages/expect/build/index.js') === -1) + .join('\n'); + + expect(normalizeDots(rest)).toMatchSnapshot(); }); test('works with snapshot failures', () => { From 13368dc9ef13613aa9b6960c0a3b2c4889f59144 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 23:26:25 +0200 Subject: [PATCH 5/7] Add stack trace for thrown non-`Error`s --- CHANGELOG.md | 2 + .../__snapshots__/failures.test.js.snap | 104 ++++++++++++++++-- .../src/expectation_result_factory.js | 15 ++- packages/jest-jasmine2/src/jasmine/Env.js | 7 -- packages/jest-jasmine2/src/jasmine/Spec.js | 7 +- packages/jest-jasmine2/src/queue_runner.js | 6 +- 6 files changed, 113 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d695d363f6f..c4de63c19772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ([#6008](https://github.com/facebook/jest/pull/6008)) * `[jest-jasmine2]` Add stack trace for timeouts ([#6008](https://github.com/facebook/jest/pull/6008)) +* `[jest-jasmine2]` Add stack trace for thrown non-`Error`s + ([#6008](https://github.com/facebook/jest/pull/6008)) * `[jest-runtime]` Prevent modules from marking themselves as their own parent ([#5235](https://github.com/facebook/jest/issues/5235)) * `[jest-mock]` Add support for auto-mocking generator functions diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index c05ee9c6ab0a..4285e01d8eb2 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -83,20 +83,50 @@ exports[`not throwing Error objects 5`] = ` ● Promise thrown during test thrown: Promise {} + + 5 | }; + 6 | + > 7 | test('Promise thrown during test', () => { + | ^ + 8 | throw Promise.resolve(5); + 9 | }); + 10 | - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 + + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/during_tests.test.js:7:1 ● Boolean thrown during test thrown: false + + 9 | }); + 10 | + > 11 | test('Boolean thrown during test', () => { + | ^ + 12 | // eslint-disable-next-line no-throw-literal + 13 | throw false; + 14 | }); + - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/during_tests.test.js:11:1 ● undefined thrown during test thrown: undefined + + 14 | }); + 15 | + > 16 | test('undefined thrown during test', () => { + | ^ + 17 | // eslint-disable-next-line no-throw-literal + 18 | throw undefined; + 19 | }); + - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/during_tests.test.js:16:1 ● Object thrown during test @@ -108,8 +138,18 @@ exports[`not throwing Error objects 5`] = ` }, ], } + + 19 | }); + 20 | + > 21 | test('Object thrown during test', () => { + | ^ + 22 | // eslint-disable-next-line no-throw-literal + 23 | throw deepObject; + 24 | }); - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 + + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/during_tests.test.js:21:1 ● Error during test @@ -150,15 +190,17 @@ exports[`not throwing Error objects 5`] = ` ], } + 33 | }); 34 | - 35 | test('done(non-error)', done => { - > 36 | done(deepObject); - | ^ + > 35 | test('done(non-error)', done => { + | ^ + 36 | done(deepObject); 37 | }); 38 | - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 - at __tests__/during_tests.test.js:36:3 + + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/during_tests.test.js:35:1 " `; @@ -301,7 +343,7 @@ exports[`works with async failures 1`] = ` 29 | 30 | setTimeout(done, 10); - at packages/jest-jasmine2/build/jasmine/Env.js:445:20 + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 at __tests__/async_failures.test.js:27:1 " @@ -344,6 +386,8 @@ exports[`works with node assert 1`] = ` 18 | test('assert with a message', () => { at __tests__/node_assertion_error.test.js:15:3 + + at __tests__/node_assertion_error.test.js:14:1 ● assert with a message @@ -366,6 +410,8 @@ exports[`works with node assert 1`] = ` 22 | test('assert.ok', () => { at __tests__/node_assertion_error.test.js:19:3 + + at __tests__/node_assertion_error.test.js:18:1 ● assert.ok @@ -385,6 +431,8 @@ exports[`works with node assert 1`] = ` 26 | test('assert.ok with a message', () => { at __tests__/node_assertion_error.test.js:23:10 + + at __tests__/node_assertion_error.test.js:22:1 ● assert.ok with a message @@ -407,6 +455,8 @@ exports[`works with node assert 1`] = ` 30 | test('assert.equal', () => { at __tests__/node_assertion_error.test.js:27:10 + + at __tests__/node_assertion_error.test.js:26:1 ● assert.equal @@ -426,6 +476,8 @@ exports[`works with node assert 1`] = ` 34 | test('assert.notEqual', () => { at __tests__/node_assertion_error.test.js:31:10 + + at __tests__/node_assertion_error.test.js:30:1 ● assert.notEqual @@ -449,6 +501,8 @@ exports[`works with node assert 1`] = ` 38 | test('assert.deepEqual', () => { at __tests__/node_assertion_error.test.js:35:10 + + at __tests__/node_assertion_error.test.js:34:1 ● assert.deepEqual @@ -482,6 +536,8 @@ exports[`works with node assert 1`] = ` 42 | test('assert.deepEqual with a message', () => { at __tests__/node_assertion_error.test.js:39:10 + + at __tests__/node_assertion_error.test.js:38:1 ● assert.deepEqual with a message @@ -518,6 +574,8 @@ exports[`works with node assert 1`] = ` 46 | test('assert.notDeepEqual', () => { at __tests__/node_assertion_error.test.js:43:10 + + at __tests__/node_assertion_error.test.js:42:1 ● assert.notDeepEqual @@ -541,6 +599,8 @@ exports[`works with node assert 1`] = ` 50 | test('assert.strictEqual', () => { at __tests__/node_assertion_error.test.js:47:10 + + at __tests__/node_assertion_error.test.js:46:1 ● assert.strictEqual @@ -560,6 +620,8 @@ exports[`works with node assert 1`] = ` 54 | test('assert.notStrictEqual', () => { at __tests__/node_assertion_error.test.js:51:10 + + at __tests__/node_assertion_error.test.js:50:1 ● assert.notStrictEqual @@ -586,6 +648,8 @@ exports[`works with node assert 1`] = ` 58 | test('assert.deepStrictEqual', () => { at __tests__/node_assertion_error.test.js:55:10 + + at __tests__/node_assertion_error.test.js:54:1 ● assert.deepStrictEqual @@ -615,6 +679,8 @@ exports[`works with node assert 1`] = ` 62 | test('assert.notDeepStrictEqual', () => { at __tests__/node_assertion_error.test.js:59:10 + + at __tests__/node_assertion_error.test.js:58:1 ● assert.notDeepStrictEqual @@ -638,12 +704,24 @@ exports[`works with node assert 1`] = ` 66 | test('assert.ifError', () => { at __tests__/node_assertion_error.test.js:63:10 + + at __tests__/node_assertion_error.test.js:62:1 ● assert.ifError thrown: 1 + + 64 | }); + 65 | + > 66 | test('assert.ifError', () => { + | ^ + 67 | assert.ifError(1); + 68 | }); + 69 | + - at packages/jest-jasmine2/build/expectation_result_factory.js:54:47 + at packages/jest-jasmine2/build/jasmine/Spec.js:79:20 + at __tests__/node_assertion_error.test.js:66:1 ● assert.doesNotThrow @@ -665,6 +743,8 @@ exports[`works with node assert 1`] = ` 74 | }); at __tests__/node_assertion_error.test.js:71:10 + + at __tests__/node_assertion_error.test.js:70:1 ● assert.throws @@ -684,6 +764,8 @@ exports[`works with node assert 1`] = ` 79 | at __tests__/node_assertion_error.test.js:77:10 + + at __tests__/node_assertion_error.test.js:76:1 " `; diff --git a/packages/jest-jasmine2/src/expectation_result_factory.js b/packages/jest-jasmine2/src/expectation_result_factory.js index 9ace1d7719cc..99e3ee15c00a 100644 --- a/packages/jest-jasmine2/src/expectation_result_factory.js +++ b/packages/jest-jasmine2/src/expectation_result_factory.js @@ -31,13 +31,16 @@ function messageFormatter({error, message, passed}) { return `thrown: ${prettyFormat(error, {maxDepth: 3})}`; } -function stackFormatter(options, errorMessage) { +function stackFormatter(options, initError, errorMessage) { if (options.passed) { return ''; } - const stack = - (options.error && options.error.stack) || new Error(errorMessage).stack; - return stack; + + if ((options.error && options.error.stack)) { + return options.error.stack; + } + + return errorMessage + '\n' + initError.stack; } type Options = { @@ -49,9 +52,9 @@ type Options = { message?: string, }; -export default function expectationResultFactory(options: Options) { +export default function expectationResultFactory(options: Options, initError) { const message = messageFormatter(options); - const stack = stackFormatter(options, message); + const stack = stackFormatter(options, initError, message); if (options.passed) { return { diff --git a/packages/jest-jasmine2/src/jasmine/Env.js b/packages/jest-jasmine2/src/jasmine/Env.js index d7cac885494d..6adaab2e8113 100644 --- a/packages/jest-jasmine2/src/jasmine/Env.js +++ b/packages/jest-jasmine2/src/jasmine/Env.js @@ -398,12 +398,6 @@ export default function(j$) { } const specFactory = function(description, fn, suite, timeout) { - const timeoutError = new Error(); - - if (Error.captureStackTrace) { - Error.captureStackTrace(timeoutError, specFactory); - } - totalSpecsDefined++; const spec = new j$.Spec({ id: getNextSpecId(), @@ -426,7 +420,6 @@ export default function(j$) { timeout() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }, - timeoutError, }, throwOnExpectationFailure, }); diff --git a/packages/jest-jasmine2/src/jasmine/Spec.js b/packages/jest-jasmine2/src/jasmine/Spec.js index 251ed59b2a9a..7cdfe84abee9 100644 --- a/packages/jest-jasmine2/src/jasmine/Spec.js +++ b/packages/jest-jasmine2/src/jasmine/Spec.js @@ -59,6 +59,11 @@ export default function Spec(attrs: Object) { this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.initError = new Error(); + this.initError.name = ''; + + this.queueableFn.initError = this.initError; + this.result = { id: this.id, description: this.description, @@ -71,7 +76,7 @@ export default function Spec(attrs: Object) { } Spec.prototype.addExpectationResult = function(passed, data, isError) { - const expectationResult = expectationResultFactory(data); + const expectationResult = expectationResultFactory(data, this.initError); if (passed) { this.result.passedExpectations.push(expectationResult); } else { diff --git a/packages/jest-jasmine2/src/queue_runner.js b/packages/jest-jasmine2/src/queue_runner.js index 166759fba83d..5155a43374ef 100644 --- a/packages/jest-jasmine2/src/queue_runner.js +++ b/packages/jest-jasmine2/src/queue_runner.js @@ -34,7 +34,7 @@ export default function queueRunner(options: Options) { onCancel(resolve); }); - const mapper = ({fn, timeout, timeoutError = new Error()}) => { + const mapper = ({fn, timeout, initError = new Error()}) => { let promise = new Promise(resolve => { const next = function(err) { if (err) { @@ -69,11 +69,11 @@ export default function queueRunner(options: Options) { options.clearTimeout, options.setTimeout, () => { - timeoutError.message = + initError.message = 'Timeout - Async callback was not invoked within the ' + timeoutMs + 'ms timeout specified by jest.setTimeout.'; - options.onException(timeoutError); + options.onException(initError); }, ); }; From d01429f531e7fc938a3ac5bcdfa34a288d1c331a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 23:32:32 +0200 Subject: [PATCH 6/7] more node 6 stack cleanup --- .../expect-async-matcher.test.js.snap | 14 ++------------ .../__tests__/expect-async-matcher.test.js | 8 +++++++- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap b/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap index b796ec647e71..3076bd3d1d5d 100644 --- a/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/expect-async-matcher.test.js.snap @@ -1,8 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`shows the correct errors in stderr when failing tests 1`] = ` -Object { - "rest": "FAIL __tests__/failure.test.js +"FAIL __tests__/failure.test.js ✕ fail with expected non promise values ✕ fail with expected non promise values and not ✕ fail with expected promise values @@ -45,7 +44,6 @@ Object { 26 | ); 27 | }); - at packages/expect/build/index.js:165:22 at __tests__/failure.test.js:24:54 at __tests__/failure.test.js:11:191 at __tests__/failure.test.js:11:437 @@ -68,18 +66,10 @@ Object { 32 | ); 33 | }); - at packages/expect/build/index.js:165:22 at __tests__/failure.test.js:30:61 at __tests__/failure.test.js:11:191 at __tests__/failure.test.js:11:437 at __tests__/failure.test.js:11:99 -", - "summary": "Test Suites: 1 failed, 1 total -Tests: 4 failed, 4 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /failure.test.js/i. -", -} +" `; diff --git a/integration-tests/__tests__/expect-async-matcher.test.js b/integration-tests/__tests__/expect-async-matcher.test.js index 24ac7e1d747f..85e668718385 100644 --- a/integration-tests/__tests__/expect-async-matcher.test.js +++ b/integration-tests/__tests__/expect-async-matcher.test.js @@ -26,5 +26,11 @@ test('shows the correct errors in stderr when failing tests', () => { const result = runJest(dir, ['failure.test.js']); expect(result.status).toBe(1); - expect(extractSummary(result.stderr)).toMatchSnapshot(); + + const rest = extractSummary(result.stderr) + .rest.split('\n') + .filter(line => line.indexOf('packages/expect/build/index.js') === -1) + .join('\n'); + + expect(rest).toMatchSnapshot(); }); From b9f27591e60492e8534baa4113d86b3fb1f6efa0 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 16 Apr 2018 23:35:50 +0200 Subject: [PATCH 7/7] please flow gods --- .../jest-jasmine2/src/expectation_result_factory.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/jest-jasmine2/src/expectation_result_factory.js b/packages/jest-jasmine2/src/expectation_result_factory.js index 99e3ee15c00a..32e04989a969 100644 --- a/packages/jest-jasmine2/src/expectation_result_factory.js +++ b/packages/jest-jasmine2/src/expectation_result_factory.js @@ -36,11 +36,15 @@ function stackFormatter(options, initError, errorMessage) { return ''; } - if ((options.error && options.error.stack)) { + if (options.error && options.error.stack) { return options.error.stack; } - return errorMessage + '\n' + initError.stack; + if (initError) { + return errorMessage + '\n' + initError.stack; + } + + return new Error(errorMessage).stack; } type Options = { @@ -52,7 +56,10 @@ type Options = { message?: string, }; -export default function expectationResultFactory(options: Options, initError) { +export default function expectationResultFactory( + options: Options, + initError?: Error, +) { const message = messageFormatter(options); const stack = stackFormatter(options, initError, message);