From e13fd13529099e2f8b65d030c415435098258778 Mon Sep 17 00:00:00 2001 From: Maksym Kobieliev Date: Fri, 13 Mar 2020 20:41:24 +0200 Subject: [PATCH 1/4] Add a toHaveBeenCalledOnceWith matcher --- spec/core/integration/MatchersSpec.js | 16 ++++ .../matchers/toHaveBeenCalledOnceWithSpec.js | 89 +++++++++++++++++++ src/core/matchers/requireMatchers.js | 1 + src/core/matchers/toHaveBeenCalledOnceWith.js | 56 ++++++++++++ 4 files changed, 162 insertions(+) create mode 100644 spec/core/matchers/toHaveBeenCalledOnceWithSpec.js create mode 100644 src/core/matchers/toHaveBeenCalledOnceWith.js diff --git a/spec/core/integration/MatchersSpec.js b/spec/core/integration/MatchersSpec.js index 25af3217..b0cf91f0 100644 --- a/spec/core/integration/MatchersSpec.js +++ b/spec/core/integration/MatchersSpec.js @@ -425,6 +425,22 @@ describe('Matchers (Integration)', function() { }); }); + describe('toHaveBeenCalledOnceWith', function() { + verifyPasses(function(env) { + var spy = env.createSpy(); + spy('5', 3); + env.addCustomEqualityTester(function(a, b) { + return a.toString() === b.toString(); + }); + env.expect(spy).toHaveBeenCalledOnceWith(5, 3); + }); + + verifyFails(function(env) { + var spy = env.createSpy(); + env.expect(spy).toHaveBeenCalledOnceWith(5, 3); + }); + }); + describe('toHaveClass', function() { beforeEach(function() { this.domHelpers = jasmine.getEnv().domHelpers(); diff --git a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js new file mode 100644 index 00000000..b3678bfb --- /dev/null +++ b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js @@ -0,0 +1,89 @@ +describe("toHaveBeenCalledOnceWith", function () { + + it("passes when the actual was called only once and with matching parameters", function () { + var util = jasmineUnderTest.matchersUtil, + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(true); + expect(result.message).toEqual("Expected spy called-spy to have been called 0 times, multiple times, or once, but with arguments different from:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'b' ].\n\n"); + }); + + it("passes through the custom equality testers", function () { + var util = { + contains: jasmine.createSpy('delegated-contains').and.returnValue(false) + }, + customEqualityTesters = [function () { return true; }], + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util, customEqualityTesters), + calledSpy = new jasmineUnderTest.Spy('called-spy'); + + calledSpy('a', 'b'); + matcher.compare(calledSpy, 'a', 'b'); + + expect(util.contains).toHaveBeenCalledWith([['a', 'b']], ['a', 'b'], customEqualityTesters); + }); + + it("fails when the actual was never called", function () { + var util = jasmineUnderTest.matchersUtil, + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut it was never called.\n\n"); + }); + + it("fails when the actual was called once with different parameters", function () { + var util = jasmineUnderTest.matchersUtil, + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'c'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'c' ].\n\n"); + }); + + it("fails when the actual was called multiple times with expected parameters", function () { + var util = jasmineUnderTest.matchersUtil, + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + calledSpy('a', 'b'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'b' ].\n\n"); + }); + + it("fails when the actual was called multiple times (one of them - with expected parameters)", function () { + var util = jasmineUnderTest.matchersUtil, + matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util), + calledSpy = new jasmineUnderTest.Spy('called-spy'), + result; + + calledSpy('a', 'b'); + calledSpy('a', 'c'); + result = matcher.compare(calledSpy, 'a', 'b'); + + expect(result.pass).toBe(false); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'b' ],\n [ 'a', 'c' ].\n\n"); + }); + + it("throws an exception when the actual is not a spy", function () { + var matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(), + fn = function () { }; + + expect(function () { matcher.compare(fn) }).toThrowError(/Expected a spy, but got Function./); + }); +}); diff --git a/src/core/matchers/requireMatchers.js b/src/core/matchers/requireMatchers.js index e8b1fd58..4ff1f841 100644 --- a/src/core/matchers/requireMatchers.js +++ b/src/core/matchers/requireMatchers.js @@ -22,6 +22,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toEqual', 'toHaveBeenCalled', 'toHaveBeenCalledBefore', + 'toHaveBeenCalledOnceWith', 'toHaveBeenCalledTimes', 'toHaveBeenCalledWith', 'toHaveClass', diff --git a/src/core/matchers/toHaveBeenCalledOnceWith.js b/src/core/matchers/toHaveBeenCalledOnceWith.js new file mode 100644 index 00000000..ccd707c7 --- /dev/null +++ b/src/core/matchers/toHaveBeenCalledOnceWith.js @@ -0,0 +1,56 @@ +getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { + + var getErrorMsg = j$.formatErrorMsg('', 'expect().toHaveBeenCalledOnceWith(...arguments)'); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. + * @function + * @name matchers#toHaveBeenCalledOnceWith + * @since 3.6.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); + */ + function toHaveBeenCalledOnceWith(util, customEqualityTesters) { + return { + compare: function () { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1); + + if (!j$.isSpy(actual)) { + throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.')); + } + + var prettyPrintedCalls = actual.calls.allArgs().map(function (argsForCall) { + return ' ' + j$.pp(argsForCall); + }); + + if (actual.calls.count() === 1 && util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) { + return { + pass: true, + message: 'Expected spy ' + actual.and.identity + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + + ' ' + j$.pp(expectedArgs) + '\n' + + 'But the actual call was:\n' + + prettyPrintedCalls.join(',\n') + '.\n\n' + }; + } + + function butString() { + return actual.calls.count() !== 0 + ? 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' + : 'But it was never called.\n\n'; + } + + return { + pass: false, + message: 'Expected spy ' + actual.and.identity + ' to have been called only once, and with given args:\n' + + ' ' + j$.pp(expectedArgs) + '\n' + + butString() + }; + } + }; + } + + return toHaveBeenCalledOnceWith; +}; From bcc28d7063971cd04cad89a1bcee2c96726abe97 Mon Sep 17 00:00:00 2001 From: Maksym Kobieliev Date: Thu, 2 Apr 2020 21:31:17 +0300 Subject: [PATCH 2/4] Output a diff if there was only one call, but with wrong parameters --- .../matchers/toHaveBeenCalledOnceWithSpec.js | 2 +- src/core/matchers/toHaveBeenCalledOnceWith.js | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js index b3678bfb..97fdaf9c 100644 --- a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js +++ b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js @@ -49,7 +49,7 @@ describe("toHaveBeenCalledOnceWith", function () { result = matcher.compare(calledSpy, 'a', 'b'); expect(result.pass).toBe(false); - expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual calls were:\n [ 'a', 'c' ].\n\n"); + expect(result.message).toEqual("Expected spy called-spy to have been called only once, and with given args:\n [ 'a', 'b' ]\nBut the actual call was:\n [ 'a', 'c' ].\nExpected $[1] = 'c' to equal 'b'.\n\n"); }); it("fails when the actual was called multiple times with expected parameters", function () { diff --git a/src/core/matchers/toHaveBeenCalledOnceWith.js b/src/core/matchers/toHaveBeenCalledOnceWith.js index ccd707c7..035848b0 100644 --- a/src/core/matchers/toHaveBeenCalledOnceWith.js +++ b/src/core/matchers/toHaveBeenCalledOnceWith.js @@ -36,10 +36,23 @@ getJasmineRequireObj().toHaveBeenCalledOnceWith = function (j$) { }; } + function getDiffs() { + return actual.calls.allArgs().map(function (argsForCall, callIx) { + var diffBuilder = new j$.DiffBuilder(); + util.equals(argsForCall, expectedArgs, customEqualityTesters, diffBuilder); + return diffBuilder.getMessage(); + }); + } + function butString() { - return actual.calls.count() !== 0 - ? 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n' - : 'But it was never called.\n\n'; + switch (actual.calls.count()) { + case 0: + return 'But it was never called.\n\n'; + case 1: + return 'But the actual call was:\n' + prettyPrintedCalls.join(',\n') + '.\n' + getDiffs().join('\n') + '\n\n'; + default: + return 'But the actual calls were:\n' + prettyPrintedCalls.join(',\n') + '.\n\n'; + } } return { From ec9904bf52e7e4c45045742c54595b6af5341aad Mon Sep 17 00:00:00 2001 From: Maksym Kobieliev Date: Thu, 2 Apr 2020 21:38:54 +0300 Subject: [PATCH 3/4] Fix test --- spec/core/matchers/toHaveBeenCalledOnceWithSpec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js index 97fdaf9c..722a05f1 100644 --- a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js +++ b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js @@ -15,7 +15,8 @@ describe("toHaveBeenCalledOnceWith", function () { it("passes through the custom equality testers", function () { var util = { - contains: jasmine.createSpy('delegated-contains').and.returnValue(false) + contains: jasmine.createSpy('delegated-contains').and.returnValue(false), + equals: jasmineUnderTest.matchersUtil.equals }, customEqualityTesters = [function () { return true; }], matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util, customEqualityTesters), From e94e6c5b7525ea705cf369def76985f279567f01 Mon Sep 17 00:00:00 2001 From: Maksym Kobieliev Date: Tue, 14 Apr 2020 22:01:56 +0300 Subject: [PATCH 4/4] Fix failing unit test --- spec/core/matchers/toHaveBeenCalledOnceWithSpec.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js index 722a05f1..d5bdab43 100644 --- a/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js +++ b/spec/core/matchers/toHaveBeenCalledOnceWithSpec.js @@ -14,11 +14,10 @@ describe("toHaveBeenCalledOnceWith", function () { }); it("passes through the custom equality testers", function () { - var util = { - contains: jasmine.createSpy('delegated-contains').and.returnValue(false), - equals: jasmineUnderTest.matchersUtil.equals - }, - customEqualityTesters = [function () { return true; }], + var util = jasmineUnderTest.matchersUtil; + spyOn(util, 'contains').and.returnValue(false); + + var customEqualityTesters = [function () { return true; }], matcher = jasmineUnderTest.matchers.toHaveBeenCalledOnceWith(util, customEqualityTesters), calledSpy = new jasmineUnderTest.Spy('called-spy');