Skip to content

Commit

Permalink
Validate if spy or mock was passed when expected (#203)
Browse files Browse the repository at this point in the history
  • Loading branch information
atsikov authored Oct 11, 2021
1 parent 75eeb52 commit d5dd8f1
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ Received second mock with invocationCallOrder:
<red>[]</>"
`;
exports[`.toHaveBeenCalledAfter fails when given first value is not a jest spy or mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Matcher error: <red>\\"received\\"</> must be a mock or spy function
Received has type: function
Received has value: <red>[Function mock1]</>"
`;
exports[`.toHaveBeenCalledAfter fails when given second mock is called after first mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expand All @@ -53,3 +62,12 @@ Expected first mock to have been called after, invocationCallOrder:
Received second mock with invocationCallOrder:
<red>[5000]</>"
`;
exports[`.toHaveBeenCalledAfter fails when given second value is not a jest spy or mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Matcher error: <green>\\"expected\\"</> must be a mock or spy function
Expected has type: function
Expected has value: <green>[Function mock2]</>"
`;
25 changes: 24 additions & 1 deletion src/matchers/toHaveBeenCalledAfter/index.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/matchers/toHaveBeenCalledAfter/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,21 @@ Expected first mock to have been called before, invocationCallOrder:
Received second mock with invocationCallOrder:
<red>[4000]</>"
`;
exports[`.toHaveBeenCalledBefore fails when given first value is not a jest spy or mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Matcher error: <red>\\"received\\"</> must be a mock or spy function
Received has type: function
Received has value: <red>[Function mock1]</>"
`;
exports[`.toHaveBeenCalledBefore fails when given second value is not a jest spy or mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Matcher error: <green>\\"expected\\"</> must be a mock or spy function
Expected has type: function
Expected has value: <green>[Function mock2]</>"
`;
25 changes: 24 additions & 1 deletion src/matchers/toHaveBeenCalledBefore/index.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions src/matchers/toHaveBeenCalledBefore/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
4 changes: 4 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
14 changes: 13 additions & 1 deletion src/utils/index.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { contains, determinePropertyMessage } from './';
import { contains, determinePropertyMessage, isJestMockOrSpy } from './';

describe('Utils', () => {
describe('.contains', () => {
Expand Down Expand Up @@ -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);
});
});
});

0 comments on commit d5dd8f1

Please sign in to comment.