|
|
/** * Sinon.JS 1.9.1, 2014/04/03 * * @author Christian Johansen (christian@cjohansen.no) * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
* * (The BSD License) * * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Christian Johansen nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
this.sinon = (function () { var samsam, formatio; function define(mod, deps, fn) { if (mod == "samsam") { samsam = deps(); } else { formatio = fn(samsam); } } define.amd = true; ((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) || (typeof module === "object" && function (m) { module.exports = m(); }) || // Node
function (m) { this.samsam = m(); } // Browser globals
)(function () { var o = Object.prototype; var div = typeof document !== "undefined" && document.createElement("div");
function isNaN(value) { // Unlike global isNaN, this avoids type coercion
// typeof check avoids IE host object issues, hat tip to
// lodash
var val = value; // JsLint thinks value !== value is "weird"
return typeof value === "number" && value !== val; }
function getClass(value) { // Returns the internal [[Class]] by calling Object.prototype.toString
// with the provided value as this. Return value is a string, naming the
// internal class, e.g. "Array"
return o.toString.call(value).split(/[ \]]/)[1]; }
/** * @name samsam.isArguments * @param Object object * * Returns ``true`` if ``object`` is an ``arguments`` object, * ``false`` otherwise. */ function isArguments(object) { if (typeof object !== "object" || typeof object.length !== "number" || getClass(object) === "Array") { return false; } if (typeof object.callee == "function") { return true; } try { object[object.length] = 6; delete object[object.length]; } catch (e) { return true; } return false; }
/** * @name samsam.isElement * @param Object object * * Returns ``true`` if ``object`` is a DOM element node. Unlike * Underscore.js/lodash, this function will return ``false`` if ``object`` * is an *element-like* object, i.e. a regular object with a ``nodeType`` * property that holds the value ``1``. */ function isElement(object) { if (!object || object.nodeType !== 1 || !div) { return false; } try { object.appendChild(div); object.removeChild(div); } catch (e) { return false; } return true; }
/** * @name samsam.keys * @param Object object * * Return an array of own property names. */ function keys(object) { var ks = [], prop; for (prop in object) { if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); } } return ks; }
/** * @name samsam.isDate * @param Object value * * Returns true if the object is a ``Date``, or *date-like*. Duck typing * of date objects work by checking that the object has a ``getTime`` * function whose return value equals the return value from the object's * ``valueOf``. */ function isDate(value) { return typeof value.getTime == "function" && value.getTime() == value.valueOf(); }
/** * @name samsam.isNegZero * @param Object value * * Returns ``true`` if ``value`` is ``-0``. */ function isNegZero(value) { return value === 0 && 1 / value === -Infinity; }
/** * @name samsam.equal * @param Object obj1 * @param Object obj2 * * Returns ``true`` if two objects are strictly equal. Compared to * ``===`` there are two exceptions: * * - NaN is considered equal to NaN * - -0 and +0 are not considered equal */ function identical(obj1, obj2) { if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) { return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2); } }
/** * @name samsam.deepEqual * @param Object obj1 * @param Object obj2 * * Deep equal comparison. Two values are "deep equal" if: * * - They are equal, according to samsam.identical * - They are both date objects representing the same time * - They are both arrays containing elements that are all deepEqual * - They are objects with the same set of properties, and each property * in ``obj1`` is deepEqual to the corresponding property in ``obj2`` * * Supports cyclic objects. */ function deepEqualCyclic(obj1, obj2) {
// used for cyclic comparison
// contain already visited objects
var objects1 = [], objects2 = [], // contain pathes (position in the object structure)
// of the already visited objects
// indexes same as in objects arrays
paths1 = [], paths2 = [], // contains combinations of already compared objects
// in the manner: { "$1['ref']$2['ref']": true }
compared = {};
/** * used to check, if the value of a property is an object * (cyclic logic is only needed for objects) * only needed for cyclic logic */ function isObject(value) {
if (typeof value === 'object' && value !== null && !(value instanceof Boolean) && !(value instanceof Date) && !(value instanceof Number) && !(value instanceof RegExp) && !(value instanceof String)) {
return true; }
return false; }
/** * returns the index of the given object in the * given objects array, -1 if not contained * only needed for cyclic logic */ function getIndex(objects, obj) {
var i; for (i = 0; i < objects.length; i++) { if (objects[i] === obj) { return i; } }
return -1; }
// does the recursion for the deep equal check
return (function deepEqual(obj1, obj2, path1, path2) { var type1 = typeof obj1; var type2 = typeof obj2;
// == null also matches undefined
if (obj1 === obj2 || isNaN(obj1) || isNaN(obj2) || obj1 == null || obj2 == null || type1 !== "object" || type2 !== "object") {
return identical(obj1, obj2); }
// Elements are only equal if identical(expected, actual)
if (isElement(obj1) || isElement(obj2)) { return false; }
var isDate1 = isDate(obj1), isDate2 = isDate(obj2); if (isDate1 || isDate2) { if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) { return false; } }
if (obj1 instanceof RegExp && obj2 instanceof RegExp) { if (obj1.toString() !== obj2.toString()) { return false; } }
var class1 = getClass(obj1); var class2 = getClass(obj2); var keys1 = keys(obj1); var keys2 = keys(obj2);
if (isArguments(obj1) || isArguments(obj2)) { if (obj1.length !== obj2.length) { return false; } } else { if (type1 !== type2 || class1 !== class2 || keys1.length !== keys2.length) { return false; } }
var key, i, l, // following vars are used for the cyclic logic
value1, value2, isObject1, isObject2, index1, index2, newPath1, newPath2;
for (i = 0, l = keys1.length; i < l; i++) { key = keys1[i]; if (!o.hasOwnProperty.call(obj2, key)) { return false; }
// Start of the cyclic logic
value1 = obj1[key]; value2 = obj2[key];
isObject1 = isObject(value1); isObject2 = isObject(value2);
// determine, if the objects were already visited
// (it's faster to check for isObject first, than to
// get -1 from getIndex for non objects)
index1 = isObject1 ? getIndex(objects1, value1) : -1; index2 = isObject2 ? getIndex(objects2, value2) : -1;
// determine the new pathes of the objects
// - for non cyclic objects the current path will be extended
// by current property name
// - for cyclic objects the stored path is taken
newPath1 = index1 !== -1 ? paths1[index1] : path1 + '[' + JSON.stringify(key) + ']'; newPath2 = index2 !== -1 ? paths2[index2] : path2 + '[' + JSON.stringify(key) + ']';
// stop recursion if current objects are already compared
if (compared[newPath1 + newPath2]) { return true; }
// remember the current objects and their pathes
if (index1 === -1 && isObject1) { objects1.push(value1); paths1.push(newPath1); } if (index2 === -1 && isObject2) { objects2.push(value2); paths2.push(newPath2); }
// remember that the current objects are already compared
if (isObject1 && isObject2) { compared[newPath1 + newPath2] = true; }
// End of cyclic logic
// neither value1 nor value2 is a cycle
// continue with next level
if (!deepEqual(value1, value2, newPath1, newPath2)) { return false; } }
return true;
}(obj1, obj2, '$1', '$2')); }
var match;
function arrayContains(array, subset) { if (subset.length === 0) { return true; } var i, l, j, k; for (i = 0, l = array.length; i < l; ++i) { if (match(array[i], subset[0])) { for (j = 0, k = subset.length; j < k; ++j) { if (!match(array[i + j], subset[j])) { return false; } } return true; } } return false; }
/** * @name samsam.match * @param Object object * @param Object matcher * * Compare arbitrary value ``object`` with matcher. */ match = function match(object, matcher) { if (matcher && typeof matcher.test === "function") { return matcher.test(object); }
if (typeof matcher === "function") { return matcher(object) === true; }
if (typeof matcher === "string") { matcher = matcher.toLowerCase(); var notNull = typeof object === "string" || !!object; return notNull && (String(object)).toLowerCase().indexOf(matcher) >= 0; }
if (typeof matcher === "number") { return matcher === object; }
if (typeof matcher === "boolean") { return matcher === object; }
if (getClass(object) === "Array" && getClass(matcher) === "Array") { return arrayContains(object, matcher); }
if (matcher && typeof matcher === "object") { var prop; for (prop in matcher) { if (!match(object[prop], matcher[prop])) { return false; } } return true; }
throw new Error("Matcher was not a string, a number, a " + "function, a boolean or an object"); };
return { isArguments: isArguments, isElement: isElement, isDate: isDate, isNegZero: isNegZero, identical: identical, deepEqual: deepEqualCyclic, match: match, keys: keys }; }); ((typeof define === "function" && define.amd && function (m) { define("formatio", ["samsam"], m); }) || (typeof module === "object" && function (m) { module.exports = m(require("samsam")); }) || function (m) { this.formatio = m(this.samsam); } )(function (samsam) {
var formatio = { excludeConstructors: ["Object", /^.$/], quoteStrings: true };
var hasOwn = Object.prototype.hasOwnProperty;
var specialObjects = []; if (typeof global !== "undefined") { specialObjects.push({ object: global, value: "[object global]" }); } if (typeof document !== "undefined") { specialObjects.push({ object: document, value: "[object HTMLDocument]" }); } if (typeof window !== "undefined") { specialObjects.push({ object: window, value: "[object Window]" }); }
function functionName(func) { if (!func) { return ""; } if (func.displayName) { return func.displayName; } if (func.name) { return func.name; } var matches = func.toString().match(/function\s+([^\(]+)/m); return (matches && matches[1]) || ""; }
function constructorName(f, object) { var name = functionName(object && object.constructor); var excludes = f.excludeConstructors || formatio.excludeConstructors || [];
var i, l; for (i = 0, l = excludes.length; i < l; ++i) { if (typeof excludes[i] === "string" && excludes[i] === name) { return ""; } else if (excludes[i].test && excludes[i].test(name)) { return ""; } }
return name; }
function isCircular(object, objects) { if (typeof object !== "object") { return false; } var i, l; for (i = 0, l = objects.length; i < l; ++i) { if (objects[i] === object) { return true; } } return false; }
function ascii(f, object, processed, indent) { if (typeof object === "string") { var qs = f.quoteStrings; var quote = typeof qs !== "boolean" || qs; return processed || quote ? '"' + object + '"' : object; }
if (typeof object === "function" && !(object instanceof RegExp)) { return ascii.func(object); }
processed = processed || [];
if (isCircular(object, processed)) { return "[Circular]"; }
if (Object.prototype.toString.call(object) === "[object Array]") { return ascii.array.call(f, object, processed); }
if (!object) { return String((1/object) === -Infinity ? "-0" : object); } if (samsam.isElement(object)) { return ascii.element(object); }
if (typeof object.toString === "function" && object.toString !== Object.prototype.toString) { return object.toString(); }
var i, l; for (i = 0, l = specialObjects.length; i < l; i++) { if (object === specialObjects[i].object) { return specialObjects[i].value; } }
return ascii.object.call(f, object, processed, indent); }
ascii.func = function (func) { return "function " + functionName(func) + "() {}"; };
ascii.array = function (array, processed) { processed = processed || []; processed.push(array); var i, l, pieces = []; for (i = 0, l = array.length; i < l; ++i) { pieces.push(ascii(this, array[i], processed)); } return "[" + pieces.join(", ") + "]"; };
ascii.object = function (object, processed, indent) { processed = processed || []; processed.push(object); indent = indent || 0; var pieces = [], properties = samsam.keys(object).sort(); var length = 3; var prop, str, obj, i, l;
for (i = 0, l = properties.length; i < l; ++i) { prop = properties[i]; obj = object[prop];
if (isCircular(obj, processed)) { str = "[Circular]"; } else { str = ascii(this, obj, processed, indent + 2); }
str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str; length += str.length; pieces.push(str); }
var cons = constructorName(this, object); var prefix = cons ? "[" + cons + "] " : ""; var is = ""; for (i = 0, l = indent; i < l; ++i) { is += " "; }
if (length + indent > 80) { return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" + is + "}"; } return prefix + "{ " + pieces.join(", ") + " }"; };
ascii.element = function (element) { var tagName = element.tagName.toLowerCase(); var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;
for (i = 0, l = attrs.length; i < l; ++i) { attr = attrs.item(i); attrName = attr.nodeName.toLowerCase().replace("html:", ""); val = attr.nodeValue; if (attrName !== "contenteditable" || val !== "inherit") { if (!!val) { pairs.push(attrName + "=\"" + val + "\""); } } }
var formatted = "<" + tagName + (pairs.length > 0 ? " " : ""); var content = element.innerHTML;
if (content.length > 20) { content = content.substr(0, 20) + "[...]"; }
var res = formatted + pairs.join(" ") + ">" + content + "</" + tagName + ">";
return res.replace(/ contentEditable="inherit"/, ""); };
function Formatio(options) { for (var opt in options) { this[opt] = options[opt]; } }
Formatio.prototype = { functionName: functionName,
configure: function (options) { return new Formatio(options); },
constructorName: function (object) { return constructorName(this, object); },
ascii: function (object, processed, indent) { return ascii(this, object, processed, indent); } };
return Formatio.prototype; }); /*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/ /*global module, require, __dirname, document*/ /** * Sinon core utilities. For internal use only. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
var sinon = (function (formatio) { var div = typeof document != "undefined" && document.createElement("div"); var hasOwn = Object.prototype.hasOwnProperty;
function isDOMNode(obj) { var success = false;
try { obj.appendChild(div); success = div.parentNode == obj; } catch (e) { return false; } finally { try { obj.removeChild(div); } catch (e) { // Remove failed, not much we can do about that
} }
return success; }
function isElement(obj) { return div && obj && obj.nodeType === 1 && isDOMNode(obj); }
function isFunction(obj) { return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply); }
function isReallyNaN(val) { return typeof val === 'number' && isNaN(val); }
function mirrorProperties(target, source) { for (var prop in source) { if (!hasOwn.call(target, prop)) { target[prop] = source[prop]; } } }
function isRestorable (obj) { return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon; }
var sinon = { wrapMethod: function wrapMethod(object, property, method) { if (!object) { throw new TypeError("Should wrap property of object"); }
if (typeof method != "function") { throw new TypeError("Method wrapper should be function"); }
var wrappedMethod = object[property], error;
if (!isFunction(wrappedMethod)) { error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " + property + " as function"); }
if (wrappedMethod.restore && wrappedMethod.restore.sinon) { error = new TypeError("Attempted to wrap " + property + " which is already wrapped"); }
if (wrappedMethod.calledBefore) { var verb = !!wrappedMethod.returns ? "stubbed" : "spied on"; error = new TypeError("Attempted to wrap " + property + " which is already " + verb); }
if (error) { if (wrappedMethod._stack) { error.stack += '\n--------------\n' + wrappedMethod._stack; } throw error; }
// IE 8 does not support hasOwnProperty on the window object and Firefox has a problem
// when using hasOwn.call on objects from other frames.
var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property); object[property] = method; method.displayName = property; // Set up a stack trace which can be used later to find what line of
// code the original method was created on.
method._stack = (new Error('Stack Trace for original')).stack;
method.restore = function () { // For prototype properties try to reset by delete first.
// If this fails (ex: localStorage on mobile safari) then force a reset
// via direct assignment.
if (!owned) { delete object[property]; } if (object[property] === method) { object[property] = wrappedMethod; } };
method.restore.sinon = true; mirrorProperties(method, wrappedMethod);
return method; },
extend: function extend(target) { for (var i = 1, l = arguments.length; i < l; i += 1) { for (var prop in arguments[i]) { if (arguments[i].hasOwnProperty(prop)) { target[prop] = arguments[i][prop]; }
// DONT ENUM bug, only care about toString
if (arguments[i].hasOwnProperty("toString") && arguments[i].toString != target.toString) { target.toString = arguments[i].toString; } } }
return target; },
create: function create(proto) { var F = function () {}; F.prototype = proto; return new F(); },
deepEqual: function deepEqual(a, b) { if (sinon.match && sinon.match.isMatcher(a)) { return a.test(b); }
if (typeof a != 'object' || typeof b != 'object') { if (isReallyNaN(a) && isReallyNaN(b)) { return true; } else { return a === b; } }
if (isElement(a) || isElement(b)) { return a === b; }
if (a === b) { return true; }
if ((a === null && b !== null) || (a !== null && b === null)) { return false; }
if (a instanceof RegExp && b instanceof RegExp) { return (a.source === b.source) && (a.global === b.global) && (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline); }
var aString = Object.prototype.toString.call(a); if (aString != Object.prototype.toString.call(b)) { return false; }
if (aString == "[object Date]") { return a.valueOf() === b.valueOf(); }
var prop, aLength = 0, bLength = 0;
if (aString == "[object Array]" && a.length !== b.length) { return false; }
for (prop in a) { aLength += 1;
if (!deepEqual(a[prop], b[prop])) { return false; } }
for (prop in b) { bLength += 1; }
return aLength == bLength; },
functionName: function functionName(func) { var name = func.displayName || func.name;
// Use function decomposition as a last resort to get function
// name. Does not rely on function decomposition to work - if it
// doesn't debugging will be slightly less informative
// (i.e. toString will say 'spy' rather than 'myFunc').
if (!name) { var matches = func.toString().match(/function ([^\s\(]+)/); name = matches && matches[1]; }
return name; },
functionToString: function toString() { if (this.getCall && this.callCount) { var thisValue, prop, i = this.callCount;
while (i--) { thisValue = this.getCall(i).thisValue;
for (prop in thisValue) { if (thisValue[prop] === this) { return prop; } } } }
return this.displayName || "sinon fake"; },
getConfig: function (custom) { var config = {}; custom = custom || {}; var defaults = sinon.defaultConfig;
for (var prop in defaults) { if (defaults.hasOwnProperty(prop)) { config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; } }
return config; },
format: function (val) { return "" + val; },
defaultConfig: { injectIntoThis: true, injectInto: null, properties: ["spy", "stub", "mock", "clock", "server", "requests"], useFakeTimers: true, useFakeServer: true },
timesInWords: function timesInWords(count) { return count == 1 && "once" || count == 2 && "twice" || count == 3 && "thrice" || (count || 0) + " times"; },
calledInOrder: function (spies) { for (var i = 1, l = spies.length; i < l; i++) { if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) { return false; } }
return true; },
orderByFirstCall: function (spies) { return spies.sort(function (a, b) { // uuid, won't ever be equal
var aCall = a.getCall(0); var bCall = b.getCall(0); var aId = aCall && aCall.callId || -1; var bId = bCall && bCall.callId || -1;
return aId < bId ? -1 : 1; }); },
log: function () {},
logError: function (label, err) { var msg = label + " threw exception: "; sinon.log(msg + "[" + err.name + "] " + err.message); if (err.stack) { sinon.log(err.stack); }
setTimeout(function () { err.message = msg + err.message; throw err; }, 0); },
typeOf: function (value) { if (value === null) { return "null"; } else if (value === undefined) { return "undefined"; } var string = Object.prototype.toString.call(value); return string.substring(8, string.length - 1).toLowerCase(); },
createStubInstance: function (constructor) { if (typeof constructor !== "function") { throw new TypeError("The constructor should be a function."); } return sinon.stub(sinon.create(constructor.prototype)); },
restore: function (object) { if (object !== null && typeof object === "object") { for (var prop in object) { if (isRestorable(object[prop])) { object[prop].restore(); } } } else if (isRestorable(object)) { object.restore(); } } };
var isNode = typeof module !== "undefined" && module.exports; var isAMD = typeof define === 'function' && typeof define.amd === 'object' && define.amd;
if (isAMD) { define(function(){ return sinon; }); } else if (isNode) { try { formatio = require("formatio"); } catch (e) {} module.exports = sinon; module.exports.spy = require("./sinon/spy"); module.exports.spyCall = require("./sinon/call"); module.exports.behavior = require("./sinon/behavior"); module.exports.stub = require("./sinon/stub"); module.exports.mock = require("./sinon/mock"); module.exports.collection = require("./sinon/collection"); module.exports.assert = require("./sinon/assert"); module.exports.sandbox = require("./sinon/sandbox"); module.exports.test = require("./sinon/test"); module.exports.testCase = require("./sinon/test_case"); module.exports.assert = require("./sinon/assert"); module.exports.match = require("./sinon/match"); }
if (formatio) { var formatter = formatio.configure({ quoteStrings: false }); sinon.format = function () { return formatter.ascii.apply(formatter, arguments); }; } else if (isNode) { try { var util = require("util"); sinon.format = function (value) { return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value; }; } catch (e) { /* Node, but no util module - would be very old, but better safe than sorry */ } }
return sinon; }(typeof formatio == "object" && formatio));
/* @depend ../sinon.js */ /*jslint eqeqeq: false, onevar: false, plusplus: false*/ /*global module, require, sinon*/ /** * Match functions * * @author Maximilian Antoni (mail@maxantoni.de) * @license BSD * * Copyright (c) 2012 Maximilian Antoni */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function assertType(value, type, name) { var actual = sinon.typeOf(value); if (actual !== type) { throw new TypeError("Expected type of " + name + " to be " + type + ", but was " + actual); } }
var matcher = { toString: function () { return this.message; } };
function isMatcher(object) { return matcher.isPrototypeOf(object); }
function matchObject(expectation, actual) { if (actual === null || actual === undefined) { return false; } for (var key in expectation) { if (expectation.hasOwnProperty(key)) { var exp = expectation[key]; var act = actual[key]; if (match.isMatcher(exp)) { if (!exp.test(act)) { return false; } } else if (sinon.typeOf(exp) === "object") { if (!matchObject(exp, act)) { return false; } } else if (!sinon.deepEqual(exp, act)) { return false; } } } return true; }
matcher.or = function (m2) { if (!arguments.length) { throw new TypeError("Matcher expected"); } else if (!isMatcher(m2)) { m2 = match(m2); } var m1 = this; var or = sinon.create(matcher); or.test = function (actual) { return m1.test(actual) || m2.test(actual); }; or.message = m1.message + ".or(" + m2.message + ")"; return or; };
matcher.and = function (m2) { if (!arguments.length) { throw new TypeError("Matcher expected"); } else if (!isMatcher(m2)) { m2 = match(m2); } var m1 = this; var and = sinon.create(matcher); and.test = function (actual) { return m1.test(actual) && m2.test(actual); }; and.message = m1.message + ".and(" + m2.message + ")"; return and; };
var match = function (expectation, message) { var m = sinon.create(matcher); var type = sinon.typeOf(expectation); switch (type) { case "object": if (typeof expectation.test === "function") { m.test = function (actual) { return expectation.test(actual) === true; }; m.message = "match(" + sinon.functionName(expectation.test) + ")"; return m; } var str = []; for (var key in expectation) { if (expectation.hasOwnProperty(key)) { str.push(key + ": " + expectation[key]); } } m.test = function (actual) { return matchObject(expectation, actual); }; m.message = "match(" + str.join(", ") + ")"; break; case "number": m.test = function (actual) { return expectation == actual; }; break; case "string": m.test = function (actual) { if (typeof actual !== "string") { return false; } return actual.indexOf(expectation) !== -1; }; m.message = "match(\"" + expectation + "\")"; break; case "regexp": m.test = function (actual) { if (typeof actual !== "string") { return false; } return expectation.test(actual); }; break; case "function": m.test = expectation; if (message) { m.message = message; } else { m.message = "match(" + sinon.functionName(expectation) + ")"; } break; default: m.test = function (actual) { return sinon.deepEqual(expectation, actual); }; } if (!m.message) { m.message = "match(" + expectation + ")"; } return m; };
match.isMatcher = isMatcher;
match.any = match(function () { return true; }, "any");
match.defined = match(function (actual) { return actual !== null && actual !== undefined; }, "defined");
match.truthy = match(function (actual) { return !!actual; }, "truthy");
match.falsy = match(function (actual) { return !actual; }, "falsy");
match.same = function (expectation) { return match(function (actual) { return expectation === actual; }, "same(" + expectation + ")"); };
match.typeOf = function (type) { assertType(type, "string", "type"); return match(function (actual) { return sinon.typeOf(actual) === type; }, "typeOf(\"" + type + "\")"); };
match.instanceOf = function (type) { assertType(type, "function", "type"); return match(function (actual) { return actual instanceof type; }, "instanceOf(" + sinon.functionName(type) + ")"); };
function createPropertyMatcher(propertyTest, messagePrefix) { return function (property, value) { assertType(property, "string", "property"); var onlyProperty = arguments.length === 1; var message = messagePrefix + "(\"" + property + "\""; if (!onlyProperty) { message += ", " + value; } message += ")"; return match(function (actual) { if (actual === undefined || actual === null || !propertyTest(actual, property)) { return false; } return onlyProperty || sinon.deepEqual(value, actual[property]); }, message); }; }
match.has = createPropertyMatcher(function (actual, property) { if (typeof actual === "object") { return property in actual; } return actual[property] !== undefined; }, "has");
match.hasOwn = createPropertyMatcher(function (actual, property) { return actual.hasOwnProperty(property); }, "hasOwn");
match.bool = match.typeOf("boolean"); match.number = match.typeOf("number"); match.string = match.typeOf("string"); match.object = match.typeOf("object"); match.func = match.typeOf("function"); match.array = match.typeOf("array"); match.regexp = match.typeOf("regexp"); match.date = match.typeOf("date");
if (commonJSModule) { module.exports = match; } else { sinon.match = match; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend match.js */ /*jslint eqeqeq: false, onevar: false, plusplus: false*/ /*global module, require, sinon*/ /** * Spy calls * * @author Christian Johansen (christian@cjohansen.no) * @author Maximilian Antoni (mail@maxantoni.de) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen * Copyright (c) 2013 Maximilian Antoni */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports; if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function throwYieldError(proxy, text, args) { var msg = sinon.functionName(proxy) + text; if (args.length) { msg += " Received [" + slice.call(args).join(", ") + "]"; } throw new Error(msg); }
var slice = Array.prototype.slice;
var callProto = { calledOn: function calledOn(thisValue) { if (sinon.match && sinon.match.isMatcher(thisValue)) { return thisValue.test(this.thisValue); } return this.thisValue === thisValue; },
calledWith: function calledWith() { for (var i = 0, l = arguments.length; i < l; i += 1) { if (!sinon.deepEqual(arguments[i], this.args[i])) { return false; } }
return true; },
calledWithMatch: function calledWithMatch() { for (var i = 0, l = arguments.length; i < l; i += 1) { var actual = this.args[i]; var expectation = arguments[i]; if (!sinon.match || !sinon.match(expectation).test(actual)) { return false; } } return true; },
calledWithExactly: function calledWithExactly() { return arguments.length == this.args.length && this.calledWith.apply(this, arguments); },
notCalledWith: function notCalledWith() { return !this.calledWith.apply(this, arguments); },
notCalledWithMatch: function notCalledWithMatch() { return !this.calledWithMatch.apply(this, arguments); },
returned: function returned(value) { return sinon.deepEqual(value, this.returnValue); },
threw: function threw(error) { if (typeof error === "undefined" || !this.exception) { return !!this.exception; }
return this.exception === error || this.exception.name === error; },
calledWithNew: function calledWithNew() { return this.proxy.prototype && this.thisValue instanceof this.proxy; },
calledBefore: function (other) { return this.callId < other.callId; },
calledAfter: function (other) { return this.callId > other.callId; },
callArg: function (pos) { this.args[pos](); },
callArgOn: function (pos, thisValue) { this.args[pos].apply(thisValue); },
callArgWith: function (pos) { this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1))); },
callArgOnWith: function (pos, thisValue) { var args = slice.call(arguments, 2); this.args[pos].apply(thisValue, args); },
"yield": function () { this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0))); },
yieldOn: function (thisValue) { var args = this.args; for (var i = 0, l = args.length; i < l; ++i) { if (typeof args[i] === "function") { args[i].apply(thisValue, slice.call(arguments, 1)); return; } } throwYieldError(this.proxy, " cannot yield since no callback was passed.", args); },
yieldTo: function (prop) { this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1))); },
yieldToOn: function (prop, thisValue) { var args = this.args; for (var i = 0, l = args.length; i < l; ++i) { if (args[i] && typeof args[i][prop] === "function") { args[i][prop].apply(thisValue, slice.call(arguments, 2)); return; } } throwYieldError(this.proxy, " cannot yield to '" + prop + "' since no callback was passed.", args); },
toString: function () { var callStr = this.proxy.toString() + "("; var args = [];
for (var i = 0, l = this.args.length; i < l; ++i) { args.push(sinon.format(this.args[i])); }
callStr = callStr + args.join(", ") + ")";
if (typeof this.returnValue != "undefined") { callStr += " => " + sinon.format(this.returnValue); }
if (this.exception) { callStr += " !" + this.exception.name;
if (this.exception.message) { callStr += "(" + this.exception.message + ")"; } }
return callStr; } };
callProto.invokeCallback = callProto.yield;
function createSpyCall(spy, thisValue, args, returnValue, exception, id) { if (typeof id !== "number") { throw new TypeError("Call id is not a number"); } var proxyCall = sinon.create(callProto); proxyCall.proxy = spy; proxyCall.thisValue = thisValue; proxyCall.args = args; proxyCall.returnValue = returnValue; proxyCall.exception = exception; proxyCall.callId = id;
return proxyCall; } createSpyCall.toString = callProto.toString; // used by mocks
if (commonJSModule) { module.exports = createSpyCall; } else { sinon.spyCall = createSpyCall; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend call.js */ /*jslint eqeqeq: false, onevar: false, plusplus: false*/ /*global module, require, sinon*/ /** * Spy functions * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports; var push = Array.prototype.push; var slice = Array.prototype.slice; var callId = 0;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function spy(object, property) { if (!property && typeof object == "function") { return spy.create(object); }
if (!object && !property) { return spy.create(function () { }); }
var method = object[property]; return sinon.wrapMethod(object, property, spy.create(method)); }
function matchingFake(fakes, args, strict) { if (!fakes) { return; }
for (var i = 0, l = fakes.length; i < l; i++) { if (fakes[i].matches(args, strict)) { return fakes[i]; } } }
function incrementCallCount() { this.called = true; this.callCount += 1; this.notCalled = false; this.calledOnce = this.callCount == 1; this.calledTwice = this.callCount == 2; this.calledThrice = this.callCount == 3; }
function createCallProperties() { this.firstCall = this.getCall(0); this.secondCall = this.getCall(1); this.thirdCall = this.getCall(2); this.lastCall = this.getCall(this.callCount - 1); }
var vars = "a,b,c,d,e,f,g,h,i,j,k,l"; function createProxy(func) { // Retain the function length:
var p; if (func.length) { eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) + ") { return p.invoke(func, this, slice.call(arguments)); });"); } else { p = function proxy() { return p.invoke(func, this, slice.call(arguments)); }; } return p; }
var uuid = 0;
// Public API
var spyApi = { reset: function () { this.called = false; this.notCalled = true; this.calledOnce = false; this.calledTwice = false; this.calledThrice = false; this.callCount = 0; this.firstCall = null; this.secondCall = null; this.thirdCall = null; this.lastCall = null; this.args = []; this.returnValues = []; this.thisValues = []; this.exceptions = []; this.callIds = []; if (this.fakes) { for (var i = 0; i < this.fakes.length; i++) { this.fakes[i].reset(); } } },
create: function create(func) { var name;
if (typeof func != "function") { func = function () { }; } else { name = sinon.functionName(func); }
var proxy = createProxy(func);
sinon.extend(proxy, spy); delete proxy.create; sinon.extend(proxy, func);
proxy.reset(); proxy.prototype = func.prototype; proxy.displayName = name || "spy"; proxy.toString = sinon.functionToString; proxy._create = sinon.spy.create; proxy.id = "spy#" + uuid++;
return proxy; },
invoke: function invoke(func, thisValue, args) { var matching = matchingFake(this.fakes, args); var exception, returnValue;
incrementCallCount.call(this); push.call(this.thisValues, thisValue); push.call(this.args, args); push.call(this.callIds, callId++);
try { if (matching) { returnValue = matching.invoke(func, thisValue, args); } else { returnValue = (this.func || func).apply(thisValue, args); }
var thisCall = this.getCall(this.callCount - 1); if (thisCall.calledWithNew() && typeof returnValue !== 'object') { returnValue = thisValue; } } catch (e) { exception = e; }
push.call(this.exceptions, exception); push.call(this.returnValues, returnValue);
createCallProperties.call(this);
if (exception !== undefined) { throw exception; }
return returnValue; },
getCall: function getCall(i) { if (i < 0 || i >= this.callCount) { return null; }
return sinon.spyCall(this, this.thisValues[i], this.args[i], this.returnValues[i], this.exceptions[i], this.callIds[i]); },
getCalls: function () { var calls = []; var i;
for (i = 0; i < this.callCount; i++) { calls.push(this.getCall(i)); }
return calls; },
calledBefore: function calledBefore(spyFn) { if (!this.called) { return false; }
if (!spyFn.called) { return true; }
return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1]; },
calledAfter: function calledAfter(spyFn) { if (!this.called || !spyFn.called) { return false; }
return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; },
withArgs: function () { var args = slice.call(arguments);
if (this.fakes) { var match = matchingFake(this.fakes, args, true);
if (match) { return match; } } else { this.fakes = []; }
var original = this; var fake = this._create(); fake.matchingAguments = args; fake.parent = this; push.call(this.fakes, fake);
fake.withArgs = function () { return original.withArgs.apply(original, arguments); };
for (var i = 0; i < this.args.length; i++) { if (fake.matches(this.args[i])) { incrementCallCount.call(fake); push.call(fake.thisValues, this.thisValues[i]); push.call(fake.args, this.args[i]); push.call(fake.returnValues, this.returnValues[i]); push.call(fake.exceptions, this.exceptions[i]); push.call(fake.callIds, this.callIds[i]); } } createCallProperties.call(fake);
return fake; },
matches: function (args, strict) { var margs = this.matchingAguments;
if (margs.length <= args.length && sinon.deepEqual(margs, args.slice(0, margs.length))) { return !strict || margs.length == args.length; } },
printf: function (format) { var spy = this; var args = slice.call(arguments, 1); var formatter;
return (format || "").replace(/%(.)/g, function (match, specifyer) { formatter = spyApi.formatters[specifyer];
if (typeof formatter == "function") { return formatter.call(null, spy, args); } else if (!isNaN(parseInt(specifyer, 10))) { return sinon.format(args[specifyer - 1]); }
return "%" + specifyer; }); } };
function delegateToCalls(method, matchAny, actual, notCalled) { spyApi[method] = function () { if (!this.called) { if (notCalled) { return notCalled.apply(this, arguments); } return false; }
var currentCall; var matches = 0;
for (var i = 0, l = this.callCount; i < l; i += 1) { currentCall = this.getCall(i);
if (currentCall[actual || method].apply(currentCall, arguments)) { matches += 1;
if (matchAny) { return true; } } }
return matches === this.callCount; }; }
delegateToCalls("calledOn", true); delegateToCalls("alwaysCalledOn", false, "calledOn"); delegateToCalls("calledWith", true); delegateToCalls("calledWithMatch", true); delegateToCalls("alwaysCalledWith", false, "calledWith"); delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch"); delegateToCalls("calledWithExactly", true); delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly"); delegateToCalls("neverCalledWith", false, "notCalledWith", function () { return true; }); delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () { return true; }); delegateToCalls("threw", true); delegateToCalls("alwaysThrew", false, "threw"); delegateToCalls("returned", true); delegateToCalls("alwaysReturned", false, "returned"); delegateToCalls("calledWithNew", true); delegateToCalls("alwaysCalledWithNew", false, "calledWithNew"); delegateToCalls("callArg", false, "callArgWith", function () { throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); }); spyApi.callArgWith = spyApi.callArg; delegateToCalls("callArgOn", false, "callArgOnWith", function () { throw new Error(this.toString() + " cannot call arg since it was not yet invoked."); }); spyApi.callArgOnWith = spyApi.callArgOn; delegateToCalls("yield", false, "yield", function () { throw new Error(this.toString() + " cannot yield since it was not yet invoked."); }); // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
spyApi.invokeCallback = spyApi.yield; delegateToCalls("yieldOn", false, "yieldOn", function () { throw new Error(this.toString() + " cannot yield since it was not yet invoked."); }); delegateToCalls("yieldTo", false, "yieldTo", function (property) { throw new Error(this.toString() + " cannot yield to '" + property + "' since it was not yet invoked."); }); delegateToCalls("yieldToOn", false, "yieldToOn", function (property) { throw new Error(this.toString() + " cannot yield to '" + property + "' since it was not yet invoked."); });
spyApi.formatters = { "c": function (spy) { return sinon.timesInWords(spy.callCount); },
"n": function (spy) { return spy.toString(); },
"C": function (spy) { var calls = [];
for (var i = 0, l = spy.callCount; i < l; ++i) { var stringifiedCall = " " + spy.getCall(i).toString(); if (/\n/.test(calls[i - 1])) { stringifiedCall = "\n" + stringifiedCall; } push.call(calls, stringifiedCall); }
return calls.length > 0 ? "\n" + calls.join("\n") : ""; },
"t": function (spy) { var objects = [];
for (var i = 0, l = spy.callCount; i < l; ++i) { push.call(objects, sinon.format(spy.thisValues[i])); }
return objects.join(", "); },
"*": function (spy, args) { var formatted = [];
for (var i = 0, l = args.length; i < l; ++i) { push.call(formatted, sinon.format(args[i])); }
return formatted.join(", "); } };
sinon.extend(spy, spyApi);
spy.spyCall = sinon.spyCall;
if (commonJSModule) { module.exports = spy; } else { sinon.spy = spy; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js */ /*jslint eqeqeq: false, onevar: false*/ /*global module, require, sinon, process, setImmediate, setTimeout*/ /** * Stub behavior * * @author Christian Johansen (christian@cjohansen.no) * @author Tim Fischbach (mail@timfischbach.de) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
var slice = Array.prototype.slice; var join = Array.prototype.join; var proto;
var nextTick = (function () { if (typeof process === "object" && typeof process.nextTick === "function") { return process.nextTick; } else if (typeof setImmediate === "function") { return setImmediate; } else { return function (callback) { setTimeout(callback, 0); }; } })();
function throwsException(error, message) { if (typeof error == "string") { this.exception = new Error(message || ""); this.exception.name = error; } else if (!error) { this.exception = new Error("Error"); } else { this.exception = error; }
return this; }
function getCallback(behavior, args) { var callArgAt = behavior.callArgAt;
if (callArgAt < 0) { var callArgProp = behavior.callArgProp;
for (var i = 0, l = args.length; i < l; ++i) { if (!callArgProp && typeof args[i] == "function") { return args[i]; }
if (callArgProp && args[i] && typeof args[i][callArgProp] == "function") { return args[i][callArgProp]; } }
return null; }
return args[callArgAt]; }
function getCallbackError(behavior, func, args) { if (behavior.callArgAt < 0) { var msg;
if (behavior.callArgProp) { msg = sinon.functionName(behavior.stub) + " expected to yield to '" + behavior.callArgProp + "', but no object with such a property was passed."; } else { msg = sinon.functionName(behavior.stub) + " expected to yield, but no callback was passed."; }
if (args.length > 0) { msg += " Received [" + join.call(args, ", ") + "]"; }
return msg; }
return "argument at index " + behavior.callArgAt + " is not a function: " + func; }
function callCallback(behavior, args) { if (typeof behavior.callArgAt == "number") { var func = getCallback(behavior, args);
if (typeof func != "function") { throw new TypeError(getCallbackError(behavior, func, args)); }
if (behavior.callbackAsync) { nextTick(function() { func.apply(behavior.callbackContext, behavior.callbackArguments); }); } else { func.apply(behavior.callbackContext, behavior.callbackArguments); } } }
proto = { create: function(stub) { var behavior = sinon.extend({}, sinon.behavior); delete behavior.create; behavior.stub = stub;
return behavior; },
isPresent: function() { return (typeof this.callArgAt == 'number' || this.exception || typeof this.returnArgAt == 'number' || this.returnThis || this.returnValueDefined); },
invoke: function(context, args) { callCallback(this, args);
if (this.exception) { throw this.exception; } else if (typeof this.returnArgAt == 'number') { return args[this.returnArgAt]; } else if (this.returnThis) { return context; }
return this.returnValue; },
onCall: function(index) { return this.stub.onCall(index); },
onFirstCall: function() { return this.stub.onFirstCall(); },
onSecondCall: function() { return this.stub.onSecondCall(); },
onThirdCall: function() { return this.stub.onThirdCall(); },
withArgs: function(/* arguments */) { throw new Error('Defining a stub by invoking "stub.onCall(...).withArgs(...)" is not supported. ' + 'Use "stub.withArgs(...).onCall(...)" to define sequential behavior for calls with certain arguments.'); },
callsArg: function callsArg(pos) { if (typeof pos != "number") { throw new TypeError("argument index is not number"); }
this.callArgAt = pos; this.callbackArguments = []; this.callbackContext = undefined; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
callsArgOn: function callsArgOn(pos, context) { if (typeof pos != "number") { throw new TypeError("argument index is not number"); } if (typeof context != "object") { throw new TypeError("argument context is not an object"); }
this.callArgAt = pos; this.callbackArguments = []; this.callbackContext = context; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
callsArgWith: function callsArgWith(pos) { if (typeof pos != "number") { throw new TypeError("argument index is not number"); }
this.callArgAt = pos; this.callbackArguments = slice.call(arguments, 1); this.callbackContext = undefined; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
callsArgOnWith: function callsArgWith(pos, context) { if (typeof pos != "number") { throw new TypeError("argument index is not number"); } if (typeof context != "object") { throw new TypeError("argument context is not an object"); }
this.callArgAt = pos; this.callbackArguments = slice.call(arguments, 2); this.callbackContext = context; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
yields: function () { this.callArgAt = -1; this.callbackArguments = slice.call(arguments, 0); this.callbackContext = undefined; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
yieldsOn: function (context) { if (typeof context != "object") { throw new TypeError("argument context is not an object"); }
this.callArgAt = -1; this.callbackArguments = slice.call(arguments, 1); this.callbackContext = context; this.callArgProp = undefined; this.callbackAsync = false;
return this; },
yieldsTo: function (prop) { this.callArgAt = -1; this.callbackArguments = slice.call(arguments, 1); this.callbackContext = undefined; this.callArgProp = prop; this.callbackAsync = false;
return this; },
yieldsToOn: function (prop, context) { if (typeof context != "object") { throw new TypeError("argument context is not an object"); }
this.callArgAt = -1; this.callbackArguments = slice.call(arguments, 2); this.callbackContext = context; this.callArgProp = prop; this.callbackAsync = false;
return this; },
"throws": throwsException, throwsException: throwsException,
returns: function returns(value) { this.returnValue = value; this.returnValueDefined = true;
return this; },
returnsArg: function returnsArg(pos) { if (typeof pos != "number") { throw new TypeError("argument index is not number"); }
this.returnArgAt = pos;
return this; },
returnsThis: function returnsThis() { this.returnThis = true;
return this; } };
// create asynchronous versions of callsArg* and yields* methods
for (var method in proto) { // need to avoid creating anotherasync versions of the newly added async methods
if (proto.hasOwnProperty(method) && method.match(/^(callsArg|yields)/) && !method.match(/Async/)) { proto[method + 'Async'] = (function (syncFnName) { return function () { var result = this[syncFnName].apply(this, arguments); this.callbackAsync = true; return result; }; })(method); } }
if (commonJSModule) { module.exports = proto; } else { sinon.behavior = proto; } }(typeof sinon == "object" && sinon || null)); /** * @depend ../sinon.js * @depend spy.js * @depend behavior.js */ /*jslint eqeqeq: false, onevar: false*/ /*global module, require, sinon*/ /** * Stub functions * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function stub(object, property, func) { if (!!func && typeof func != "function") { throw new TypeError("Custom stub should be function"); }
var wrapper;
if (func) { wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; } else { wrapper = stub.create(); }
if (!object && typeof property === "undefined") { return sinon.stub.create(); }
if (typeof property === "undefined" && typeof object == "object") { for (var prop in object) { if (typeof object[prop] === "function") { stub(object, prop); } }
return object; }
return sinon.wrapMethod(object, property, wrapper); }
function getDefaultBehavior(stub) { return stub.defaultBehavior || getParentBehaviour(stub) || sinon.behavior.create(stub); }
function getParentBehaviour(stub) { return (stub.parent && getCurrentBehavior(stub.parent)); }
function getCurrentBehavior(stub) { var behavior = stub.behaviors[stub.callCount - 1]; return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stub); }
var uuid = 0;
sinon.extend(stub, (function () { var proto = { create: function create() { var functionStub = function () { return getCurrentBehavior(functionStub).invoke(this, arguments); };
functionStub.id = "stub#" + uuid++; var orig = functionStub; functionStub = sinon.spy.create(functionStub); functionStub.func = orig;
sinon.extend(functionStub, stub); functionStub._create = sinon.stub.create; functionStub.displayName = "stub"; functionStub.toString = sinon.functionToString;
functionStub.defaultBehavior = null; functionStub.behaviors = [];
return functionStub; },
resetBehavior: function () { var i;
this.defaultBehavior = null; this.behaviors = [];
delete this.returnValue; delete this.returnArgAt; this.returnThis = false;
if (this.fakes) { for (i = 0; i < this.fakes.length; i++) { this.fakes[i].resetBehavior(); } } },
onCall: function(index) { if (!this.behaviors[index]) { this.behaviors[index] = sinon.behavior.create(this); }
return this.behaviors[index]; },
onFirstCall: function() { return this.onCall(0); },
onSecondCall: function() { return this.onCall(1); },
onThirdCall: function() { return this.onCall(2); } };
for (var method in sinon.behavior) { if (sinon.behavior.hasOwnProperty(method) && !proto.hasOwnProperty(method) && method != 'create' && method != 'withArgs' && method != 'invoke') { proto[method] = (function(behaviorMethod) { return function() { this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this); this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments); return this; }; }(method)); } }
return proto; }()));
if (commonJSModule) { module.exports = stub; } else { sinon.stub = stub; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend stub.js */ /*jslint eqeqeq: false, onevar: false, nomen: false*/ /*global module, require, sinon*/ /** * Mock functions. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports; var push = [].push; var match;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
match = sinon.match;
if (!match && commonJSModule) { match = require("./match"); }
function mock(object) { if (!object) { return sinon.expectation.create("Anonymous mock"); }
return mock.create(object); }
sinon.mock = mock;
sinon.extend(mock, (function () { function each(collection, callback) { if (!collection) { return; }
for (var i = 0, l = collection.length; i < l; i += 1) { callback(collection[i]); } }
return { create: function create(object) { if (!object) { throw new TypeError("object is null"); }
var mockObject = sinon.extend({}, mock); mockObject.object = object; delete mockObject.create;
return mockObject; },
expects: function expects(method) { if (!method) { throw new TypeError("method is falsy"); }
if (!this.expectations) { this.expectations = {}; this.proxies = []; }
if (!this.expectations[method]) { this.expectations[method] = []; var mockObject = this;
sinon.wrapMethod(this.object, method, function () { return mockObject.invokeMethod(method, this, arguments); });
push.call(this.proxies, method); }
var expectation = sinon.expectation.create(method); push.call(this.expectations[method], expectation);
return expectation; },
restore: function restore() { var object = this.object;
each(this.proxies, function (proxy) { if (typeof object[proxy].restore == "function") { object[proxy].restore(); } }); },
verify: function verify() { var expectations = this.expectations || {}; var messages = [], met = [];
each(this.proxies, function (proxy) { each(expectations[proxy], function (expectation) { if (!expectation.met()) { push.call(messages, expectation.toString()); } else { push.call(met, expectation.toString()); } }); });
this.restore();
if (messages.length > 0) { sinon.expectation.fail(messages.concat(met).join("\n")); } else { sinon.expectation.pass(messages.concat(met).join("\n")); }
return true; },
invokeMethod: function invokeMethod(method, thisValue, args) { var expectations = this.expectations && this.expectations[method]; var length = expectations && expectations.length || 0, i;
for (i = 0; i < length; i += 1) { if (!expectations[i].met() && expectations[i].allowsCall(thisValue, args)) { return expectations[i].apply(thisValue, args); } }
var messages = [], available, exhausted = 0;
for (i = 0; i < length; i += 1) { if (expectations[i].allowsCall(thisValue, args)) { available = available || expectations[i]; } else { exhausted += 1; } push.call(messages, " " + expectations[i].toString()); }
if (exhausted === 0) { return available.apply(thisValue, args); }
messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ proxy: method, args: args }));
sinon.expectation.fail(messages.join("\n")); } }; }()));
var times = sinon.timesInWords;
sinon.expectation = (function () { var slice = Array.prototype.slice; var _invoke = sinon.spy.invoke;
function callCountInWords(callCount) { if (callCount == 0) { return "never called"; } else { return "called " + times(callCount); } }
function expectedCallCountInWords(expectation) { var min = expectation.minCalls; var max = expectation.maxCalls;
if (typeof min == "number" && typeof max == "number") { var str = times(min);
if (min != max) { str = "at least " + str + " and at most " + times(max); }
return str; }
if (typeof min == "number") { return "at least " + times(min); }
return "at most " + times(max); }
function receivedMinCalls(expectation) { var hasMinLimit = typeof expectation.minCalls == "number"; return !hasMinLimit || expectation.callCount >= expectation.minCalls; }
function receivedMaxCalls(expectation) { if (typeof expectation.maxCalls != "number") { return false; }
return expectation.callCount == expectation.maxCalls; }
function verifyMatcher(possibleMatcher, arg){ if (match && match.isMatcher(possibleMatcher)) { return possibleMatcher.test(arg); } else { return true; } }
return { minCalls: 1, maxCalls: 1,
create: function create(methodName) { var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); delete expectation.create; expectation.method = methodName;
return expectation; },
invoke: function invoke(func, thisValue, args) { this.verifyCallAllowed(thisValue, args);
return _invoke.apply(this, arguments); },
atLeast: function atLeast(num) { if (typeof num != "number") { throw new TypeError("'" + num + "' is not number"); }
if (!this.limitsSet) { this.maxCalls = null; this.limitsSet = true; }
this.minCalls = num;
return this; },
atMost: function atMost(num) { if (typeof num != "number") { throw new TypeError("'" + num + "' is not number"); }
if (!this.limitsSet) { this.minCalls = null; this.limitsSet = true; }
this.maxCalls = num;
return this; },
never: function never() { return this.exactly(0); },
once: function once() { return this.exactly(1); },
twice: function twice() { return this.exactly(2); },
thrice: function thrice() { return this.exactly(3); },
exactly: function exactly(num) { if (typeof num != "number") { throw new TypeError("'" + num + "' is not a number"); }
this.atLeast(num); return this.atMost(num); },
met: function met() { return !this.failed && receivedMinCalls(this); },
verifyCallAllowed: function verifyCallAllowed(thisValue, args) { if (receivedMaxCalls(this)) { this.failed = true; sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); }
if ("expectedThis" in this && this.expectedThis !== thisValue) { sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + this.expectedThis); }
if (!("expectedArguments" in this)) { return; }
if (!args) { sinon.expectation.fail(this.method + " received no arguments, expected " + sinon.format(this.expectedArguments)); }
if (args.length < this.expectedArguments.length) { sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) + "), expected " + sinon.format(this.expectedArguments)); }
if (this.expectsExactArgCount && args.length != this.expectedArguments.length) { sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) + "), expected " + sinon.format(this.expectedArguments)); }
for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
if (!verifyMatcher(this.expectedArguments[i],args[i])) { sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + ", didn't match " + this.expectedArguments.toString()); }
if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) + ", expected " + sinon.format(this.expectedArguments)); } } },
allowsCall: function allowsCall(thisValue, args) { if (this.met() && receivedMaxCalls(this)) { return false; }
if ("expectedThis" in this && this.expectedThis !== thisValue) { return false; }
if (!("expectedArguments" in this)) { return true; }
args = args || [];
if (args.length < this.expectedArguments.length) { return false; }
if (this.expectsExactArgCount && args.length != this.expectedArguments.length) { return false; }
for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { if (!verifyMatcher(this.expectedArguments[i],args[i])) { return false; }
if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { return false; } }
return true; },
withArgs: function withArgs() { this.expectedArguments = slice.call(arguments); return this; },
withExactArgs: function withExactArgs() { this.withArgs.apply(this, arguments); this.expectsExactArgCount = true; return this; },
on: function on(thisValue) { this.expectedThis = thisValue; return this; },
toString: function () { var args = (this.expectedArguments || []).slice();
if (!this.expectsExactArgCount) { push.call(args, "[...]"); }
var callStr = sinon.spyCall.toString.call({ proxy: this.method || "anonymous mock expectation", args: args });
var message = callStr.replace(", [...", "[, ...") + " " + expectedCallCountInWords(this);
if (this.met()) { return "Expectation met: " + message; }
return "Expected " + message + " (" + callCountInWords(this.callCount) + ")"; },
verify: function verify() { if (!this.met()) { sinon.expectation.fail(this.toString()); } else { sinon.expectation.pass(this.toString()); }
return true; },
pass: function(message) { sinon.assert.pass(message); }, fail: function (message) { var exception = new Error(message); exception.name = "ExpectationError";
throw exception; } }; }());
if (commonJSModule) { module.exports = mock; } else { sinon.mock = mock; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend stub.js * @depend mock.js */ /*jslint eqeqeq: false, onevar: false, forin: true*/ /*global module, require, sinon*/ /** * Collections of stubs, spies and mocks. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports; var push = [].push; var hasOwnProperty = Object.prototype.hasOwnProperty;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function getFakes(fakeCollection) { if (!fakeCollection.fakes) { fakeCollection.fakes = []; }
return fakeCollection.fakes; }
function each(fakeCollection, method) { var fakes = getFakes(fakeCollection);
for (var i = 0, l = fakes.length; i < l; i += 1) { if (typeof fakes[i][method] == "function") { fakes[i][method](); } } }
function compact(fakeCollection) { var fakes = getFakes(fakeCollection); var i = 0; while (i < fakes.length) { fakes.splice(i, 1); } }
var collection = { verify: function resolve() { each(this, "verify"); },
restore: function restore() { each(this, "restore"); compact(this); },
verifyAndRestore: function verifyAndRestore() { var exception;
try { this.verify(); } catch (e) { exception = e; }
this.restore();
if (exception) { throw exception; } },
add: function add(fake) { push.call(getFakes(this), fake); return fake; },
spy: function spy() { return this.add(sinon.spy.apply(sinon, arguments)); },
stub: function stub(object, property, value) { if (property) { var original = object[property];
if (typeof original != "function") { if (!hasOwnProperty.call(object, property)) { throw new TypeError("Cannot stub non-existent own property " + property); }
object[property] = value;
return this.add({ restore: function () { object[property] = original; } }); } } if (!property && !!object && typeof object == "object") { var stubbedObj = sinon.stub.apply(sinon, arguments);
for (var prop in stubbedObj) { if (typeof stubbedObj[prop] === "function") { this.add(stubbedObj[prop]); } }
return stubbedObj; }
return this.add(sinon.stub.apply(sinon, arguments)); },
mock: function mock() { return this.add(sinon.mock.apply(sinon, arguments)); },
inject: function inject(obj) { var col = this;
obj.spy = function () { return col.spy.apply(col, arguments); };
obj.stub = function () { return col.stub.apply(col, arguments); };
obj.mock = function () { return col.mock.apply(col, arguments); };
return obj; } };
if (commonJSModule) { module.exports = collection; } else { sinon.collection = collection; } }(typeof sinon == "object" && sinon || null));
/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/ /*global module, require, window*/ /** * Fake timer API * setTimeout * setInterval * clearTimeout * clearInterval * tick * reset * Date * * Inspired by jsUnitMockTimeOut from JsUnit * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
if (typeof sinon == "undefined") { var sinon = {}; }
(function (global) { // node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
// browsers, a number.
// see https://github.com/cjohansen/Sinon.JS/pull/436
var timeoutResult = setTimeout(function() {}, 0); var addTimerReturnsObject = typeof timeoutResult === 'object'; clearTimeout(timeoutResult);
var id = 1;
function addTimer(args, recurring) { if (args.length === 0) { throw new Error("Function requires at least 1 parameter"); }
if (typeof args[0] === "undefined") { throw new Error("Callback must be provided to timer calls"); }
var toId = id++; var delay = args[1] || 0;
if (!this.timeouts) { this.timeouts = {}; }
this.timeouts[toId] = { id: toId, func: args[0], callAt: this.now + delay, invokeArgs: Array.prototype.slice.call(args, 2) };
if (recurring === true) { this.timeouts[toId].interval = delay; }
if (addTimerReturnsObject) { return { id: toId, ref: function() {}, unref: function() {} }; } else { return toId; } }
function parseTime(str) { if (!str) { return 0; }
var strings = str.split(":"); var l = strings.length, i = l; var ms = 0, parsed;
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { throw new Error("tick only understands numbers and 'h:m:s'"); }
while (i--) { parsed = parseInt(strings[i], 10);
if (parsed >= 60) { throw new Error("Invalid time " + str); }
ms += parsed * Math.pow(60, (l - i - 1)); }
return ms * 1000; }
function createObject(object) { var newObject;
if (Object.create) { newObject = Object.create(object); } else { var F = function () {}; F.prototype = object; newObject = new F(); }
newObject.Date.clock = newObject; return newObject; }
sinon.clock = { now: 0,
create: function create(now) { var clock = createObject(this);
if (typeof now == "number") { clock.now = now; }
if (!!now && typeof now == "object") { throw new TypeError("now should be milliseconds since UNIX epoch"); }
return clock; },
setTimeout: function setTimeout(callback, timeout) { return addTimer.call(this, arguments, false); },
clearTimeout: function clearTimeout(timerId) { if (!this.timeouts) { this.timeouts = []; }
if (timerId in this.timeouts) { delete this.timeouts[timerId]; } },
setInterval: function setInterval(callback, timeout) { return addTimer.call(this, arguments, true); },
clearInterval: function clearInterval(timerId) { this.clearTimeout(timerId); },
setImmediate: function setImmediate(callback) { var passThruArgs = Array.prototype.slice.call(arguments, 1);
return addTimer.call(this, [callback, 0].concat(passThruArgs), false); },
clearImmediate: function clearImmediate(timerId) { this.clearTimeout(timerId); },
tick: function tick(ms) { ms = typeof ms == "number" ? ms : parseTime(ms); var tickFrom = this.now, tickTo = this.now + ms, previous = this.now; var timer = this.firstTimerInRange(tickFrom, tickTo);
var firstException; while (timer && tickFrom <= tickTo) { if (this.timeouts[timer.id]) { tickFrom = this.now = timer.callAt; try { this.callTimer(timer); } catch (e) { firstException = firstException || e; } }
timer = this.firstTimerInRange(previous, tickTo); previous = tickFrom; }
this.now = tickTo;
if (firstException) { throw firstException; }
return this.now; },
firstTimerInRange: function (from, to) { var timer, smallest = null, originalTimer;
for (var id in this.timeouts) { if (this.timeouts.hasOwnProperty(id)) { if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) { continue; }
if (smallest === null || this.timeouts[id].callAt < smallest) { originalTimer = this.timeouts[id]; smallest = this.timeouts[id].callAt;
timer = { func: this.timeouts[id].func, callAt: this.timeouts[id].callAt, interval: this.timeouts[id].interval, id: this.timeouts[id].id, invokeArgs: this.timeouts[id].invokeArgs }; } } }
return timer || null; },
callTimer: function (timer) { if (typeof timer.interval == "number") { this.timeouts[timer.id].callAt += timer.interval; } else { delete this.timeouts[timer.id]; }
try { if (typeof timer.func == "function") { timer.func.apply(null, timer.invokeArgs); } else { eval(timer.func); } } catch (e) { var exception = e; }
if (!this.timeouts[timer.id]) { if (exception) { throw exception; } return; }
if (exception) { throw exception; } },
reset: function reset() { this.timeouts = {}; },
Date: (function () { var NativeDate = Date;
function ClockDate(year, month, date, hour, minute, second, ms) { // Defensive and verbose to avoid potential harm in passing
// explicit undefined when user does not pass argument
switch (arguments.length) { case 0: return new NativeDate(ClockDate.clock.now); case 1: return new NativeDate(year); case 2: return new NativeDate(year, month); case 3: return new NativeDate(year, month, date); case 4: return new NativeDate(year, month, date, hour); case 5: return new NativeDate(year, month, date, hour, minute); case 6: return new NativeDate(year, month, date, hour, minute, second); default: return new NativeDate(year, month, date, hour, minute, second, ms); } }
return mirrorDateProperties(ClockDate, NativeDate); }()) };
function mirrorDateProperties(target, source) { if (source.now) { target.now = function now() { return target.clock.now; }; } else { delete target.now; }
if (source.toSource) { target.toSource = function toSource() { return source.toSource(); }; } else { delete target.toSource; }
target.toString = function toString() { return source.toString(); };
target.prototype = source.prototype; target.parse = source.parse; target.UTC = source.UTC; target.prototype.toUTCString = source.prototype.toUTCString;
for (var prop in source) { if (source.hasOwnProperty(prop)) { target[prop] = source[prop]; } }
return target; }
var methods = ["Date", "setTimeout", "setInterval", "clearTimeout", "clearInterval"];
if (typeof global.setImmediate !== "undefined") { methods.push("setImmediate"); }
if (typeof global.clearImmediate !== "undefined") { methods.push("clearImmediate"); }
function restore() { var method;
for (var i = 0, l = this.methods.length; i < l; i++) { method = this.methods[i];
if (global[method].hadOwnProperty) { global[method] = this["_" + method]; } else { try { delete global[method]; } catch (e) {} } }
// Prevent multiple executions which will completely remove these props
this.methods = []; }
function stubGlobal(method, clock) { clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method); clock["_" + method] = global[method];
if (method == "Date") { var date = mirrorDateProperties(clock[method], global[method]); global[method] = date; } else { global[method] = function () { return clock[method].apply(clock, arguments); };
for (var prop in clock[method]) { if (clock[method].hasOwnProperty(prop)) { global[method][prop] = clock[method][prop]; } } }
global[method].clock = clock; }
sinon.useFakeTimers = function useFakeTimers(now) { var clock = sinon.clock.create(now); clock.restore = restore; clock.methods = Array.prototype.slice.call(arguments, typeof now == "number" ? 1 : 0);
if (clock.methods.length === 0) { clock.methods = methods; }
for (var i = 0, l = clock.methods.length; i < l; i++) { stubGlobal(clock.methods[i], clock); }
return clock; }; }(typeof global != "undefined" && typeof global !== "function" ? global : this));
sinon.timers = { setTimeout: setTimeout, clearTimeout: clearTimeout, setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined), clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined), setInterval: setInterval, clearInterval: clearInterval, Date: Date };
if (typeof module !== 'undefined' && module.exports) { module.exports = sinon; }
/*jslint eqeqeq: false, onevar: false*/ /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ /** * Minimal Event interface implementation * * Original implementation by Sven Fuchs: https://gist.github.com/995028
* Modifications and tests by Christian Johansen. * * @author Sven Fuchs (svenfuchs@artweb-design.de) * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2011 Sven Fuchs, Christian Johansen */
if (typeof sinon == "undefined") { this.sinon = {}; }
(function () { var push = [].push;
sinon.Event = function Event(type, bubbles, cancelable, target) { this.initEvent(type, bubbles, cancelable, target); };
sinon.Event.prototype = { initEvent: function(type, bubbles, cancelable, target) { this.type = type; this.bubbles = bubbles; this.cancelable = cancelable; this.target = target; },
stopPropagation: function () {},
preventDefault: function () { this.defaultPrevented = true; } };
sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) { this.initEvent(type, false, false, target); this.loaded = progressEventRaw.loaded || null; this.total = progressEventRaw.total || null; };
sinon.ProgressEvent.prototype = new sinon.Event();
sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent;
sinon.CustomEvent = function CustomEvent(type, customData, target) { this.initEvent(type, false, false, target); this.detail = customData.detail || null; };
sinon.CustomEvent.prototype = new sinon.Event();
sinon.CustomEvent.prototype.constructor = sinon.CustomEvent;
sinon.EventTarget = { addEventListener: function addEventListener(event, listener) { this.eventListeners = this.eventListeners || {}; this.eventListeners[event] = this.eventListeners[event] || []; push.call(this.eventListeners[event], listener); },
removeEventListener: function removeEventListener(event, listener) { var listeners = this.eventListeners && this.eventListeners[event] || [];
for (var i = 0, l = listeners.length; i < l; ++i) { if (listeners[i] == listener) { return listeners.splice(i, 1); } } },
dispatchEvent: function dispatchEvent(event) { var type = event.type; var listeners = this.eventListeners && this.eventListeners[type] || [];
for (var i = 0; i < listeners.length; i++) { if (typeof listeners[i] == "function") { listeners[i].call(this, event); } else { listeners[i].handleEvent(event); } }
return !!event.defaultPrevented; } }; }());
/** * @depend ../../sinon.js * @depend event.js */ /*jslint eqeqeq: false, onevar: false*/ /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ /** * Fake XMLHttpRequest object * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
// wrapper for global
(function(global) { if (typeof sinon === "undefined") { global.sinon = {}; }
var supportsProgress = typeof ProgressEvent !== "undefined"; var supportsCustomEvent = typeof CustomEvent !== "undefined"; sinon.xhr = { XMLHttpRequest: global.XMLHttpRequest }; var xhr = sinon.xhr; xhr.GlobalXMLHttpRequest = global.XMLHttpRequest; xhr.GlobalActiveXObject = global.ActiveXObject; xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined"; xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined"; xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false; xhr.supportsCORS = 'withCredentials' in (new sinon.xhr.GlobalXMLHttpRequest());
/*jsl:ignore*/ var unsafeHeaders = { "Accept-Charset": true, "Accept-Encoding": true, "Connection": true, "Content-Length": true, "Cookie": true, "Cookie2": true, "Content-Transfer-Encoding": true, "Date": true, "Expect": true, "Host": true, "Keep-Alive": true, "Referer": true, "TE": true, "Trailer": true, "Transfer-Encoding": true, "Upgrade": true, "User-Agent": true, "Via": true }; /*jsl:end*/
function FakeXMLHttpRequest() { this.readyState = FakeXMLHttpRequest.UNSENT; this.requestHeaders = {}; this.requestBody = null; this.status = 0; this.statusText = ""; this.upload = new UploadProgress(); if (sinon.xhr.supportsCORS) { this.withCredentials = false; }
var xhr = this; var events = ["loadstart", "load", "abort", "loadend"];
function addEventListener(eventName) { xhr.addEventListener(eventName, function (event) { var listener = xhr["on" + eventName];
if (listener && typeof listener == "function") { listener.call(this, event); } }); }
for (var i = events.length - 1; i >= 0; i--) { addEventListener(events[i]); }
if (typeof FakeXMLHttpRequest.onCreate == "function") { FakeXMLHttpRequest.onCreate(this); } }
// An upload object is created for each
// FakeXMLHttpRequest and allows upload
// events to be simulated using uploadProgress
// and uploadError.
function UploadProgress() { this.eventListeners = { "progress": [], "load": [], "abort": [], "error": [] } }
UploadProgress.prototype.addEventListener = function(event, listener) { this.eventListeners[event].push(listener); };
UploadProgress.prototype.removeEventListener = function(event, listener) { var listeners = this.eventListeners[event] || [];
for (var i = 0, l = listeners.length; i < l; ++i) { if (listeners[i] == listener) { return listeners.splice(i, 1); } } };
UploadProgress.prototype.dispatchEvent = function(event) { var listeners = this.eventListeners[event.type] || [];
for (var i = 0, listener; (listener = listeners[i]) != null; i++) { listener(event); } };
function verifyState(xhr) { if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { throw new Error("INVALID_STATE_ERR"); }
if (xhr.sendFlag) { throw new Error("INVALID_STATE_ERR"); } }
// filtering to enable a white-list version of Sinon FakeXhr,
// where whitelisted requests are passed through to real XHR
function each(collection, callback) { if (!collection) return; for (var i = 0, l = collection.length; i < l; i += 1) { callback(collection[i]); } } function some(collection, callback) { for (var index = 0; index < collection.length; index++) { if(callback(collection[index]) === true) return true; } return false; } // largest arity in XHR is 5 - XHR#open
var apply = function(obj,method,args) { switch(args.length) { case 0: return obj[method](); case 1: return obj[method](args[0]); case 2: return obj[method](args[0],args[1]); case 3: return obj[method](args[0],args[1],args[2]); case 4: return obj[method](args[0],args[1],args[2],args[3]); case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]); } };
FakeXMLHttpRequest.filters = []; FakeXMLHttpRequest.addFilter = function(fn) { this.filters.push(fn) }; var IE6Re = /MSIE 6/; FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) { var xhr = new sinon.xhr.workingXHR(); each(["open","setRequestHeader","send","abort","getResponseHeader", "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"], function(method) { fakeXhr[method] = function() { return apply(xhr,method,arguments); }; });
var copyAttrs = function(args) { each(args, function(attr) { try { fakeXhr[attr] = xhr[attr] } catch(e) { if(!IE6Re.test(navigator.userAgent)) throw e; } }); };
var stateChange = function() { fakeXhr.readyState = xhr.readyState; if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) { copyAttrs(["status","statusText"]); } if(xhr.readyState >= FakeXMLHttpRequest.LOADING) { copyAttrs(["responseText"]); } if(xhr.readyState === FakeXMLHttpRequest.DONE) { copyAttrs(["responseXML"]); } if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr }); }; if(xhr.addEventListener) { for(var event in fakeXhr.eventListeners) { if(fakeXhr.eventListeners.hasOwnProperty(event)) { each(fakeXhr.eventListeners[event],function(handler) { xhr.addEventListener(event, handler); }); } } xhr.addEventListener("readystatechange",stateChange); } else { xhr.onreadystatechange = stateChange; } apply(xhr,"open",xhrArgs); }; FakeXMLHttpRequest.useFilters = false;
function verifyRequestOpened(xhr) { if (xhr.readyState != FakeXMLHttpRequest.OPENED) { throw new Error("INVALID_STATE_ERR - " + xhr.readyState); } }
function verifyRequestSent(xhr) { if (xhr.readyState == FakeXMLHttpRequest.DONE) { throw new Error("Request done"); } }
function verifyHeadersReceived(xhr) { if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { throw new Error("No headers received"); } }
function verifyResponseBodyType(body) { if (typeof body != "string") { var error = new Error("Attempted to respond to fake XMLHttpRequest with " + body + ", which is not a string."); error.name = "InvalidBodyException"; throw error; } }
sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { async: true,
open: function open(method, url, async, username, password) { this.method = method; this.url = url; this.async = typeof async == "boolean" ? async : true; this.username = username; this.password = password; this.responseText = null; this.responseXML = null; this.requestHeaders = {}; this.sendFlag = false; if(sinon.FakeXMLHttpRequest.useFilters === true) { var xhrArgs = arguments; var defake = some(FakeXMLHttpRequest.filters,function(filter) { return filter.apply(this,xhrArgs) }); if (defake) { return sinon.FakeXMLHttpRequest.defake(this,arguments); } } this.readyStateChange(FakeXMLHttpRequest.OPENED); },
readyStateChange: function readyStateChange(state) { this.readyState = state;
if (typeof this.onreadystatechange == "function") { try { this.onreadystatechange(); } catch (e) { sinon.logError("Fake XHR onreadystatechange handler", e); } }
this.dispatchEvent(new sinon.Event("readystatechange"));
switch (this.readyState) { case FakeXMLHttpRequest.DONE: this.dispatchEvent(new sinon.Event("load", false, false, this)); this.dispatchEvent(new sinon.Event("loadend", false, false, this)); this.upload.dispatchEvent(new sinon.Event("load", false, false, this)); if (supportsProgress) { this.upload.dispatchEvent(new sinon.ProgressEvent('progress', {loaded: 100, total: 100})); } break; } },
setRequestHeader: function setRequestHeader(header, value) { verifyState(this);
if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { throw new Error("Refused to set unsafe header \"" + header + "\""); }
if (this.requestHeaders[header]) { this.requestHeaders[header] += "," + value; } else { this.requestHeaders[header] = value; } },
// Helps testing
setResponseHeaders: function setResponseHeaders(headers) { verifyRequestOpened(this); this.responseHeaders = {};
for (var header in headers) { if (headers.hasOwnProperty(header)) { this.responseHeaders[header] = headers[header]; } }
if (this.async) { this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); } else { this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED; } },
// Currently treats ALL data as a DOMString (i.e. no Document)
send: function send(data) { verifyState(this);
if (!/^(get|head)$/i.test(this.method)) { if (this.requestHeaders["Content-Type"]) { var value = this.requestHeaders["Content-Type"].split(";"); this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8"; } else { this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; }
this.requestBody = data; }
this.errorFlag = false; this.sendFlag = this.async; this.readyStateChange(FakeXMLHttpRequest.OPENED);
if (typeof this.onSend == "function") { this.onSend(this); }
this.dispatchEvent(new sinon.Event("loadstart", false, false, this)); },
abort: function abort() { this.aborted = true; this.responseText = null; this.errorFlag = true; this.requestHeaders = {};
if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) { this.readyStateChange(sinon.FakeXMLHttpRequest.DONE); this.sendFlag = false; }
this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
this.dispatchEvent(new sinon.Event("abort", false, false, this));
this.upload.dispatchEvent(new sinon.Event("abort", false, false, this));
if (typeof this.onerror === "function") { this.onerror(); } },
getResponseHeader: function getResponseHeader(header) { if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { return null; }
if (/^Set-Cookie2?$/i.test(header)) { return null; }
header = header.toLowerCase();
for (var h in this.responseHeaders) { if (h.toLowerCase() == header) { return this.responseHeaders[h]; } }
return null; },
getAllResponseHeaders: function getAllResponseHeaders() { if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { return ""; }
var headers = "";
for (var header in this.responseHeaders) { if (this.responseHeaders.hasOwnProperty(header) && !/^Set-Cookie2?$/i.test(header)) { headers += header + ": " + this.responseHeaders[header] + "\r\n"; } }
return headers; },
setResponseBody: function setResponseBody(body) { verifyRequestSent(this); verifyHeadersReceived(this); verifyResponseBodyType(body);
var chunkSize = this.chunkSize || 10; var index = 0; this.responseText = "";
do { if (this.async) { this.readyStateChange(FakeXMLHttpRequest.LOADING); }
this.responseText += body.substring(index, index + chunkSize); index += chunkSize; } while (index < body.length);
var type = this.getResponseHeader("Content-Type");
if (this.responseText && (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { try { this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); } catch (e) { // Unable to parse XML - no biggie
} }
if (this.async) { this.readyStateChange(FakeXMLHttpRequest.DONE); } else { this.readyState = FakeXMLHttpRequest.DONE; } },
respond: function respond(status, headers, body) { this.status = typeof status == "number" ? status : 200; this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; this.setResponseHeaders(headers || {}); this.setResponseBody(body || ""); },
uploadProgress: function uploadProgress(progressEventRaw) { if (supportsProgress) { this.upload.dispatchEvent(new sinon.ProgressEvent("progress", progressEventRaw)); } },
uploadError: function uploadError(error) { if (supportsCustomEvent) { this.upload.dispatchEvent(new sinon.CustomEvent("error", {"detail": error})); } } });
sinon.extend(FakeXMLHttpRequest, { UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4 });
// Borrowed from JSpec
FakeXMLHttpRequest.parseXML = function parseXML(text) { var xmlDoc;
if (typeof DOMParser != "undefined") { var parser = new DOMParser(); xmlDoc = parser.parseFromString(text, "text/xml"); } else { xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(text); }
return xmlDoc; };
FakeXMLHttpRequest.statusCodes = { 100: "Continue", 101: "Switching Protocols", 200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 300: "Multiple Choice", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 307: "Temporary Redirect", 400: "Bad Request", 401: "Unauthorized", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Request Entity Too Large", 414: "Request-URI Too Long", 415: "Unsupported Media Type", 416: "Requested Range Not Satisfiable", 417: "Expectation Failed", 422: "Unprocessable Entity", 500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported" };
sinon.useFakeXMLHttpRequest = function () { sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) { if (xhr.supportsXHR) { global.XMLHttpRequest = xhr.GlobalXMLHttpRequest; }
if (xhr.supportsActiveX) { global.ActiveXObject = xhr.GlobalActiveXObject; }
delete sinon.FakeXMLHttpRequest.restore;
if (keepOnCreate !== true) { delete sinon.FakeXMLHttpRequest.onCreate; } }; if (xhr.supportsXHR) { global.XMLHttpRequest = sinon.FakeXMLHttpRequest; }
if (xhr.supportsActiveX) { global.ActiveXObject = function ActiveXObject(objId) { if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
return new sinon.FakeXMLHttpRequest(); }
return new xhr.GlobalActiveXObject(objId); }; }
return sinon.FakeXMLHttpRequest; };
sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
})(typeof global === "object" ? global : this);
if (typeof module !== 'undefined' && module.exports) { module.exports = sinon; }
/** * @depend fake_xml_http_request.js */ /*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/ /*global module, require, window*/ /** * The Sinon "server" mimics a web server that receives requests from * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, * both synchronously and asynchronously. To respond synchronuously, canned * answers have to be provided upfront. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
if (typeof sinon == "undefined") { var sinon = {}; }
sinon.fakeServer = (function () { var push = [].push; function F() {}
function create(proto) { F.prototype = proto; return new F(); }
function responseArray(handler) { var response = handler;
if (Object.prototype.toString.call(handler) != "[object Array]") { response = [200, {}, handler]; }
if (typeof response[2] != "string") { throw new TypeError("Fake server response body should be string, but was " + typeof response[2]); }
return response; }
var wloc = typeof window !== "undefined" ? window.location : {}; var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
function matchOne(response, reqMethod, reqUrl) { var rmeth = response.method; var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase(); var url = response.url; var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
return matchMethod && matchUrl; }
function match(response, request) { var requestUrl = request.url;
if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { requestUrl = requestUrl.replace(rCurrLoc, ""); }
if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { if (typeof response.response == "function") { var ru = response.url; var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []); return response.response.apply(response, args); }
return true; }
return false; }
function log(response, request) { var str;
str = "Request:\n" + sinon.format(request) + "\n\n"; str += "Response:\n" + sinon.format(response) + "\n\n";
sinon.log(str); }
return { create: function () { var server = create(this); this.xhr = sinon.useFakeXMLHttpRequest(); server.requests = [];
this.xhr.onCreate = function (xhrObj) { server.addRequest(xhrObj); };
return server; },
addRequest: function addRequest(xhrObj) { var server = this; push.call(this.requests, xhrObj);
xhrObj.onSend = function () { server.handleRequest(this);
if (server.autoRespond && !server.responding) { setTimeout(function () { server.responding = false; server.respond(); }, server.autoRespondAfter || 10);
server.responding = true; } }; },
getHTTPMethod: function getHTTPMethod(request) { if (this.fakeHTTPMethods && /post/i.test(request.method)) { var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); return !!matches ? matches[1] : request.method; }
return request.method; },
handleRequest: function handleRequest(xhr) { if (xhr.async) { if (!this.queue) { this.queue = []; }
push.call(this.queue, xhr); } else { this.processRequest(xhr); } },
respondWith: function respondWith(method, url, body) { if (arguments.length == 1 && typeof method != "function") { this.response = responseArray(method); return; }
if (!this.responses) { this.responses = []; }
if (arguments.length == 1) { body = method; url = method = null; }
if (arguments.length == 2) { body = url; url = method; method = null; }
push.call(this.responses, { method: method, url: url, response: typeof body == "function" ? body : responseArray(body) }); },
respond: function respond() { if (arguments.length > 0) this.respondWith.apply(this, arguments); var queue = this.queue || []; var requests = queue.splice(0); var request;
while(request = requests.shift()) { this.processRequest(request); } },
processRequest: function processRequest(request) { try { if (request.aborted) { return; }
var response = this.response || [404, {}, ""];
if (this.responses) { for (var l = this.responses.length, i = l - 1; i >= 0; i--) { if (match.call(this, this.responses[i], request)) { response = this.responses[i].response; break; } } }
if (request.readyState != 4) { log(response, request);
request.respond(response[0], response[1], response[2]); } } catch (e) { sinon.logError("Fake server request processing", e); } },
restore: function restore() { return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); } }; }());
if (typeof module !== 'undefined' && module.exports) { module.exports = sinon; }
/** * @depend fake_server.js * @depend fake_timers.js */ /*jslint browser: true, eqeqeq: false, onevar: false*/ /*global sinon*/ /** * Add-on for sinon.fakeServer that automatically handles a fake timer along with * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, * it polls the object for completion with setInterval. Dispite the direct * motivation, there is nothing jQuery-specific in this file, so it can be used * in any environment where the ajax implementation depends on setInterval or * setTimeout. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function () { function Server() {} Server.prototype = sinon.fakeServer;
sinon.fakeServerWithClock = new Server();
sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { if (xhr.async) { if (typeof setTimeout.clock == "object") { this.clock = setTimeout.clock; } else { this.clock = sinon.useFakeTimers(); this.resetClock = true; }
if (!this.longestTimeout) { var clockSetTimeout = this.clock.setTimeout; var clockSetInterval = this.clock.setInterval; var server = this;
this.clock.setTimeout = function (fn, timeout) { server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
return clockSetTimeout.apply(this, arguments); };
this.clock.setInterval = function (fn, timeout) { server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
return clockSetInterval.apply(this, arguments); }; } }
return sinon.fakeServer.addRequest.call(this, xhr); };
sinon.fakeServerWithClock.respond = function respond() { var returnVal = sinon.fakeServer.respond.apply(this, arguments);
if (this.clock) { this.clock.tick(this.longestTimeout || 0); this.longestTimeout = 0;
if (this.resetClock) { this.clock.restore(); this.resetClock = false; } }
return returnVal; };
sinon.fakeServerWithClock.restore = function restore() { if (this.clock) { this.clock.restore(); }
return sinon.fakeServer.restore.apply(this, arguments); }; }());
/** * @depend ../sinon.js * @depend collection.js * @depend util/fake_timers.js * @depend util/fake_server_with_clock.js */ /*jslint eqeqeq: false, onevar: false, plusplus: false*/ /*global require, module*/ /** * Manages fake collections as well as fake utilities such as Sinon's * timers and fake XHR implementation in one convenient object. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
if (typeof module !== 'undefined' && module.exports) { var sinon = require("../sinon"); sinon.extend(sinon, require("./util/fake_timers")); }
(function () { var push = [].push;
function exposeValue(sandbox, config, key, value) { if (!value) { return; }
if (config.injectInto && !(key in config.injectInto)) { config.injectInto[key] = value; sandbox.injectedKeys.push(key); } else { push.call(sandbox.args, value); } }
function prepareSandboxFromConfig(config) { var sandbox = sinon.create(sinon.sandbox);
if (config.useFakeServer) { if (typeof config.useFakeServer == "object") { sandbox.serverPrototype = config.useFakeServer; }
sandbox.useFakeServer(); }
if (config.useFakeTimers) { if (typeof config.useFakeTimers == "object") { sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); } else { sandbox.useFakeTimers(); } }
return sandbox; }
sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { useFakeTimers: function useFakeTimers() { this.clock = sinon.useFakeTimers.apply(sinon, arguments);
return this.add(this.clock); },
serverPrototype: sinon.fakeServer,
useFakeServer: function useFakeServer() { var proto = this.serverPrototype || sinon.fakeServer;
if (!proto || !proto.create) { return null; }
this.server = proto.create(); return this.add(this.server); },
inject: function (obj) { sinon.collection.inject.call(this, obj);
if (this.clock) { obj.clock = this.clock; }
if (this.server) { obj.server = this.server; obj.requests = this.server.requests; }
return obj; },
restore: function () { sinon.collection.restore.apply(this, arguments); this.restoreContext(); },
restoreContext: function () { if (this.injectedKeys) { for (var i = 0, j = this.injectedKeys.length; i < j; i++) { delete this.injectInto[this.injectedKeys[i]]; } this.injectedKeys = []; } },
create: function (config) { if (!config) { return sinon.create(sinon.sandbox); }
var sandbox = prepareSandboxFromConfig(config); sandbox.args = sandbox.args || []; sandbox.injectedKeys = []; sandbox.injectInto = config.injectInto; var prop, value, exposed = sandbox.inject({});
if (config.properties) { for (var i = 0, l = config.properties.length; i < l; i++) { prop = config.properties[i]; value = exposed[prop] || prop == "sandbox" && sandbox; exposeValue(sandbox, config, prop, value); } } else { exposeValue(sandbox, config, "sandbox", value); }
return sandbox; } });
sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
if (typeof module !== 'undefined' && module.exports) { module.exports = sinon.sandbox; } }());
/** * @depend ../sinon.js * @depend stub.js * @depend mock.js * @depend sandbox.js */ /*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/ /*global module, require, sinon*/ /** * Test function, sandboxes fakes * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function test(callback) { var type = typeof callback;
if (type != "function") { throw new TypeError("sinon.test needs to wrap a test function, got " + type); }
return function () { var config = sinon.getConfig(sinon.config); config.injectInto = config.injectIntoThis && this || config.injectInto; var sandbox = sinon.sandbox.create(config); var exception, result; var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
try { result = callback.apply(this, args); } catch (e) { exception = e; }
if (typeof exception !== "undefined") { sandbox.restore(); throw exception; } else { sandbox.verifyAndRestore(); }
return result; }; }
test.config = { injectIntoThis: true, injectInto: null, properties: ["spy", "stub", "mock", "clock", "server", "requests"], useFakeTimers: true, useFakeServer: true };
if (commonJSModule) { module.exports = test; } else { sinon.test = test; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend test.js */ /*jslint eqeqeq: false, onevar: false, eqeqeq: false*/ /*global module, require, sinon*/ /** * Test case, sandboxes all test functions * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon) { var commonJSModule = typeof module !== 'undefined' && module.exports;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon || !Object.prototype.hasOwnProperty) { return; }
function createTest(property, setUp, tearDown) { return function () { if (setUp) { setUp.apply(this, arguments); }
var exception, result;
try { result = property.apply(this, arguments); } catch (e) { exception = e; }
if (tearDown) { tearDown.apply(this, arguments); }
if (exception) { throw exception; }
return result; }; }
function testCase(tests, prefix) { /*jsl:ignore*/ if (!tests || typeof tests != "object") { throw new TypeError("sinon.testCase needs an object with test functions"); } /*jsl:end*/
prefix = prefix || "test"; var rPrefix = new RegExp("^" + prefix); var methods = {}, testName, property, method; var setUp = tests.setUp; var tearDown = tests.tearDown;
for (testName in tests) { if (tests.hasOwnProperty(testName)) { property = tests[testName];
if (/^(setUp|tearDown)$/.test(testName)) { continue; }
if (typeof property == "function" && rPrefix.test(testName)) { method = property;
if (setUp || tearDown) { method = createTest(property, setUp, tearDown); }
methods[testName] = sinon.test(method); } else { methods[testName] = tests[testName]; } } }
return methods; }
if (commonJSModule) { module.exports = testCase; } else { sinon.testCase = testCase; } }(typeof sinon == "object" && sinon || null));
/** * @depend ../sinon.js * @depend stub.js */ /*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/ /*global module, require, sinon*/ /** * Assertions matching the test spy retrieval interface. * * @author Christian Johansen (christian@cjohansen.no) * @license BSD * * Copyright (c) 2010-2013 Christian Johansen */
(function (sinon, global) { var commonJSModule = typeof module !== "undefined" && module.exports; var slice = Array.prototype.slice; var assert;
if (!sinon && commonJSModule) { sinon = require("../sinon"); }
if (!sinon) { return; }
function verifyIsStub() { var method;
for (var i = 0, l = arguments.length; i < l; ++i) { method = arguments[i];
if (!method) { assert.fail("fake is not a spy"); }
if (typeof method != "function") { assert.fail(method + " is not a function"); }
if (typeof method.getCall != "function") { assert.fail(method + " is not stubbed"); } } }
function failAssertion(object, msg) { object = object || global; var failMethod = object.fail || assert.fail; failMethod.call(object, msg); }
function mirrorPropAsAssertion(name, method, message) { if (arguments.length == 2) { message = method; method = name; }
assert[name] = function (fake) { verifyIsStub(fake);
var args = slice.call(arguments, 1); var failed = false;
if (typeof method == "function") { failed = !method(fake); } else { failed = typeof fake[method] == "function" ? !fake[method].apply(fake, args) : !fake[method]; }
if (failed) { failAssertion(this, fake.printf.apply(fake, [message].concat(args))); } else { assert.pass(name); } }; }
function exposedName(prefix, prop) { return !prefix || /^fail/.test(prop) ? prop : prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); }
assert = { failException: "AssertError",
fail: function fail(message) { var error = new Error(message); error.name = this.failException || assert.failException;
throw error; },
pass: function pass(assertion) {},
callOrder: function assertCallOrder() { verifyIsStub.apply(null, arguments); var expected = "", actual = "";
if (!sinon.calledInOrder(arguments)) { try { expected = [].join.call(arguments, ", "); var calls = slice.call(arguments); var i = calls.length; while (i) { if (!calls[--i].called) { calls.splice(i, 1); } } actual = sinon.orderByFirstCall(calls).join(", "); } catch (e) { // If this fails, we'll just fall back to the blank string
}
failAssertion(this, "expected " + expected + " to be " + "called in order but were called as " + actual); } else { assert.pass("callOrder"); } },
callCount: function assertCallCount(method, count) { verifyIsStub(method);
if (method.callCount != count) { var msg = "expected %n to be called " + sinon.timesInWords(count) + " but was called %c%C"; failAssertion(this, method.printf(msg)); } else { assert.pass("callCount"); } },
expose: function expose(target, options) { if (!target) { throw new TypeError("target is null or undefined"); }
var o = options || {}; var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix; var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
for (var method in this) { if (method != "export" && (includeFail || !/^(fail)/.test(method))) { target[exposedName(prefix, method)] = this[method]; } }
return target; },
match: function match(actual, expectation) { var matcher = sinon.match(expectation); if (matcher.test(actual)) { assert.pass("match"); } else { var formatted = [ "expected value to match", " expected = " + sinon.format(expectation), " actual = " + sinon.format(actual) ] failAssertion(this, formatted.join("\n")); } } };
mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; }, "expected %n to not have been called but was called %c%C"); mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new"); mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new"); mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C"); mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C"); mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C"); mirrorPropAsAssertion("threw", "%n did not throw exception%C"); mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
if (commonJSModule) { module.exports = assert; } else { sinon.assert = assert; } }(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
return sinon;}.call(typeof window != 'undefined' && window || {}));
|