Skip to content

Commit 4e7079e

Browse files
SimenBcpojer
authored andcommitted
set correct stack trace for async rejections of non-errors (jestjs#6230)
1 parent 3705742 commit 4e7079e

File tree

6 files changed

+78
-11
lines changed

6 files changed

+78
-11
lines changed

integration-tests/__tests__/__snapshots__/failures.test.js.snap

+25-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ exports[`not throwing Error objects 5`] = `
130130
✕ Error during test
131131
✕ done(Error)
132132
✕ done(non-error)
133+
✕ returned promise rejection
133134
134135
● Promise thrown during test
135136
@@ -243,10 +244,33 @@ exports[`not throwing Error objects 5`] = `
243244
| ^
244245
37 | });
245246
38 |
247+
39 | test('returned promise rejection', () => {
246248
247-
at packages/jest-jasmine2/build/jasmine/Env.js:541:34
249+
at packages/jest-jasmine2/build/jasmine/Env.js:537:34
248250
at __tests__/during_tests.test.js:36:3
249251
252+
returned promise rejection
253+
254+
Failed: Object {
255+
\\"notAnError\\": Array [
256+
Object {
257+
\\"hello\\": true,
258+
\\"tooDeep\\": [Object],
259+
},
260+
],
261+
}
262+
263+
37 | });
264+
38 |
265+
> 39 | test('returned promise rejection', () => {
266+
| ^
267+
40 | return Promise.reject(deepObject);
268+
41 | });
269+
42 |
270+
271+
at packages/jest-jasmine2/build/jasmine_async.js:102:24
272+
at __tests__/during_tests.test.js:39:1
273+
250274
"
251275
`;
252276

integration-tests/failures/__tests__/during_tests.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ test('done(Error)', done => {
3535
test('done(non-error)', done => {
3636
done(deepObject);
3737
});
38+
39+
test('returned promise rejection', () => {
40+
return Promise.reject(deepObject);
41+
});

jest

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ This source code is licensed under the MIT license found in the
77
LICENSE file in the root directory of this source tree.
88
'
99

10-
node ./packages/jest-cli/bin/jest "$@"
10+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
11+
12+
node "$DIR"/packages/jest-cli/bin/jest "$@"
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import prettyFormat from 'pretty-format';
11+
12+
export default function isError(potentialError: any) {
13+
// duck-type Error, see #2549
14+
const isError =
15+
typeof potentialError === 'object' &&
16+
typeof potentialError.message === 'string' &&
17+
typeof potentialError.name === 'string';
18+
const message = isError
19+
? null
20+
: `Failed: ${prettyFormat(potentialError, {maxDepth: 3})}`;
21+
22+
return {isError, message};
23+
}

packages/jest-jasmine2/src/jasmine/Env.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3232

3333
import queueRunner from '../queue_runner';
3434
import treeProcessor from '../tree_processor';
35-
import prettyFormat from 'pretty-format';
35+
import checkIsError from '../is_error';
3636

3737
// Try getting the real promise object from the context, if available. Someone
3838
// could have overridden it in a test. Async functions return it implicitly.
@@ -547,12 +547,7 @@ export default function(j$) {
547547
};
548548

549549
this.fail = function(error) {
550-
// duck-type Error, see #2549
551-
const isError =
552-
typeof error === 'object' &&
553-
typeof error.message === 'string' &&
554-
typeof error.name === 'string';
555-
const message = `Failed: ${prettyFormat(error, {maxDepth: 3})}`;
550+
const {isError, message} = checkIsError(error);
556551

557552
currentRunnable().addExpectationResult(false, {
558553
matcherName: '',

packages/jest-jasmine2/src/jasmine_async.js

+21-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {Global} from 'types/Global';
1616

1717
import isGeneratorFn from 'is-generator-fn';
1818
import co from 'co';
19+
import checkIsError from './is_error';
1920

2021
function isPromise(obj) {
2122
return obj && typeof obj.then === 'function';
@@ -34,14 +35,23 @@ function promisifyLifeCycleFunction(originalFn, env) {
3435
return originalFn.call(env, fn, timeout);
3536
}
3637

38+
const extraError = new Error();
39+
3740
// We make *all* functions async and run `done` right away if they
3841
// didn't return a promise.
3942
const asyncFn = function(done) {
4043
const wrappedFn = isGeneratorFn(fn) ? co.wrap(fn) : fn;
4144
const returnValue = wrappedFn.call({});
4245

4346
if (isPromise(returnValue)) {
44-
returnValue.then(done.bind(null, null), done.fail);
47+
returnValue.then(done.bind(null, null), error => {
48+
const {isError, message} = checkIsError(error);
49+
50+
if (message) {
51+
extraError.message = message;
52+
}
53+
done.fail(isError ? error : extraError);
54+
});
4555
} else {
4656
done();
4757
}
@@ -67,12 +77,21 @@ function promisifyIt(originalFn, env) {
6777
return originalFn.call(env, specName, fn, timeout);
6878
}
6979

80+
const extraError = new Error();
81+
7082
const asyncFn = function(done) {
7183
const wrappedFn = isGeneratorFn(fn) ? co.wrap(fn) : fn;
7284
const returnValue = wrappedFn.call({});
7385

7486
if (isPromise(returnValue)) {
75-
returnValue.then(done.bind(null, null), done.fail);
87+
returnValue.then(done.bind(null, null), error => {
88+
const {isError, message} = checkIsError(error);
89+
90+
if (message) {
91+
extraError.message = message;
92+
}
93+
done.fail(isError ? error : extraError);
94+
});
7695
} else if (returnValue === undefined) {
7796
done();
7897
} else {

0 commit comments

Comments
 (0)