From 4585c7343da35bbb72d87140cda43e0aeecb0986 Mon Sep 17 00:00:00 2001 From: Hyeonsu Lee Date: Thu, 25 Jan 2018 01:55:38 +0900 Subject: [PATCH] Make `rejects` and `resolves` synchronous (#5364) * Make `makeRejectMatcher` synchronizable (#5361) * Reorganize code using promise instaed of async/await * Update changelog * Move rejectedHandler onto then * Make `makeResolveMatcher` synchronizable * Update a snapshot * Update CHANGELOG.md * Update CHANGELOG.md --- CHANGELOG.md | 2 + .../__snapshots__/failures.test.js.snap | 2 +- .../__snapshots__/matchers.test.js.snap | 62 +++++++++++++++++++ .../expect/src/__tests__/matchers.test.js | 25 ++++++++ packages/expect/src/index.js | 55 ++++++++-------- 5 files changed, 117 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 577612471a8b..fd8b3afdee6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ ([#5367](https://github.com/facebook/jest/pull/5367)) * `[jest-cli]` Fix npm update command for snapshot summary. ([#5376](https://github.com/facebook/jest/pull/5376)) +* `[expect]` Make `rejects` and `resolves` synchronously validate its argument. + ([#5364](https://github.com/facebook/jest/pull/5364)) ## jest 22.1.4 diff --git a/integration-tests/__tests__/__snapshots__/failures.test.js.snap b/integration-tests/__tests__/__snapshots__/failures.test.js.snap index b2c5d619caa8..80542fc0622e 100644 --- a/integration-tests/__tests__/__snapshots__/failures.test.js.snap +++ b/integration-tests/__tests__/__snapshots__/failures.test.js.snap @@ -118,7 +118,7 @@ exports[`works with async failures 1`] = ` + \\"foo\\": \\"bar\\", } - at ../../packages/expect/build/index.js:156:54 + at ../../packages/expect/build/index.js:145:57 " `; diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index 19415cdb3828..a539fdf2462e 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -84,6 +84,14 @@ Received: string: \\"a\\"" `; +exports[`.resolves fails non-promise value "a" synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + string: \\"a\\"" +`; + exports[`.resolves fails non-promise value [1] 1`] = ` "expect(received).resolves.toBeDefined() @@ -92,6 +100,14 @@ Received: array: [1]" `; +exports[`.resolves fails non-promise value [1] synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + array: [1]" +`; + exports[`.resolves fails non-promise value [Function anonymous] 1`] = ` "expect(received).resolves.toBeDefined() @@ -100,6 +116,14 @@ Received: function: [Function anonymous]" `; +exports[`.resolves fails non-promise value [Function anonymous] synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + function: [Function anonymous]" +`; + exports[`.resolves fails non-promise value {"a": 1} 1`] = ` "expect(received).resolves.toBeDefined() @@ -108,6 +132,14 @@ Received: object: {\\"a\\": 1}" `; +exports[`.resolves fails non-promise value {"a": 1} synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + object: {\\"a\\": 1}" +`; + exports[`.resolves fails non-promise value 4 1`] = ` "expect(received).resolves.toBeDefined() @@ -116,6 +148,14 @@ Received: number: 4" `; +exports[`.resolves fails non-promise value 4 synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + number: 4" +`; + exports[`.resolves fails non-promise value null 1`] = ` "expect(received).resolves.toBeDefined() @@ -123,6 +163,13 @@ exports[`.resolves fails non-promise value null 1`] = ` Received: null" `; +exports[`.resolves fails non-promise value null synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: null" +`; + exports[`.resolves fails non-promise value true 1`] = ` "expect(received).resolves.toBeDefined() @@ -131,6 +178,14 @@ Received: boolean: true" `; +exports[`.resolves fails non-promise value true synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: + boolean: true" +`; + exports[`.resolves fails non-promise value undefined 1`] = ` "expect(received).resolves.toBeDefined() @@ -138,6 +193,13 @@ exports[`.resolves fails non-promise value undefined 1`] = ` Received: undefined" `; +exports[`.resolves fails non-promise value undefined synchronously 1`] = ` +"expect(received).resolves.toBeDefined() + +received value must be a Promise. +Received: undefined" +`; + exports[`.toBe() does not crash on circular references 1`] = ` "expect(received).toBe(expected) diff --git a/packages/expect/src/__tests__/matchers.test.js b/packages/expect/src/__tests__/matchers.test.js index 278497721087..62c026a26c98 100644 --- a/packages/expect/src/__tests__/matchers.test.js +++ b/packages/expect/src/__tests__/matchers.test.js @@ -37,6 +37,18 @@ describe('.rejects', () => { await jestExpect(fn()).rejects.toThrow('some error'); }); + [4, [1], {a: 1}, 'a', true, null, undefined, () => {}].forEach(value => { + it(`fails non-promise value ${stringify(value)} synchronously`, () => { + let error; + try { + jestExpect(value).rejects.toBe(111); + } catch (e) { + error = e; + } + expect(error).toBeDefined(); + }); + }); + [4, [1], {a: 1}, 'a', true, null, undefined, () => {}].forEach(value => { it(`fails non-promise value ${stringify(value)}`, async () => { let error; @@ -81,6 +93,19 @@ describe('.resolves', () => { ).resolves.toThrow(); }); + [4, [1], {a: 1}, 'a', true, null, undefined, () => {}].forEach(value => { + it(`fails non-promise value ${stringify(value)} synchronously`, () => { + let error; + try { + jestExpect(value).resolves.toBeDefined(); + } catch (e) { + error = e; + } + expect(error).toBeDefined(); + expect(error.message).toMatchSnapshot(); + }); + }); + [4, [1], {a: 1}, 'a', true, null, undefined, () => {}].forEach(value => { it(`fails non-promise value ${stringify(value)}`, async () => { let error; diff --git a/packages/expect/src/index.js b/packages/expect/src/index.js index a74ffffa13c8..50a495f50332 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.js @@ -130,7 +130,7 @@ const makeResolveMatcher = ( matcher: RawMatcherFn, isNot: boolean, actual: Promise, -): PromiseMatcherFn => async (...args) => { +): PromiseMatcherFn => (...args) => { const matcherStatement = `.resolves.${isNot ? 'not.' : ''}${matcherName}`; if (!isPromise(actual)) { throw new JestAssertionError( @@ -141,19 +141,19 @@ const makeResolveMatcher = ( ); } - let result; - try { - result = await actual; - } catch (e) { - throw new JestAssertionError( - utils.matcherHint(matcherStatement, 'received', '') + - '\n\n' + - `Expected ${utils.RECEIVED_COLOR('received')} Promise to resolve, ` + - 'instead it rejected to value\n' + - ` ${utils.printReceived(e)}`, - ); - } - return makeThrowingMatcher(matcher, isNot, result).apply(null, args); + return actual.then( + result => makeThrowingMatcher(matcher, isNot, result).apply(null, args), + reason => { + const err = new JestAssertionError( + utils.matcherHint(matcherStatement, 'received', '') + + '\n\n' + + `Expected ${utils.RECEIVED_COLOR('received')} Promise to resolve, ` + + 'instead it rejected to value\n' + + ` ${utils.printReceived(reason)}`, + ); + return Promise.reject(err); + }, + ); }; const makeRejectMatcher = ( @@ -161,7 +161,7 @@ const makeRejectMatcher = ( matcher: RawMatcherFn, isNot: boolean, actual: Promise, -): PromiseMatcherFn => async (...args) => { +): PromiseMatcherFn => (...args) => { const matcherStatement = `.rejects.${isNot ? 'not.' : ''}${matcherName}`; if (!isPromise(actual)) { throw new JestAssertionError( @@ -172,19 +172,18 @@ const makeRejectMatcher = ( ); } - let result; - try { - result = await actual; - } catch (e) { - return makeThrowingMatcher(matcher, isNot, e).apply(null, args); - } - - throw new JestAssertionError( - utils.matcherHint(matcherStatement, 'received', '') + - '\n\n' + - `Expected ${utils.RECEIVED_COLOR('received')} Promise to reject, ` + - 'instead it resolved to value\n' + - ` ${utils.printReceived(result)}`, + return actual.then( + result => { + const err = new JestAssertionError( + utils.matcherHint(matcherStatement, 'received', '') + + '\n\n' + + `Expected ${utils.RECEIVED_COLOR('received')} Promise to reject, ` + + 'instead it resolved to value\n' + + ` ${utils.printReceived(result)}`, + ); + return Promise.reject(err); + }, + reason => makeThrowingMatcher(matcher, isNot, reason).apply(null, args), ); };