Merge branch 'kapke-spy-arity-preservation'
This commit is contained in:
@@ -66,6 +66,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
|
|||||||
j$.QueueRunner = jRequire.QueueRunner(j$);
|
j$.QueueRunner = jRequire.QueueRunner(j$);
|
||||||
j$.ReportDispatcher = jRequire.ReportDispatcher();
|
j$.ReportDispatcher = jRequire.ReportDispatcher();
|
||||||
j$.Spec = jRequire.Spec(j$);
|
j$.Spec = jRequire.Spec(j$);
|
||||||
|
j$.Spy = jRequire.Spy(j$);
|
||||||
j$.SpyRegistry = jRequire.SpyRegistry(j$);
|
j$.SpyRegistry = jRequire.SpyRegistry(j$);
|
||||||
j$.SpyStrategy = jRequire.SpyStrategy(j$);
|
j$.SpyStrategy = jRequire.SpyStrategy(j$);
|
||||||
j$.StringMatching = jRequire.StringMatching(j$);
|
j$.StringMatching = jRequire.StringMatching(j$);
|
||||||
@@ -189,38 +190,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
j$.createSpy = function(name, originalFn) {
|
j$.createSpy = function(name, originalFn) {
|
||||||
|
return j$.Spy(name, originalFn);
|
||||||
var spyStrategy = new j$.SpyStrategy({
|
|
||||||
name: name,
|
|
||||||
fn: originalFn,
|
|
||||||
getSpy: function() { return spy; }
|
|
||||||
}),
|
|
||||||
callTracker = new j$.CallTracker(),
|
|
||||||
spy = function() {
|
|
||||||
var callData = {
|
|
||||||
object: this,
|
|
||||||
args: Array.prototype.slice.apply(arguments)
|
|
||||||
};
|
|
||||||
|
|
||||||
callTracker.track(callData);
|
|
||||||
var returnValue = spyStrategy.exec.apply(this, arguments);
|
|
||||||
callData.returnValue = returnValue;
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var prop in originalFn) {
|
|
||||||
if (prop === 'and' || prop === 'calls') {
|
|
||||||
throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
|
|
||||||
}
|
|
||||||
|
|
||||||
spy[prop] = originalFn[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
spy.and = spyStrategy;
|
|
||||||
spy.calls = callTracker;
|
|
||||||
|
|
||||||
return spy;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
j$.isSpy = function(putativeSpy) {
|
j$.isSpy = function(putativeSpy) {
|
||||||
@@ -2078,6 +2048,65 @@ getJasmineRequireObj().ReportDispatcher = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
getJasmineRequireObj().Spy = function (j$) {
|
||||||
|
|
||||||
|
function Spy(name, originalFn) {
|
||||||
|
var args = buildArgs(),
|
||||||
|
/*`eval` is the only option to preserve both this and context:
|
||||||
|
- former is needed to work as expected with methods,
|
||||||
|
- latter is needed to access real spy function and allows to reduce eval'ed code to absolute minimum
|
||||||
|
More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
|
||||||
|
*/
|
||||||
|
/* jshint evil: true */
|
||||||
|
wrapper = eval('(function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
|
||||||
|
spyStrategy = new j$.SpyStrategy({
|
||||||
|
name: name,
|
||||||
|
fn: originalFn,
|
||||||
|
getSpy: function () {
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
callTracker = new j$.CallTracker(),
|
||||||
|
spy = function () {
|
||||||
|
var callData = {
|
||||||
|
object: this,
|
||||||
|
args: Array.prototype.slice.apply(arguments)
|
||||||
|
};
|
||||||
|
|
||||||
|
callTracker.track(callData);
|
||||||
|
var returnValue = spyStrategy.exec.apply(this, arguments);
|
||||||
|
callData.returnValue = returnValue;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildArgs() {
|
||||||
|
var args = [];
|
||||||
|
|
||||||
|
while (originalFn instanceof Function && args.length < originalFn.length) {
|
||||||
|
args.push('arg' + args.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var prop in originalFn) {
|
||||||
|
if (prop === 'and' || prop === 'calls') {
|
||||||
|
throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper[prop] = originalFn[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.and = spyStrategy;
|
||||||
|
wrapper.calls = callTracker;
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Spy;
|
||||||
|
};
|
||||||
|
|
||||||
getJasmineRequireObj().SpyRegistry = function(j$) {
|
getJasmineRequireObj().SpyRegistry = function(j$) {
|
||||||
|
|
||||||
var getErrorMsg = j$.formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
|
var getErrorMsg = j$.formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
|
||||||
|
|||||||
@@ -57,6 +57,24 @@ describe('Spies', function () {
|
|||||||
|
|
||||||
expect(trackSpy.calls.mostRecent().args[0].returnValue).toEqual("return value");
|
expect(trackSpy.calls.mostRecent().args[0].returnValue).toEqual("return value");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("preserves arity of original function", function () {
|
||||||
|
var functions = [
|
||||||
|
function nullary () {},
|
||||||
|
function unary (arg) {},
|
||||||
|
function binary (arg1, arg2) {},
|
||||||
|
function ternary (arg1, arg2, arg3) {},
|
||||||
|
function quaternary (arg1, arg2, arg3, arg4) {},
|
||||||
|
function quinary (arg1, arg2, arg3, arg4, arg5) {},
|
||||||
|
function senary (arg1, arg2, arg3, arg4, arg5, arg6) {}
|
||||||
|
];
|
||||||
|
|
||||||
|
functions.forEach(function (someFunction, arity) {
|
||||||
|
var spy = jasmineUnderTest.createSpy(someFunction.name, someFunction);
|
||||||
|
|
||||||
|
expect(spy.length).toEqual(arity);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("createSpyObj", function() {
|
describe("createSpyObj", function() {
|
||||||
|
|||||||
58
src/core/Spy.js
Normal file
58
src/core/Spy.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
getJasmineRequireObj().Spy = function (j$) {
|
||||||
|
|
||||||
|
function Spy(name, originalFn) {
|
||||||
|
var args = buildArgs(),
|
||||||
|
/*`eval` is the only option to preserve both this and context:
|
||||||
|
- former is needed to work as expected with methods,
|
||||||
|
- latter is needed to access real spy function and allows to reduce eval'ed code to absolute minimum
|
||||||
|
More explanation here (look at comments): http://www.bennadel.com/blog/1909-javascript-function-constructor-does-not-create-a-closure.htm
|
||||||
|
*/
|
||||||
|
/* jshint evil: true */
|
||||||
|
wrapper = eval('(function (' + args + ') { return spy.apply(this, Array.prototype.slice.call(arguments)); })'),
|
||||||
|
spyStrategy = new j$.SpyStrategy({
|
||||||
|
name: name,
|
||||||
|
fn: originalFn,
|
||||||
|
getSpy: function () {
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
callTracker = new j$.CallTracker(),
|
||||||
|
spy = function () {
|
||||||
|
var callData = {
|
||||||
|
object: this,
|
||||||
|
args: Array.prototype.slice.apply(arguments)
|
||||||
|
};
|
||||||
|
|
||||||
|
callTracker.track(callData);
|
||||||
|
var returnValue = spyStrategy.exec.apply(this, arguments);
|
||||||
|
callData.returnValue = returnValue;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildArgs() {
|
||||||
|
var args = [];
|
||||||
|
|
||||||
|
while (originalFn instanceof Function && args.length < originalFn.length) {
|
||||||
|
args.push('arg' + args.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var prop in originalFn) {
|
||||||
|
if (prop === 'and' || prop === 'calls') {
|
||||||
|
throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper[prop] = originalFn[prop];
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.and = spyStrategy;
|
||||||
|
wrapper.calls = callTracker;
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Spy;
|
||||||
|
};
|
||||||
@@ -71,38 +71,7 @@ getJasmineRequireObj().base = function(j$, jasmineGlobal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
j$.createSpy = function(name, originalFn) {
|
j$.createSpy = function(name, originalFn) {
|
||||||
|
return j$.Spy(name, originalFn);
|
||||||
var spyStrategy = new j$.SpyStrategy({
|
|
||||||
name: name,
|
|
||||||
fn: originalFn,
|
|
||||||
getSpy: function() { return spy; }
|
|
||||||
}),
|
|
||||||
callTracker = new j$.CallTracker(),
|
|
||||||
spy = function() {
|
|
||||||
var callData = {
|
|
||||||
object: this,
|
|
||||||
args: Array.prototype.slice.apply(arguments)
|
|
||||||
};
|
|
||||||
|
|
||||||
callTracker.track(callData);
|
|
||||||
var returnValue = spyStrategy.exec.apply(this, arguments);
|
|
||||||
callData.returnValue = returnValue;
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var prop in originalFn) {
|
|
||||||
if (prop === 'and' || prop === 'calls') {
|
|
||||||
throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
|
|
||||||
}
|
|
||||||
|
|
||||||
spy[prop] = originalFn[prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
spy.and = spyStrategy;
|
|
||||||
spy.calls = callTracker;
|
|
||||||
|
|
||||||
return spy;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
j$.isSpy = function(putativeSpy) {
|
j$.isSpy = function(putativeSpy) {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ var getJasmineRequireObj = (function (jasmineGlobal) {
|
|||||||
j$.QueueRunner = jRequire.QueueRunner(j$);
|
j$.QueueRunner = jRequire.QueueRunner(j$);
|
||||||
j$.ReportDispatcher = jRequire.ReportDispatcher();
|
j$.ReportDispatcher = jRequire.ReportDispatcher();
|
||||||
j$.Spec = jRequire.Spec(j$);
|
j$.Spec = jRequire.Spec(j$);
|
||||||
|
j$.Spy = jRequire.Spy(j$);
|
||||||
j$.SpyRegistry = jRequire.SpyRegistry(j$);
|
j$.SpyRegistry = jRequire.SpyRegistry(j$);
|
||||||
j$.SpyStrategy = jRequire.SpyStrategy(j$);
|
j$.SpyStrategy = jRequire.SpyStrategy(j$);
|
||||||
j$.StringMatching = jRequire.StringMatching(j$);
|
j$.StringMatching = jRequire.StringMatching(j$);
|
||||||
|
|||||||
Reference in New Issue
Block a user