Skip to content

Commit

Permalink
Feature: Add support for .rejects.toThrowWithMessage (#315)
Browse files Browse the repository at this point in the history
Gerkin authored Oct 11, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 870801c commit cbdbe11
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
@@ -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()
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>)</>
22 changes: 15 additions & 7 deletions src/matchers/toThrowWithMessage/index.js
Original file line number Diff line number Diff line change
@@ -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`,
};
}

@@ -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) {
87 changes: 87 additions & 0 deletions src/matchers/toThrowWithMessage/index.test.js
Original file line number Diff line number Diff line change
@@ -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.