(function (factory) { if (typeof define === 'function' && define.amd) { define(['should'], factory); } else if (typeof exports === 'object') { module.exports = factory(require('should')); } else { factory(should); } }(function (should) { var Assertion = should.Assertion; Assertion.add('sinonStub', function() { this.params = { operator: 'to be sinon stub' }; this.is.not.null().and.not.undefined(); var method = this.obj; if(method.proxy && method.proxy.isSinonProxy) { method = method.proxy; } method.should.be.a.Function() .and.have.property('getCall') .which.is.a.Function(); }); function isStub(stub) { if (!stub) { return false; } if (method.proxy && method.proxy.isSinonProxy) { verifyIsStub(method.proxy); } else { if (typeof method !== "function") { assert.fail(method + " is not a function"); } if (typeof method.getCall !== "function") { assert.fail(method + " is not stubbed"); } } } function timesInWords(count) { switch (count) { case 1: return "once"; case 2: return "twice"; case 3: return "thrice"; default: return (count || 0) + " times"; } } function isCall(call) { return call && isSpy(call.proxy); } function isSpy(spy) { return typeof spy === "function" && typeof spy.getCall === "function" && typeof spy.calledWithExactly === "function"; } function proxySinonBooleanProperty(name, message) { Assertion.add(name, function() { var obj = this.obj; if(!isSpy(obj) && !isCall(obj)) { this.params = { obj: obj.toString(), operator: 'to be sinon spy or spy call' }; this.fail(); } if(isCall(obj)) { obj = obj.proxy; } this.params = { obj: obj.toString(), operator: obj.printf(message) }; should(obj[name]).be.true(); }); } function proxySinonMethod(name, message) { Assertion.add(name, function() { var obj = this.obj; if(!isSpy(obj) && !isCall(obj)) { this.params = { obj: obj.toString(), operator: 'to be sinon spy or spy call' }; this.fail(); } if(isCall(obj)) { obj = obj.proxy; } var args = Array.prototype.slice.call(arguments); args.unshift(message); this.params = { obj: obj.toString(), operator: obj.printf.apply(obj, args) }; should(obj[name].apply(obj, arguments)).be.true(); }); } /** * Assert stub was called at least once * * @name called * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var callback = sinon.spy(); * callback(); * callback.should.be.called(); */ proxySinonBooleanProperty('called', 'to have been called, but was called %c'); /** * Assert stub was called at exactly once * * @name calledOnce * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var callback = sinon.spy(); * callback(); * callback.should.be.calledOnce(); */ proxySinonBooleanProperty('calledOnce', 'to be called once but was called %c%C'); /** * Assert stub was called at exactly twice * * @name calledTwice * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var callback = sinon.spy(); * callback(); * callback(); * callback.should.be.calledTwice(); */ proxySinonBooleanProperty('calledTwice', 'to be called twice but was called %c%C'); /** * Assert stub was called at exactly thrice * * @name calledThrice * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var callback = sinon.spy(); * callback(); * callback(); * callback(); * callback.should.be.calledThrice(); */ proxySinonBooleanProperty('calledThrice', 'to be called thrice but was called %c%C'); /** * Assert stub was called with given object as this * * @name calledOn * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {*} obj - object that was used as this * @example * * var callback = sinon.spy(); * var obj = {}; * callback.call(obj); * callback.should.be.calledOn(obj); */ proxySinonMethod('calledOn', 'to be called with %1 as this but was called with %t'); /** * Assert stub was called with given object as this always. So if you call stub several times * all should be with the same object * * @name alwaysCalledOn * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {*} obj - object that was used as this * @example * * var callback = sinon.spy(); * var obj = {}; * callback.call(obj); * callback.should.be.alwaysCalledOn(obj); */ proxySinonMethod('alwaysCalledOn', 'to always be called with %1 as this but was called with %t'); /** * Asserts that stub was called with new * * @name calledWithNew * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var Class = sinon.spy(); * * var c = new Class(); * * Class.should.be.calledWithNew; */ proxySinonMethod('calledWithNew', 'to be called with new'); /** * @name alwaysCalledWithNew * @memberOf Assertion * @category assertion stubs * @module should-sinon * @example * * var Class = sinon.spy(); * * var c1 = new Class(); * var c2 = new Class(); * * Class.should.be.alwaysCalledWithNew; */ proxySinonMethod('alwaysCalledWithNew', 'to always be called with new'); /** * Asserts that stub was called with given arguments * * @name calledWith * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling * @example * * var callback = sinon.spy(); * * callback(1, 2, 3); * * callback.should.be.calledWith(1, 2, 3); */ proxySinonMethod('calledWith', 'to be called with arguments %*%C'); /** * @name alwaysCalledWith * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling * @example * * var callback = sinon.spy(); * * callback(1, 2, 3); * * callback.should.be.alwaysCalledWith(1, 2, 3); */ proxySinonMethod('alwaysCalledWith', 'to always be called with arguments %*%C'); /** * Returns true if the spy/stub was never called with the provided arguments. * * @name neverCalledWith * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling * @example * * var callback = sinon.spy(); * * callback(1, 2, 3); * * callback.should.be.neverCalledWith(1, 2, 3); */ proxySinonMethod('neverCalledWith', 'to never be called with arguments %*%C'); /** * Returns true if spy was called with matching arguments (and possibly others). * * @name calledWithMatch * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling */ proxySinonMethod('calledWithMatch', 'to be called with match %*%C'); /** * Returns true if spy was always called with matching arguments (and possibly others). * * @name alwaysCalledWithMatch * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling */ proxySinonMethod('alwaysCalledWithMatch', 'to always be called with match %*%C'); /** * Returns true if the spy/stub was never called with matching arguments. * * @name neverCalledWithMatch * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling */ proxySinonMethod('neverCalledWithMatch', 'to never be called with match %*%C'); /** * Returns true if call received provided arguments and no others. * * @name calledWithExactly * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling */ proxySinonMethod('calledWithExactly', 'to be called with exact arguments %*%C'); /** * Passes if the spy was always called with the provided arguments and no others. * * @name alwaysCalledWithExactly * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {...*} args - arguments that was used for calling */ proxySinonMethod('alwaysCalledWithExactly', 'to always be called with exact arguments %*%C'); /** * Passes if the spy threw the given exception. The exception can be a * string denoting its type, or an actual object. If no argument is * provided, the assertion passes if the spy ever threw any exception. * * @name threw * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {string|Error} ex - exception to be thrown */ proxySinonMethod('threw', 'to throw exception%C'); /** * Passes if the spy always threw the given exception. The exception can be a * string denoting its type, or an actual object. If no argument is * provided, the assertion passes if the spy ever threw any exception. * * @name alwaysThrew * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {string|Error} ex - exception to be thrown */ proxySinonMethod('alwaysThrew', 'to always throw exception%C'); /** * Assert stub was called at exact number of times * * @name callCount * @memberOf Assertion * @category assertion stubs * @module should-sinon * @param {Number} count - number of calles * @example * * var callback = sinon.spy(); * callback.should.have.callCount(0); * callback(); * callback.should.have.callCount(1); * callback(); * callback.should.have.callCount(2); */ Assertion.add('callCount', function(count) { var obj = this.obj; if(!isSpy(obj) && !isCall(obj)) { this.params = { obj: obj.toString(), operator: 'to be sinon spy or spy call' }; this.fail(); } if(isCall(obj)) { obj = obj.proxy; } this.params = { operator: obj.printf('to be called ' + timesInWords(count) + ' but was called %c%C' )}; this.assert(obj.callCount === count); }); }));