chai-spies-next
Advanced tools
Comparing version 0.9.0 to 0.9.1
@@ -32,4 +32,152 @@ !function (context, definition) { | ||
, i = _.inspect | ||
, STATE_KEY = typeof Symbol === 'undefined' ? '__state' : Symbol('state') | ||
, spyAmount = 0 | ||
, DEFAULT_SANDBOX = new Sandbox() | ||
/** | ||
* # Sandbox constructor (function) | ||
* | ||
* Initialize new Sandbox instance | ||
* | ||
* @returns new sandbox | ||
* @api private | ||
*/ | ||
function Sandbox() { | ||
this[STATE_KEY] = {}; | ||
} | ||
/** | ||
* # Sandbox.on (function) | ||
* | ||
* Wraps an object method into spy assigned to sandbox. All calls will | ||
* pass through to the original function. | ||
* | ||
* var spy = chai.spy.sandbox(); | ||
* var isArray = spy.on(Array, 'isArray'); | ||
* | ||
* const array = [] | ||
* const spy = chai.spy.sandbox(); | ||
* const [push, pop] = spy.on(array, ['push', 'pop']); | ||
* | ||
* spy.on(array, 'push', returns => 1) | ||
* | ||
* @param {Object} object | ||
* @param {String|String[]} method name or methods names to spy on | ||
* @param {Function} [fn] mock implementation | ||
* @returns created spy or created spies | ||
* @api public | ||
*/ | ||
Sandbox.prototype.on = function (object, methodName, fn) { | ||
if (Array.isArray(methodName)) { | ||
return methodName.map(function (name) { | ||
return this.on(object, name, fn); | ||
}, this); | ||
} | ||
var isMethod = typeof object[methodName] === 'function' | ||
if (methodName in object && !isMethod) { | ||
throw new Error([ | ||
'Unable to spy property "', methodName, | ||
'". Only methods and non-existing properties can be spied.' | ||
].join('')) | ||
} | ||
if (isMethod && object[methodName].__spy) { | ||
throw new Error('"' + methodName + '" is already a spy') | ||
} | ||
var method = chai.spy('object.' + methodName, fn || object[methodName]); | ||
var trackingId = ++spyAmount | ||
this[STATE_KEY][trackingId] = method; | ||
method.__spy.tracked = { | ||
object: object | ||
, methodName: methodName | ||
, originalMethod: object[methodName] | ||
, isOwnMethod: object.hasOwnProperty(methodName) | ||
}; | ||
object[methodName] = method; | ||
return method; | ||
}; | ||
/** | ||
* # Sandbox.restore (function) | ||
* | ||
* Restores previously wrapped object's method. | ||
* Restores all spied objects of a sandbox if called without parameters. | ||
* | ||
* var spy = chai.spy.sandbox(); | ||
* var object = spy.on(Array, 'isArray'); | ||
* spy.restore(Array, 'isArray'); // or spy.restore(); | ||
* | ||
* @param {Object} [object] | ||
* @param {String|String[]} [methods] method name or method names | ||
* @return {Sandbox} Sandbox instance | ||
* @api public | ||
*/ | ||
Sandbox.prototype.restore = function (object, methods) { | ||
var hasFilter = Boolean(object && methods); | ||
var sandbox = this; | ||
if (methods && !Array.isArray(methods)) { | ||
methods = [methods] | ||
} | ||
Object.keys(this[STATE_KEY]).some(function (spyId) { | ||
var spy = sandbox[STATE_KEY][spyId]; | ||
var tracked = spy.__spy.tracked; | ||
var isObjectSpied = !object || object === tracked.object; | ||
var isMethodSpied = !methods || methods.indexOf(tracked.methodName) !== -1; | ||
delete sandbox[STATE_KEY][spyId]; | ||
if (!isObjectSpied && !isMethodSpied) { | ||
return false; | ||
} | ||
sandbox.restoreTrackedObject(spy); | ||
if (hasFilter) { | ||
return true; | ||
} | ||
}); | ||
return this; | ||
}; | ||
/** | ||
* # Sandbox.restoreTrackedObject (function) | ||
* | ||
* Restores tracked object's method | ||
* | ||
* var spy = chai.spy.sandbox(); | ||
* var isArray = spy.on(Array, 'isArray'); | ||
* spy.restoreTrackedObject(isArray); | ||
* | ||
* @param {Spy} spy | ||
* @api private | ||
*/ | ||
Sandbox.prototype.restoreTrackedObject = function (spy) { | ||
var tracked = spy.__spy.tracked; | ||
if (!tracked) { | ||
throw new Error('It is not possible to restore a non-tracked spy.') | ||
} | ||
if (tracked.isOwnMethod) { | ||
tracked.object[tracked.methodName] = tracked.originalMethod; | ||
} else { | ||
delete tracked.object[tracked.methodName]; | ||
} | ||
spy.__spy.tracked = null; | ||
}; | ||
/** | ||
* # chai.spy (function) | ||
@@ -82,6 +230,7 @@ * | ||
proxy.toString = function toString() { | ||
var l = this.__spy.calls.length; | ||
var state = this.__spy; | ||
var l = state.calls.length; | ||
var s = "{ Spy"; | ||
if (this.__spy.name) | ||
s += " '" + this.__spy.name + "'"; | ||
if (state.name) | ||
s += " '" + state.name + "'"; | ||
if (l > 0) | ||
@@ -92,20 +241,30 @@ s += ", " + l + " call" + (l > 1 ? 's' : ''); | ||
}; | ||
proxy.__spy = { | ||
calls: [] | ||
, called: false | ||
, name: name | ||
}; | ||
return proxy; | ||
} | ||
/** | ||
* # proxy.reset (function) | ||
* # chai.spy.sandbox (function) | ||
* | ||
* Resets __spy object parameters for instantiation and reuse | ||
* @returns proxy spy object | ||
* Creates sandbox which allow to restore spied objects with spy.on. | ||
* All calls will pass through to the original function. | ||
* | ||
* var spy = chai.spy.sandbox(); | ||
* var isArray = spy.on(Array, 'isArray'); | ||
* | ||
* @param {Object} object | ||
* @param {String} method name to spy on | ||
* @returns passed object | ||
* @api public | ||
*/ | ||
proxy.reset = function() { | ||
this.__spy = { | ||
calls: [] | ||
, called: false | ||
, name: name | ||
}; | ||
return this; | ||
} | ||
return proxy.reset(); | ||
} | ||
chai.spy.sandbox = function () { | ||
return new Sandbox() | ||
}; | ||
@@ -115,32 +274,31 @@ /** | ||
* | ||
* Wraps an object method into spy. All calls will | ||
* pass through to the original function. | ||
* The same as Sandbox.on. | ||
* Assignes newly created spy to DEFAULT sandbox | ||
* | ||
* var spy = chai.spy.on(Array, 'isArray'); | ||
* var isArray = chai.spy.on(Array, 'isArray'); | ||
* | ||
* @param {Object} object | ||
* @param {...String} method names to spy on | ||
* @returns passed object | ||
* @see Sandbox.on | ||
* @api public | ||
*/ | ||
chai.spy.on = function (object) { | ||
var methodNames = Array.prototype.slice.call(arguments, 1); | ||
methodNames.forEach(function(methodName) { | ||
object[methodName] = chai.spy(object[methodName]); | ||
}); | ||
return object; | ||
chai.spy.on = function () { | ||
return DEFAULT_SANDBOX.on.apply(DEFAULT_SANDBOX, arguments) | ||
}; | ||
/** | ||
* # chai.spy.object (function) | ||
* # chai.spy.interface (function) | ||
* | ||
* Creates an object with spied methods. | ||
* Creates an object interface with spied methods. | ||
* | ||
* var object = chai.spy.object('Array', [ 'push', 'pop' ]); | ||
* var events = chai.spy.interface('Events', ['trigger', 'on']); | ||
* | ||
* @param {String} [name] object name | ||
* @param {String[]|Object} method names or method definitions | ||
* var array = chai.spy.interface({ | ||
* push(item) { | ||
* this.items = this.items || []; | ||
* return this.items.push(item); | ||
* } | ||
* }); | ||
* | ||
* @param {String|Object} name object or object name | ||
* @param {String[]} [methods] method names | ||
* @returns object with spied methods | ||
@@ -150,15 +308,11 @@ * @api public | ||
chai.spy.object = function (name, methods) { | ||
chai.spy.interface = function (name, methods) { | ||
var defs = {}; | ||
if (name && typeof name === 'object') { | ||
methods = name; | ||
name = 'object'; | ||
methods = Object.keys(name); | ||
defs = name; | ||
name = 'mock'; | ||
} | ||
if (methods && !Array.isArray(methods)) { | ||
defs = methods; | ||
methods = Object.keys(methods); | ||
} | ||
return methods.reduce(function (object, methodName) { | ||
@@ -171,2 +325,23 @@ object[methodName] = chai.spy(name + '.' + methodName, defs[methodName]); | ||
/** | ||
* # chai.spy.restore (function) | ||
* | ||
* The same as Sandbox.restore. | ||
* Restores spy assigned to DEFAULT sandbox | ||
* | ||
* var array = [] | ||
* chai.spy.on(array, 'push'); | ||
* expect(array.push).to.be.spy // true | ||
* | ||
* chai.spy.restore() | ||
* expect(array.push).to.be.spy // false | ||
* | ||
* @see Sandbox.restore | ||
* @api public | ||
*/ | ||
chai.spy.restore = function () { | ||
return DEFAULT_SANDBOX.restore.apply(DEFAULT_SANDBOX, arguments) | ||
}; | ||
/** | ||
* # chai.spy.returns (function) | ||
@@ -289,42 +464,36 @@ * | ||
new Assertion(this._obj).to.be.spy; | ||
var args = [].slice.call(arguments, 0) | ||
var expArgs = [].slice.call(arguments, 0) | ||
, calls = this._obj.__spy.calls | ||
, always = _.flag(this, 'spy always') | ||
, passed; | ||
, passed = 0; | ||
if (always) { | ||
passed = 0 | ||
calls.forEach(function (call) { | ||
var found = 0; | ||
args.forEach(function (arg) { | ||
for (var i = 0; i < call.length; i++) { | ||
if (_.eql(call[i], arg)) found++; | ||
calls.forEach(function (call) { | ||
var actArgs = call.slice() | ||
, found = 0; | ||
expArgs.forEach(function (expArg) { | ||
for (var i = 0; i < actArgs.length; i++) { | ||
if (_.eql(actArgs[i], expArg)) { | ||
found++; | ||
actArgs.splice(i, 1); | ||
break; | ||
} | ||
}); | ||
if (found === args.length) passed++; | ||
} | ||
}); | ||
if (found === expArgs.length) passed++; | ||
}); | ||
if (always) { | ||
this.assert( | ||
passed === calls.length | ||
, 'expected ' + this._obj + ' to have been always called with #{exp} but got ' + passed + ' out of ' + calls.length | ||
, 'expected ' + this._his + ' to have not always been called with #{exp}' | ||
, args | ||
, 'expected ' + this._obj + ' to have not always been called with #{exp}' | ||
, expArgs | ||
); | ||
} else { | ||
passed = 0; | ||
calls.forEach(function (call) { | ||
var found = 0; | ||
args.forEach(function (arg) { | ||
for (var i = 0; i < call.length; i++) { | ||
if (_.eql(call[i], arg)) found++; | ||
} | ||
}); | ||
if (found === args.length) passed++; | ||
}); | ||
this.assert( | ||
passed > 0 | ||
, 'expected ' + this._obj + ' to have been called with #{exp}' | ||
, 'expected ' + this._his + ' to have not been called with #{exp} but got ' + passed + ' times' | ||
, args | ||
, 'expected ' + this._obj + ' to have not been called with #{exp} but got ' + passed + ' times' | ||
, expArgs | ||
); | ||
@@ -331,0 +500,0 @@ } |
@@ -15,3 +15,3 @@ { | ||
], | ||
"version": "0.9.0", | ||
"version": "0.9.1", | ||
"license": "MIT", | ||
@@ -18,0 +18,0 @@ "repository": { |
@@ -11,4 +11,4 @@ /*! | ||
var folio = require('folio'); | ||
const folio = require('folio'); | ||
const path = require('path'); | ||
/*! | ||
@@ -15,0 +15,0 @@ * Folio Definition |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
72189
1699