diff --git a/CHANGELOG.md b/CHANGELOG.md index abc4bd929f62..905ce31c0884 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Fixes +* `[expect]` fix .toThrow for promises + ([#4884](https://github.com/facebook/jest/pull/4884)) * `[jest-docblock]` pragmas should preserve urls ([#4837](https://github.com/facebook/jest/pull/4629)) * `[jest-cli]` Check if `npm_lifecycle_script` calls Jest directly diff --git a/docs/ExpectAPI.md b/docs/ExpectAPI.md index 58b95414ec30..07e626e7ce74 100644 --- a/docs/ExpectAPI.md +++ b/docs/ExpectAPI.md @@ -416,7 +416,7 @@ For example, this code tests that the promise rejects with reason `'octopus'`: ```js test('rejects to octopus', () => { // make sure to add a return statement - return expect(Promise.reject('octopus')).rejects.toBe('octopus'); + return expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); }); ``` @@ -424,7 +424,7 @@ Alternatively, you can use `async/await` in combination with `.rejects`. ```js test('rejects to octopus', async () => { - await expect(Promise.reject('octopus')).rejects.toBe('octopus'); + await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus'); }); ``` diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index de3db5d90046..c0f368dcf059 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -27,11 +27,14 @@ describe('.rejects', () => { await jestExpect(Promise.reject({a: 1, b: 2})).rejects.not.toMatchObject({ c: 1, }); - await jestExpect( - Promise.reject(() => { - throw new Error(); - }), - ).rejects.toThrow(); + await jestExpect(Promise.reject(new Error())).rejects.toThrow(); + }); + + it('should reject with toThrow', async () => { + async function fn() { + throw new Error('some error'); + } + await jestExpect(fn()).rejects.toThrow('some error'); }); [4, [1], {a: 1}, 'a', true, null, undefined, () => {}].forEach(value => { diff --git a/packages/expect/src/index.js b/packages/expect/src/index.js index e43ebb343987..aae81704bc66 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.js @@ -21,7 +21,9 @@ import type { import * as utils from 'jest-matcher-utils'; import matchers from './matchers'; import spyMatchers from './spy_matchers'; -import toThrowMatchers from './to_throw_matchers'; +import toThrowMatchers, { + createMatcher as createThrowMatcher, +} from './to_throw_matchers'; import {equals} from './jasmine_utils'; import { any, @@ -51,6 +53,13 @@ const isPromise = obj => { ); }; +const getPromiseMatcher = name => { + if (name === 'toThrow' || name === 'toThrowError') { + return createThrowMatcher('.' + name, true); + } + return null; +}; + const expect = (actual: any, ...rest): ExpectationObject => { if (rest.length !== 0) { throw new Error('Expect takes at most one argument.'); @@ -64,35 +73,33 @@ const expect = (actual: any, ...rest): ExpectationObject => { }; Object.keys(allMatchers).forEach(name => { - expectation[name] = makeThrowingMatcher(allMatchers[name], false, actual); - expectation.not[name] = makeThrowingMatcher( - allMatchers[name], - true, - actual, - ); + const matcher = allMatchers[name]; + const promiseMatcher = getPromiseMatcher(name) || matcher; + expectation[name] = makeThrowingMatcher(matcher, false, actual); + expectation.not[name] = makeThrowingMatcher(matcher, true, actual); expectation.resolves[name] = makeResolveMatcher( name, - allMatchers[name], + promiseMatcher, false, actual, ); expectation.resolves.not[name] = makeResolveMatcher( name, - allMatchers[name], + promiseMatcher, true, actual, ); expectation.rejects[name] = makeRejectMatcher( name, - allMatchers[name], + promiseMatcher, false, actual, ); expectation.rejects.not[name] = makeRejectMatcher( name, - allMatchers[name], + matcher, true, actual, ); diff --git a/packages/expect/src/to_throw_matchers.js b/packages/expect/src/to_throw_matchers.js index 3a448df231d6..9bab72a182c5 100644 --- a/packages/expect/src/to_throw_matchers.js +++ b/packages/expect/src/to_throw_matchers.js @@ -21,26 +21,29 @@ import { } from 'jest-matcher-utils'; import {equals} from './jasmine_utils'; -const createMatcher = matcherName => ( +export const createMatcher = (matcherName: string, fromPromise?: boolean) => ( actual: Function, expected: string | Error | RegExp, ) => { const value = expected; let error; - if (typeof actual !== 'function') { - throw new Error( - matcherHint(matcherName, 'function', getType(value)) + - '\n\n' + - 'Received value must be a function, but instead ' + - `"${getType(actual)}" was found`, - ); - } - - try { - actual(); - } catch (e) { - error = e; + if (fromPromise) { + error = actual; + } else { + if (typeof actual !== 'function') { + throw new Error( + matcherHint(matcherName, 'function', getType(value)) + + '\n\n' + + 'Received value must be a function, but instead ' + + `"${getType(actual)}" was found`, + ); + } + try { + actual(); + } catch (e) { + error = e; + } } if (typeof expected === 'string') {