Skip to content

Commit 211cd88

Browse files
committed
Make t.throws() and t.notThrows() accept async function as parameter.
Refs: #1371
1 parent bcb77fc commit 211cd88

File tree

5 files changed

+97
-6
lines changed

5 files changed

+97
-6
lines changed

index.js.flow

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ type AssertContext = {
5656
// Assert that value is not deep equal to expected.
5757
notDeepEqual<U>(value: U, expected: U, message?: string): void;
5858
// Assert that function throws an error or promise rejects.
59+
// If the given function returns a Promise, then the Promise will be evaluated instead.
5960
// @param error Can be a constructor, regex, error message or validation function.
6061
throws: {
6162
(value: PromiseLike<mixed>, error?: ErrorValidator, message?: string): Promise<any>;
6263
(value: () => mixed, error?: ErrorValidator, message?: string): any;
6364
};
6465
// Assert that function doesn't throw an error or promise resolves.
66+
// If the given function returns a Promise, then the Promise will be evaluated instead.
6567
notThrows: {
6668
(value: PromiseLike<mixed>, message?: string): Promise<void>;
6769
(value: () => mixed, message?: string): void;

lib/assert.js

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,14 @@ function wrapAssertions(callbacks) {
182182
coreAssertThrowsErrorArg = err;
183183
}
184184

185+
let maybePromise;
185186
const test = (fn, stack) => {
186187
let actual;
187188
let threw = false;
188189
try {
189190
coreAssert.throws(() => {
190191
try {
191-
fn();
192+
maybePromise = fn();
192193
} catch (err) {
193194
actual = err;
194195
threw = true;
@@ -208,7 +209,7 @@ function wrapAssertions(callbacks) {
208209
}
209210
};
210211

211-
if (promise) {
212+
const handlePromise = promise => {
212213
// Record stack before it gets lost in the promise chain.
213214
const stack = getStack();
214215
const intermediate = promise.then(value => {
@@ -222,13 +223,25 @@ function wrapAssertions(callbacks) {
222223
pending(this, intermediate);
223224
// Don't reject the returned promise, even if the assertion fails.
224225
return intermediate.catch(noop);
226+
};
227+
228+
if (promise) {
229+
return handlePromise(promise);
225230
}
226231

227232
try {
228233
const retval = test(fn);
229234
pass(this);
230235
return retval;
231236
} catch (err) {
237+
if (maybePromise) {
238+
if (isPromise(maybePromise)) {
239+
return handlePromise(maybePromise);
240+
}
241+
if (isObservable(maybePromise)) {
242+
return handlePromise(observableToPromise(maybePromise));
243+
}
244+
}
232245
fail(this, err);
233246
}
234247
},
@@ -249,9 +262,12 @@ function wrapAssertions(callbacks) {
249262
return;
250263
}
251264

265+
let maybePromise;
252266
const test = (fn, stack) => {
253267
try {
254-
coreAssert.doesNotThrow(fn);
268+
coreAssert.doesNotThrow(() => {
269+
maybePromise = fn();
270+
});
255271
} catch (err) {
256272
throw new AssertionError({
257273
assertion: 'notThrows',
@@ -262,17 +278,29 @@ function wrapAssertions(callbacks) {
262278
}
263279
};
264280

265-
if (promise) {
281+
const handlePromise = promise => {
266282
// Record stack before it gets lost in the promise chain.
267283
const stack = getStack();
268284
const intermediate = promise.then(noop, reason => test(makeRethrow(reason), stack));
269285
pending(this, intermediate);
270286
// Don't reject the returned promise, even if the assertion fails.
271287
return intermediate.catch(noop);
288+
};
289+
290+
if (promise) {
291+
return handlePromise(promise);
272292
}
273293

274294
try {
275295
test(fn);
296+
if (maybePromise) {
297+
if (isPromise(maybePromise)) {
298+
return handlePromise(maybePromise);
299+
}
300+
if (isObservable(maybePromise)) {
301+
return handlePromise(observableToPromise(maybePromise));
302+
}
303+
}
276304
pass(this);
277305
} catch (err) {
278306
fail(this, err);

readme.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ Assert that `value` is not deeply equal to `expected`. The inverse of `.deepEqua
948948

949949
### `.throws(function|promise, [error, [message]])`
950950

951-
Assert that `function` throws an error, or `promise` rejects with an error.
951+
Assert that `function` throws an error, `promise` rejects with an error, or `function` returns a rejected `promise`.
952952

953953
`error` can be an error constructor, error message, regex matched against the error message, or validation function.
954954

@@ -979,6 +979,16 @@ test('rejects', async t => {
979979
});
980980
```
981981

982+
```js
983+
test('throws', async t => {
984+
const error = await t.throws(async () => {
985+
throw new TypeError('🦄');
986+
}, TypeError);
987+
988+
t.is(error.message, '🦄');
989+
});
990+
```
991+
982992
When testing a promise you must wait for the assertion to complete:
983993

984994
```js
@@ -987,9 +997,19 @@ test('rejects', async t => {
987997
});
988998
```
989999

1000+
When testing an `async function` you must also wait for the assertion to complete:
1001+
1002+
```js
1003+
test('rejects', async t => {
1004+
await t.throws(async () => {
1005+
throw new TypeError('🦄');
1006+
});
1007+
});
1008+
```
1009+
9901010
### `.notThrows(function|promise, [message])`
9911011

992-
Assert that `function` does not throw an error or that `promise` does not reject with an error.
1012+
Assert that `function` does not throw an error or that `promise` does not reject with an error. When you pass a `function` that returns a `promise` as parameter, the returned promise will be evaluated instead.
9931013

9941014
Like the `.throws()` assertion, when testing a promise you must wait for the assertion to complete:
9951015

test/assert.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const stripAnsi = require('strip-ansi');
66
const React = require('react');
77
const renderer = require('react-test-renderer');
88
const test = require('tap').test;
9+
const Observable = require('zen-observable');
910
const assert = require('../lib/assert');
1011
const snapshotManager = require('../lib/snapshot-manager');
1112
const Test = require('../lib/test');
@@ -678,6 +679,28 @@ test('.throws() returns the rejection reason of promise', t => {
678679
});
679680
});
680681

682+
test('.throws() returns the rejection reason of function that return rejected `Promise`', t => {
683+
const expected = new Error();
684+
685+
return assertions.throws(() => {
686+
return Promise.reject(expected);
687+
}).then(actual => {
688+
t.is(actual, expected);
689+
t.end();
690+
});
691+
});
692+
693+
test('.throws() throws exception from `Observable`', t => {
694+
const expected = new Error();
695+
696+
return assertions.throws(() => {
697+
return new Observable(observer => observer.error(expected));
698+
}).then(actual => {
699+
t.is(actual, expected);
700+
t.end();
701+
});
702+
});
703+
681704
test('.throws() fails if passed a bad value', t => {
682705
failsWith(t, () => {
683706
assertions.throws('not a function');
@@ -732,6 +755,22 @@ test('.notThrows() returns undefined for a fulfilled promise', t => {
732755
});
733756
});
734757

758+
test('.notThrows() returns undefined for a function that return fulfilled promise', t => {
759+
return assertions.notThrows(() => {
760+
return Promise.resolve(Symbol(''));
761+
}).then(actual => {
762+
t.is(actual, undefined);
763+
});
764+
});
765+
766+
test('.notThrows() returns undefined for a function that return `Observable`', t => {
767+
return assertions.notThrows(() => {
768+
return Observable.of(Symbol(''));
769+
}).then(actual => {
770+
t.is(actual, undefined);
771+
});
772+
});
773+
735774
test('.notThrows() fails if passed a bad value', t => {
736775
failsWith(t, () => {
737776
assertions.notThrows('not a function');

types/base.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ export interface AssertContext {
6767
* @param error Can be a constructor, regex, error message or validation function.
6868
*/
6969
throws(value: PromiseLike<any>, error?: ErrorValidator, message?: string): Promise<any>;
70+
throws(value: () => PromiseLike<any>, error?: ErrorValidator, message?: string): Promise<any>;
7071
throws(value: () => void, error?: ErrorValidator, message?: string): any;
7172
/**
7273
* Assert that function doesn't throw an error or promise resolves.
7374
*/
7475
notThrows(value: PromiseLike<any>, message?: string): Promise<void>;
76+
notThrows(value: () => PromiseLike<any>, message?: string): Promise<void>;
7577
notThrows(value: () => void, message?: string): void;
7678
/**
7779
* Assert that contents matches regex.

0 commit comments

Comments
 (0)