Merge branch 'enelson/callThrough-constructor' of https://github.com/elliot-nelson/jasmine into elliot-nelson-enelson/callThrough-constructor

- Merges #1782 from @enelson
- Fixes #1760
This commit is contained in:
Gregg Van Hove
2020-02-12 11:38:07 -08:00
4 changed files with 99 additions and 63 deletions

View File

@@ -3856,7 +3856,8 @@ getJasmineRequireObj().GlobalErrors = function(j$) {
var browserRejectionHandler = function browserRejectionHandler(event) { var browserRejectionHandler = function browserRejectionHandler(event) {
if (j$.isError_(event.reason)) { if (j$.isError_(event.reason)) {
event.reason.jasmineMessage = 'Unhandled promise rejection: ' + event.reason; event.reason.jasmineMessage =
'Unhandled promise rejection: ' + event.reason;
onerror(event.reason); onerror(event.reason);
} else { } else {
onerror('Unhandled promise rejection: ' + event.reason); onerror('Unhandled promise rejection: ' + event.reason);
@@ -7038,8 +7039,8 @@ getJasmineRequireObj().Spy = function(j$) {
getPromise getPromise
) { ) {
var numArgs = typeof originalFn === 'function' ? originalFn.length : 0, var numArgs = typeof originalFn === 'function' ? originalFn.length : 0,
wrapper = makeFunc(numArgs, function() { wrapper = makeFunc(numArgs, function(context, args, invokeNew) {
return spy.apply(this, Array.prototype.slice.call(arguments)); return spy(context, args, invokeNew);
}), }),
strategyDispatcher = new SpyStrategyDispatcher({ strategyDispatcher = new SpyStrategyDispatcher({
name: name, name: name,
@@ -7051,7 +7052,7 @@ getJasmineRequireObj().Spy = function(j$) {
getPromise: getPromise getPromise: getPromise
}), }),
callTracker = new j$.CallTracker(), callTracker = new j$.CallTracker(),
spy = function() { spy = function(context, args, invokeNew) {
/** /**
* @name Spy.callData * @name Spy.callData
* @property {object} object - `this` context for the invocation. * @property {object} object - `this` context for the invocation.
@@ -7059,13 +7060,13 @@ getJasmineRequireObj().Spy = function(j$) {
* @property {Array} args - The arguments passed for this invocation. * @property {Array} args - The arguments passed for this invocation.
*/ */
var callData = { var callData = {
object: this, object: context,
invocationOrder: nextOrder(), invocationOrder: nextOrder(),
args: Array.prototype.slice.apply(arguments) args: Array.prototype.slice.apply(args)
}; };
callTracker.track(callData); callTracker.track(callData);
var returnValue = strategyDispatcher.exec(this, arguments); var returnValue = strategyDispatcher.exec(context, args, invokeNew);
callData.returnValue = returnValue; callData.returnValue = returnValue;
return returnValue; return returnValue;
@@ -7074,44 +7075,44 @@ getJasmineRequireObj().Spy = function(j$) {
function makeFunc(length, fn) { function makeFunc(length, fn) {
switch (length) { switch (length) {
case 1: case 1:
return function(a) { return function wrap1(a) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap1);
}; };
case 2: case 2:
return function(a, b) { return function wrap2(a, b) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap2);
}; };
case 3: case 3:
return function(a, b, c) { return function wrap3(a, b, c) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap3);
}; };
case 4: case 4:
return function(a, b, c, d) { return function wrap4(a, b, c, d) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap4);
}; };
case 5: case 5:
return function(a, b, c, d, e) { return function wrap5(a, b, c, d, e) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap5);
}; };
case 6: case 6:
return function(a, b, c, d, e, f) { return function wrap6(a, b, c, d, e, f) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap6);
}; };
case 7: case 7:
return function(a, b, c, d, e, f, g) { return function wrap7(a, b, c, d, e, f, g) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap7);
}; };
case 8: case 8:
return function(a, b, c, d, e, f, g, h) { return function wrap8(a, b, c, d, e, f, g, h) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap8);
}; };
case 9: case 9:
return function(a, b, c, d, e, f, g, h, i) { return function wrap9(a, b, c, d, e, f, g, h, i) {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap9);
}; };
default: default:
return function() { return function wrap() {
return fn.apply(this, arguments); return fn(this, arguments, this instanceof wrap);
}; };
} }
} }
@@ -7168,7 +7169,7 @@ getJasmineRequireObj().Spy = function(j$) {
this.and = baseStrategy; this.and = baseStrategy;
this.exec = function(spy, args) { this.exec = function(spy, args, invokeNew) {
var strategy = argsStrategies.get(args); var strategy = argsStrategies.get(args);
if (!strategy) { if (!strategy) {
@@ -7185,7 +7186,7 @@ getJasmineRequireObj().Spy = function(j$) {
} }
} }
return strategy.exec(spy, args); return strategy.exec(spy, args, invokeNew);
}; };
this.withArgs = function() { this.withArgs = function() {
@@ -7618,8 +7619,13 @@ getJasmineRequireObj().SpyStrategy = function(j$) {
* @since 2.0.0 * @since 2.0.0
* @function * @function
*/ */
SpyStrategy.prototype.exec = function(context, args) { SpyStrategy.prototype.exec = function(context, args, invokeNew) {
return this.plan.apply(context, args); var contextArgs = [context].concat(
args ? Array.prototype.slice.call(args) : []
);
var target = this.plan.bind.apply(this.plan, contextArgs);
return invokeNew ? new target() : target();
}; };
/** /**

View File

@@ -808,6 +808,31 @@ describe("Env integration", function() {
expect(originalFunctionWasCalled).toEqual(true); 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() {
var result = new spy('passing', 'extra', 'arguments', 'to', 'constructor');
expect(result instanceof MyClass).toBeTruthy();
expect(result.foo).toEqual('passing');
}).not.toThrow();
expect(function() {
spy('hello world');
}).toThrowError('You must use the new keyword.');
});
env.execute(); env.execute();
}); });
@@ -821,8 +846,8 @@ describe("Env integration", function() {
env.allowRespy(true); env.allowRespy(true);
env.addReporter({ jasmineDone: done }); env.addReporter({ jasmineDone: done });
env.describe('test suite', function(){ env.describe('test suite', function() {
env.it('spec 0', function(){ env.it('spec 0', function() {
env.spyOn(foo,'bar'); env.spyOn(foo,'bar');
var error = null; var error = null;

View File

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

View File

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