Skip to content

Commit

Permalink
Change return value of yield and callArg
Browse files Browse the repository at this point in the history
Instead of returning false this change makes yield,
yieldOn, yieldTo, yieldToOn, callArg, and callArgOn
return the callbacks return values in an Array.

Resolves #903
  • Loading branch information
stoically authored and mroderick committed Feb 23, 2018
1 parent 2c2bc0d commit b81d20f
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 22 deletions.
2 changes: 2 additions & 0 deletions docs/release-source/release/stubs.md
Expand Up @@ -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`.


Expand Down
16 changes: 8 additions & 8 deletions lib/sinon/call.js
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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 () {
Expand Down
33 changes: 19 additions & 14 deletions lib/sinon/spy.js
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -414,6 +416,9 @@ function delegateToCalls(method, matchAny, actual, notCalled, totalCallCount) {
}
}

if (returnsValues) {
return returnValues;
}
return matches === this.callCount;
};
}
Expand All @@ -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);
Expand All @@ -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.");
});
Expand Down
94 changes: 94 additions & 0 deletions test/call-test.js
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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!");
Expand Down Expand Up @@ -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!");
Expand Down Expand Up @@ -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 () {
Expand Down Expand Up @@ -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 () {
Expand Down

0 comments on commit b81d20f

Please sign in to comment.