Skip to content
Merged
12 changes: 12 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,18 @@
"code"
]
},
{
"login": "natealcedo",
"name": "Alcedo Nathaniel De Guzman Jr",
"avatar_url": "https://avatars0.githubusercontent.com/u/18214059?v=4",
"profile": "https://twitter.com/natealcedo",
"contributions": [
"code",
"doc",
"example",
"test"
],
},
{
"login": "moredip",
"name": "Pete Hodgson",
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ If you've come here to help contribute - Thanks! Take a look at the [contributin
- Further proposals in [#117](https://github.com/jest-community/jest-extended/issues/117) PRs welcome
- [Function](#function)
- [.toBeFunction()](#tobefunction)
- [.toThrowWithMessage()](#tothrowwithmessagetype-message)
- [Mock](#mock)
- [.toHaveBeenCalledBefore()](#tohavebeencalledbefore)
- [.toHaveBeenCalledAfter()](#tohavebeencalledafter)
Expand Down Expand Up @@ -180,7 +181,7 @@ expect().fail('test should fail');

#### .toBeEmpty()

Use `.toBeEmpty` when checking if a `String` `''`, `Array` `[]`, `Object` `{}`, or `[Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Built-in_iterables)` is empty. Because `toBeEmpty` supports checking for emptiness of Iterables, you can use it to check whether a `Map`, or `Set` is empty, as well as checking that a generator yields no values.
Use `.toBeEmpty` when checking if a `String` `''`, `Array` `[]`, `Object` `{}`, or `[Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#Built-in_iterables)` is empty. Because `toBeEmpty` supports checking for emptiness of Iterables, you can use it to check whether a `Map`, or `Set` is empty, as well as checking that a generator yields no values.

```js
test('passes when given an empty string', () => {
Expand Down Expand Up @@ -409,6 +410,30 @@ test('passes when value is a function', () => {
});
```

#### .toThrowWithMessage(type, message)

Use `.toThrowWithMessage` when checking if a callback function throws an error with a given error type and given error message. Message can either be a `String` or a `RegExp`.

```js
test('throws an error of type TypeError with message "hello world"', () => {
expect(() => {
throw TypeError("hello world");
}).toThrowWithMessage(TypeError, "hello world");

expect(() => {
throw TypeError("hello world");
}).toThrowWithMessage(TypeError, /hello world/);

expect(() => {
throw TypeError("hello world 2");
}).not.toThrowWithMessage(TypeError, "hello world");

expect(() => {
throw TypeError("hello world 2");
}).not.toThrowWithMessage(TypeError, /hello world/);
});
```

### Mock

#### .toHaveBeenCalledBefore()
Expand Down Expand Up @@ -874,7 +899,7 @@ test('passes when value includes all substrings', () => {
| [<img src="https://avatars1.githubusercontent.com/u/10330923?v=4" width="100px;"/><br /><sub>Amish Shah</sub>](https://hydrabolt.me/)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=hydrabolt "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=hydrabolt "Tests") | [<img src="https://avatars3.githubusercontent.com/u/2045206?v=4" width="100px;"/><br /><sub>Dave Cooper</sub>](http://davecooper.org)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=grug "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=grug "Tests") | [<img src="https://avatars3.githubusercontent.com/u/3630495?v=4" width="100px;"/><br /><sub>Swann Polydor</sub>](https://github.com/soueuls)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=soueuls "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=soueuls "Tests") | [<img src="https://avatars1.githubusercontent.com/u/2027003?v=4" width="100px;"/><br /><sub>vikneshwar</sub>](https://github.com/vikneshwar)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=vikneshwar "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=vikneshwar "Tests") | [<img src="https://avatars1.githubusercontent.com/u/1243921?v=4" width="100px;"/><br /><sub>Budi Irawan</sub>](http://budiirawan.com)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=deerawan "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=deerawan "Tests") | [<img src="https://avatars2.githubusercontent.com/u/980783?v=4" width="100px;"/><br /><sub>Tejas Bubane</sub>](http://foss-geek.blogspot.com/)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=tejasbubane "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=tejasbubane "Tests") [📖](https://github.com/mattphillips/jest-extended/commits?author=tejasbubane "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/13134653?v=4" width="100px;"/><br /><sub>Subinoy Ghosh</sub>](https://github.com/subinoy7)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=subinoy7 "Code") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=subinoy7 "Tests") |
| [<img src="https://avatars1.githubusercontent.com/u/1404810?v=4" width="100px;"/><br /><sub>Simen Bekkhus</sub>](https://github.com/SimenB)<br />[📖](https://github.com/mattphillips/jest-extended/commits?author=SimenB "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/49038?v=4" width="100px;"/><br /><sub>Orta</sub>](http://orta.io)<br />[📖](https://github.com/mattphillips/jest-extended/commits?author=orta "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/17221813?v=4" width="100px;"/><br /><sub>Tom</sub>](https://jsdevtom.com)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=jsdevtom "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=jsdevtom "Documentation") [💡](#example-jsdevtom "Examples") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=jsdevtom "Tests") | [<img src="https://avatars0.githubusercontent.com/u/15064535?v=4" width="100px;"/><br /><sub>Lucian Buzzo</sub>](https://github.com/LucianBuzzo)<br /> | [<img src="https://avatars3.githubusercontent.com/u/2997844?v=4" width="100px;"/><br /><sub>Thiago Delgado Pinto</sub>](https://github.com/thiagodp)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=thiagodp "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=thiagodp "Documentation") [💡](#example-thiagodp "Examples") [🤔](#ideas-thiagodp "Ideas, Planning, & Feedback") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=thiagodp "Tests") | [<img src="https://avatars0.githubusercontent.com/u/3042904?v=4" width="100px;"/><br /><sub>Ragnar Laud</sub>](https://github.com/xprn)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=xprn "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=xprn "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/3047126?v=4" width="100px;"/><br /><sub>Luiz Américo</sub>](https://github.com/blikblum)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=blikblum "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/615334?v=4" width="100px;"/><br /><sub>Frederick Fogerty</sub>](https://github.com/frederickfogerty)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=frederickfogerty "Code") [🤔](#ideas-frederickfogerty "Ideas, Planning, & Feedback") | [<img src="https://avatars1.githubusercontent.com/u/10714808?v=4" width="100px;"/><br /><sub>Benjamin Kay</sub>](https://github.com/benjaminkay93)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=benjaminkay93 "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=benjaminkay93 "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/868844?v=4" width="100px;"/><br /><sub>Gilles De Mey</sub>](https://demey.io)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=gillesdemey "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=gillesdemey "Documentation") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=gillesdemey "Tests") | [<img src="https://avatars0.githubusercontent.com/u/50928?v=4" width="100px;"/><br /><sub>Deniz Dogan</sub>](https://github.com/denizdogan)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=denizdogan "Code") | [<img src="https://avatars1.githubusercontent.com/u/13043635?v=4" width="100px;"/><br /><sub>Mikey Powers</sub>](https://github.com/mvpowers)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=mvpowers "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=mvpowers "Documentation") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=mvpowers "Tests") | [<img src="https://avatars2.githubusercontent.com/u/26580?v=4" width="100px;"/><br /><sub>Tony Trinh</sub>](https://github.com/tony19)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=tony19 "Code") | [<img src="https://avatars2.githubusercontent.com/u/2844046?v=4" width="100px;"/><br /><sub>Nikita Kurpas</sub>](https://github.com/NikitaKurpas)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=NikitaKurpas "Code") |
| [<img src="https://avatars1.githubusercontent.com/u/65444?v=4" width="100px;"/><br /><sub>Pete Hodgson</sub>](http://thepete.net)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=moredip "Code") |
| [<img src="https://avatars0.githubusercontent.com/u/18214059?v=4" width="100px;"/><br /><sub>Alcedo Nathaniel De Guzman Jr</sub>](https://twitter.com/natealcedo)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=natealcedo "Code") [📖](https://github.com/mattphillips/jest-extended/commits?author=natealcedo "Documentation") [💡](#example-natealcedo "Examples") [⚠️](https://github.com/mattphillips/jest-extended/commits?author=natealcedo "Tests") | [<img src="https://avatars1.githubusercontent.com/u/65444?v=4" width="100px;"/><br /><sub>Pete Hodgson</sub>](http://thepete.net)<br />[💻](https://github.com/mattphillips/jest-extended/commits?author=moredip "Code") |
<!-- ALL-CONTRIBUTORS-LIST:END -->

## LICENSE
Expand Down
68 changes: 68 additions & 0 deletions src/matchers/toThrowWithMessage/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.toThrowWithMessage fails when a callback function is not a function 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Received value must be a function but instead \\"2\\" was found"
`;

exports[`.toThrowWithMessage fails when a callback provided doesnt throw an error 1`] = `
"Expected the function to throw an error.
But it didn't throw anything."
`;

exports[`.toThrowWithMessage fails when a wrong type of error is thrown 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Expected to throw:
<green>[TypeError: Expected message]</>
Thrown:
<red>[SyntaxError: Expected message]</>
"
`;

exports[`.toThrowWithMessage fails when callback function is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Received value must be a function but instead \\"undefined\\" was found"
`;

exports[`.toThrowWithMessage fails when error message is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Message argument is required. "
`;

exports[`.toThrowWithMessage fails when error message provided is not a string or regex 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Unexpected argument for message
Expected: \\"string\\" or \\"regexp
Got: \\"2\\""
`;

exports[`.toThrowWithMessage fails when error type is not provided 1`] = `
"<dim>expect(</><red>function</><dim>).toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Expected type to be a function but instead \\"undefined\\" was found"
`;

exports[`.toThrowWithMessage passes when given an Error with a regex error message 1`] = `
"<dim>expect(</><red>function</><dim>).not.toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Expected not to throw:
<green>[TypeError: /Expected message/]</>
Thrown:
<red>[TypeError: Expected message]</>
"
`;

exports[`.toThrowWithMessage passes when given an Error with a string error message 1`] = `
"<dim>expect(</><red>function</><dim>).not.toThrowWithMessage(</><green>type</>, <green>message</><dim>)</>

Expected not to throw:
<green>[TypeError: Expected message]</>
Thrown:
<red>[TypeError: Expected message]</>
"
`;
80 changes: 80 additions & 0 deletions src/matchers/toThrowWithMessage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils';

import predicate from './predicate';

const positiveHint = matcherHint('.toThrowWithMessage', 'function', 'type', { secondArgument: 'message' });
const negativeHint = matcherHint('.not.toThrowWithMessage', 'function', 'type', { secondArgument: 'message' });

const passMessage = (received, expected) => () =>
negativeHint +
'\n\n' +
'Expected not to throw:\n' +
` ${printExpected(expected)}\n` +
'Thrown:\n' +
` ${printReceived(received)}\n`;

const failMessage = (received, expected) => () =>
positiveHint +
'\n\n' +
'Expected to throw:\n' +
` ${printExpected(expected)}\n` +
'Thrown:\n' +
` ${printReceived(received)}\n`;

export default {
toThrowWithMessage: (callback, type, message) => {
if (!callback || typeof callback !== 'function') {
return {
pass: false,
message: () => positiveHint + '\n\n' + `Received value must be a function but instead "${callback}" was found`
};
}

if (!type || typeof type !== 'function') {
return {
pass: false,
message: () => positiveHint + '\n\n' + `Expected type to be a function but instead "${type}" was found`
};
}

if (!message) {
return {
pass: false,
message: () => positiveHint + '\n\n' + ' Message argument is required. '
};
}

if (typeof message !== 'string' && !(message instanceof RegExp)) {
return {
pass: false,
message: () =>
positiveHint +
'\n\n' +
'Unexpected argument for message\n' +
'Expected: "string" or "regexp\n' +
`Got: "${message}"`
};
}

let error;
try {
callback();
} catch (e) {
error = e;
}

if (!error) {
return {
pass: false,
message: () => 'Expected the function to throw an error.\n' + "But it didn't throw anything."
};
}

const pass = predicate(error, type, message);
if (pass) {
return { pass: true, message: passMessage(error, new type(message)) };
}

return { pass: false, message: failMessage(error, new type(message)) };
}
};
85 changes: 85 additions & 0 deletions src/matchers/toThrowWithMessage/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import matcher from './';
const { toThrowWithMessage } = matcher;

expect.extend(matcher);

describe('.toThrowWithMessage', () => {
test('fails when callback function is not provided', () => {
const { pass, message } = toThrowWithMessage();
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when a callback function is not a function', () => {
const { pass, message } = toThrowWithMessage(2);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when error message is not provided', () => {
const callback = () => {};
const { pass, message } = toThrowWithMessage(callback, Error);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when error type is not provided', () => {
const callback = () => {};
const { pass, message } = toThrowWithMessage(callback);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when error message provided is not a string or regex', () => {
const callback = () => {};
const { pass, message } = toThrowWithMessage(callback, Error, 2);
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when a callback provided doesnt throw an error', () => {
const callback = () => {};
const { pass, message } = toThrowWithMessage(callback, Error, 'error');
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('fails when a wrong type of error is thrown', () => {
const callback = () => {
throw SyntaxError('Expected message');
};
const { pass, message } = toThrowWithMessage(callback, TypeError, 'Expected message');
expect(pass).toBe(false);
expect(message()).toMatchSnapshot();
});

test('passes when given an Error with a string error message', () => {
const callback = () => {
throw TypeError('Expected message');
};
const { pass, message } = toThrowWithMessage(callback, TypeError, 'Expected message');
expect(pass).toBe(true);
expect(message()).toMatchSnapshot();
});

test('passes when given an Error with a regex error message', () => {
const callback = () => {
throw TypeError('Expected message');
};
const { pass, message } = toThrowWithMessage(callback, TypeError, /Expected message/);
expect(pass).toBe(true);
expect(message()).toMatchSnapshot();
});

test('passes when given an Error with a string error message: end to end', () => {
expect(() => {
throw new TypeError('Expected message');
}).toThrowWithMessage(TypeError, 'Expected message');
});

test('passes when given an Error with a regex error message: end to end', () => {
expect(() => {
throw new SyntaxError('Expected message');
}).toThrowWithMessage(SyntaxError, /Expected message/);
});
});
6 changes: 6 additions & 0 deletions src/matchers/toThrowWithMessage/predicate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default (error, type, message) => {
if (message instanceof RegExp) {
return error && error instanceof type && message.test(error.message);
}
return error && error instanceof type && error.message === message;
};
Loading