diff --git a/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap index fd64c30e1931..80f11d07460e 100644 --- a/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/spy_matchers.test.js.snap @@ -1059,3 +1059,75 @@ exports[`toHaveBeenNthCalledWith works with trailing undefined arguments 1`] = ` Expected mock function first call to have been called with: Did not expect argument 2 but it was called with undefined." `; + +exports[`toHaveReturnedValue .not passes when called 1`] = ` +"expect(jest.fn()).toHaveReturnedValue(expected) + +Expected mock function to have returned: + \\"Some Other Value\\"" +`; + +exports[`toHaveReturnedValue .not passes when called with no arguments 1`] = ` +"expect(jest.fn()).toHaveReturnedValue(expected) + +Expected mock function to have returned: + undefined" +`; + +exports[`toHaveReturnedValue passes when called 1`] = ` +"expect(jest.fn()).not.toHaveReturnedValue(expected) + +Expected mock function not to have returned: + \\"Return Value\\"" +`; + +exports[`toHaveReturnedValue passes with no arguments 1`] = ` +"expect(jest.fn()).not.toHaveReturnedValue(expected) + +Expected mock function not to have returned: + undefined" +`; + +exports[`toHaveReturnedValue works only on spies or jest.fn 1`] = ` +"expect(jest.fn())[.not].toHaveReturnedValue() + +jest.fn() value must be a mock function or spy. +Received: + function: [Function fn]" +`; + +exports[`toReturnValue .not passes when called 1`] = ` +"expect(jest.fn()).toReturnValue(expected) + +Expected mock function to have returned: + \\"Some Other Value\\"" +`; + +exports[`toReturnValue .not passes when called with no arguments 1`] = ` +"expect(jest.fn()).toReturnValue(expected) + +Expected mock function to have returned: + undefined" +`; + +exports[`toReturnValue passes when called 1`] = ` +"expect(jest.fn()).not.toReturnValue(expected) + +Expected mock function not to have returned: + \\"Return Value\\"" +`; + +exports[`toReturnValue passes with no arguments 1`] = ` +"expect(jest.fn()).not.toReturnValue(expected) + +Expected mock function not to have returned: + undefined" +`; + +exports[`toReturnValue works only on spies or jest.fn 1`] = ` +"expect(jest.fn())[.not].toReturnValue() + +jest.fn() value must be a mock function or spy. +Received: + function: [Function fn]" +`; diff --git a/packages/expect/src/__tests__/spy_matchers.test.js b/packages/expect/src/__tests__/spy_matchers.test.js index 91624ef28515..689204abbe04 100644 --- a/packages/expect/src/__tests__/spy_matchers.test.js +++ b/packages/expect/src/__tests__/spy_matchers.test.js @@ -342,3 +342,70 @@ const jestExpect = require('../'); } }); }); + +['toReturnValue', 'toHaveReturnedValue'].forEach(called => { + describe(`${called}`, () => { + test(`works only on spies or jest.fn`, () => { + const fn = function fn() {}; + + expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot(); + }); + + test(`passes with no arguments`, () => { + const fn = jest.fn(); + fn(); + jestExpect(fn)[called](); + expect(() => jestExpect(fn).not[called]()).toThrowErrorMatchingSnapshot(); + }); + + test(`.not passes when called with no arguments`, () => { + const fn = jest.fn(() => 'Return Value'); + + fn(); + jestExpect(fn).not[called](); + expect(() => jestExpect(fn)[called]()).toThrowErrorMatchingSnapshot(); + }); + + test(`passes when called`, () => { + const fn = jest.fn(() => 'Return Value'); + fn(); + jestExpect(fn)[called]('Return Value'); + expect(() => + jestExpect(fn).not[called]('Return Value'), + ).toThrowErrorMatchingSnapshot(); + }); + + test(`.not passes when called`, () => { + const fn = jest.fn(() => 'Return Value'); + fn(); + jestExpect(fn).not[called]('Some Other Value'); + expect(() => + jestExpect(fn)[called]('Some Other Value'), + ).toThrowErrorMatchingSnapshot(); + }); + + test(`passes with mutliple calls`, () => { + const fn = jest.fn(a => a * 2); + + fn(1); + fn(2); + fn(3); + + jestExpect(fn)[called](2); + jestExpect(fn)[called](4); + jestExpect(fn)[called](6); + }); + + test(`.not passes with multiple calls`, () => { + const fn = jest.fn(a => a * 2); + + fn(1); + fn(2); + fn(3); + + jestExpect(fn).not[called](1); + jestExpect(fn).not[called](3); + jestExpect(fn).not[called](5); + }); + }); +}); diff --git a/packages/expect/src/spy_matchers.js b/packages/expect/src/spy_matchers.js index f1e4e7585dd4..0ed5c25f5b74 100644 --- a/packages/expect/src/spy_matchers.js +++ b/packages/expect/src/spy_matchers.js @@ -118,6 +118,38 @@ const createToBeCalledWithMatcher = matcherName => ( return {message, pass}; }; +const createToReturnValuesMatcher = matcherName => ( + received: any, + expected: any, +) => { + ensureMock(received, matcherName); + + const receivedIsSpy = isSpy(received); + const type = receivedIsSpy ? 'spy' : 'mock function'; + const receivedName = receivedIsSpy ? 'spy' : received.getMockName(); + + const calls = receivedIsSpy + ? received.returnValues.all().map(x => x.args) + : received.mock.returnValues; + + const [match] = partition(calls, call => equals(expected, call)); + const pass = match.length > 0; + + const message = pass + ? () => + matcherHint('.not' + matcherName, receivedName) + + '\n\n' + + `Expected ${type} not to have returned:\n` + + ` ${printExpected(expected)}` + : () => + matcherHint(matcherName, receivedName) + + '\n\n' + + `Expected ${type} to have returned:\n` + + ` ${printExpected(expected)}`; + + return {message, pass}; +}; + const createLastCalledWithMatcher = matcherName => ( received: any, ...expected: any @@ -206,6 +238,8 @@ const spyMatchers: MatchersObject = { toHaveBeenNthCalledWith: createNthCalledWithMatcher( '.toHaveBeenNthCalledWith', ), + toHaveReturnedValue: createToReturnValuesMatcher('.toHaveReturnedValue'), + toReturnValue: createToReturnValuesMatcher('.toReturnValue'), }; const isSpy = spy => spy.calls && typeof spy.calls.count === 'function';