Skip to content

Commit

Permalink
feat(jest-snapshot): Support Error.cause in snapshots (#13965)
Browse files Browse the repository at this point in the history
  • Loading branch information
dubzzz committed Sep 20, 2023
1 parent 0081f94 commit 571b230
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- `[jest-environment-jsdom]` [**BREAKING**] Upgrade JSDOM to v22 ([#13825](https://github.com/jestjs/jest/pull/13825))
- `[@jest/fake-timers]` [**BREAKING**] Upgrade `@sinonjs/fake-timers` to v11 ([#14544](https://github.com/jestjs/jest/pull/14544))
- `[@jest/schemas]` Upgrade `@sinclair/typebox` to v0.31 ([#14072](https://github.com/jestjs/jest/pull/14072))
- `[jest-snapshot]` [**BREAKING**] Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in snapshots ([#13965](https://github.com/facebook/jest/pull/13965))
- `[pretty-format]` [**BREAKING**] Do not render empty string children (`''`) in React plugin ([#14470](https://github.com/facebook/jest/pull/14470))

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,50 @@ exports[`updates existing snapshot: updated snapshot 1`] = `
"
`;
exports[`works fine when function throws error with cause: initial write with cause 1`] = `
"test('works fine when function throws error', () => {
function ErrorWithCause(message, cause) {
const err = new Error(message, {cause});
if (err.cause !== cause) {
// cause does not exist in old versions of node
err.cause = cause;
}
return err;
}
expect(() => {
throw ErrorWithCause(
'apple',
ErrorWithCause('banana', ErrorWithCause('orange')),
);
}).toThrowErrorMatchingInlineSnapshot(\`
"apple
Cause: banana
Cause: orange"
\`);
});
"
`;
exports[`works fine when function throws error with string cause: initial write with cause 1`] = `
"test('works fine when function throws error', () => {
function ErrorWithCause(message, cause) {
const err = new Error(message, {cause});
if (err.cause !== cause) {
// cause does not exist in old versions of node
err.cause = cause;
}
return err;
}
expect(() => {
throw ErrorWithCause('apple', 'here is a cause');
}).toThrowErrorMatchingInlineSnapshot(\`
"apple
Cause: here is a cause"
\`);
});
"
`;
exports[`works fine when function throws error: initial write 1`] = `
"test('works fine when function throws error', () => {
expect(() => {
Expand Down
63 changes: 63 additions & 0 deletions e2e/__tests__/toThrowErrorMatchingInlineSnapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,69 @@ test('works fine when function throws error', () => {
}
});

test('works fine when function throws error with cause', () => {
const filename = 'works-fine-when-function-throws-error-with-cause.test.js';
const template = makeTemplate(`
test('works fine when function throws error', () => {
function ErrorWithCause(message, cause) {
const err = new Error(message, {cause});
if (err.cause !== cause) {
// cause does not exist in old versions of node
err.cause = cause;
}
return err;
}
expect(() => {
throw ErrorWithCause('apple',
ErrorWithCause('banana',
ErrorWithCause('orange')
)
);
})
.toThrowErrorMatchingInlineSnapshot();
});
`);

{
writeFiles(TESTS_DIR, {[filename]: template()});
const {stderr, exitCode} = runJest(DIR, ['-w=1', '--ci=false', filename]);
const fileAfter = readFile(filename);
expect(stderr).toMatch('1 snapshot written from 1 test suite.');
expect(fileAfter).toMatchSnapshot('initial write with cause');
expect(exitCode).toBe(0);
}
});

test('works fine when function throws error with string cause', () => {
const filename =
'works-fine-when-function-throws-error-with-string-cause.test.js';
const template = makeTemplate(`
test('works fine when function throws error', () => {
function ErrorWithCause(message, cause) {
const err = new Error(message, {cause});
if (err.cause !== cause) {
// cause does not exist in old versions of node
err.cause = cause;
}
return err;
}
expect(() => {
throw ErrorWithCause('apple', 'here is a cause');
})
.toThrowErrorMatchingInlineSnapshot();
});
`);

{
writeFiles(TESTS_DIR, {[filename]: template()});
const {stderr, exitCode} = runJest(DIR, ['-w=1', '--ci=false', filename]);
const fileAfter = readFile(filename);
expect(stderr).toMatch('1 snapshot written from 1 test suite.');
expect(fileAfter).toMatchSnapshot('initial write with cause');
expect(exitCode).toBe(0);
}
});

test('updates existing snapshot', () => {
const filename = 'updates-existing-snapshot.test.js';
const template = makeTemplate(`
Expand Down
16 changes: 15 additions & 1 deletion packages/jest-snapshot/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {types} from 'util';
import * as fs from 'graceful-fs';
import type {Config} from '@jest/types';
import type {MatcherFunctionWithContext} from 'expect';
Expand Down Expand Up @@ -518,12 +519,25 @@ const _toThrowErrorMatchingSnapshot = (
);
}

let message = error.message;
while ('cause' in error) {
error = error.cause;
if (types.isNativeError(error) || error instanceof Error) {
message += `\nCause: ${error.message}`;
} else {
if (typeof error === 'string') {
message += `\nCause: ${error}`;
}
break;
}
}

return _toMatchSnapshot({
context,
hint,
inlineSnapshot,
isInline,
matcherName,
received: error.message,
received: message,
});
};

0 comments on commit 571b230

Please sign in to comment.