diff --git a/src/matchers/toHaveBeenCalledAfter/__snapshots__/index.test.js.snap b/src/matchers/toHaveBeenCalledAfter/__snapshots__/index.test.js.snap index dd81e1a6..a81b5de5 100644 --- a/src/matchers/toHaveBeenCalledAfter/__snapshots__/index.test.js.snap +++ b/src/matchers/toHaveBeenCalledAfter/__snapshots__/index.test.js.snap @@ -45,6 +45,15 @@ Received second mock with invocationCallOrder: []" `; +exports[`.toHaveBeenCalledAfter fails when given first value is not a jest spy or mock 1`] = ` +"expect(received).toHaveBeenCalledAfter(expected) + +Matcher error: \\"received\\" must be a mock or spy function + +Received has type: function +Received has value: [Function mock1]" +`; + exports[`.toHaveBeenCalledAfter fails when given second mock is called after first mock 1`] = ` "expect(received).toHaveBeenCalledAfter(expected) @@ -53,3 +62,12 @@ Expected first mock to have been called after, invocationCallOrder: Received second mock with invocationCallOrder: [5000]" `; + +exports[`.toHaveBeenCalledAfter fails when given second value is not a jest spy or mock 1`] = ` +"expect(received).toHaveBeenCalledAfter(expected) + +Matcher error: \\"expected\\" must be a mock or spy function + +Expected has type: function +Expected has value: [Function mock2]" +`; diff --git a/src/matchers/toHaveBeenCalledAfter/index.js b/src/matchers/toHaveBeenCalledAfter/index.js index a036c80c..675ce978 100644 --- a/src/matchers/toHaveBeenCalledAfter/index.js +++ b/src/matchers/toHaveBeenCalledAfter/index.js @@ -1,4 +1,6 @@ -import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils'; +import { matcherHint, printExpected, printReceived, printWithType } from 'jest-matcher-utils'; + +import { isJestMockOrSpy } from '../../utils'; import predicate from './predicate'; @@ -18,8 +20,29 @@ const failMessage = (firstInvocationCallOrder, secondInvocationCallOrder) => () 'Received second mock with invocationCallOrder:\n' + ` ${printReceived(secondInvocationCallOrder)}`; +const mockCheckFailMessage = (value, isReceivedValue) => () => { + const valueKind = isReceivedValue ? 'Received' : 'Expected'; + const valueKindPrintFunc = isReceivedValue ? printReceived : printExpected; + + return ( + matcherHint('.toHaveBeenCalledAfter') + + '\n\n' + + `Matcher error: ${valueKindPrintFunc(valueKind.toLowerCase())} must be a mock or spy function` + + '\n\n' + + printWithType(valueKind, value, valueKindPrintFunc) + ); +}; + export default { toHaveBeenCalledAfter: (firstMock, secondMock) => { + if (!isJestMockOrSpy(firstMock)) { + return { pass: false, message: mockCheckFailMessage(firstMock, true) }; + } + + if (!isJestMockOrSpy(secondMock)) { + return { pass: false, message: mockCheckFailMessage(secondMock, false) }; + } + const firstInvocationCallOrder = firstMock.mock.invocationCallOrder; const secondInvocationCallOrder = secondMock.mock.invocationCallOrder; const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder); diff --git a/src/matchers/toHaveBeenCalledAfter/index.test.js b/src/matchers/toHaveBeenCalledAfter/index.test.js index 4efd0340..0320b3d8 100644 --- a/src/matchers/toHaveBeenCalledAfter/index.test.js +++ b/src/matchers/toHaveBeenCalledAfter/index.test.js @@ -58,6 +58,18 @@ describe('.toHaveBeenCalledAfter', () => { mock2.mock.invocationCallOrder[0] = 4000; expect(mock1).toHaveBeenCalledAfter(mock2); }); + + test('fails when given first value is not a jest spy or mock', () => { + const mock1 = () => {}; + const mock2 = jest.fn(); + expect(() => expect(mock1).toHaveBeenCalledAfter(mock2)).toThrowErrorMatchingSnapshot(); + }); + + test('fails when given second value is not a jest spy or mock', () => { + const mock1 = jest.fn(); + const mock2 = () => {}; + expect(() => expect(mock1).toHaveBeenCalledAfter(mock2)).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toHaveBeenCalledAfter', () => { diff --git a/src/matchers/toHaveBeenCalledBefore/__snapshots__/index.test.js.snap b/src/matchers/toHaveBeenCalledBefore/__snapshots__/index.test.js.snap index 9f446784..db4243ec 100644 --- a/src/matchers/toHaveBeenCalledBefore/__snapshots__/index.test.js.snap +++ b/src/matchers/toHaveBeenCalledBefore/__snapshots__/index.test.js.snap @@ -53,3 +53,21 @@ Expected first mock to have been called before, invocationCallOrder: Received second mock with invocationCallOrder: [4000]" `; + +exports[`.toHaveBeenCalledBefore fails when given first value is not a jest spy or mock 1`] = ` +"expect(received).toHaveBeenCalledAfter(expected) + +Matcher error: \\"received\\" must be a mock or spy function + +Received has type: function +Received has value: [Function mock1]" +`; + +exports[`.toHaveBeenCalledBefore fails when given second value is not a jest spy or mock 1`] = ` +"expect(received).toHaveBeenCalledAfter(expected) + +Matcher error: \\"expected\\" must be a mock or spy function + +Expected has type: function +Expected has value: [Function mock2]" +`; diff --git a/src/matchers/toHaveBeenCalledBefore/index.js b/src/matchers/toHaveBeenCalledBefore/index.js index 603c1263..bf30a2a9 100644 --- a/src/matchers/toHaveBeenCalledBefore/index.js +++ b/src/matchers/toHaveBeenCalledBefore/index.js @@ -1,4 +1,6 @@ -import { matcherHint, printExpected, printReceived } from 'jest-matcher-utils'; +import { matcherHint, printExpected, printReceived, printWithType } from 'jest-matcher-utils'; + +import { isJestMockOrSpy } from '../../utils'; import predicate from './predicate'; @@ -18,8 +20,29 @@ const failMessage = (firstInvocationCallOrder, secondInvocationCallOrder) => () 'Received second mock with invocationCallOrder:\n' + ` ${printReceived(secondInvocationCallOrder)}`; +const mockCheckFailMessage = (value, isReceivedValue) => () => { + const valueKind = isReceivedValue ? 'Received' : 'Expected'; + const valueKindPrintFunc = isReceivedValue ? printReceived : printExpected; + + return ( + matcherHint('.toHaveBeenCalledAfter') + + '\n\n' + + `Matcher error: ${valueKindPrintFunc(valueKind.toLowerCase())} must be a mock or spy function` + + '\n\n' + + printWithType(valueKind, value, valueKindPrintFunc) + ); +}; + export default { toHaveBeenCalledBefore: (firstMock, secondMock) => { + if (!isJestMockOrSpy(firstMock)) { + return { pass: false, message: mockCheckFailMessage(firstMock, true) }; + } + + if (!isJestMockOrSpy(secondMock)) { + return { pass: false, message: mockCheckFailMessage(secondMock, false) }; + } + const firstInvocationCallOrder = firstMock.mock.invocationCallOrder; const secondInvocationCallOrder = secondMock.mock.invocationCallOrder; const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder); diff --git a/src/matchers/toHaveBeenCalledBefore/index.test.js b/src/matchers/toHaveBeenCalledBefore/index.test.js index b3f835c3..62abc838 100644 --- a/src/matchers/toHaveBeenCalledBefore/index.test.js +++ b/src/matchers/toHaveBeenCalledBefore/index.test.js @@ -58,6 +58,18 @@ describe('.toHaveBeenCalledBefore', () => { mock2.mock.invocationCallOrder[0] = 4000; expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot(); }); + + test('fails when given first value is not a jest spy or mock', () => { + const mock1 = () => {}; + const mock2 = jest.fn(); + expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot(); + }); + + test('fails when given second value is not a jest spy or mock', () => { + const mock1 = jest.fn(); + const mock2 = () => {}; + expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot(); + }); }); describe('.not.toHaveBeenCalledBefore', () => { diff --git a/src/utils/index.js b/src/utils/index.js index 59a4cc4b..5ad9224e 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -8,4 +8,8 @@ export const determinePropertyMessage = (actual, property, message = 'Not Access return actual && Object.hasOwnProperty.call(actual, property) ? actual[property] : message; }; +export const isJestMockOrSpy = value => { + return !!(value && value._isMockFunction === true && typeof value.mock === 'object'); +}; + export { equals }; diff --git a/src/utils/index.test.js b/src/utils/index.test.js index 9859ab66..f4e10435 100644 --- a/src/utils/index.test.js +++ b/src/utils/index.test.js @@ -1,4 +1,4 @@ -import { contains, determinePropertyMessage } from './'; +import { contains, determinePropertyMessage, isJestMockOrSpy } from './'; describe('Utils', () => { describe('.contains', () => { @@ -48,4 +48,16 @@ describe('Utils', () => { }); } }); + + describe('.isJestMockOrSpy', () => { + test('returns true if value is a jest mock', () => { + const spy = jest.fn(); + expect(isJestMockOrSpy(spy)).toBe(true); + }); + + test('returns false if value is not a jest mock', () => { + const fn = () => {}; + expect(isJestMockOrSpy(fn)).toBe(false); + }); + }); });