Skip to content

Commit

Permalink
Feature: Add support for .rejects.toThrowWithMessage (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerkin authored Oct 11, 2021
1 parent 870801c commit cbdbe11
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 7 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,14 @@ test('throws an error of type TypeError with message "hello world"', () => {
});
```

This works for promise rejections too.

```js
test('throws an error of type TypeError with message "hello world"', async () => {
await expect(Promise.reject(new TypeError("hello world async")).rejects.toThrowWithMessage(TypeError, /hello world/);
});
```
### Mock
#### .toHaveBeenCalledBefore()
Expand Down
56 changes: 56 additions & 0 deletions src/matchers/toThrowWithMessage/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.toThrowWithMessage Async fails on rejects when a wrong type of error is thrown 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expected to throw:
<green>[TypeError: Expected message]</>
Thrown:
<red>[SyntaxError: Expected message]</>
"
`;
exports[`.toThrowWithMessage Async fails on rejects when error message is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Message argument is required. "
`;
exports[`.toThrowWithMessage Async fails on rejects when error message provided is not a string or regex 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Unexpected argument for message
Expected: \\"string\\" or \\"regexp
Got: \\"2\\""
`;
exports[`.toThrowWithMessage Async fails on rejects when error type is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expected type to be a function but instead \\"undefined\\" was found"
`;
exports[`.toThrowWithMessage Async fails on rejects when return value is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expected type to be a function but instead \\"undefined\\" was found"
`;
exports[`.toThrowWithMessage Async passes on rejects when given an Error with a regex error message 1`] = `
"<dim>expect(</><red>function</><dim>).not.toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expected not to throw:
<green>[TypeError: /Expected message/]</>
Thrown:
<red>[TypeError: Expected message]</>
"
`;
exports[`.toThrowWithMessage Async passes on rejects when given an Error with a string error message 1`] = `
"<dim>expect(</><red>function</><dim>).not.toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expected not to throw:
<green>[TypeError: Expected message]</>
Thrown:
<red>[TypeError: Expected message]</>
"
`;
exports[`.toThrowWithMessage fails when a callback function is not a function 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</><dim>, </><green>message</><dim>)</>
Expand Down
22 changes: 15 additions & 7 deletions src/matchers/toThrowWithMessage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ const failMessage = (received, expected) => () =>
` ${printReceived(received)}\n`;

export default {
toThrowWithMessage: (callback, type, message) => {
if (!callback || typeof callback !== 'function') {
toThrowWithMessage(callbackOrPromiseReturn, type, message) {
const isFromReject = this && this.promise === 'rejects'; // See https://github.com/facebook/jest/pull/7621#issue-244312550
if ((!callbackOrPromiseReturn || typeof callbackOrPromiseReturn !== 'function') && !isFromReject) {
return {
pass: false,
message: () => positiveHint + '\n\n' + `Received value must be a function but instead "${callback}" was found`,
message: () =>
positiveHint +
'\n\n' +
`Received value must be a function but instead "${callbackOrPromiseReturn}" was found`,
};
}

Expand Down Expand Up @@ -57,10 +61,14 @@ export default {
}

let error;
try {
callback();
} catch (e) {
error = e;
if (isFromReject) {
error = callbackOrPromiseReturn;
} else {
try {
callbackOrPromiseReturn();
} catch (e) {
error = e;
}
}

if (!error) {
Expand Down
87 changes: 87 additions & 0 deletions src/matchers/toThrowWithMessage/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,91 @@ describe('.toThrowWithMessage', () => {
throw new SyntaxError('Expected message');
}).toThrowWithMessage(SyntaxError, /Expected message/);
});

describe('Async', () => {
test('fails on rejects when return value is not provided', () => {
const { pass, message } = toThrowWithMessage.call({ promise: 'rejects' });
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails on rejects when error message is not provided', () => {
const rejectValue = true;
const { pass, message } = toThrowWithMessage.call({ promise: 'rejects' }, rejectValue, Error);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails on rejects when error type is not provided', () => {
const rejectValue = true;
const { pass, message } = toThrowWithMessage.call({ promise: 'rejects' }, rejectValue);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails on rejects when error message provided is not a string or regex', () => {
const rejectValue = true;
const { pass, message } = toThrowWithMessage.call({ promise: 'rejects' }, rejectValue, Error, 2);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails on rejects when a wrong type of error is thrown', () => {
const rejectValue = SyntaxError('Expected message');
const { pass, message } = toThrowWithMessage.call(
{ promise: 'rejects' },
rejectValue,
TypeError,
'Expected message',
);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('passes on rejects when given an Error with a string error message', () => {
const rejectValue = TypeError('Expected message');
const { pass, message } = toThrowWithMessage.call(
{ promise: 'rejects' },
rejectValue,
TypeError,
'Expected message',
);
expect(pass).toBe(true);
expect(message()).toMatchSnapshot();
});

test('passes on rejects when given an Error with a regex error message', () => {
const rejectValue = new TypeError('Expected message');
const { pass, message } = toThrowWithMessage.call(
{ promise: 'rejects' },
rejectValue,
TypeError,
/Expected message/,
);
expect(pass).toBe(true);
expect(message()).toMatchSnapshot();
});
test('passes on rejects with rejected promise when given an Error with a string error message: end to end', async () => {
await expect(Promise.reject(new SyntaxError('Expected message'))).rejects.toThrowWithMessage(
SyntaxError,
'Expected message',
);
});

test('passes on rejects with rejected promise when given an Error with a regex error message: end to end', async () => {
await expect(Promise.reject(new SyntaxError('Expected message'))).rejects.toThrowWithMessage(
SyntaxError,
/Expected message/,
);
});
test('fails on rejects with resolved promise: end to end', async () => {
expect.assertions(1);
await expect(
expect(Promise.resolve(new SyntaxError('Expected message'))).rejects.toThrowWithMessage(
SyntaxError,
/Expected message/,
),
).rejects.toThrowWithMessage(Error, /Received promise resolved instead of rejected/);
});
});
});

0 comments on commit cbdbe11

Please sign in to comment.