Skip to content

Commit ccf3980

Browse files
fix: error cause bug
Closes #15316 #15111
1 parent 0a0a9f7 commit ccf3980

File tree

3 files changed

+63
-10
lines changed

3 files changed

+63
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
### Fixes
5151

52+
- `[expect]` Fix `error causes` bug ([#15339](https://github.com/jestjs/jest/pull/15339))
5253
- `[babel-plugin-jest-hoist]` Use `denylist` instead of the deprecated `blacklist` for Babel 8 support ([#14109](https://github.com/jestjs/jest/pull/14109))
5354
- `[expect]` Check error instance type for `toThrow/toThrowError` ([#14576](https://github.com/jestjs/jest/pull/14576))
5455
- `[expect]` Improve diff for failing `expect.objectContaining` ([#15038](https://github.com/jestjs/jest/pull/15038))

packages/expect/src/__tests__/toThrowMatchers.test.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,37 @@ describe('toThrow', () => {
307307
throw new Error('good', {cause: errorA});
308308
}).not.toThrow(expected);
309309
});
310+
311+
test('isNot false, compare Error with object', () => {
312+
jestExpect(() => {
313+
throw errorB;
314+
}).toThrow({
315+
cause: {
316+
message: 'A',
317+
},
318+
message: 'B',
319+
});
320+
});
321+
322+
test('isNot false, cause is string', () => {
323+
jestExpect(() => {
324+
throw new Error('Message', {cause: 'line 123'});
325+
}).toThrow({
326+
cause: 'line 123',
327+
message: 'Message',
328+
});
329+
});
330+
331+
test('isNot false, cause is object', () => {
332+
jestExpect(() => {
333+
throw new Error('Message', {
334+
cause: {prop1: true, prop2: false, prop3: null, prop4: undefined},
335+
});
336+
}).toThrow({
337+
cause: {prop1: true, prop2: false, prop3: null, prop4: undefined},
338+
message: 'Message',
339+
});
340+
});
310341
});
311342

312343
describe('fail', () => {

packages/expect/src/toThrowMatchers.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -467,22 +467,43 @@ const formatStack = (thrown: Thrown | null) =>
467467
},
468468
);
469469

470-
function createMessageAndCauseMessage(error: Error): string {
471-
if (error.cause instanceof Error) {
472-
return `{ message: ${error.message}, cause: ${createMessageAndCauseMessage(
473-
error.cause,
474-
)}}`;
470+
function createMessageAndCause(error: Error) {
471+
if (error.cause) {
472+
const seen = new WeakSet();
473+
return JSON.stringify(buildSerializeError(error), (_, value) => {
474+
if (value !== null && value === undefined && typeof value === 'object') {
475+
if (seen.has(value)) return;
476+
seen.add(value); // stop circular references
477+
}
478+
return value === undefined ? String(undefined) : value;
479+
});
475480
}
476481

477-
return `{ message: ${error.message} }`;
482+
return error.message;
478483
}
479484

480-
function createMessageAndCause(error: Error) {
481-
if (error.cause instanceof Error) {
482-
return createMessageAndCauseMessage(error);
485+
function buildSerializeError(error: {[key: string]: any}) {
486+
if (!isObject(error)) {
487+
return error;
483488
}
484489

485-
return error.message;
490+
const result: {[key: string]: any} = {};
491+
for (const name of Object.getOwnPropertyNames(error).sort()) {
492+
if (['stack', 'fileName', 'lineNumber'].includes(name)) {
493+
continue;
494+
}
495+
if (name === 'cause') {
496+
result[name] = buildSerializeError(error['cause']);
497+
continue;
498+
}
499+
result[name] = error[name];
500+
}
501+
502+
return result;
503+
}
504+
505+
function isObject(obj: unknown) {
506+
return !!obj && typeof obj === 'object';
486507
}
487508

488509
function messageAndCause(error: Error) {

0 commit comments

Comments
 (0)