Skip to content

Commit

Permalink
Support message testing function in throws & throwsAsync assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
il3ven committed May 16, 2022
1 parent f5caa8f commit e387cba
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 7 deletions.
10 changes: 8 additions & 2 deletions docs/03-assertions.md
Expand Up @@ -172,7 +172,10 @@ Assert that an error is thrown. `fn` must be a function which should throw. The

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: either a string, which is compared against the thrown error's message, or a regular expression, which is matched against this message
* `message`: the following types are valid:
* *string* - it is compared against the thrown error's message
* *regular expression* - it is matched against this message
* *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed
* `name`: the expected `.name` value of the thrown error
* `code`: the expected `.code` value of the thrown error

Expand Down Expand Up @@ -204,7 +207,10 @@ The thrown value *must* be an error. It is returned so you can run more assertio

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expectation.is`
* `message`: either a string, which is compared against the thrown error's message, or a regular expression, which is matched against this message
* `message`: the following types are valid:
* *string* - it is compared against the thrown error's message
* *regular expression* - it is matched against this message
* *function* - it is passed the thrown error message and must return a boolean for whether the assertion passed
* `name`: the expected `.name` value of the thrown error
* `code`: the expected `.code` value of the thrown error

Expand Down
22 changes: 20 additions & 2 deletions lib/assert.js
Expand Up @@ -106,10 +106,15 @@ function validateExpectations(assertion, expectations, numberArgs) { // eslint-d
});
}

if (hasOwnProperty(expectations, 'message') && typeof expectations.message !== 'string' && !(expectations.message instanceof RegExp)) {
if (
hasOwnProperty(expectations, 'message')
&& typeof expectations.message !== 'string'
&& !(expectations.message instanceof RegExp)
&& !(typeof expectations.message === 'function')
) {
throw new AssertionError({
assertion,
message: `The \`message\` property of the second argument to \`t.${assertion}()\` must be a string or regular expression`,
message: `The \`message\` property of the second argument to \`t.${assertion}()\` must be a string, regular expression or a function`,
values: [formatWithLabel('Called with:', expectations)],
});
}
Expand Down Expand Up @@ -230,6 +235,19 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
});
}

if (typeof expectations.message === 'function' && expectations.message(actual.message) === false) {
throw new AssertionError({
assertion,
message,
savedError,
actualStack,
values: [
formatWithLabel(`${prefix} unexpected exception:`, actual),
formatWithLabel('Expected message to return true:', expectations.message),
],
});
}

if (typeof expectations.code !== 'undefined' && actual.code !== expectations.code) {
throw new AssertionError({
assertion,
Expand Down
76 changes: 74 additions & 2 deletions test-tap/assert.js
Expand Up @@ -899,6 +899,78 @@ test('.throws()', gather(t => {
formatted: /null/,
}],
});

// Fails because the string in the message is incorrect
failsWith(
t,
() =>
assertions.throws(
() => {
throw new Error('error');
},
{message: 'my error'},
),
{
assertion: 'throws',
message: '',
values: [
{label: 'Function threw unexpected exception:', formatted: /error/},
{label: 'Expected message to equal:', formatted: /my error/},
],
},
);

passes(t, () => assertions.throws(() => {
throw new Error('error');
}, {message: 'error'}));

// Fails because the regular expression in the message is incorrect
failsWith(
t,
() =>
assertions.throws(
() => {
throw new Error('error');
},
{message: /my error/},
),
{
assertion: 'throws',
message: '',
values: [
{label: 'Function threw unexpected exception:', formatted: /error/},
{label: 'Expected message to match:', formatted: /my error/},
],
},
);

passes(t, () => assertions.throws(() => {
throw new Error('error');
}, {message: /error/}));

// Fails because the function in the message returns false
failsWith(
t,
() =>
assertions.throws(
() => {
throw new Error('error');
},
{message: () => false},
),
{
assertion: 'throws',
message: '',
values: [
{label: 'Function threw unexpected exception:', formatted: /error/},
{label: 'Expected message to return true:', formatted: /Function/},
],
},
);

passes(t, () => assertions.throws(() => {
throw new Error('error');
}, {message: () => true}));
}));

test('.throws() returns the thrown error', t => {
Expand Down Expand Up @@ -1066,7 +1138,7 @@ test('.throws() fails if passed a bad expectation', t => {

failsWith(t, () => assertions.throws(() => {}, {message: null}), {
assertion: 'throws',
message: 'The `message` property of the second argument to `t.throws()` must be a string or regular expression',
message: 'The `message` property of the second argument to `t.throws()` must be a string, regular expression or a function',
values: [{label: 'Called with:', formatted: /message: null/}],
});

Expand Down Expand Up @@ -1136,7 +1208,7 @@ test('.throwsAsync() fails if passed a bad expectation', t => {

failsWith(t, () => assertions.throwsAsync(() => {}, {message: null}), {
assertion: 'throwsAsync',
message: 'The `message` property of the second argument to `t.throwsAsync()` must be a string or regular expression',
message: 'The `message` property of the second argument to `t.throwsAsync()` must be a string, regular expression or a function',
values: [{label: 'Called with:', formatted: /message: null/}],
}, {expectBoolean: false});

Expand Down
2 changes: 1 addition & 1 deletion types/assertions.d.ts
Expand Up @@ -12,7 +12,7 @@ export type ThrowsExpectation = {
is?: Error;

/** The thrown error must have a message that equals the given string, or matches the regular expression. */
message?: string | RegExp;
message?: string | RegExp | ((message: string) => boolean);

/** The thrown error must have a name that equals the given string. */
name?: string;
Expand Down

0 comments on commit e387cba

Please sign in to comment.