diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c57e8fc17c7..d0ca48a0a8dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - `[expect]` Highlight substring differences when matcher fails, part 1 ([#8448](https://github.com/facebook/jest/pull/8448)) - `[expect]` Highlight substring differences when matcher fails, part 2 ([#8528](https://github.com/facebook/jest/pull/8528)) - `[expect]` Improve report when mock-spy matcher fails, part 1 ([#8640](https://github.com/facebook/jest/pull/8640)) +- `[expect]` Improve report when mock-spy matcher fails, part 2 ([#8649](https://github.com/facebook/jest/pull/8649)) - `[jest-snapshot]` Highlight substring differences when matcher fails, part 3 ([#8569](https://github.com/facebook/jest/pull/8569)) - `[jest-cli]` Improve chai support (with detailed output, to match jest exceptions) ([#8454](https://github.com/facebook/jest/pull/8454)) - `[*]` Manage the global timeout with `--testTimeout` command line argument. ([#8456](https://github.com/facebook/jest/pull/8456)) diff --git a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap index 8d7d7584d4fc..963c808e88c7 100644 --- a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap @@ -607,9 +607,10 @@ Expected has value: 555" `; exports[`toBeCalled .not passes when called 1`] = ` -"expect(jest.fn()).toBeCalled() +"expect(jest.fn()).toBeCalled() -Expected mock function to have been called, but it was not called." +Expected number of calls: >= 1 +Received number of calls: 0" `; exports[`toBeCalled fails with any argument passed 1`] = ` @@ -622,17 +623,21 @@ Expected has value: 555" `; exports[`toBeCalled includes the custom mock name in the error message 1`] = ` -"expect(named-mock).not.toBeCalled() +"expect(named-mock).not.toBeCalled() -Expected mock function \\"named-mock\\" not to be called but it was called with: - []" +Expected number of calls: 0 +Received number of calls: 1 + +1: called with no arguments" `; exports[`toBeCalled passes when called 1`] = ` -"expect(jest.fn()).not.toBeCalled() +"expect(jest.fn()).not.toBeCalled() + +Expected number of calls: 0 +Received number of calls: 1 -Expected mock function not to be called but it was called with: - []" +1: \\"arg0\\", \\"arg1\\", \\"arg2\\"" `; exports[`toBeCalled works only on spies or jest.fn 1`] = ` @@ -699,15 +704,17 @@ Expected has value: [Function anonymous]" `; exports[`toBeCalledTimes .not passes if function called less than expected times 1`] = ` -"expect(jest.fn()).toBeCalledTimes(2) +"expect(jest.fn()).toBeCalledTimes(expected) -Expected mock function to have been called two times, but it was called one time." +Expected number of calls: 2 +Received number of calls: 1" `; exports[`toBeCalledTimes .not passes if function called more than expected times 1`] = ` -"expect(jest.fn()).toBeCalledTimes(2) +"expect(jest.fn()).toBeCalledTimes(expected) -Expected mock function to have been called two times, but it was called three times." +Expected number of calls: 2 +Received number of calls: 3" `; exports[`toBeCalledTimes .not works only on spies or jest.fn 1`] = ` @@ -720,9 +727,10 @@ Received has value: [Function fn]" `; exports[`toBeCalledTimes includes the custom mock name in the error message 1`] = ` -"expect(named-mock).toBeCalledTimes(2) +"expect(named-mock).toBeCalledTimes(expected) -Expected mock function \\"named-mock\\" to have been called two times, but it was called one time." +Expected number of calls: 2 +Received number of calls: 1" `; exports[`toBeCalledTimes only accepts a number argument 1`] = ` @@ -780,9 +788,9 @@ Expected has value: [Function anonymous]" `; exports[`toBeCalledTimes passes if function called equal to expected times 1`] = ` -"expect(jest.fn()).not.toBeCalledTimes(2) +"expect(jest.fn()).not.toBeCalledTimes(expected) -Expected mock function not to be called two times, but it was called exactly two times." +Expected number of calls: not 2" `; exports[`toBeCalledWith includes the custom mock name in the error message 1`] = ` @@ -929,9 +937,10 @@ Expected has value: 555" `; exports[`toHaveBeenCalled .not passes when called 1`] = ` -"expect(jest.fn()).toHaveBeenCalled() +"expect(jest.fn()).toHaveBeenCalled() -Expected mock function to have been called, but it was not called." +Expected number of calls: >= 1 +Received number of calls: 0" `; exports[`toHaveBeenCalled fails with any argument passed 1`] = ` @@ -944,17 +953,21 @@ Expected has value: 555" `; exports[`toHaveBeenCalled includes the custom mock name in the error message 1`] = ` -"expect(named-mock).not.toHaveBeenCalled() +"expect(named-mock).not.toHaveBeenCalled() + +Expected number of calls: 0 +Received number of calls: 1 -Expected mock function \\"named-mock\\" not to be called but it was called with: - []" +1: called with no arguments" `; exports[`toHaveBeenCalled passes when called 1`] = ` -"expect(jest.fn()).not.toHaveBeenCalled() +"expect(jest.fn()).not.toHaveBeenCalled() -Expected mock function not to be called but it was called with: - []" +Expected number of calls: 0 +Received number of calls: 1 + +1: \\"arg0\\", \\"arg1\\", \\"arg2\\"" `; exports[`toHaveBeenCalled works only on spies or jest.fn 1`] = ` @@ -1021,15 +1034,17 @@ Expected has value: [Function anonymous]" `; exports[`toHaveBeenCalledTimes .not passes if function called less than expected times 1`] = ` -"expect(jest.fn()).toHaveBeenCalledTimes(2) +"expect(jest.fn()).toHaveBeenCalledTimes(expected) -Expected mock function to have been called two times, but it was called one time." +Expected number of calls: 2 +Received number of calls: 1" `; exports[`toHaveBeenCalledTimes .not passes if function called more than expected times 1`] = ` -"expect(jest.fn()).toHaveBeenCalledTimes(2) +"expect(jest.fn()).toHaveBeenCalledTimes(expected) -Expected mock function to have been called two times, but it was called three times." +Expected number of calls: 2 +Received number of calls: 3" `; exports[`toHaveBeenCalledTimes .not works only on spies or jest.fn 1`] = ` @@ -1042,9 +1057,10 @@ Received has value: [Function fn]" `; exports[`toHaveBeenCalledTimes includes the custom mock name in the error message 1`] = ` -"expect(named-mock).toHaveBeenCalledTimes(2) +"expect(named-mock).toHaveBeenCalledTimes(expected) -Expected mock function \\"named-mock\\" to have been called two times, but it was called one time." +Expected number of calls: 2 +Received number of calls: 1" `; exports[`toHaveBeenCalledTimes only accepts a number argument 1`] = ` @@ -1102,9 +1118,9 @@ Expected has value: [Function anonymous]" `; exports[`toHaveBeenCalledTimes passes if function called equal to expected times 1`] = ` -"expect(jest.fn()).not.toHaveBeenCalledTimes(2) +"expect(jest.fn()).not.toHaveBeenCalledTimes(expected) -Expected mock function not to be called two times, but it was called exactly two times." +Expected number of calls: not 2" `; exports[`toHaveBeenCalledWith includes the custom mock name in the error message 1`] = ` @@ -1848,21 +1864,26 @@ Expected has value: 555" `; exports[`toHaveReturned .not passes when a call throws undefined 1`] = ` -"expect(jest.fn()).toHaveReturned() +"expect(jest.fn()).toHaveReturned() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 1" `; exports[`toHaveReturned .not passes when all calls throw 1`] = ` -"expect(jest.fn()).toHaveReturned() +"expect(jest.fn()).toHaveReturned() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 2" `; exports[`toHaveReturned .not passes when not returned 1`] = ` -"expect(jest.fn()).toHaveReturned() +"expect(jest.fn()).toHaveReturned() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0" `; exports[`toHaveReturned .not works only on jest.fn 1`] = ` @@ -1884,39 +1905,50 @@ Expected has value: 555" `; exports[`toHaveReturned includes the custom mock name in the error message 1`] = ` -"expect(named-mock).not.toHaveReturned() +"expect(named-mock).not.toHaveReturned() + +Expected number of returns: 0 +Received number of returns: 1 -Expected mock function \\"named-mock\\" not to have returned, but it returned: - 42" +1: 42" `; exports[`toHaveReturned incomplete recursive calls are handled properly 1`] = ` -"expect(jest.fn()).toHaveReturned() +"expect(jest.fn()).toHaveReturned() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 4" `; exports[`toHaveReturned passes when at least one call does not throw 1`] = ` -"expect(jest.fn()).not.toHaveReturned() +"expect(jest.fn()).not.toHaveReturned() -Expected mock function not to have returned, but it returned: - 42 +Expected number of returns: 0 +Received number of returns: 2 - 42" +1: 42 +3: 42 + +Received number of calls: 3" `; exports[`toHaveReturned passes when returned 1`] = ` -"expect(jest.fn()).not.toHaveReturned() +"expect(jest.fn()).not.toHaveReturned() + +Expected number of returns: 0 +Received number of returns: 1 -Expected mock function not to have returned, but it returned: - 42" +1: 42" `; exports[`toHaveReturned passes when undefined is returned 1`] = ` -"expect(jest.fn()).not.toHaveReturned() +"expect(jest.fn()).not.toHaveReturned() -Expected mock function not to have returned, but it returned: - undefined" +Expected number of returns: 0 +Received number of returns: 1 + +1: undefined" `; exports[`toHaveReturnedTimes .not only accepts a number argument 1`] = ` @@ -1974,45 +2006,54 @@ Expected has value: [Function anonymous]" `; exports[`toHaveReturnedTimes .not passes if function called less than expected times 1`] = ` -"expect(jest.fn()).toHaveReturnedTimes(2) +"expect(jest.fn()).toHaveReturnedTimes(expected) -Expected mock function to have returned two times, but it returned one time." +Expected number of returns: 2 +Received number of returns: 1" `; exports[`toHaveReturnedTimes .not passes if function returned more than expected times 1`] = ` -"expect(jest.fn()).toHaveReturnedTimes(2) +"expect(jest.fn()).toHaveReturnedTimes(expected) -Expected mock function to have returned two times, but it returned three times." +Expected number of returns: 2 +Received number of returns: 3" `; exports[`toHaveReturnedTimes calls that return undefined are counted as returns 1`] = ` -"expect(jest.fn()).not.toHaveReturnedTimes(2) +"expect(jest.fn()).not.toHaveReturnedTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2" `; exports[`toHaveReturnedTimes calls that throw are not counted 1`] = ` -"expect(jest.fn()).not.toHaveReturnedTimes(2) +"expect(jest.fn()).toHaveReturnedTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: 3 +Received number of returns: 2 +Received number of calls: 3" `; exports[`toHaveReturnedTimes calls that throw undefined are not counted 1`] = ` -"expect(jest.fn()).not.toHaveReturnedTimes(2) +"expect(jest.fn()).not.toHaveReturnedTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2 + +Received number of calls: 3" `; exports[`toHaveReturnedTimes includes the custom mock name in the error message 1`] = ` -"expect(named-mock).toHaveReturnedTimes(1) +"expect(named-mock).toHaveReturnedTimes(expected) -Expected mock function \\"named-mock\\" to have returned one time, but it returned two times." +Expected number of returns: 1 +Received number of returns: 2" `; exports[`toHaveReturnedTimes incomplete recursive calls are handled properly 1`] = ` -"expect(jest.fn()).not.toHaveReturnedTimes(2) +"expect(jest.fn()).not.toHaveReturnedTimes(expected) + +Expected number of returns: not 2 -Expected mock function not to have returned two times, but it returned exactly two times." +Received number of calls: 4" `; exports[`toHaveReturnedTimes only accepts a number argument 1`] = ` @@ -2070,9 +2111,9 @@ Expected has value: [Function anonymous]" `; exports[`toHaveReturnedTimes passes if function returned equal to expected times 1`] = ` -"expect(jest.fn()).not.toHaveReturnedTimes(2) +"expect(jest.fn()).not.toHaveReturnedTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2" `; exports[`toHaveReturnedTimes works only on spies or jest.fn 1`] = ` @@ -2243,21 +2284,26 @@ Expected has value: 555" `; exports[`toReturn .not passes when a call throws undefined 1`] = ` -"expect(jest.fn()).toReturn() +"expect(jest.fn()).toReturn() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 1" `; exports[`toReturn .not passes when all calls throw 1`] = ` -"expect(jest.fn()).toReturn() +"expect(jest.fn()).toReturn() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 2" `; exports[`toReturn .not passes when not returned 1`] = ` -"expect(jest.fn()).toReturn() +"expect(jest.fn()).toReturn() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0" `; exports[`toReturn .not works only on jest.fn 1`] = ` @@ -2279,39 +2325,50 @@ Expected has value: 555" `; exports[`toReturn includes the custom mock name in the error message 1`] = ` -"expect(named-mock).not.toReturn() +"expect(named-mock).not.toReturn() + +Expected number of returns: 0 +Received number of returns: 1 -Expected mock function \\"named-mock\\" not to have returned, but it returned: - 42" +1: 42" `; exports[`toReturn incomplete recursive calls are handled properly 1`] = ` -"expect(jest.fn()).toReturn() +"expect(jest.fn()).toReturn() -Expected mock function to have returned." +Expected number of returns: >= 1 +Received number of returns: 0 +Received number of calls: 4" `; exports[`toReturn passes when at least one call does not throw 1`] = ` -"expect(jest.fn()).not.toReturn() +"expect(jest.fn()).not.toReturn() -Expected mock function not to have returned, but it returned: - 42 +Expected number of returns: 0 +Received number of returns: 2 - 42" +1: 42 +3: 42 + +Received number of calls: 3" `; exports[`toReturn passes when returned 1`] = ` -"expect(jest.fn()).not.toReturn() +"expect(jest.fn()).not.toReturn() + +Expected number of returns: 0 +Received number of returns: 1 -Expected mock function not to have returned, but it returned: - 42" +1: 42" `; exports[`toReturn passes when undefined is returned 1`] = ` -"expect(jest.fn()).not.toReturn() +"expect(jest.fn()).not.toReturn() -Expected mock function not to have returned, but it returned: - undefined" +Expected number of returns: 0 +Received number of returns: 1 + +1: undefined" `; exports[`toReturnTimes .not only accepts a number argument 1`] = ` @@ -2369,45 +2426,54 @@ Expected has value: [Function anonymous]" `; exports[`toReturnTimes .not passes if function called less than expected times 1`] = ` -"expect(jest.fn()).toReturnTimes(2) +"expect(jest.fn()).toReturnTimes(expected) -Expected mock function to have returned two times, but it returned one time." +Expected number of returns: 2 +Received number of returns: 1" `; exports[`toReturnTimes .not passes if function returned more than expected times 1`] = ` -"expect(jest.fn()).toReturnTimes(2) +"expect(jest.fn()).toReturnTimes(expected) -Expected mock function to have returned two times, but it returned three times." +Expected number of returns: 2 +Received number of returns: 3" `; exports[`toReturnTimes calls that return undefined are counted as returns 1`] = ` -"expect(jest.fn()).not.toReturnTimes(2) +"expect(jest.fn()).not.toReturnTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2" `; exports[`toReturnTimes calls that throw are not counted 1`] = ` -"expect(jest.fn()).not.toReturnTimes(2) +"expect(jest.fn()).toReturnTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: 3 +Received number of returns: 2 +Received number of calls: 3" `; exports[`toReturnTimes calls that throw undefined are not counted 1`] = ` -"expect(jest.fn()).not.toReturnTimes(2) +"expect(jest.fn()).not.toReturnTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2 + +Received number of calls: 3" `; exports[`toReturnTimes includes the custom mock name in the error message 1`] = ` -"expect(named-mock).toReturnTimes(1) +"expect(named-mock).toReturnTimes(expected) -Expected mock function \\"named-mock\\" to have returned one time, but it returned two times." +Expected number of returns: 1 +Received number of returns: 2" `; exports[`toReturnTimes incomplete recursive calls are handled properly 1`] = ` -"expect(jest.fn()).not.toReturnTimes(2) +"expect(jest.fn()).not.toReturnTimes(expected) + +Expected number of returns: not 2 -Expected mock function not to have returned two times, but it returned exactly two times." +Received number of calls: 4" `; exports[`toReturnTimes only accepts a number argument 1`] = ` @@ -2465,9 +2531,9 @@ Expected has value: [Function anonymous]" `; exports[`toReturnTimes passes if function returned equal to expected times 1`] = ` -"expect(jest.fn()).not.toReturnTimes(2) +"expect(jest.fn()).not.toReturnTimes(expected) -Expected mock function not to have returned two times, but it returned exactly two times." +Expected number of returns: not 2" `; exports[`toReturnTimes works only on spies or jest.fn 1`] = ` diff --git a/packages/expect/src/__tests__/spyMatchers.test.js b/packages/expect/src/__tests__/spyMatchers.test.js index 54e0b336f236..710ef46eaf0e 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.js +++ b/packages/expect/src/__tests__/spyMatchers.test.js @@ -3,7 +3,6 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ const Immutable = require('immutable'); @@ -19,7 +18,7 @@ const jestExpect = require('../'); test(`passes when called`, () => { const fn = jest.fn(); - fn(); + fn('arg0', 'arg1', 'arg2'); jestExpect(fn)[called](); expect(() => jestExpect(fn).not[called]()).toThrowErrorMatchingSnapshot(); }); @@ -617,10 +616,10 @@ const jestExpect = require('../'); fn(false); - jestExpect(fn)[returnedTimes](2); + jestExpect(fn).not[returnedTimes](3); expect(() => - jestExpect(fn).not[returnedTimes](2), + jestExpect(fn)[returnedTimes](3), ).toThrowErrorMatchingSnapshot(); }); diff --git a/packages/expect/src/spyMatchers.ts b/packages/expect/src/spyMatchers.ts index 6286dbc60b35..b21c446bdaa5 100644 --- a/packages/expect/src/spyMatchers.ts +++ b/packages/expect/src/spyMatchers.ts @@ -3,18 +3,15 @@ * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - * */ import { diff, ensureExpectedIsNumber, ensureNoExpected, - EXPECTED_COLOR, matcherErrorMessage, matcherHint, MatcherHintOptions, - pluralize, printExpected, printReceived, printWithType, @@ -24,10 +21,16 @@ import {MatchersObject, MatcherState, SyncExpectationResult} from './types'; import {equals} from './jasmineUtils'; import {iterableEquality, partition, isOneline} from './utils'; +const PRINT_LIMIT = 3; const CALL_PRINT_LIMIT = 3; const RETURN_PRINT_LIMIT = 5; const LAST_CALL_PRINT_LIMIT = 1; +const printReceivedArgs = (args: Array): string => + args.length === 0 + ? 'called with no arguments' + : args.map(arg => printReceived(arg)).join(', '); + const createToBeCalledMatcher = (matcherName: string) => function( this: MatcherState, @@ -39,16 +42,11 @@ const createToBeCalledMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName.slice(1), options); - ensureMock(received, matcherName.slice(1), expectedArgument, options); + ensureNoExpected(expected, matcherName, options); + ensureMock(received, matcherName, expectedArgument, options); const receivedIsSpy = isSpy(received); - const type = receivedIsSpy ? 'spy' : 'mock function'; const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); - const identifier = - receivedIsSpy || receivedName === 'jest.fn()' - ? type - : `${type} "${receivedName}"`; const count = receivedIsSpy ? received.calls.count() : received.mock.calls.length; @@ -58,14 +56,24 @@ const createToBeCalledMatcher = (matcherName: string) => const pass = count > 0; const message = pass ? () => - matcherHint('.not' + matcherName, receivedName, '') + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} not to be called ` + - formatReceivedCalls(calls, CALL_PRINT_LIMIT, {sameSentence: true}) + `Expected number of calls: ${printExpected(0)}\n` + + `Received number of calls: ${printReceived(count)}\n\n` + + calls + .reduce((lines: Array, args: any, i: number) => { + if (lines.length < PRINT_LIMIT) { + lines.push(`${i + 1}: ${printReceivedArgs(args)}`); + } + + return lines; + }, []) + .join('\n') : () => - matcherHint(matcherName, receivedName, '') + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} to have been called, but it was not called.`; + `Expected number of calls: >= ${printExpected(1)}\n` + + `Received number of calls: ${printReceived(count)}`; return {message, pass}; }; @@ -81,33 +89,49 @@ const createToReturnMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureNoExpected(expected, matcherName.slice(1), options); - ensureMock(received, matcherName.slice(1), expectedArgument, options); + ensureNoExpected(expected, matcherName, options); + ensureMock(received, matcherName, expectedArgument, options); const receivedName = received.getMockName(); - const identifier = - receivedName === 'jest.fn()' - ? 'mock function' - : `mock function "${receivedName}"`; - // List of return values that correspond only to calls that returned - const returnValues = received.mock.results - .filter((result: any) => result.type === 'return') - .map((result: any) => result.value); + // Count return values that correspond only to calls that returned + const count = received.mock.results.reduce( + (n: number, result: any) => (result.type === 'return' ? n + 1 : n), + 0, + ); - const count = returnValues.length; const pass = count > 0; const message = pass ? () => - matcherHint('.not' + matcherName, receivedName, '') + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} not to have returned, but it returned:\n` + - ` ${getPrintedReturnValues(returnValues, RETURN_PRINT_LIMIT)}` + `Expected number of returns: ${printExpected(0)}\n` + + `Received number of returns: ${printReceived(count)}\n\n` + + received.mock.results + .reduce((lines: Array, result: any, i: number) => { + if (result.type === 'return' && lines.length < PRINT_LIMIT) { + lines.push(`${i + 1}: ${printReceived(result.value)}`); + } + + return lines; + }, []) + .join('\n') + + (received.mock.calls.length !== count + ? `\n\nReceived number of calls: ${printReceived( + received.mock.calls.length, + )}` + : '') : () => - matcherHint(matcherName, receivedName, '') + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} to have returned.`; + `Expected number of returns: >= ${printExpected(1)}\n` + + `Received number of returns: ${printReceived(count)}` + + (received.mock.calls.length !== count + ? `\nReceived number of calls: ${printReceived( + received.mock.calls.length, + )}` + : ''); return {message, pass}; }; @@ -123,33 +147,27 @@ const createToBeCalledTimesMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNumber(expected, matcherName.slice(1), options); - ensureMock(received, matcherName.slice(1), expectedArgument, options); + ensureExpectedIsNumber(expected, matcherName, options); + ensureMock(received, matcherName, expectedArgument, options); const receivedIsSpy = isSpy(received); - const type = receivedIsSpy ? 'spy' : 'mock function'; const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); - const identifier = - receivedIsSpy || receivedName === 'jest.fn()' - ? type - : `${type} "${receivedName}"`; const count = receivedIsSpy ? received.calls.count() : received.mock.calls.length; + const pass = count === expected; + const message = pass ? () => - matcherHint('.not' + matcherName, receivedName, String(expected)) + + matcherHint(matcherName, receivedName, expectedArgument, options) + `\n\n` + - `Expected ${identifier} not to be called ` + - `${EXPECTED_COLOR(pluralize('time', expected))}, but it was` + - ` called exactly ${RECEIVED_COLOR(pluralize('time', count))}.` + `Expected number of calls: not ${printExpected(expected)}` : () => - matcherHint(matcherName, receivedName, String(expected)) + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} to have been called ` + - `${EXPECTED_COLOR(pluralize('time', expected))},` + - ` but it was called ${RECEIVED_COLOR(pluralize('time', count))}.`; + `Expected number of calls: ${printExpected(expected)}\n` + + `Received number of calls: ${printReceived(count)}`; return {message, pass}; }; @@ -165,36 +183,39 @@ const createToReturnTimesMatcher = (matcherName: string) => isNot: this.isNot, promise: this.promise, }; - ensureExpectedIsNumber(expected, matcherName.slice(1), options); - ensureMock(received, matcherName.slice(1), expectedArgument, options); + ensureExpectedIsNumber(expected, matcherName, options); + ensureMock(received, matcherName, expectedArgument, options); const receivedName = received.getMockName(); - const identifier = - receivedName === 'jest.fn()' - ? 'mock function' - : `mock function "${receivedName}"`; - // List of return results that correspond only to calls that returned - const returnResults = received.mock.results.filter( - (result: any) => result.type === 'return', + // Count return values that correspond only to calls that returned + const count = received.mock.results.reduce( + (n: number, result: any) => (result.type === 'return' ? n + 1 : n), + 0, ); - const count = returnResults.length; const pass = count === expected; const message = pass ? () => - matcherHint('.not' + matcherName, receivedName, String(expected)) + + matcherHint(matcherName, receivedName, expectedArgument, options) + `\n\n` + - `Expected ${identifier} not to have returned ` + - `${EXPECTED_COLOR(pluralize('time', expected))}, but it` + - ` returned exactly ${RECEIVED_COLOR(pluralize('time', count))}.` + `Expected number of returns: not ${printExpected(expected)}` + + (received.mock.calls.length !== count + ? `\n\nReceived number of calls: ${printReceived( + received.mock.calls.length, + )}` + : '') : () => - matcherHint(matcherName, receivedName, String(expected)) + + matcherHint(matcherName, receivedName, expectedArgument, options) + '\n\n' + - `Expected ${identifier} to have returned ` + - `${EXPECTED_COLOR(pluralize('time', expected))},` + - ` but it returned ${RECEIVED_COLOR(pluralize('time', count))}.`; + `Expected number of returns: ${printExpected(expected)}\n` + + `Received number of returns: ${printReceived(count)}` + + (received.mock.calls.length !== count + ? `\nReceived number of calls: ${printReceived( + received.mock.calls.length, + )}` + : ''); return {message, pass}; }; @@ -520,11 +541,11 @@ const spyMatchers: MatchersObject = { lastReturnedWith: createLastReturnedMatcher('.lastReturnedWith'), nthCalledWith: createNthCalledWithMatcher('.nthCalledWith'), nthReturnedWith: createNthReturnedWithMatcher('.nthReturnedWith'), - toBeCalled: createToBeCalledMatcher('.toBeCalled'), - toBeCalledTimes: createToBeCalledTimesMatcher('.toBeCalledTimes'), + toBeCalled: createToBeCalledMatcher('toBeCalled'), + toBeCalledTimes: createToBeCalledTimesMatcher('toBeCalledTimes'), toBeCalledWith: createToBeCalledWithMatcher('.toBeCalledWith'), - toHaveBeenCalled: createToBeCalledMatcher('.toHaveBeenCalled'), - toHaveBeenCalledTimes: createToBeCalledTimesMatcher('.toHaveBeenCalledTimes'), + toHaveBeenCalled: createToBeCalledMatcher('toHaveBeenCalled'), + toHaveBeenCalledTimes: createToBeCalledTimesMatcher('toHaveBeenCalledTimes'), toHaveBeenCalledWith: createToBeCalledWithMatcher('.toHaveBeenCalledWith'), toHaveBeenLastCalledWith: createLastCalledWithMatcher( '.toHaveBeenLastCalledWith', @@ -534,11 +555,11 @@ const spyMatchers: MatchersObject = { ), toHaveLastReturnedWith: createLastReturnedMatcher('.toHaveLastReturnedWith'), toHaveNthReturnedWith: createNthReturnedWithMatcher('.toHaveNthReturnedWith'), - toHaveReturned: createToReturnMatcher('.toHaveReturned'), - toHaveReturnedTimes: createToReturnTimesMatcher('.toHaveReturnedTimes'), + toHaveReturned: createToReturnMatcher('toHaveReturned'), + toHaveReturnedTimes: createToReturnTimesMatcher('toHaveReturnedTimes'), toHaveReturnedWith: createToReturnWithMatcher('.toHaveReturnedWith'), - toReturn: createToReturnMatcher('.toReturn'), - toReturnTimes: createToReturnTimesMatcher('.toReturnTimes'), + toReturn: createToReturnMatcher('toReturn'), + toReturnTimes: createToReturnTimesMatcher('toReturnTimes'), toReturnWith: createToReturnWithMatcher('.toReturnWith'), }; @@ -595,28 +616,6 @@ const getPrintedReturnValues = (calls: Array, limit: number): string => { return result.join('\n\n '); }; -const formatReceivedCalls = ( - calls: Array, - limit: number, - options: any, -) => { - if (calls.length) { - const but = options && options.sameSentence ? 'but' : 'But'; - const count = calls.length - limit; - const printedCalls = getPrintedCalls(calls, limit, ', ', printReceived); - return ( - `${but} it was called ` + - `with:\n ` + - printedCalls + - (count > 0 - ? '\nand ' + RECEIVED_COLOR(pluralize('more call', count)) + '.' - : '') - ); - } else { - return `But it was ${RECEIVED_COLOR('not called')}.`; - } -}; - const formatMismatchedCalls = ( calls: Array, expected: any,