Allow callThrough to call constructor functions without errors

This commit is contained in:
Elliot Nelson
2020-01-28 19:40:44 -05:00
parent 5e98ee951c
commit 9febe3159d
3 changed files with 56 additions and 30 deletions

View File

@@ -808,6 +808,25 @@ describe("Env integration", function() {
expect(originalFunctionWasCalled).toEqual(true);
});
env.it("works with constructors when using callThrough spy strategy", function() {
function MyClass(foo) {
if (!(this instanceof MyClass)) throw new Error('You must use the new keyword.');
this.foo = foo;
}
var subject = { MyClass: MyClass };
var spy = env.spyOn(subject, 'MyClass').and.callThrough();
expect(function() {
var result = new spy('hello world');
expect(result instanceof MyClass).toBeTruthy();
expect(result.foo).toEqual('hello world');
}).not.toThrow();
expect(function() {
spy('hello world');
}).toThrowError('You must use the new keyword.');
});
env.execute();
});

View File

@@ -20,8 +20,8 @@ getJasmineRequireObj().Spy = function(j$) {
getPromise
) {
var numArgs = typeof originalFn === 'function' ? originalFn.length : 0,
wrapper = makeFunc(numArgs, function() {
return spy.apply(this, Array.prototype.slice.call(arguments));
wrapper = makeFunc(numArgs, function(context, args, isConstructor) {
return spy(context, args, isConstructor);
}),
strategyDispatcher = new SpyStrategyDispatcher({
name: name,
@@ -33,7 +33,7 @@ getJasmineRequireObj().Spy = function(j$) {
getPromise: getPromise
}),
callTracker = new j$.CallTracker(),
spy = function() {
spy = function(context, args, isConstructor) {
/**
* @name Spy.callData
* @property {object} object - `this` context for the invocation.
@@ -41,13 +41,13 @@ getJasmineRequireObj().Spy = function(j$) {
* @property {Array} args - The arguments passed for this invocation.
*/
var callData = {
object: this,
object: context,
invocationOrder: nextOrder(),
args: Array.prototype.slice.apply(arguments)
args: Array.prototype.slice.apply(args)
};
callTracker.track(callData);
var returnValue = strategyDispatcher.exec(this, arguments);
var returnValue = strategyDispatcher.exec(this, args, isConstructor);
callData.returnValue = returnValue;
return returnValue;
@@ -56,44 +56,44 @@ getJasmineRequireObj().Spy = function(j$) {
function makeFunc(length, fn) {
switch (length) {
case 1:
return function(a) {
return fn.apply(this, arguments);
return function fnargs(a) {
return fn(this, arguments, this instanceof fnargs);
};
case 2:
return function(a, b) {
return fn.apply(this, arguments);
return function fnargs(a, b) {
return fn(this, arguments, this instanceof fnargs);
};
case 3:
return function(a, b, c) {
return fn.apply(this, arguments);
return function fnargs(a, b, c) {
return fn(this, arguments, this instanceof fnargs);
};
case 4:
return function(a, b, c, d) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d) {
return fn(this, arguments, this instanceof fnargs);
};
case 5:
return function(a, b, c, d, e) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d, e) {
return fn(this, arguments, this instanceof fnargs);
};
case 6:
return function(a, b, c, d, e, f) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d, e, f) {
return fn(this, arguments, this instanceof fnargs);
};
case 7:
return function(a, b, c, d, e, f, g) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d, e, f, g) {
return fn(this, arguments, this instanceof fnargs);
};
case 8:
return function(a, b, c, d, e, f, g, h) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d, e, f, g, h) {
return fn(this, arguments, this instanceof fnargs);
};
case 9:
return function(a, b, c, d, e, f, g, h, i) {
return fn.apply(this, arguments);
return function fnargs(a, b, c, d, e, f, g, h, i) {
return fn(this, arguments, this instanceof fnargs);
};
default:
return function() {
return fn.apply(this, arguments);
return function fnargs() {
return fn(this, arguments, this instanceof fnargs);
};
}
}
@@ -150,7 +150,7 @@ getJasmineRequireObj().Spy = function(j$) {
this.and = baseStrategy;
this.exec = function(spy, args) {
this.exec = function(spy, args, isConstructor) {
var strategy = argsStrategies.get(args);
if (!strategy) {
@@ -167,7 +167,7 @@ getJasmineRequireObj().Spy = function(j$) {
}
}
return strategy.exec(spy, args);
return strategy.exec(spy, args, isConstructor);
};
this.withArgs = function() {

View File

@@ -96,8 +96,15 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @since 2.0.0
* @function
*/
SpyStrategy.prototype.exec = function(context, args) {
return this.plan.apply(context, args);
SpyStrategy.prototype.exec = function(context, args, isConstructor) {
var list = [context].concat(args ? Array.prototype.slice.call(args) : []);
var target = this.plan.bind.apply(this.plan, list);
if (isConstructor) {
return new target();
} else {
return target();
}
};
/**