Skip to content

Commit

Permalink
Update toThrow() to be able to use Error.cause (#13606)
Browse files Browse the repository at this point in the history
Co-authored-by: Simen Bekkhus <sbekkhus91@gmail.com>
  • Loading branch information
ibuibu and SimenB committed Feb 15, 2023
1 parent af5d5a1 commit 0621b2b
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

### Features

- `[expect]` Update `toThrow()` to be able to use [error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) ([#13606](https://github.com/facebook/jest/pull/13606))
- `[jest-core]` allow to use workerIdleMemoryLimit with only 1 worker or runInBand option ([#13846](https://github.com/facebook/jest/pull/13846))
- `[jest-message-util]` Add support for [error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) ([#13868](https://github.com/facebook/jest/pull/13868) & [#13912](https://github.com/facebook/jest/pull/13912))
- `[jest-runtime]` Revert `import assertions` for JSON modules as it's been relegated to Stage 2 ([#13911](https://github.com/facebook/jest/pull/13911))
Expand Down
54 changes: 53 additions & 1 deletion packages/expect/src/__tests__/toThrowMatchers.test.ts
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {alignedAnsiStyleSerializer} from '@jest/test-utils';
import {alignedAnsiStyleSerializer, onNodeVersions} from '@jest/test-utils';
import jestExpect from '../';

expect.addSnapshotSerializer(alignedAnsiStyleSerializer);
Expand Down Expand Up @@ -278,6 +278,58 @@ describe.each(['toThrowError', 'toThrow'] as const)('%s', toThrow => {
});
});

describe('error message and cause', () => {
const errorA = new Error('A');
const errorB = new Error('B', {cause: errorA});
const expected = new Error('good', {cause: errorB});

describe('pass', () => {
test('isNot false', () => {
jestExpect(() => {
throw new Error('good', {cause: errorB});
})[toThrow](expected);
});

test('isNot true, incorrect message', () => {
jestExpect(() => {
throw new Error('bad', {cause: errorB});
}).not[toThrow](expected);
});

onNodeVersions('>=16.9.0', () => {
test('isNot true, incorrect cause', () => {
jestExpect(() => {
throw new Error('good', {cause: errorA});
}).not[toThrow](expected);
});
});
});

describe('fail', () => {
onNodeVersions('>=16.9.0', () => {
test('isNot false, incorrect message', () => {
expect(() =>
jestExpect(() => {
throw new Error('bad', {cause: errorB});
})[toThrow](expected),
).toThrow(
/^(?=.*Expected message and cause: ).*Received message and cause: /s,
);
});

test('isNot true, incorrect cause', () => {
expect(() =>
jestExpect(() => {
throw new Error('good', {cause: errorA});
})[toThrow](expected),
).toThrow(
/^(?=.*Expected message and cause: ).*Received message and cause: /s,
);
});
});
});
});

describe('asymmetric', () => {
describe('any-Class', () => {
describe('pass', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/expect/src/__tests__/tsconfig.json
@@ -1,5 +1,8 @@
{
"extends": "../../../../tsconfig.test.json",
"compilerOptions": {
"lib": ["es2022.error"]
},
"include": ["./**/*"],
"references": [{"path": "../../"}, {"path": "../../../test-utils"}]
}
54 changes: 45 additions & 9 deletions packages/expect/src/toThrowMatchers.ts
Expand Up @@ -225,14 +225,23 @@ const toThrowExpectedObject = (
thrown: Thrown | null,
expected: Error,
): SyncExpectationResult => {
const pass = thrown !== null && thrown.message === expected.message;
const expectedMessageAndCause = createMessageAndCause(expected);
const thrownMessageAndCause =
thrown !== null ? createMessageAndCause(thrown.value) : null;
const pass =
thrown !== null &&
thrown.message === expected.message &&
thrownMessageAndCause === expectedMessageAndCause;

const message = pass
? () =>
// eslint-disable-next-line prefer-template
matcherHint(matcherName, undefined, undefined, options) +
'\n\n' +
formatExpected('Expected message: not ', expected.message) +
formatExpected(
`Expected ${messageAndCause(expected)}: not `,
expectedMessageAndCause,
) +
(thrown !== null && thrown.hasMessage
? formatStack(thrown)
: formatReceived('Received value: ', thrown, 'value'))
Expand All @@ -242,22 +251,27 @@ const toThrowExpectedObject = (
'\n\n' +
(thrown === null
? // eslint-disable-next-line prefer-template
formatExpected('Expected message: ', expected.message) +
formatExpected(
`Expected ${messageAndCause(expected)}: `,
expectedMessageAndCause,
) +
'\n' +
DID_NOT_THROW
: thrown.hasMessage
? // eslint-disable-next-line prefer-template
printDiffOrStringify(
expected.message,
thrown.message,
'Expected message',
'Received message',
expectedMessageAndCause,
thrownMessageAndCause,
`Expected ${messageAndCause(expected)}`,
`Received ${messageAndCause(thrown.value)}`,
true,
) +
'\n' +
formatStack(thrown)
: formatExpected('Expected message: ', expected.message) +
formatReceived('Received value: ', thrown, 'value'));
: formatExpected(
`Expected ${messageAndCause(expected)}: `,
expectedMessageAndCause,
) + formatReceived('Received value: ', thrown, 'value'));

return {message, pass};
};
Expand Down Expand Up @@ -447,4 +461,26 @@ const formatStack = (thrown: Thrown | null) =>
},
);

function createMessageAndCauseMessage(error: Error): string {
if (error.cause instanceof Error) {
return `{ message: ${error.message}, cause: ${createMessageAndCauseMessage(
error.cause,
)}}`;
}

return `{ message: ${error.message} }`;
}

function createMessageAndCause(error: Error) {
if (error.cause instanceof Error) {
return createMessageAndCauseMessage(error);
}

return error.message;
}

function messageAndCause(error: Error) {
return error.cause === undefined ? 'message' : 'message and cause';
}

export default matchers;
2 changes: 1 addition & 1 deletion packages/expect/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"lib": ["es2020", "dom"],
"lib": ["es2020", "es2021.promise", "es2022.error", "dom"],
"rootDir": "src",
"outDir": "build"
},
Expand Down

0 comments on commit 0621b2b

Please sign in to comment.