Skip to content

Commit f39d750

Browse files
committed
Expect only overrides error stack for built-in matchers
1 parent e879099 commit f39d750

5 files changed

Lines changed: 51 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
## master
22

3-
None for now
4-
53
### Fixes
64

75
* `[babel-jest]` moduleFileExtensions not passed to babel transformer.
86
([#4637](https://github.com/facebook/jest/issues/4637))
7+
* Do not override `Error` stack (with `Error.captureStackTrace`) for custom matchers.
8+
([#5162](https://github.com/facebook/jest/pull/5162))
99

1010
### Features
1111

packages/expect/src/__tests__/stacktrace.test.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,25 @@ jestExpect.extend({
1616
pass: true,
1717
};
1818
},
19+
toCustomMatch(callback, expectation) {
20+
const actual = callback();
21+
22+
if (actual !== expectation) {
23+
return {
24+
pass: false,
25+
message: () => `Expected "${expectation}" but got "${actual}"`
26+
};
27+
}
28+
29+
return {pass: true};
30+
},
1931
});
2032

2133
it('stack trace points to correct location when using matchers', () => {
2234
try {
2335
jestExpect(true).toBe(false);
2436
} catch (error) {
25-
expect(error.stack).toContain('stacktrace.test.js:23');
37+
expect(error.stack).toContain('stacktrace.test.js:35');
2638
}
2739
});
2840

@@ -32,6 +44,22 @@ it('stack trace points to correct location when using nested matchers', () => {
3244
jestExpect(value).toBe(false);
3345
});
3446
} catch (error) {
35-
expect(error.stack).toContain('stacktrace.test.js:32');
47+
expect(error.stack).toContain('stacktrace.test.js:44');
48+
}
49+
});
50+
51+
it('stack trace points to correct location when throwing from a custom matcher', () => {
52+
try {
53+
jestExpect(() => {
54+
const foo = () => bar();
55+
const bar = () => baz();
56+
const baz = () => {
57+
throw new Error('Expected');
58+
};
59+
60+
foo();
61+
}).toCustomMatch('bar');
62+
} catch (error) {
63+
expect(error.stack).toContain('stacktrace.test.js:57');
3664
}
3765
});

packages/expect/src/index.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ const makeThrowingMatcher = (
214214
result = matcher.apply(matcherContext, [actual].concat(args));
215215
} catch (error) {
216216
if (
217+
matcher.__jestInternal === true &&
217218
!(error instanceof JestAssertionError) &&
218219
error.name !== 'PrettyFormatPluginError' &&
219220
// Guard for some environments (browsers) that do not support this feature.
@@ -252,7 +253,8 @@ const makeThrowingMatcher = (
252253
};
253254
};
254255

255-
expect.extend = (matchers: MatchersObject): void => setMatchers(matchers);
256+
expect.extend = (matchers: MatchersObject): void =>
257+
setMatchers(matchers, false);
256258

257259
expect.anything = anything;
258260
expect.any = any;
@@ -280,9 +282,9 @@ const _validateResult = result => {
280282
};
281283

282284
// add default jest matchers
283-
expect.extend(matchers);
284-
expect.extend(spyMatchers);
285-
expect.extend(toThrowMatchers);
285+
setMatchers(matchers, true);
286+
setMatchers(spyMatchers, true);
287+
setMatchers(toThrowMatchers, true);
286288

287289
expect.addSnapshotSerializer = () => void 0;
288290
expect.assertions = (expected: number) => {
@@ -296,4 +298,8 @@ expect.getState = getState;
296298
expect.setState = setState;
297299
expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors;
298300

301+
// Expose JestAssertionError for custom matchers
302+
// This enables them to preserve the stack for specific errors
303+
expect.JestAssertionError = JestAssertionError;
304+
299305
module.exports = (expect: Expect);

packages/expect/src/jest_matchers_object.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ export const setState = (state: Object) => {
3535

3636
export const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers;
3737

38-
export const setMatchers = (matchers: MatchersObject) => {
38+
export const setMatchers = (matchers: MatchersObject, isInternal: boolean) => {
39+
for (const key in matchers) {
40+
const matcher = matchers[key];
41+
Object.defineProperty(matcher, '__jestInternal', {
42+
value: isInternal,
43+
});
44+
}
45+
3946
Object.assign(global[JEST_MATCHERS_OBJECT].matchers, matchers);
4047
};

types/Matchers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type RawMatcherFn = (
1919
expected: any,
2020
actual: any,
2121
options: any,
22+
__jestInternal?: boolean,
2223
) => ExpectationResult;
2324

2425
export type ThrowingMatcherFn = (actual: any) => void;

0 commit comments

Comments
 (0)