diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f377ac43c15..58d18aacc151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,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)) - `[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 4015f9948b3b..8d7d7584d4fc 100644 --- a/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/spyMatchers.test.js.snap @@ -8,7 +8,7 @@ Expected mock function \\"named-mock\\" to not have been last called with: `; exports[`lastCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].lastCalledWith() +"expect(received).lastCalledWith(...expected) Matcher error: received value must be a mock or spy function @@ -159,7 +159,7 @@ But the last call has not returned yet" `; exports[`lastReturnedWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].lastReturnedWith() +"expect(received).lastReturnedWith(expected) Matcher error: received value must be a mock or spy function @@ -293,7 +293,7 @@ Expected mock function first call to not have been called with: `; exports[`nthCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].nthCalledWith() +"expect(received).nthCalledWith(n, ...expected) Matcher error: received value must be a mock or spy function @@ -491,7 +491,7 @@ But the first call returned exactly: `; exports[`nthReturnedWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].nthReturnedWith() +"expect(received).nthReturnedWith(n, expected) Matcher error: received value must be a mock or spy function @@ -598,7 +598,7 @@ But the first call returned exactly: `; exports[`toBeCalled .not fails with any argument passed 1`] = ` -"expect(received)[.not].toBeCalled() +"expect(received).not.toBeCalled() Matcher error: this matcher must not have an expected argument @@ -613,7 +613,7 @@ Expected mock function to have been called, but it was not called." `; exports[`toBeCalled fails with any argument passed 1`] = ` -"expect(received)[.not].toBeCalled() +"expect(received).toBeCalled() Matcher error: this matcher must not have an expected argument @@ -636,7 +636,7 @@ Expected mock function not to be called but it was called with: `; exports[`toBeCalled works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toBeCalled() +"expect(received).toBeCalled() Matcher error: received value must be a mock or spy function @@ -645,7 +645,7 @@ Received has value: [Function fn]" `; exports[`toBeCalledTimes .not only accepts a number argument 1`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -654,7 +654,7 @@ Expected has value: {}" `; exports[`toBeCalledTimes .not only accepts a number argument 2`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -663,7 +663,7 @@ Expected has value: []" `; exports[`toBeCalledTimes .not only accepts a number argument 3`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -672,7 +672,7 @@ Expected has value: true" `; exports[`toBeCalledTimes .not only accepts a number argument 4`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -681,7 +681,7 @@ Expected has value: \\"a\\"" `; exports[`toBeCalledTimes .not only accepts a number argument 5`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -690,7 +690,7 @@ Expected has value: Map {}" `; exports[`toBeCalledTimes .not only accepts a number argument 6`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).not.toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -710,6 +710,15 @@ exports[`toBeCalledTimes .not passes if function called more than expected times Expected mock function to have been called two times, but it was called three times." `; +exports[`toBeCalledTimes .not works only on spies or jest.fn 1`] = ` +"expect(received).not.toBeCalledTimes(expected) + +Matcher error: received value must be a mock or spy function + +Received has type: function +Received has value: [Function fn]" +`; + exports[`toBeCalledTimes includes the custom mock name in the error message 1`] = ` "expect(named-mock).toBeCalledTimes(2) @@ -717,7 +726,7 @@ Expected mock function \\"named-mock\\" to have been called two times, `; exports[`toBeCalledTimes only accepts a number argument 1`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -726,7 +735,7 @@ Expected has value: {}" `; exports[`toBeCalledTimes only accepts a number argument 2`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -735,7 +744,7 @@ Expected has value: []" `; exports[`toBeCalledTimes only accepts a number argument 3`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -744,7 +753,7 @@ Expected has value: true" `; exports[`toBeCalledTimes only accepts a number argument 4`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -753,7 +762,7 @@ Expected has value: \\"a\\"" `; exports[`toBeCalledTimes only accepts a number argument 5`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -762,7 +771,7 @@ Expected has value: Map {}" `; exports[`toBeCalledTimes only accepts a number argument 6`] = ` -"expect(received)[.not].toBeCalledTimes(expected) +"expect(received).toBeCalledTimes(expected) Matcher error: expected value must be a number @@ -776,15 +785,6 @@ exports[`toBeCalledTimes passes if function called equal to expected times 1`] = Expected mock function not to be called two times, but it was called exactly two times." `; -exports[`toBeCalledTimes works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toBeCalledTimes() - -Matcher error: received value must be a mock or spy function - -Received has type: function -Received has value: [Function fn]" -`; - exports[`toBeCalledWith includes the custom mock name in the error message 1`] = ` "expect(named-mock).not.toBeCalledWith(expected) @@ -793,7 +793,7 @@ Expected mock function \\"named-mock\\" not to have been called with: `; exports[`toBeCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toBeCalledWith() +"expect(received).toBeCalledWith(...expected) Matcher error: received value must be a mock or spy function @@ -920,7 +920,7 @@ Expected mock function to have been called with: `; exports[`toHaveBeenCalled .not fails with any argument passed 1`] = ` -"expect(received)[.not].toHaveBeenCalled() +"expect(received).not.toHaveBeenCalled() Matcher error: this matcher must not have an expected argument @@ -935,7 +935,7 @@ Expected mock function to have been called, but it was not called." `; exports[`toHaveBeenCalled fails with any argument passed 1`] = ` -"expect(received)[.not].toHaveBeenCalled() +"expect(received).toHaveBeenCalled() Matcher error: this matcher must not have an expected argument @@ -958,7 +958,7 @@ Expected mock function not to be called but it was called with: `; exports[`toHaveBeenCalled works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveBeenCalled() +"expect(received).toHaveBeenCalled() Matcher error: received value must be a mock or spy function @@ -967,7 +967,7 @@ Received has value: [Function fn]" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 1`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -976,7 +976,7 @@ Expected has value: {}" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 2`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -985,7 +985,7 @@ Expected has value: []" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 3`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -994,7 +994,7 @@ Expected has value: true" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 4`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1003,7 +1003,7 @@ Expected has value: \\"a\\"" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 5`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1012,7 +1012,7 @@ Expected has value: Map {}" `; exports[`toHaveBeenCalledTimes .not only accepts a number argument 6`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).not.toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1032,6 +1032,15 @@ exports[`toHaveBeenCalledTimes .not passes if function called more than expected Expected mock function to have been called two times, but it was called three times." `; +exports[`toHaveBeenCalledTimes .not works only on spies or jest.fn 1`] = ` +"expect(received).not.toHaveBeenCalledTimes(expected) + +Matcher error: received value must be a mock or spy function + +Received has type: function +Received has value: [Function fn]" +`; + exports[`toHaveBeenCalledTimes includes the custom mock name in the error message 1`] = ` "expect(named-mock).toHaveBeenCalledTimes(2) @@ -1039,7 +1048,7 @@ Expected mock function \\"named-mock\\" to have been called two times, `; exports[`toHaveBeenCalledTimes only accepts a number argument 1`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1048,7 +1057,7 @@ Expected has value: {}" `; exports[`toHaveBeenCalledTimes only accepts a number argument 2`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1057,7 +1066,7 @@ Expected has value: []" `; exports[`toHaveBeenCalledTimes only accepts a number argument 3`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1066,7 +1075,7 @@ Expected has value: true" `; exports[`toHaveBeenCalledTimes only accepts a number argument 4`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1075,7 +1084,7 @@ Expected has value: \\"a\\"" `; exports[`toHaveBeenCalledTimes only accepts a number argument 5`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1084,7 +1093,7 @@ Expected has value: Map {}" `; exports[`toHaveBeenCalledTimes only accepts a number argument 6`] = ` -"expect(received)[.not].toHaveBeenCalledTimes(expected) +"expect(received).toHaveBeenCalledTimes(expected) Matcher error: expected value must be a number @@ -1098,15 +1107,6 @@ exports[`toHaveBeenCalledTimes passes if function called equal to expected times Expected mock function not to be called two times, but it was called exactly two times." `; -exports[`toHaveBeenCalledTimes works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveBeenCalledTimes() - -Matcher error: received value must be a mock or spy function - -Received has type: function -Received has value: [Function fn]" -`; - exports[`toHaveBeenCalledWith includes the custom mock name in the error message 1`] = ` "expect(named-mock).not.toHaveBeenCalledWith(expected) @@ -1115,7 +1115,7 @@ Expected mock function \\"named-mock\\" not to have been called with: `; exports[`toHaveBeenCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveBeenCalledWith() +"expect(received).toHaveBeenCalledWith(...expected) Matcher error: received value must be a mock or spy function @@ -1249,7 +1249,7 @@ Expected mock function \\"named-mock\\" to not have been last called with: `; exports[`toHaveBeenLastCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveBeenLastCalledWith() +"expect(received).toHaveBeenLastCalledWith(...expected) Matcher error: received value must be a mock or spy function @@ -1395,7 +1395,7 @@ Expected mock function first call to not have been called with: `; exports[`toHaveBeenNthCalledWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveBeenNthCalledWith() +"expect(received).toHaveBeenNthCalledWith(n, ...expected) Matcher error: received value must be a mock or spy function @@ -1537,7 +1537,7 @@ But the last call has not returned yet" `; exports[`toHaveLastReturnedWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveLastReturnedWith() +"expect(received).toHaveLastReturnedWith(expected) Matcher error: received value must be a mock or spy function @@ -1732,7 +1732,7 @@ But the first call returned exactly: `; exports[`toHaveNthReturnedWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveNthReturnedWith() +"expect(received).toHaveNthReturnedWith(n, expected) Matcher error: received value must be a mock or spy function @@ -1839,7 +1839,7 @@ But the first call returned exactly: `; exports[`toHaveReturned .not fails with any argument passed 1`] = ` -"expect(received)[.not].toHaveReturned() +"expect(received).not.toHaveReturned() Matcher error: this matcher must not have an expected argument @@ -1865,8 +1865,17 @@ exports[`toHaveReturned .not passes when not returned 1`] = ` Expected mock function to have returned." `; +exports[`toHaveReturned .not works only on jest.fn 1`] = ` +"expect(received).not.toHaveReturned() + +Matcher error: received value must be a mock or spy function + +Received has type: function +Received has value: [Function fn]" +`; + exports[`toHaveReturned fails with any argument passed 1`] = ` -"expect(received)[.not].toHaveReturned() +"expect(received).toHaveReturned() Matcher error: this matcher must not have an expected argument @@ -1910,17 +1919,8 @@ Expected mock function not to have returned, but it returned: undefined" `; -exports[`toHaveReturned works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveReturned() - -Matcher error: received value must be a mock or spy function - -Received has type: function -Received has value: [Function fn]" -`; - exports[`toHaveReturnedTimes .not only accepts a number argument 1`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -1929,7 +1929,7 @@ Expected has value: {}" `; exports[`toHaveReturnedTimes .not only accepts a number argument 2`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -1938,7 +1938,7 @@ Expected has value: []" `; exports[`toHaveReturnedTimes .not only accepts a number argument 3`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -1947,7 +1947,7 @@ Expected has value: true" `; exports[`toHaveReturnedTimes .not only accepts a number argument 4`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -1956,7 +1956,7 @@ Expected has value: \\"a\\"" `; exports[`toHaveReturnedTimes .not only accepts a number argument 5`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -1965,7 +1965,7 @@ Expected has value: Map {}" `; exports[`toHaveReturnedTimes .not only accepts a number argument 6`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).not.toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2016,7 +2016,7 @@ Expected mock function not to have returned two times, but it returned `; exports[`toHaveReturnedTimes only accepts a number argument 1`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2025,7 +2025,7 @@ Expected has value: {}" `; exports[`toHaveReturnedTimes only accepts a number argument 2`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2034,7 +2034,7 @@ Expected has value: []" `; exports[`toHaveReturnedTimes only accepts a number argument 3`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2043,7 +2043,7 @@ Expected has value: true" `; exports[`toHaveReturnedTimes only accepts a number argument 4`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2052,7 +2052,7 @@ Expected has value: \\"a\\"" `; exports[`toHaveReturnedTimes only accepts a number argument 5`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2061,7 +2061,7 @@ Expected has value: Map {}" `; exports[`toHaveReturnedTimes only accepts a number argument 6`] = ` -"expect(received)[.not].toHaveReturnedTimes(expected) +"expect(received).toHaveReturnedTimes(expected) Matcher error: expected value must be a number @@ -2076,7 +2076,7 @@ Expected mock function not to have returned two times, but it returned `; exports[`toHaveReturnedTimes works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveReturnedTimes() +"expect(received).toHaveReturnedTimes(expected) Matcher error: received value must be a mock or spy function @@ -2117,7 +2117,7 @@ But it did not return." `; exports[`toHaveReturnedWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toHaveReturnedWith() +"expect(received).toHaveReturnedWith(expected) Matcher error: received value must be a mock or spy function @@ -2234,7 +2234,7 @@ But it returned exactly: `; exports[`toReturn .not fails with any argument passed 1`] = ` -"expect(received)[.not].toReturn() +"expect(received).not.toReturn() Matcher error: this matcher must not have an expected argument @@ -2260,8 +2260,17 @@ exports[`toReturn .not passes when not returned 1`] = ` Expected mock function to have returned." `; +exports[`toReturn .not works only on jest.fn 1`] = ` +"expect(received).not.toReturn() + +Matcher error: received value must be a mock or spy function + +Received has type: function +Received has value: [Function fn]" +`; + exports[`toReturn fails with any argument passed 1`] = ` -"expect(received)[.not].toReturn() +"expect(received).toReturn() Matcher error: this matcher must not have an expected argument @@ -2305,17 +2314,8 @@ Expected mock function not to have returned, but it returned: undefined" `; -exports[`toReturn works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toReturn() - -Matcher error: received value must be a mock or spy function - -Received has type: function -Received has value: [Function fn]" -`; - exports[`toReturnTimes .not only accepts a number argument 1`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2324,7 +2324,7 @@ Expected has value: {}" `; exports[`toReturnTimes .not only accepts a number argument 2`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2333,7 +2333,7 @@ Expected has value: []" `; exports[`toReturnTimes .not only accepts a number argument 3`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2342,7 +2342,7 @@ Expected has value: true" `; exports[`toReturnTimes .not only accepts a number argument 4`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2351,7 +2351,7 @@ Expected has value: \\"a\\"" `; exports[`toReturnTimes .not only accepts a number argument 5`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2360,7 +2360,7 @@ Expected has value: Map {}" `; exports[`toReturnTimes .not only accepts a number argument 6`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).not.toReturnTimes(expected) Matcher error: expected value must be a number @@ -2411,7 +2411,7 @@ Expected mock function not to have returned two times, but it returned `; exports[`toReturnTimes only accepts a number argument 1`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2420,7 +2420,7 @@ Expected has value: {}" `; exports[`toReturnTimes only accepts a number argument 2`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2429,7 +2429,7 @@ Expected has value: []" `; exports[`toReturnTimes only accepts a number argument 3`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2438,7 +2438,7 @@ Expected has value: true" `; exports[`toReturnTimes only accepts a number argument 4`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2447,7 +2447,7 @@ Expected has value: \\"a\\"" `; exports[`toReturnTimes only accepts a number argument 5`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2456,7 +2456,7 @@ Expected has value: Map {}" `; exports[`toReturnTimes only accepts a number argument 6`] = ` -"expect(received)[.not].toReturnTimes(expected) +"expect(received).toReturnTimes(expected) Matcher error: expected value must be a number @@ -2471,7 +2471,7 @@ Expected mock function not to have returned two times, but it returned `; exports[`toReturnTimes works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toReturnTimes() +"expect(received).toReturnTimes(expected) Matcher error: received value must be a mock or spy function @@ -2512,7 +2512,7 @@ But it did not return." `; exports[`toReturnWith works only on spies or jest.fn 1`] = ` -"expect(jest.fn())[.not].toReturnWith() +"expect(received).toReturnWith(expected) Matcher error: received value must be a mock or spy function diff --git a/packages/expect/src/__tests__/spyMatchers.test.js b/packages/expect/src/__tests__/spyMatchers.test.js index 33256514ebc9..54e0b336f236 100644 --- a/packages/expect/src/__tests__/spyMatchers.test.js +++ b/packages/expect/src/__tests__/spyMatchers.test.js @@ -58,11 +58,11 @@ const jestExpect = require('../'); ['toBeCalledTimes', 'toHaveBeenCalledTimes'].forEach(calledTimes => { describe(`${calledTimes}`, () => { - test('works only on spies or jest.fn', () => { + test('.not works only on spies or jest.fn', () => { const fn = function fn() {}; expect(() => - jestExpect(fn)[calledTimes](2), + jestExpect(fn).not[calledTimes](2), ).toThrowErrorMatchingSnapshot(); }); @@ -373,10 +373,12 @@ const jestExpect = require('../'); ['toReturn', 'toHaveReturned'].forEach(returned => { describe(`${returned}`, () => { - test(`works only on spies or jest.fn`, () => { + test(`.not works only on jest.fn`, () => { const fn = function fn() {}; - expect(() => jestExpect(fn)[returned]()).toThrowErrorMatchingSnapshot(); + expect(() => + jestExpect(fn).not[returned](), + ).toThrowErrorMatchingSnapshot(); }); test(`passes when returned`, () => { diff --git a/packages/expect/src/spyMatchers.ts b/packages/expect/src/spyMatchers.ts index 3fafa1175029..6286dbc60b35 100644 --- a/packages/expect/src/spyMatchers.ts +++ b/packages/expect/src/spyMatchers.ts @@ -13,13 +13,14 @@ import { EXPECTED_COLOR, matcherErrorMessage, matcherHint, + MatcherHintOptions, pluralize, printExpected, printReceived, printWithType, RECEIVED_COLOR, } from 'jest-matcher-utils'; -import {MatchersObject, SyncExpectationResult} from './types'; +import {MatchersObject, MatcherState, SyncExpectationResult} from './types'; import {equals} from './jasmineUtils'; import {iterableEquality, partition, isOneline} from './utils'; @@ -27,420 +28,492 @@ const CALL_PRINT_LIMIT = 3; const RETURN_PRINT_LIMIT = 5; const LAST_CALL_PRINT_LIMIT = 1; -const createToBeCalledMatcher = (matcherName: string) => ( - received: any, - expected: unknown, -): SyncExpectationResult => { - ensureNoExpected(expected, matcherName); - ensureMock(received, matcherName); - - 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 calls = receivedIsSpy - ? received.calls.all().map((x: any) => x.args) - : received.mock.calls; - const pass = count > 0; - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName, '') + - '\n\n' + - `Expected ${identifier} not to be called ` + - formatReceivedCalls(calls, CALL_PRINT_LIMIT, {sameSentence: true}) - : () => - matcherHint(matcherName, receivedName, '') + - '\n\n' + - `Expected ${identifier} to have been called, but it was not called.`; - - return {message, pass}; -}; +const createToBeCalledMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: unknown, + ): SyncExpectationResult { + const expectedArgument = ''; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureNoExpected(expected, matcherName.slice(1), options); + ensureMock(received, matcherName.slice(1), 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 calls = receivedIsSpy + ? received.calls.all().map((x: any) => x.args) + : received.mock.calls; + const pass = count > 0; + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName, '') + + '\n\n' + + `Expected ${identifier} not to be called ` + + formatReceivedCalls(calls, CALL_PRINT_LIMIT, {sameSentence: true}) + : () => + matcherHint(matcherName, receivedName, '') + + '\n\n' + + `Expected ${identifier} to have been called, but it was not called.`; -const createToReturnMatcher = (matcherName: string) => ( - received: any, - expected: unknown, -): SyncExpectationResult => { - ensureNoExpected(expected, matcherName); - ensureMock(received, matcherName); - - 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); - - const count = returnValues.length; - const pass = count > 0; - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName, '') + - '\n\n' + - `Expected ${identifier} not to have returned, but it returned:\n` + - ` ${getPrintedReturnValues(returnValues, RETURN_PRINT_LIMIT)}` - : () => - matcherHint(matcherName, receivedName, '') + - '\n\n' + - `Expected ${identifier} to have returned.`; - - return {message, pass}; -}; + return {message, pass}; + }; + +const createToReturnMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: unknown, + ): SyncExpectationResult { + const expectedArgument = ''; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureNoExpected(expected, matcherName.slice(1), options); + ensureMock(received, matcherName.slice(1), 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); + + const count = returnValues.length; + const pass = count > 0; + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName, '') + + '\n\n' + + `Expected ${identifier} not to have returned, but it returned:\n` + + ` ${getPrintedReturnValues(returnValues, RETURN_PRINT_LIMIT)}` + : () => + matcherHint(matcherName, receivedName, '') + + '\n\n' + + `Expected ${identifier} to have returned.`; -const createToBeCalledTimesMatcher = (matcherName: string) => ( - received: any, - expected: number, -): SyncExpectationResult => { - ensureExpectedIsNumber(expected, matcherName); - ensureMock(received, matcherName); - - 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)) + - `\n\n` + - `Expected ${identifier} not to be called ` + - `${EXPECTED_COLOR(pluralize('time', expected))}, but it was` + - ` called exactly ${RECEIVED_COLOR(pluralize('time', count))}.` - : () => - matcherHint(matcherName, receivedName, String(expected)) + - '\n\n' + - `Expected ${identifier} to have been called ` + - `${EXPECTED_COLOR(pluralize('time', expected))},` + - ` but it was called ${RECEIVED_COLOR(pluralize('time', count))}.`; - - return {message, pass}; -}; + return {message, pass}; + }; + +const createToBeCalledTimesMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: number, + ): SyncExpectationResult { + const expectedArgument = 'expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureExpectedIsNumber(expected, matcherName.slice(1), options); + ensureMock(received, matcherName.slice(1), 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)) + + `\n\n` + + `Expected ${identifier} not to be called ` + + `${EXPECTED_COLOR(pluralize('time', expected))}, but it was` + + ` called exactly ${RECEIVED_COLOR(pluralize('time', count))}.` + : () => + matcherHint(matcherName, receivedName, String(expected)) + + '\n\n' + + `Expected ${identifier} to have been called ` + + `${EXPECTED_COLOR(pluralize('time', expected))},` + + ` but it was called ${RECEIVED_COLOR(pluralize('time', count))}.`; -const createToReturnTimesMatcher = (matcherName: string) => ( - received: any, - expected: number, -): SyncExpectationResult => { - ensureExpectedIsNumber(expected, matcherName); - ensureMock(received, matcherName); - - 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', - ); - - const count = returnResults.length; - const pass = count === expected; - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName, String(expected)) + - `\n\n` + - `Expected ${identifier} not to have returned ` + - `${EXPECTED_COLOR(pluralize('time', expected))}, but it` + - ` returned exactly ${RECEIVED_COLOR(pluralize('time', count))}.` - : () => - matcherHint(matcherName, receivedName, String(expected)) + - '\n\n' + - `Expected ${identifier} to have returned ` + - `${EXPECTED_COLOR(pluralize('time', expected))},` + - ` but it returned ${RECEIVED_COLOR(pluralize('time', count))}.`; - - return {message, pass}; -}; + return {message, pass}; + }; + +const createToReturnTimesMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: number, + ): SyncExpectationResult { + const expectedArgument = 'expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureExpectedIsNumber(expected, matcherName.slice(1), options); + ensureMock(received, matcherName.slice(1), 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', + ); -const createToBeCalledWithMatcher = (matcherName: string) => ( - received: any, - ...expected: Array -): SyncExpectationResult => { - ensureMock(received, matcherName); - - 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 calls = receivedIsSpy - ? received.calls.all().map((x: any) => x.args) - : received.mock.calls; - - const [match, fail] = partition(calls, call => - equals(call, expected, [iterableEquality]), - ); - const pass = match.length > 0; - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} not to have been called with:\n` + - ` ${printExpected(expected)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to have been called with:\n` + - formatMismatchedCalls(fail, expected, CALL_PRINT_LIMIT); - - return {message, pass}; -}; + const count = returnResults.length; + const pass = count === expected; + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName, String(expected)) + + `\n\n` + + `Expected ${identifier} not to have returned ` + + `${EXPECTED_COLOR(pluralize('time', expected))}, but it` + + ` returned exactly ${RECEIVED_COLOR(pluralize('time', count))}.` + : () => + matcherHint(matcherName, receivedName, String(expected)) + + '\n\n' + + `Expected ${identifier} to have returned ` + + `${EXPECTED_COLOR(pluralize('time', expected))},` + + ` but it returned ${RECEIVED_COLOR(pluralize('time', count))}.`; -const createToReturnWithMatcher = (matcherName: string) => ( - received: any, - expected: unknown, -): SyncExpectationResult => { - ensureMock(received, matcherName); - - 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); - - const [match] = partition(returnValues, value => - equals(expected, value, [iterableEquality]), - ); - const pass = match.length > 0; - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} not to have returned:\n` + - ` ${printExpected(expected)}\n` + - `But it returned exactly:\n` + - ` ${printReceived(expected)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to have returned:\n` + - formatMismatchedReturnValues( - returnValues, - expected, - RETURN_PRINT_LIMIT, - ); - - return {message, pass}; -}; + return {message, pass}; + }; + +const createToBeCalledWithMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + ...expected: Array + ): SyncExpectationResult { + const expectedArgument = '...expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureMock(received, matcherName.slice(1), 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 calls = receivedIsSpy + ? received.calls.all().map((x: any) => x.args) + : received.mock.calls; + + const [match, fail] = partition(calls, call => + equals(call, expected, [iterableEquality]), + ); + const pass = match.length > 0; + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} not to have been called with:\n` + + ` ${printExpected(expected)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to have been called with:\n` + + formatMismatchedCalls(fail, expected, CALL_PRINT_LIMIT); -const createLastCalledWithMatcher = (matcherName: string) => ( - received: any, - ...expected: Array -): SyncExpectationResult => { - ensureMock(received, matcherName); - - 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 calls = receivedIsSpy - ? received.calls.all().map((x: any) => x.args) - : received.mock.calls; - const pass = equals(calls[calls.length - 1], expected, [iterableEquality]); - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to not have been last called with:\n` + - ` ${printExpected(expected)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to have been last called with:\n` + - formatMismatchedCalls(calls, expected, LAST_CALL_PRINT_LIMIT); - - return {message, pass}; -}; + return {message, pass}; + }; + +const createToReturnWithMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: unknown, + ): SyncExpectationResult { + const expectedArgument = 'expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureMock(received, matcherName.slice(1), 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); + + const [match] = partition(returnValues, value => + equals(expected, value, [iterableEquality]), + ); + const pass = match.length > 0; + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} not to have returned:\n` + + ` ${printExpected(expected)}\n` + + `But it returned exactly:\n` + + ` ${printReceived(expected)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to have returned:\n` + + formatMismatchedReturnValues( + returnValues, + expected, + RETURN_PRINT_LIMIT, + ); -const createLastReturnedMatcher = (matcherName: string) => ( - received: any, - expected: unknown, -): SyncExpectationResult => { - ensureMock(received, matcherName); - - const receivedName = received.getMockName(); - const identifier = - receivedName === 'jest.fn()' - ? 'mock function' - : `mock function "${receivedName}"`; - - const results = received.mock.results; - const lastResult = results[results.length - 1]; - const pass = - !!lastResult && - lastResult.type === 'return' && - equals(lastResult.value, expected, [iterableEquality]); - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to not have last returned:\n` + - ` ${printExpected(expected)}\n` + - `But it last returned exactly:\n` + - ` ${printReceived(lastResult.value)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} to have last returned:\n` + - ` ${printExpected(expected)}\n` + - (!lastResult - ? `But it was ${RECEIVED_COLOR('not called')}` - : lastResult.type === 'incomplete' - ? `But the last call ${RECEIVED_COLOR('has not returned yet')}` - : lastResult.type === 'throw' - ? `But the last call ${RECEIVED_COLOR('threw an error')}` - : `But the last call returned:\n ${printReceived( - lastResult.value, - )}`); - - return {message, pass}; -}; + return {message, pass}; + }; + +const createLastCalledWithMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + ...expected: Array + ): SyncExpectationResult { + const expectedArgument = '...expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureMock(received, matcherName.slice(1), 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 calls = receivedIsSpy + ? received.calls.all().map((x: any) => x.args) + : received.mock.calls; + const pass = equals(calls[calls.length - 1], expected, [iterableEquality]); + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to not have been last called with:\n` + + ` ${printExpected(expected)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to have been last called with:\n` + + formatMismatchedCalls(calls, expected, LAST_CALL_PRINT_LIMIT); -const createNthCalledWithMatcher = (matcherName: string) => ( - received: any, - nth: number, - ...expected: Array -): SyncExpectationResult => { - ensureMock(received, matcherName); - - const receivedIsSpy = isSpy(received); - const type = receivedIsSpy ? 'spy' : 'mock function'; - - // @ts-ignore - if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { - const message = () => - `nth value ${printReceived( - nth, - )} must be a positive integer greater than ${printExpected(0)}`; - const pass = false; return {message, pass}; - } + }; + +const createLastReturnedMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + expected: unknown, + ): SyncExpectationResult { + const expectedArgument = 'expected'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + }; + ensureMock(received, matcherName.slice(1), expectedArgument, options); + + const receivedName = received.getMockName(); + const identifier = + receivedName === 'jest.fn()' + ? 'mock function' + : `mock function "${receivedName}"`; + + const results = received.mock.results; + const lastResult = results[results.length - 1]; + const pass = + !!lastResult && + lastResult.type === 'return' && + equals(lastResult.value, expected, [iterableEquality]); + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to not have last returned:\n` + + ` ${printExpected(expected)}\n` + + `But it last returned exactly:\n` + + ` ${printReceived(lastResult.value)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} to have last returned:\n` + + ` ${printExpected(expected)}\n` + + (!lastResult + ? `But it was ${RECEIVED_COLOR('not called')}` + : lastResult.type === 'incomplete' + ? `But the last call ${RECEIVED_COLOR('has not returned yet')}` + : lastResult.type === 'throw' + ? `But the last call ${RECEIVED_COLOR('threw an error')}` + : `But the last call returned:\n ${printReceived( + lastResult.value, + )}`); - const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); - const identifier = - receivedIsSpy || receivedName === 'jest.fn()' - ? type - : `${type} "${receivedName}"`; - const calls = receivedIsSpy - ? received.calls.all().map((x: any) => x.args) - : received.mock.calls; - const pass = equals(calls[nth - 1], expected, [iterableEquality]); - - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} ${nthToString( - nth, - )} call to not have been called with:\n` + - ` ${printExpected(expected)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} ${nthToString( + return {message, pass}; + }; + +const createNthCalledWithMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + nth: number, + ...expected: Array + ): SyncExpectationResult { + const expectedArgument = 'n'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + secondArgument: '...expected', + }; + ensureMock(received, matcherName.slice(1), expectedArgument, options); + + const receivedIsSpy = isSpy(received); + const type = receivedIsSpy ? 'spy' : 'mock function'; + + // @ts-ignore + if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { + const message = () => + `nth value ${printReceived( nth, - )} call to have been called with:\n` + - formatMismatchedCalls( - calls[nth - 1] ? [calls[nth - 1]] : [], - expected, - LAST_CALL_PRINT_LIMIT, - ); - - return {message, pass}; -}; + )} must be a positive integer greater than ${printExpected(0)}`; + const pass = false; + return {message, pass}; + } + + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + const identifier = + receivedIsSpy || receivedName === 'jest.fn()' + ? type + : `${type} "${receivedName}"`; + const calls = receivedIsSpy + ? received.calls.all().map((x: any) => x.args) + : received.mock.calls; + const pass = equals(calls[nth - 1], expected, [iterableEquality]); + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} ${nthToString( + nth, + )} call to not have been called with:\n` + + ` ${printExpected(expected)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} ${nthToString( + nth, + )} call to have been called with:\n` + + formatMismatchedCalls( + calls[nth - 1] ? [calls[nth - 1]] : [], + expected, + LAST_CALL_PRINT_LIMIT, + ); -const createNthReturnedWithMatcher = (matcherName: string) => ( - received: any, - nth: number, - expected: unknown, -): SyncExpectationResult => { - ensureMock(received, matcherName); - - //@ts-ignore - if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { - const message = () => - `nth value ${printReceived( - nth, - )} must be a positive integer greater than ${printExpected(0)}`; - const pass = false; return {message, pass}; - } + }; + +const createNthReturnedWithMatcher = (matcherName: string) => + function( + this: MatcherState, + received: any, + nth: number, + expected: unknown, + ): SyncExpectationResult { + const expectedArgument = 'n'; + const options: MatcherHintOptions = { + isNot: this.isNot, + promise: this.promise, + secondArgument: 'expected', + }; + ensureMock(received, matcherName.slice(1), expectedArgument, options); + + //@ts-ignore + if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { + const message = () => + `nth value ${printReceived( + nth, + )} must be a positive integer greater than ${printExpected(0)}`; + const pass = false; + return {message, pass}; + } - const receivedName = received.getMockName(); - const identifier = - receivedName === 'jest.fn()' - ? 'mock function' - : `mock function "${receivedName}"`; - - const results = received.mock.results; - const nthResult = results[nth - 1]; - const pass = - !!nthResult && - nthResult.type === 'return' && - equals(nthResult.value, expected, [iterableEquality]); - const nthString = nthToString(nth); - const message = pass - ? () => - matcherHint('.not' + matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} ${nthString} call to not have returned with:\n` + - ` ${printExpected(expected)}\n` + - `But the ${nthString} call returned exactly:\n` + - ` ${printReceived(nthResult.value)}` - : () => - matcherHint(matcherName, receivedName) + - '\n\n' + - `Expected ${identifier} ${nthString} call to have returned with:\n` + - ` ${printExpected(expected)}\n` + - (results.length === 0 - ? `But it was ${RECEIVED_COLOR('not called')}` - : nth > results.length - ? `But it was only called ${printReceived(results.length)} times` - : nthResult.type === 'incomplete' - ? `But the ${nthString} call ${RECEIVED_COLOR( - 'has not returned yet', - )}` - : nthResult.type === 'throw' - ? `But the ${nthString} call ${RECEIVED_COLOR('threw an error')}` - : `But the ${nthString} call returned with:\n ${printReceived( - nthResult.value, - )}`); - - return {message, pass}; -}; + const receivedName = received.getMockName(); + const identifier = + receivedName === 'jest.fn()' + ? 'mock function' + : `mock function "${receivedName}"`; + + const results = received.mock.results; + const nthResult = results[nth - 1]; + const pass = + !!nthResult && + nthResult.type === 'return' && + equals(nthResult.value, expected, [iterableEquality]); + const nthString = nthToString(nth); + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} ${nthString} call to not have returned with:\n` + + ` ${printExpected(expected)}\n` + + `But the ${nthString} call returned exactly:\n` + + ` ${printReceived(nthResult.value)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${identifier} ${nthString} call to have returned with:\n` + + ` ${printExpected(expected)}\n` + + (results.length === 0 + ? `But it was ${RECEIVED_COLOR('not called')}` + : nth > results.length + ? `But it was only called ${printReceived(results.length)} times` + : nthResult.type === 'incomplete' + ? `But the ${nthString} call ${RECEIVED_COLOR( + 'has not returned yet', + )}` + : nthResult.type === 'throw' + ? `But the ${nthString} call ${RECEIVED_COLOR('threw an error')}` + : `But the ${nthString} call returned with:\n ${printReceived( + nthResult.value, + )}`); + + return {message, pass}; + }; const spyMatchers: MatchersObject = { lastCalledWith: createLastCalledWithMatcher('.lastCalledWith'), @@ -471,17 +544,22 @@ const spyMatchers: MatchersObject = { const isSpy = (spy: any) => spy.calls && typeof spy.calls.count === 'function'; -const ensureMock = (mockOrSpy: any, matcherName: any) => { +const ensureMock = ( + received: any, + matcherName: string, + expectedArgument: string, + options: MatcherHintOptions, +) => { if ( - !mockOrSpy || - ((mockOrSpy.calls === undefined || mockOrSpy.calls.all === undefined) && - mockOrSpy._isMockFunction !== true) + !received || + ((received.calls === undefined || received.calls.all === undefined) && + received._isMockFunction !== true) ) { throw new Error( matcherErrorMessage( - matcherHint('[.not]' + matcherName, 'jest.fn()', ''), + matcherHint(matcherName, undefined, expectedArgument, options), `${RECEIVED_COLOR('received')} value must be a mock or spy function`, - printWithType('Received', mockOrSpy, printReceived), + printWithType('Received', received, printReceived), ), ); }