Skip to content

Commit

Permalink
Remove legacy .throws() and .throwsAsync() interfaces
Browse files Browse the repository at this point in the history
Fixes #2301.
  • Loading branch information
novemberborn committed Jan 5, 2020
1 parent 54ff130 commit 7f99aef
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 150 deletions.
18 changes: 9 additions & 9 deletions docs/03-assertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,19 @@ Assert that `value` is deeply equal to `expected`. See [Concordance](https://git

Assert that `value` is not deeply equal to `expected`. The inverse of `.deepEqual()`.

### `.throws(fn, [expected, [message]])`
### `.throws(fn, [expectation, [message]])`

Assert that an error is thrown. `fn` must be a function which should throw. The thrown value *must* be an error. It is returned so you can run more assertions against it.

`expected` can be a constructor, in which case the thrown error must be an instance of the constructor. It can be a string, which is compared against the thrown error's message, or a regular expression which is matched against this message. You can also specify a matcher object with one or more of the following properties:
`expectation` can be an object with one or more of the following properties:

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expected.is`
* `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
* `name`: the expected `.name` value of the thrown error
* `code`: the expected `.code` value of the thrown error

`expected` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `null`.
`expectation` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `null`.

Example:

Expand All @@ -231,27 +231,27 @@ const fn = () => {
test('throws', t => {
const error = t.throws(() => {
fn();
}, TypeError);
}, {instanceOf: TypeError});

t.is(error.message, '🦄');
});
```

### `.throwsAsync(thrower, [expected, [message]])`
### `.throwsAsync(thrower, [expectation, [message]])`

Assert that an error is thrown. `thrower` can be an async function which should throw, or a promise that should reject. This assertion must be awaited.

The thrown value *must* be an error. It is returned so you can run more assertions against it.

`expected` can be a constructor, in which case the thrown error must be an instance of the constructor. It can be a string, which is compared against the thrown error's message, or a regular expression which is matched against this message. You can also specify a matcher object with one or more of the following properties:
`expectation` can be an object with one or more of the following properties:

* `instanceOf`: a constructor, the thrown error must be an instance of
* `is`: the thrown error must be strictly equal to `expected.is`
* `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
* `name`: the expected `.name` value of the thrown error
* `code`: the expected `.code` value of the thrown error

`expected` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `null`.
`expectation` does not need to be specified. If you don't need it but do want to set an assertion message you have to specify `null`.

Example:

Expand Down
54 changes: 0 additions & 54 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,6 @@ export interface ThrowsAssertion {
*/
<ThrownError extends Error>(fn: () => any, expectations?: null, message?: string): ThrownError;

/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must be an instance of the given constructor.
*/
<ThrownError extends Error>(fn: () => any, constructor: Constructor, message?: string): ThrownError;

/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must have a message that matches the regular expression.
*/
<ThrownError extends Error>(fn: () => any, regex: RegExp, message?: string): ThrownError;

/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must have a message equal to `errorMessage`.
*/
<ThrownError extends Error>(fn: () => any, errorMessage: string, message?: string): ThrownError;

/**
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
* The error must satisfy all expectations.
Expand All @@ -287,24 +269,6 @@ export interface ThrowsAsyncAssertion {
*/
<ThrownError extends Error>(fn: () => PromiseLike<any>, expectations?: null, message?: string): Promise<ThrownError>;

/**
* Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error
* value. You must await the result. The error must be an instance of the given constructor.
*/
<ThrownError extends Error>(fn: () => PromiseLike<any>, constructor: Constructor, message?: string): Promise<ThrownError>;

/**
* Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error
* value. You must await the result. The error must have a message that matches the regular expression.
*/
<ThrownError extends Error>(fn: () => PromiseLike<any>, regex: RegExp, message?: string): Promise<ThrownError>;

/**
* Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error
* value. You must await the result. The error must have a message equal to `errorMessage`.
*/
<ThrownError extends Error>(fn: () => PromiseLike<any>, errorMessage: string, message?: string): Promise<ThrownError>;

/**
* Assert that the async function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error
* value. You must await the result. The error must satisfy all expectations.
Expand All @@ -317,24 +281,6 @@ export interface ThrowsAsyncAssertion {
*/
<ThrownError extends Error>(promise: PromiseLike<any>, expectations?: null, message?: string): Promise<ThrownError>;

/**
* Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the
* rejection reason. You must await the result. The error must be an instance of the given constructor.
*/
<ThrownError extends Error>(promise: PromiseLike<any>, constructor: Constructor, message?: string): Promise<ThrownError>;

/**
* Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the
* rejection reason. You must await the result. The error must have a message that matches the regular expression.
*/
<ThrownError extends Error>(promise: PromiseLike<any>, regex: RegExp, message?: string): Promise<ThrownError>;

/**
* Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the
* rejection reason. You must await the result. The error must have a message equal to `errorMessage`.
*/
<ThrownError extends Error>(promise: PromiseLike<any>, errorMessage: string, message?: string): Promise<ThrownError>;

/**
* Assert that the promise rejects with [an error](https://www.npmjs.com/package/is-error). If so, returns the
* rejection reason. You must await the result. The error must satisfy all expectations.
Expand Down
17 changes: 10 additions & 7 deletions lib/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,19 @@ function getErrorWithLongStackTrace() {
}

function validateExpectations(assertion, expectations, numArgs) { // eslint-disable-line complexity
if (typeof expectations === 'function') {
expectations = {instanceOf: expectations};
} else if (typeof expectations === 'string' || expectations instanceof RegExp) {
expectations = {message: expectations};
} else if (numArgs === 1 || expectations === null) {
if (numArgs === 1 || expectations === null) {
expectations = {};
} else if (typeof expectations !== 'object' || Array.isArray(expectations) || Object.keys(expectations).length === 0) {
} else if (
typeof expectations === 'function' ||
typeof expectations === 'string' ||
expectations instanceof RegExp ||
typeof expectations !== 'object' ||
Array.isArray(expectations) ||
Object.keys(expectations).length === 0
) {
throw new AssertionError({
assertion,
message: `The second argument to \`t.${assertion}()\` must be a function, string, regular expression, expectation object or \`null\``,
message: `The second argument to \`t.${assertion}()\` must be an expectation object or \`null\``,
values: [formatWithLabel('Called with:', expectations)]
});
} else {
Expand Down
111 changes: 54 additions & 57 deletions test/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -835,44 +835,6 @@ test('.throws()', gather(t => {
]
});

// Fails because thrown error's message is not equal to 'bar'
failsWith(t, () => {
const err = new Error('foo');
assertions.throws(() => {
throw err;
}, 'bar');
}, {
assertion: 'throws',
message: '',
values: [
{label: 'Function threw unexpected exception:', formatted: /foo/},
{label: 'Expected message to equal:', formatted: /bar/}
]
});

// Fails because thrown error is not the right instance
failsWith(t, () => {
const err = new Error('foo');
assertions.throws(() => {
throw err;
}, class Foo {});
}, {
assertion: 'throws',
message: '',
values: [
{label: 'Function threw unexpected exception:', formatted: /foo/},
{label: 'Expected instance of:', formatted: /Foo/}
]
});

// Passes because thrown error's message is equal to 'bar'
passes(t, () => {
const err = new Error('foo');
assertions.throws(() => {
throw err;
}, 'foo');
});

// Passes because an error is thrown.
passes(t, () => {
assertions.throws(() => {
Expand Down Expand Up @@ -1043,19 +1005,6 @@ test('.throwsAsync()', gather(t => {
// Passes because the function returned a promise rejected with an error.
eventuallyPasses(t, () => assertions.throwsAsync(() => Promise.reject(new Error())));

// Passes because the error's message matches the regex
eventuallyPasses(t, () => assertions.throwsAsync(Promise.reject(new Error('abc')), /abc/));

// Fails because the error's message does not match the regex
eventuallyFailsWith(t, () => assertions.throwsAsync(Promise.reject(new Error('abc')), /def/), {
assertion: 'throwsAsync',
message: '',
values: [
{label: 'Promise rejected with unexpected exception:', formatted: /Error/},
{label: 'Expected message to match:', formatted: /\/def\//}
]
});

// Fails because the function throws synchronously
eventuallyFailsWith(t, () => assertions.throwsAsync(() => {
throw new Error('sync');
Expand Down Expand Up @@ -1136,23 +1085,47 @@ test('.throws() fails if passed a bad expectation', t => {
assertions.throws(() => {}, true);
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be a function, string, regular expression, expectation object or `null`',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /true/}]
});

failsWith(t, () => {
assertions.throws(() => {}, 'foo');
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /foo/}]
});

failsWith(t, () => {
assertions.throws(() => {}, /baz/);
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /baz/}]
});

failsWith(t, () => {
assertions.throws(() => {}, class Bar {});
}, {
assertion: 'throws',
message: 'The second argument to `t.throws()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /Bar/}]
});

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

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

Expand Down Expand Up @@ -1204,23 +1177,47 @@ test('.throwsAsync() fails if passed a bad expectation', t => {
assertions.throwsAsync(() => {}, true);
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be a function, string, regular expression, expectation object or `null`',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /true/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, 'foo');
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /foo/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, /baz/);
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /baz/}]
});

failsWith(t, () => {
assertions.throwsAsync(() => {}, class Bar {});
}, {
assertion: 'throwsAsync',
message: 'The second argument to `t.throwsAsync()` must be an expectation object or `null`',
values: [{label: 'Called with:', formatted: /Bar/}]
});

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

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

Expand Down
6 changes: 3 additions & 3 deletions test/fixture/report/regular/traces-in-t-throws.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function returnRejectedPromise() {
}

test('throws', t => {
t.throws(() => throwError(), TypeError);
t.throws(() => throwError(), {instanceOf: TypeError});
});

test('notThrows', t => {
Expand All @@ -21,9 +21,9 @@ test('notThrowsAsync', t => {
});

test('throwsAsync', t => {
t.throwsAsync(() => throwError(), TypeError);
t.throwsAsync(() => throwError(), {instanceOf: TypeError});
});

test('throwsAsync different error', t => {
return t.throwsAsync(returnRejectedPromise, TypeError);
return t.throwsAsync(returnRejectedPromise, {instanceOf: TypeError});
});
18 changes: 9 additions & 9 deletions test/reporters/mini.regular.log
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,9 @@ stderr

traces-in-t-throws.js:12

11: test('throws', t => {
 12: t.throws(() => throwError(), TypeError);
13: });
11: test('throws', t => {
 12: t.throws(() => throwError(), {instanceOf: TypeError});
13: });

Function threw unexpected exception:

Expand Down Expand Up @@ -395,9 +395,9 @@ stderr

traces-in-t-throws.js:24

23: test('throwsAsync', t => {
 24: t.throwsAsync(() => throwError(), TypeError);
25: });
23: test('throwsAsync', t => {
 24: t.throwsAsync(() => throwError(), {instanceOf: TypeError});
25: });

Function threw synchronously. Use `t.throws()` instead:

Expand All @@ -415,9 +415,9 @@ stderr

traces-in-t-throws.js:28

27: test('throwsAsync different error', t => {
 28: return t.throwsAsync(returnRejectedPromise, TypeError);
29: });
27: test('throwsAsync different error', t => {
 28: return t.throwsAsync(returnRejectedPromise, {instanceOf: TypeError});
29: });

Returned promise rejected with unexpected exception:

Expand Down
Loading

0 comments on commit 7f99aef

Please sign in to comment.