From b81d20f4a8888c8200d9d97e9d26640981e62ecb Mon Sep 17 00:00:00 2001 From: stoically Date: Tue, 13 Feb 2018 06:50:13 +0100 Subject: [PATCH] Change return value of yield and callArg Instead of returning false this change makes yield, yieldOn, yieldTo, yieldToOn, callArg, and callArgOn return the callbacks return values in an Array. Resolves #903 --- docs/release-source/release/stubs.md | 2 + lib/sinon/call.js | 16 ++-- lib/sinon/spy.js | 33 ++++---- test/call-test.js | 94 +++++++++++++++++++++++ test/spy-test.js | 111 +++++++++++++++++++++++++++ 5 files changed, 234 insertions(+), 22 deletions(-) diff --git a/docs/release-source/release/stubs.md b/docs/release-source/release/stubs.md index 491e58116..7efe48aaa 100644 --- a/docs/release-source/release/stubs.md +++ b/docs/release-source/release/stubs.md @@ -409,6 +409,8 @@ Invoke callbacks passed to the `stub` with the given arguments. If the stub was never called with a function argument, `yield` throws an error. +Returns an Array with all callbacks return values in the order they were called, if no error is thrown. + Also aliased as `invokeCallback`. diff --git a/lib/sinon/call.js b/lib/sinon/call.js index 9d698e56b..4aa5bf07c 100644 --- a/lib/sinon/call.js +++ b/lib/sinon/call.js @@ -98,20 +98,20 @@ var callProto = { }, callArg: function (pos) { - this.args[pos](); + return this.args[pos](); }, callArgOn: function (pos, thisValue) { - this.args[pos].apply(thisValue); + return this.args[pos].apply(thisValue); }, callArgWith: function (pos) { - this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); + return this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); }, callArgOnWith: function (pos, thisValue) { var args = slice.call(arguments, 2); - this.args[pos].apply(thisValue, args); + return this.args[pos].apply(thisValue, args); }, throwArg: function (pos) { @@ -127,7 +127,7 @@ var callProto = { }, "yield": function () { - this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); + return this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); }, yieldOn: function (thisValue) { @@ -140,11 +140,11 @@ var callProto = { throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); } - yieldFn.apply(thisValue, slice.call(arguments, 1)); + return yieldFn.apply(thisValue, slice.call(arguments, 1)); }, yieldTo: function (prop) { - this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); + return this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); }, yieldToOn: function (prop, thisValue) { @@ -159,7 +159,7 @@ var callProto = { "' since no callback was passed.", args); } - yieldFn.apply(thisValue, slice.call(arguments, 2)); + return yieldFn.apply(thisValue, slice.call(arguments, 2)); }, toString: function () { diff --git a/lib/sinon/spy.js b/lib/sinon/spy.js index 97681c581..d898bcf4f 100644 --- a/lib/sinon/spy.js +++ b/lib/sinon/spy.js @@ -386,7 +386,7 @@ var spyApi = { } }; -function delegateToCalls(method, matchAny, actual, notCalled, totalCallCount) { +function delegateToCalls(method, matchAny, actual, returnsValues, notCalled, totalCallCount) { spyApi[method] = function () { if (!this.called) { if (notCalled) { @@ -401,11 +401,13 @@ function delegateToCalls(method, matchAny, actual, notCalled, totalCallCount) { var currentCall; var matches = 0; + var returnValues = []; for (var i = 0, l = this.callCount; i < l; i += 1) { currentCall = this.getCall(i); - - if (currentCall[actual || method].apply(currentCall, arguments)) { + var returnValue = currentCall[actual || method].apply(currentCall, arguments); + returnValues.push(returnValue); + if (returnValue) { matches += 1; if (matchAny) { @@ -414,6 +416,9 @@ function delegateToCalls(method, matchAny, actual, notCalled, totalCallCount) { } } + if (returnsValues) { + return returnValues; + } return matches === this.callCount; }; } @@ -423,17 +428,17 @@ spyApi.reset = deprecated.wrap(spyApi.resetHistory, deprecated.defaultMsg("reset delegateToCalls("calledOn", true); delegateToCalls("alwaysCalledOn", false, "calledOn"); delegateToCalls("calledWith", true); -delegateToCalls("calledOnceWith", true, "calledWith", undefined, 1); +delegateToCalls("calledOnceWith", true, "calledWith", false, undefined, 1); delegateToCalls("calledWithMatch", true); delegateToCalls("alwaysCalledWith", false, "calledWith"); delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); delegateToCalls("calledWithExactly", true); -delegateToCalls("calledOnceWithExactly", true, "calledWithExactly", undefined, 1); +delegateToCalls("calledOnceWithExactly", true, "calledWithExactly", false, undefined, 1); delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); -delegateToCalls("neverCalledWith", false, "notCalledWith", function () { +delegateToCalls("neverCalledWith", false, "notCalledWith", false, function () { return true; }); -delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { +delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", false, function () { return true; }); delegateToCalls("threw", true); @@ -442,30 +447,30 @@ delegateToCalls("returned", true); delegateToCalls("alwaysReturned", false, "returned"); delegateToCalls("calledWithNew", true); delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); -delegateToCalls("callArg", false, "callArgWith", function () { +delegateToCalls("callArg", false, "callArgWith", true, function () { throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); }); spyApi.callArgWith = spyApi.callArg; -delegateToCalls("callArgOn", false, "callArgOnWith", function () { +delegateToCalls("callArgOn", false, "callArgOnWith", true, function () { throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); }); spyApi.callArgOnWith = spyApi.callArgOn; -delegateToCalls("throwArg", false, "throwArg", function () { +delegateToCalls("throwArg", false, "throwArg", false, function () { throw new Error(this.toString() + " cannot throw arg since it was not yet invoked."); }); -delegateToCalls("yield", false, "yield", function () { +delegateToCalls("yield", false, "yield", true, function () { throw new Error(this.toString() + " cannot yield since it was not yet invoked."); }); // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode. spyApi.invokeCallback = spyApi.yield; -delegateToCalls("yieldOn", false, "yieldOn", function () { +delegateToCalls("yieldOn", false, "yieldOn", true, function () { throw new Error(this.toString() + " cannot yield since it was not yet invoked."); }); -delegateToCalls("yieldTo", false, "yieldTo", function (property) { +delegateToCalls("yieldTo", false, "yieldTo", true, function (property) { throw new Error(this.toString() + " cannot yield to '" + valueToString(property) + "' since it was not yet invoked."); }); -delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { +delegateToCalls("yieldToOn", false, "yieldToOn", true, function (property) { throw new Error(this.toString() + " cannot yield to '" + valueToString(property) + "' since it was not yet invoked."); }); diff --git a/test/call-test.js b/test/call-test.js index f720de7bd..366610d74 100644 --- a/test/call-test.js +++ b/test/call-test.js @@ -234,6 +234,17 @@ describe("sinonSpy.call", function () { }, "TypeError"); }); + it("returns callbacks return value", function () { + var callback = sinonSpy(function () { + return "useful value"; + }); + this.args.push(1, 2, callback); + + var returnValue = this.call.callArg(2); + + assert.equals(returnValue, "useful value"); + }); + it("throws if index is not number", function () { var call = this.call; @@ -267,6 +278,18 @@ describe("sinonSpy.call", function () { }, "TypeError"); }); + it("returns callbacks return value", function () { + var callback = sinonSpy(function () { + return "useful value"; + }); + var thisObj = { name1: "value1", name2: "value2" }; + this.args.push(1, 2, callback); + + var returnValue = this.call.callArgOn(2, thisObj); + + assert.equals(returnValue, "useful value"); + }); + it("throws if index is not number", function () { var thisObj = { name1: "value1", name2: "value2" }; var call = this.call; @@ -310,6 +333,18 @@ describe("sinonSpy.call", function () { assert(callback.calledWith(object, array)); }); + it("returns callbacks return value", function () { + var object = {}; + var callback = sinonSpy(function () { + return "useful value"; + }); + this.args.push(1, callback); + + var returnValue = this.call.callArgWith(1, object); + + assert.equals(returnValue, "useful value"); + }); + it("throws if no index is specified", function () { var call = this.call; @@ -366,6 +401,19 @@ describe("sinonSpy.call", function () { assert(callback.calledOn(thisObj)); }); + it("returns callbacks return value", function () { + var object = {}; + var thisObj = { name1: "value1", name2: "value2" }; + var callback = sinonSpy(function () { + return "useful value"; + }); + this.args.push(1, callback); + + var returnValue = this.call.callArgOnWith(1, thisObj, object); + + assert.equals(returnValue, "useful value"); + }); + it("throws if index is not number", function () { var thisObj = { name1: "value1", name2: "value2" }; var call = this.call; @@ -449,6 +497,17 @@ describe("sinonSpy.call", function () { assert(spy.calledWith(obj, "Crazy")); }); + it("returns callbacks return value", function () { + var spy = sinonSpy(function () { + return "useful value"; + }); + this.args.push(24, {}, spy); + + var returnValue = this.call.yield(); + + assert.equals(returnValue, "useful value"); + }); + it("throws if callback throws", function () { this.args.push(function () { throw new Error("d'oh!"); @@ -554,6 +613,18 @@ describe("sinonSpy.call", function () { assert(spy.calledOn(thisObj)); }); + it("returns callbacks return value", function () { + var spy = sinonSpy(function () { + return "useful value"; + }); + var thisObj = { name1: "value1", name2: "value2" }; + this.args.push(24, {}, spy); + + var returnValue = this.call.yieldOn(thisObj); + + assert.equals(returnValue, "useful value"); + }); + it("throws if callback throws", function () { this.args.push(function () { throw new Error("d'oh!"); @@ -642,6 +713,17 @@ describe("sinonSpy.call", function () { assert(spy.calledWith(obj, "Crazy")); }); + it("returns callbacks return value", function () { + var spy = sinonSpy(function () { + return "useful value"; + }); + this.args.push(24, {}, { success: spy }); + + var returnValue = this.call.yieldTo("success"); + + assert.equals(returnValue, "useful value"); + }); + it("throws if callback throws", function () { this.args.push({ success: function () { @@ -757,6 +839,18 @@ describe("sinonSpy.call", function () { assert(spy.calledOn(thisObj)); }); + it("returns callbacks return value", function () { + var spy = sinonSpy(function () { + return "useful value"; + }); + var thisObj = { name1: "value1", name2: "value2" }; + this.args.push(24, {}, { success: spy }); + + var returnValue = this.call.yieldToOn("success", thisObj); + + assert.equals(returnValue, "useful value"); + }); + it("throws if callback throws", function () { this.args.push({ success: function () { diff --git a/test/spy-test.js b/test/spy-test.js index 44bba6df4..a6f22c0ac 100644 --- a/test/spy-test.js +++ b/test/spy-test.js @@ -2159,6 +2159,24 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + spy(1, 2, callback); + spy(3, 4, callback); + + var returnValues = spy.callArg(2); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".callArgOn", function () { @@ -2244,6 +2262,25 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); assert(callback.calledOn(thisObj)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + var thisObj = { name1: "value1", name2: "value2" }; + spy(1, 2, callback); + spy(3, 4, callback); + + var returnValues = spy.callArgOn(2, thisObj); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".callArgWith", function () { @@ -2319,6 +2356,24 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + spy(1, 2, callback); + spy(3, 4, callback); + + var returnValues = spy.yield(); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".invokeCallback", function () { @@ -2392,6 +2447,25 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); assert(callback.calledOn(thisObj)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + var thisObj = { name1: "value1", name2: "value2" }; + spy(1, 2, callback); + spy(3, 4, callback); + + var returnValues = spy.yieldOn(thisObj); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".yieldTo", function () { @@ -2466,6 +2540,24 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + spy(1, 2, { success: callback }); + spy(3, 4, { success: callback }); + + var returnValues = spy.yieldTo("success"); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".yieldToOn", function () { @@ -2547,6 +2639,25 @@ describe("spy", function () { assert(callback.calledWith("abc", 123, array, object)); assert(callback.calledOn(thisObj)); }); + + it("returns callbacks return values for all calls", function () { + var spy = createSpy(); + var i = 0; + var callback = createSpy(function () { + i++; + return "useful value " + i; + }); + var thisObj = { name1: "value1", name2: "value2" }; + spy(1, 2, { success: callback }); + spy(3, 4, { success: callback }); + + var returnValues = spy.yieldToOn("success", thisObj); + + assert.equals(returnValues, [ + "useful value 1", + "useful value 2" + ]); + }); }); describe(".throwArg", function () {