Skip to content

Commit

Permalink
Update toHaveBeenCalled matchers to fail if other mock is never called (
Browse files Browse the repository at this point in the history
#420)

* Update toHaveBeenCalledAfter to fail if second mock is never called (with optional arg for previous behavior)
* Update toHaveBeenCalledBefore to fail if first mock is never called (with optional arg for previous behavior)
  • Loading branch information
theryansmee committed Mar 15, 2022
1 parent 30fd85e commit ebf5cb5
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 25 deletions.
8 changes: 4 additions & 4 deletions src/matchers/toHaveBeenCalledAfter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isJestMockOrSpy } from '../utils';

export function toHaveBeenCalledAfter(actual, expected) {
export function toHaveBeenCalledAfter(actual, expected, failIfNoFirstInvocation = true) {
const { printReceived, printExpected, matcherHint } = this.utils;

if (!isJestMockOrSpy(actual)) {
Expand All @@ -13,7 +13,7 @@ export function toHaveBeenCalledAfter(actual, expected) {

const firstInvocationCallOrder = actual.mock.invocationCallOrder;
const secondInvocationCallOrder = expected.mock.invocationCallOrder;
const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder);
const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder, failIfNoFirstInvocation);

const passMessage =
matcherHint('.not.toHaveBeenCalledAfter') +
Expand All @@ -36,8 +36,8 @@ export function toHaveBeenCalledAfter(actual, expected) {

const smallest = ns => ns.reduce((acc, n) => (acc < n ? acc : n));

const predicate = (firstInvocationCallOrder, secondInvocationCallOrder) => {
if (firstInvocationCallOrder.length === 0) return true;
const predicate = (firstInvocationCallOrder, secondInvocationCallOrder, failIfNoFirstInvocation) => {
if (firstInvocationCallOrder.length === 0) return !failIfNoFirstInvocation;
if (secondInvocationCallOrder.length === 0) return false;

const firstSmallest = smallest(firstInvocationCallOrder);
Expand Down
8 changes: 4 additions & 4 deletions src/matchers/toHaveBeenCalledBefore.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isJestMockOrSpy } from '../utils';

export function toHaveBeenCalledBefore(actual, expected) {
export function toHaveBeenCalledBefore(actual, expected, failIfNoSecondInvocation = true) {
const { printReceived, printExpected, matcherHint } = this.utils;

if (!isJestMockOrSpy(actual)) {
Expand All @@ -13,7 +13,7 @@ export function toHaveBeenCalledBefore(actual, expected) {

const firstInvocationCallOrder = actual.mock.invocationCallOrder;
const secondInvocationCallOrder = expected.mock.invocationCallOrder;
const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder);
const pass = predicate(firstInvocationCallOrder, secondInvocationCallOrder, failIfNoSecondInvocation);

const passMessage =
matcherHint('.not.toHaveBeenCalledBefore') +
Expand Down Expand Up @@ -49,9 +49,9 @@ const mockCheckFailMessage = (utils, value, isReceivedValue) => () => {

const smallest = ns => ns.reduce((acc, n) => (acc < n ? acc : n));

const predicate = (firstInvocationCallOrder, secondInvocationCallOrder) => {
const predicate = (firstInvocationCallOrder, secondInvocationCallOrder, failIfNoSecondInvocation) => {
if (firstInvocationCallOrder.length === 0) return false;
if (secondInvocationCallOrder.length === 0) return true;
if (secondInvocationCallOrder.length === 0) return !failIfNoSecondInvocation;

const firstSmallest = smallest(firstInvocationCallOrder);
const secondSmallest = smallest(secondInvocationCallOrder);
Expand Down
20 changes: 19 additions & 1 deletion test/matchers/__snapshots__/toHaveBeenCalledAfter.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toHaveBeenCalledAfter fails when given a first mock has not been called 1`] = `
exports[`.not.toHaveBeenCalledAfter failIfNoFirstInvocation is passed as false failed when given first mock has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).not.toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expected first mock to not have been called after, invocationCallOrder:
Expand All @@ -27,6 +27,24 @@ Received second mock with invocationCallOrder:
<red>[25]</>"
`;
exports[`.toHaveBeenCalledAfter failIfNoFirstInvocation is passed as true failed when given first mock has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expected first mock to have been called after, invocationCallOrder:
<green>[]</>
Received second mock with invocationCallOrder:
<red>[]</>"
`;
exports[`.toHaveBeenCalledAfter fails when given first mock has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expected first mock to have been called after, invocationCallOrder:
<green>[]</>
Received second mock with invocationCallOrder:
<red>[]</>"
`;
exports[`.toHaveBeenCalledAfter fails when given first mock is called before multiple calls to second mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expand Down
26 changes: 22 additions & 4 deletions test/matchers/__snapshots__/toHaveBeenCalledBefore.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toHaveBeenCalledBefore failIfNoSecondInvocation is passed as false fails when given first mock that has been called and a second mock that has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).not.toHaveBeenCalledBefore(</><green>expected</><dim>)</>
Expected first mock to not have been called before, invocationCallOrder:
<green>[31]</>
Received second mock with invocationCallOrder:
<red>[]</>"
`;
exports[`.not.toHaveBeenCalledBefore fails when given first mock is called before multiple calls to second mock 1`] = `
"<dim>expect(</><red>received</><dim>).not.toHaveBeenCalledBefore(</><green>expected</><dim>)</>
Expand All @@ -18,11 +27,11 @@ Received second mock with invocationCallOrder:
<red>[5000]</>"
`;
exports[`.not.toHaveBeenCalledBefore fails when given first mock that has been called and a second mock that has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).not.toHaveBeenCalledBefore(</><green>expected</><dim>)</>
exports[`.toHaveBeenCalledBefore failIfNoSecondInvocation is passed as true fails when given first mock that has been called and a second mock that has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledBefore(</><green>expected</><dim>)</>
Expected first mock to not have been called before, invocationCallOrder:
<green>[4000]</>
Expected first mock to have been called before, invocationCallOrder:
<green>[16]</>
Received second mock with invocationCallOrder:
<red>[]</>"
`;
Expand Down Expand Up @@ -54,6 +63,15 @@ Received second mock with invocationCallOrder:
<red>[4000]</>"
`;
exports[`.toHaveBeenCalledBefore fails when given first mock that has been called and a second mock that has not been called 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledBefore(</><green>expected</><dim>)</>
Expected first mock to have been called before, invocationCallOrder:
<green>[1]</>
Received second mock with invocationCallOrder:
<red>[]</>"
`;
exports[`.toHaveBeenCalledBefore fails when given first value is not a jest spy or mock 1`] = `
"<dim>expect(</><red>received</><dim>).toHaveBeenCalledAfter(</><green>expected</><dim>)</>
Expand Down
45 changes: 41 additions & 4 deletions test/matchers/toHaveBeenCalledAfter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import * as matcher from 'src/matchers/toHaveBeenCalledAfter';
expect.extend(matcher);

describe('.toHaveBeenCalledAfter', () => {
test('passes when given a first mock has not been called', () => {
test('fails when given first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();
expect(mock1).toHaveBeenCalledAfter(mock2);

expect(() => expect(mock1).toHaveBeenCalledAfter(mock2)).toThrowErrorMatchingSnapshot();
});

test('fails when given first mock that has been called and a second mock that has not been called', () => {
Expand Down Expand Up @@ -84,13 +85,31 @@ describe('.toHaveBeenCalledAfter', () => {
mock2.mock.invocationCallOrder[0] = lessThan;
expect(mock1).toHaveBeenCalledAfter(mock2);
});

describe('failIfNoFirstInvocation is passed as false', () => {
test('passes when given first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

expect(mock1).toHaveBeenCalledAfter(mock2, false);
});
});

describe('failIfNoFirstInvocation is passed as true', () => {
test('failed when given first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

expect(() => expect(mock1).toHaveBeenCalledAfter(mock2, true)).toThrowErrorMatchingSnapshot();
});
});
});

describe('.not.toHaveBeenCalledAfter', () => {
test('fails when given a first mock has not been called', () => {
test('passes when given a first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();
expect(() => expect(mock1).not.toHaveBeenCalledAfter(mock2)).toThrowErrorMatchingSnapshot();
expect(mock1).not.toHaveBeenCalledAfter(mock2);
});

test('passes when given first mock that has been called and a second mock that has not been called', () => {
Expand Down Expand Up @@ -145,4 +164,22 @@ describe('.not.toHaveBeenCalledAfter', () => {
mock1();
expect(() => expect(mock1).not.toHaveBeenCalledAfter(mock2)).toThrowErrorMatchingSnapshot();
});

describe('failIfNoFirstInvocation is passed as false', () => {
test('failed when given first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

expect(() => expect(mock1).not.toHaveBeenCalledAfter(mock2, false)).toThrowErrorMatchingSnapshot();
});
});

describe('failIfNoFirstInvocation is passed as true', () => {
test('passes when given first mock has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

expect(mock1).not.toHaveBeenCalledAfter(mock2, true);
});
});
});
52 changes: 48 additions & 4 deletions test/matchers/toHaveBeenCalledBefore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ describe('.toHaveBeenCalledBefore', () => {
expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot();
});

test('passes when given first mock that has been called and a second mock that has not been called', () => {
test('fails when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();
mock1();
expect(mock1).toHaveBeenCalledBefore(mock2);
expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot();
});

test('passes when given first mock is called before second mock', () => {
Expand Down Expand Up @@ -84,6 +84,28 @@ describe('.toHaveBeenCalledBefore', () => {
const mock2 = () => {};
expect(() => expect(mock1).toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot();
});

describe('failIfNoSecondInvocation is passed as false', () => {
test('passes when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

mock1();

expect(mock1).toHaveBeenCalledBefore(mock2, false);
});
});

describe('failIfNoSecondInvocation is passed as true', () => {
test('fails when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

mock1();

expect(() => expect(mock1).toHaveBeenCalledBefore(mock2, true)).toThrowErrorMatchingSnapshot();
});
});
});

describe('.not.toHaveBeenCalledBefore', () => {
Expand All @@ -93,12 +115,12 @@ describe('.not.toHaveBeenCalledBefore', () => {
expect(mock1).not.toHaveBeenCalledBefore(mock2);
});

test('fails when given first mock that has been called and a second mock that has not been called', () => {
test('passes when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();
mock1();
mock1.mock.invocationCallOrder[0] = 4000; // amend the value for the snapshot
expect(() => expect(mock1).not.toHaveBeenCalledBefore(mock2)).toThrowErrorMatchingSnapshot();
expect(mock1).not.toHaveBeenCalledBefore(mock2);
});

test('fails when given first mock is called before second mock', () => {
Expand Down Expand Up @@ -145,4 +167,26 @@ describe('.not.toHaveBeenCalledBefore', () => {
mock1();
expect(mock1).not.toHaveBeenCalledBefore(mock2);
});

describe('failIfNoSecondInvocation is passed as false', () => {
test('fails when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

mock1();

expect(() => expect(mock1).not.toHaveBeenCalledBefore(mock2, false)).toThrowErrorMatchingSnapshot();
});
});

describe('failIfNoSecondInvocation is passed as true', () => {
test('passes when given first mock that has been called and a second mock that has not been called', () => {
const mock1 = jest.fn();
const mock2 = jest.fn();

mock1();

expect(mock1).not.toHaveBeenCalledBefore(mock2, true);
});
});
});
12 changes: 8 additions & 4 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,19 @@ declare namespace jest {
* Note: Required Jest version >=23
*
* @param {Mock} mock
* @param {boolean} [failIfNoSecondInvocation=true]
*/
toHaveBeenCalledBefore(mock: jest.Mock): R;
toHaveBeenCalledBefore(mock: jest.Mock, failIfNoSecondInvocation: boolean): R;

/**
* Use `.toHaveBeenCalledAfter` when checking if a `Mock` was called after another `Mock`.
*
* Note: Required Jest version >=23
*
* @param {Mock} mock
* @param {boolean} [failIfNoFirstInvocation=true]
*/
toHaveBeenCalledAfter(mock: jest.Mock): R;
toHaveBeenCalledAfter(mock: jest.Mock, failIfNoFirstInvocation: boolean): R;

/**
* Use `.toHaveBeenCalledOnce` to check if a `Mock` was called exactly one time.
Expand Down Expand Up @@ -569,17 +571,19 @@ declare namespace jest {
* Note: Required Jest version >=23
*
* @param {Mock} mock
* @param {boolean} [failIfNoSecondInvocation=true]
*/
toHaveBeenCalledBefore(mock: jest.Mock): any;
toHaveBeenCalledBefore(mock: jest.Mock, failIfNoSecondInvocation: boolean): any;

/**
* Use `.toHaveBeenCalledAfter` when checking if a `Mock` was called after another `Mock`.
*
* Note: Required Jest version >=23
*
* @param {Mock} mock
* @param {boolean} [failIfNoFirstInvocation=true]
*/
toHaveBeenCalledAfter(mock: jest.Mock): any;
toHaveBeenCalledAfter(mock: jest.Mock, failIfNoFirstInvocation: boolean): any;

/**
* Use `.toHaveBeenCalledOnce` to check if a `Mock` was called exactly one time.
Expand Down

0 comments on commit ebf5cb5

Please sign in to comment.