📜 ⬆️ ⬇️

Sinon.js - mock-library for JavaScript

Sinon.js is a JavaScript mock library that can be used with any test framework. It provides functions for emulating and testing the desired behavior in JavaScript. The library has three types of testing with spy, stub and mock. In this post, we will look at the Sinon.js API documentation along with a brief introduction to the concept of the methods of this library.



Spy


Spy is a function that records the arguments, the return value, the initial value, and the errors returned (if any) for all calls. Test spy can be an anonymous function or can be created on top of an existing function.

Test spies are useful to check callbacks and how certain functions / methods are used throughout the system during tests. The following simplified example shows how to use spies to test processing a callback function:
')
"test should call subscribers on publish": function () { var callback = sinon.spy(); PubSub.subscribe("message", callback); PubSub.publishSync("message"); assertTrue(callback.called); } 


Sinon.spy can also monitor existing functions. When executing the original function, it will behave just as normal, but you will have access to data about all the calls. The following is a slightly abstract example.

 { setUp: function () { sinon.spy(jQuery, "ajax"); }, tearDown: function () { jQuery.ajax.restore(); // Unwraps the spy }, "test should inspect jQuery.getJSON's usage of jQuery.ajax": function () { jQuery.getJSON("/some/resource"); assert(jQuery.ajax.calledOnce); assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url); assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType); } } 

Spyware creation: sinon.spy ()


var spy = sinon.spy();
Specifies an anonymous function that records the arguments of the current value, error messages, and return value arguments for all calls.
var spy = sinon.spy(myFunc);
Creates a spy on top of the finished function.
var spy = sinon.spy(object, "method");
Creates a spy for object.method and replaces the original method. It acts exactly the same as the original version in all cases. The original method can be restored using object.method.restore () .

Spy api


Spy provides a wide interface for controlling their use. The examples above showed the logical property calledOnce , as well as the getCall method and the arguments of the returned object. There are three ways to check call data.

The most preferred approach is to use the method calledWith , since it saves your test from unnecessary specifics. After all, you don’t always need to know how many times a function call was made; sometimes the return value data is enough.

 "test should call subscribers with message as first argument" : function () { var message = 'an example message'; var spy = sinon.spy(); PubSub.subscribe(message, spy); PubSub.publishSync(message, "some payload"); assert(spy.calledWith(message)); } 

If you want more specificity, you can directly check the first parameter of the first call. The following methods will help you do this:

 "test should call subscribers with message as first argument" : function () { var message = 'an example message'; var spy = sinon.spy(); PubSub.subscribe(message, spy); PubSub.publishSync(message, "some payload"); assertEquals(message, spy.args[0][0]); } 


 "test should call subscribers with message as first argument" : function () { var message = 'an example message'; var spy = sinon.spy(); PubSub.subscribe(message, spy); PubSub.publishSync(message, "some payload"); assertEquals(message, spy.getCall(0).args[0]); } 


The first example uses a two-dimensional array of arguments directly on the spy, while the second example selects the first value of the call, and then refers to its argument. Which way to use is a matter of preference, but it is recommended to use the spy.calledWith (arg1, arg2...) approach spy.calledWith (arg1, arg2...) if there is no need to make the tests more detailed.

Spy api
Spy object or object returned from sinon.spy() . When tracking existing methods with sinon.spy (object, method) , the following properties and methods are also available for use on object.method .

spy.withArgs(arg1[, arg2, ...]);
Creates a spy that records calls only when the received arguments match those passed for withArgs .

 "should call method once with each argument": function () { var object = { method: function () {} }; var spy = sinon.spy(object, "method"); spy.withArgs(42); spy.withArgs(1); object.method(42); object.method(1); assert(spy.withArgs(42).calledOnce); assert(spy.withArgs(1).calledOnce); } 

spy.callCount
Displays the number of calls registered.
spy.called
Provides confirmation if spy was called at least once.
spy.calledOnce
Provides confirmation if spy was called only once.
spy.calledTwice
Provides confirmation if spy was called only two times.
spy.calledThrice
Provides confirmation if spy was called only three times.
spy.firstCall
First call
spy.secondCall
Second call
spy.thirdCall
The third challenge.
spy.lastCall
Last call.
spy.calledBefore(anotherSpy);
Gives confirmation if the spy was called before anotherSpy .
spy.calledAfter(anotherSpy);
Gives confirmation if the spy was called after anotherSpy .
spy.calledOn(obj);
Gives confirmation if the spy was called at least once with obj as this.
spy.alwaysCalledOn(obj);
Returns a confirmation if the spy is always called with obj like this.
spy.calledWith(arg1, arg2, ...);
Gives confirmation if the spy is called at least once with the arguments provided. It can be executed even in case of partial coincidence, Sinon checks only the arguments provided against the actual arguments, thus getting a match with the provided arguments (in the same places) will issue a confirmation.
spy.alwaysCalledWith(arg1, arg2, ...);
Gives confirmation if the spy is always called with the provided arguments (and partly with others).
spy.alwaysCalledWithExactly(arg1, arg2, ...);
Gives confirmation if the spy is always called in full compliance with the arguments provided.
spy.calledWithMatch(arg1, arg2, ...);
Gives confirmation if the spy is always called with the provided arguments (and partly with others). It behaves just like spy.calledWith(sinon.match(arg1), sinon.match(arg2), ...) .
spy.alwaysCalledWithMatch(arg1, arg2, ...);
Gives confirmation if the spy is always called with the provided arguments. It behaves just like spy.alwaysCalledWith(sinon.match(arg1), sinon.match(arg2), ...) .
spy.calledWithNew();
Gives confirmation if spy / stub was called by a new operator. Remember that this is a result based on the value of this object and the prototype of the spy function; this can give a false positive result if you actively return the correct object type.
spy.neverCalledWith(arg1, arg2, ...);
Displays if spy / stub has never been called with the provided arguments.
spy.neverCalledWithMatch(arg1, arg2, ...);
Gives confirmation if spy / stub has never been called with a large value provided by the arguments. It behaves just like spy.neverCalledWith(sinon.match(arg1), sinon.match(arg2), ...). .
spy.threw();
Gives confirmation if the spy issued an exception at least once.
spy.threw("TypeError");
Gives confirmation if the spy has thrown an exception for the specified type, at least once.
spy.threw(obj);
Gives confirmation if the spy has thrown an exception for the specified object at least once.
spy.alwaysThrew();
Gives confirmation if the spy always threw an exception.
spy.alwaysThrew("TypeError");
Gives confirmation if the spy always throws an exception for the specified type.
spy.alwaysThrew(obj);
Returns a confirmation if the spy always throws exceptions for the specified object.
spy.returned(obj);
Returns a confirmation if the spy returns the specified value at least once. Uses deep comparison for objects and arrays. Use spy.returned (sinon.match.same (obj)) for a strict comparison.
spy.alwaysReturned(obj);
Gives confirmation if the spy always returns the specified value.
var spyCall = spy.getCall(n);
Displays the nth [call] (# spycall). Provides access to individual calls, helps to more closely check the behavior if the spy is called more than once. Example:

 sinon.spy(jQuery, "ajax"); jQuery.ajax("/stuffs"); var spyCall = jQuery.ajax.getCall(0); assertEquals("/stuffs", spyCall.args[0]); 

spy.thisValues
The area for the spy.thisValues[0] object represents the object for the first call.
spy.args
The argument array spy.args [0] is an array with the arguments obtained in the first call.
spy.exceptions
The array for issued exceptions spy.exceptions[0] presents the exceptions issued by the first call. If the call did not generate an error, the value in the .exceptions field will be 'undefined' .
spy.returnValues
The array of returned values, spy.returnValues[0] is the return value of the first call. If the call returned an .returnValues field value in .returnValues would be 'undefined' .
spy.reset()
Resets the spy status.
spy.printf(format string", [arg1, arg2, ...])`
Returns the transmitted sequence format with the following substitutions:
  • #n: spy name ("spy" by default)
  • #s: how many times the spy was called in words (“once”, “twice”, etc.)
  • #C: list of consecutive spy calls, each call is marked with a new line prefix and four spaces
  • #t: a comma delimited list of values ​​with which the spy was called
  • #: n formatted value of the nth argument passed to printf
  • # *: a comma-separated list of (non-formatted sequences) arguments passed to printf

Individual spy calls


var spyCall = spy.getCall(n)
Provides nth [call](#spycall) . Access to individual calls helps with more detailed behavioral testing when a spy is called several times. Example:

 sinon.spy(jQuery, "ajax"); jQuery.ajax("/stuffs"); var spyCall = jQuery.ajax.getCall(0); assertEquals("/stuffs", spyCall.args[0]); 

spyCall.calledOn(obj);
Provides confirmation if the object was relevant for this call.
spyCall.calledWith(arg1, arg2, ...);
Provides confirmation if the call received the provided arguments (possibly similar).
spyCall.calledWithExactly(arg1, arg2, ...);
Provides confirmation if the call received the provided arguments and no other.
spyCall.calledWithMatch(arg1, arg2, ...);
Provides confirmation if the call received the provided arguments (possibly similar). It behaves just like spyCall.calledWith (sinon.match (arg1), sinon.match (arg2)...).
spyCall.notCalledWith(arg1, arg2, ...);
Provides acknowledgment if the call did not receive the provided arguments.
spyCall.notCalledWithMatch(arg1, arg2, ...);
Provides acknowledgment if the call did not receive the provided arguments. It behaves just like spyCall.notCalledWith(sinon.match(arg1), sinon.match(arg2), ...).;
spyCall.threw();
Provides confirmation if the call issued an error.
spyCall.threw(TypeError");
Provides confirmation if the call issued an error of the specified type.
spyCall.threw(obj);
Provides confirmation if the call issued an error for the intended object.
spyCall.thisValue
Challenges to the value of this .
spyCall.args
Array of arguments received.
spyCall.exception
The issued exception (error), if any.
spyCall.returnValue
Return value.

Stubs


Stubs (stubs) are functions with preprogrammed behavior. They fully support the spyware API in addition to methods that can be used to change the behavior of stubs.

Like spies, stubs can be anonymous or wrap existing functions. When wrapping an existing function with a cap, the original function is not called.

Use stubs is worth when you want:
  1. Control the behavior of the test and force the code to issue an error at some point Examples include forcing error output methods to verify its processing.
  2. If you want to prevent an immediate call to a specific method (perhaps because it causes undesirable behavior, for example, XMLHttpRequest or similar).

The following example is another PubSubJS test by Morgan Roderick, which shows how to create an anonymous stub that gives an error when called.

 "test should call all subscribers, even if there are exceptions" : function(){ var message = 'an example message'; var error = 'an example error message'; var stub = sinon.stub().throws(); var spy1 = sinon.spy(); var spy2 = sinon.spy(); PubSub.subscribe(message, stub); PubSub.subscribe(message, spy1); PubSub.subscribe(message, spy2); PubSub.publishSync(message, undefined); assert(spy1.called); assert(spy2.called); assert(stub.calledBefore(spy1)); } 

Please note that the plugs are also included in the spy interface. The test verifies that all necessary callbacks were called and the stub produced an error for the current call before the new call.

Determining the behavior of stubs on consecutive calls

Repeated calls to certain behaviors, such as returns or throws override the stub behavior. Starting with Sinon 1.8, you can use the <codeonCall </ code method to get a different response from a stub on consecutive calls.

Note that in Sinon versions 1.5 and 1.7, multiple calls for yields* and callsArg* are a family of methods for defining a behavioral series during successive calls.

Stub API
var stub = sinon.stub();
Creates an anonymous stub function.
var stub = sinon.stub(object, "method");
Replaces object.method with a stub function. The original function can be restored by calling object.method.restore (); or stub.restore (); . An error may be issued if the properties are not related to the function — this is done to avoid typing errors when using the stub.
var stub = sinon.stub(object, "method", func);
Replaces object.method and func wrapped by spy. Commonly used object.method.restore (); to restore the original method.
var stub = sinon.stub(obj);
Stub for the specified object. Note that the method of individual stubs is the best practice for use on objects that you do not understand or those that control all methods (for example, depending on the library). The method of individual stubs tests the behavior of an object more accurately and less susceptible to unexpected behavior during changes in the object's code.

If you want to create a stub object for MyConstructor , but do not want the constructor to be called, use this utility function:
 var stub = sinon.createStubInstance(MyConstructor) 

stub.withArgs(arg1[, arg2, ...]);
The stub method is for specified arguments only. This method is useful for gaining more expression in your statements, where you can access the spy with the same challenge. It is also useful to create a stub that may act differently in response to various parameters.

 "test should stub method differently based on arguments": function () { var callback = sinon.stub(); callback.withArgs(42).returns(1); callback.withArgs(1).throws("TypeError"); callback(); // No return value, no exception callback(42); // Returns 1 callback(1); // Throws TypeError } 

stub.onCall(n);
Added to Sinon.JS 1.8.
Defines the behavior of a stub on an nth call. Useful for testing sequential interactions.

 "test should stub method differently on consecutive calls": function () { var callback = sinon.stub(); callback.onCall(0).returns(1); callback.onCall(1).returns(2); callback.returns(3); callback(); // Returns 1 callback(); // Returns 2 callback(); // All following calls return 3 } 

There are ways such as onFirstCall, onSecondCall, onThirdCall , to make reading certain stubs more natural.
`onCall` can be combined with all the methods defining the behavior in this section. In particular, it can be used with 'withArgs' .

 "test should stub method differently on consecutive calls with certain argument": function () { var callback = sinon.stub(); callback.withArgs(42) .onFirstCall().returns(1) .onSecondCall().returns(2); callback.returns(0); callback(1); // Returns 0 callback(42); // Returns 1 callback(1); // Returns 0 callback(42); // Returns 2 callback(1); // Returns 0 callback(42); // Returns 0 } 

Notice how the stub behavior for argument 42 returns to the default behavior once the calls are no longer defined.
stub.onFirstCall();
Nickname for stub.onCall (0);
stub.onSecondCall();
Nickname for stub.onCall (1);
stub.onThirdCall();
Nickname for stub.onCall (2);
stub.returns(obj);
Causes the stub to return the specified object.
stub.returnsArg(index);
Causes the stub to return an argument with the specified index. stub.callsArg (0); causes the stub to return the primary argument.
stub.returnsThis();
The stub returns the value of this .
stub.throws();
Causes the stub to throw an exception (Error).
stub.throws("TypeError");
Causes the stub to produce an error of the specified type.
stub.throws(obj);
Causes the stub to generate an error for the specified object.
stub.callsArg(index);
Causes the stub to invoke the argument at the specified index as a callback function. stub.callsArg (0); causes the stub to invoke the primary argument, like a callback.
stub.callsArgOn(index, context);
Runs just like stub.callsArg(index); , but with an additional parameter for passing this context.
stub.callsArgWith(index, arg1, arg2, ...);
It is executed in the same way as callsArg, but with arguments to pass to the callback function.
stub.callsArgOnWith(index, context, arg1, arg2, ...);
It is executed exactly the same as stub.callsArgWith(index, arg1, arg2, ...); , but with an additional parameter for passing this context.
stub.yields([arg1, arg2, ...])
It behaves almost the same as callsArg. Causes the stub to invoke the first callback it receives with the specified arguments (if any). If the method accepts more than one callback, you need to use callsArg, so that the stub has the ability to call other callbacks other than the first.
stub.yieldsOn(context, [arg1, arg2, ...])
It is executed exactly the same as stub.yieldsOn(context, [arg1, arg2, ...]); , but with an additional parameter for passing this context.
stub.yieldsTo(property, [arg1, arg2, ...])
Causes the spy to call the callback passed as a property of the object to the spy. Just like yields, yieldsTo takes the first matching argument, finds the callback and calls it with (optional) arguments.
stub.yieldsToOn(property, context, [arg1, arg2, ...])
It is executed in the same way as stub.yieldsTo(property, [arg1, arg2, ...]); , but with an additional parameter for passing this context.

 "test should fake successful ajax request": function () { sinon.stub(jQuery, "ajax").yieldsTo("success", [1, 2, 3]); jQuery.ajax({ success: function (data) { assertEquals([1, 2, 3], data); } }); } 

stub.yield([arg1, arg2, ...])
Calls callbacks sent to a stub with the specified parameters. If the stub is never called with the specified function arguments, the yield will invokeCallback error, with the alias invokeCallback .
stub.yieldTo(callback, [arg1, arg2, ...])
Calls callbacks passed as a property of an object to a spy. Just like yields, yieldsTo takes the first matching argument, finds the callback and calls it with (optional) arguments.
stub.callArg(argNum)
Just like yield , but with the exact argument number, the defining callback to be called again. Useful if the function is called with more than one callback, and simply calling the first callback is not required.

 "calling the last callback": function () { var callback = sinon.stub(); callback(function () { console.log("Success!"); }, function () { console.log("Oh noes!"); }); callback.callArg(1); // Logs "Oh noes!" } 

stub.callArgWith(argNum, [arg1, arg2, ...])
Like stub.callArg(argNum) , but with arguments.
stub.callsArgAsync(index);
Performs the same as the corresponding synchronous duplicate, but with a postponed callback (which is executed not immediately, but after a short timeout, and in another “thread”).

stub.callsArgAsync(index);
stub.callsArgOnAsync(index, context);
stub.callsArgWithAsync(index, arg1, arg2, ...);
stub.callsArgOnWithAsync(index, context, arg1, arg2, ...);
stub.yieldsAsync([arg1, arg2, ...])
stub.yieldsOnAsync(context, [arg1, arg2, ...])
stub.yieldsToAsync(property, [arg1, arg2, ...])
stub.yieldsToOnAsync(property, context, [arg1, arg2, ...])
They are executed in the same way as their corresponding synchronous duplicates, but with a postponed callback (which is executed a immediately, but after a short timeout and in another “thread”)


Mocks


Mocks (imitation) are imitation methods (like spies) with preprogrammed behavior (like a stub), as well as preprogrammed waiting. Simulation will not be able to perform the test if it is not used exactly as expected.

Simulations can only be used for the test method. In each unit testing there must be at least one unit in the testing process. To control how this block is used and to set the expected result (as opposed to statements obtained after the fact), use an imitation.

Simulations are completed with the test expectations that you set, which can lead to the failure of the entire test. Thus, the test imposes execution details. There is a simple rule: if you do not add an assertion for some calls, do not imitate them. Better use a stub instead. In any case, you should not have more than one simulation (possibly with several specified expectations) in one test.

Expectations can use both spy and stub APIs.

To see how imitation looks in Sinon.JS, here is one example of PubSubJS. This time, using the callback method and how its behavior is checked using imitation:

 "test should call all subscribers when exceptions": function () { var myAPI = { method: function () {} }; var spy = sinon.spy(); var mock = sinon.mock(myAPI); mock.expects("method").once().throws(); PubSub.subscribe("message", myAPI.method); PubSub.subscribe("message", spy); PubSub.publishSync("message", undefined); mock.verify(); assert(spy.calledOnce); } 

Mocks api
var mock = sinon.mock(obj);
Creates a simulation for the specified object. The object itself does not change, but it wraps a mock object to set expectations on the object's methods.
var expectation = mock.expects("method");
Overrides obj.method with a simulation function and returns it.
mock.restore();
Restores all simulation methods.
mock.verify();
Searches for the specified imitation. If the specified simulation was not detected, an error message is displayed. In addition, it helps to restore imitation methods.

Expectations


All expectations methods (expectations) return expectations. This means that you can chain them together. Typical use:
 sinon.mock(jQuery).expects("ajax").atLeast(2).atMost(5); jQuery.ajax.verify(); 

Expectations API
var expectation = sinon.expectation.create([methodName]);
Creates a wait without a mock = object, basically it is an anonymous simulation function. , .
var expectation = sinon.mock();
var expectation = sinon.expectation.create([methodName]);
expectation.atLeast(number);
.
expectation.atMost(number);
.
expectation.never();
, .
expectation.once();
, .
expectation.twice();
, .
expectation.thrice();
, .
expectation.exactly(number);
, .
expectation.withArgs(arg1, arg2, ...);
, ( ).
expectation.withExactArgs(arg1, arg2, ...);
, .
expectation.on(obj);
, obj this .
expectation.verify();
, .

Fake timers


Fake timers are a synchronous implementation of setTimeout and Sinon.JS helpers, which can overwrite global functions and allow you to more easily test code using them.

To use fake timers with IE, you need to install sinon-ie-1.17.2 immediately after installing sinon-1.17.2.js.

For offline use of fake timers, it is recommended to use the lolex package. This provides the same functional set and was previously extracted from Sinon.JS.

 { setUp: function () { this.clock = sinon.useFakeTimers(); }, tearDown: function () { this.clock.restore(); }, "test should animate element over 500ms" : function(){ var el = jQuery("<div></div>"); el.appendTo(document.body); el.animate({ height: "200px", width: "200px" }); this.clock.tick(510); assertEquals("200px", el.css("height")); assertEquals("200px", el.css("width")); } } 

Fake timers API
var clock = sinon.useFakeTimers();
Sinon setTimeout, setInterval, clearTimeout, clearInterval , . UNIX .
var clock = sinon.useFakeTimers(now);
, , (now) .
var clock = sinon.useFakeTimers([now, ]prop1, prop2, ...);
. — setTimeout, clearTimeout, setInterval , clearInterval , . .
clock.tick(ms);
ms . , .
clock.restore();
. tearDown

Fake XMLHttpRequest


It provides a fake implementation XMLHttpRequestand provides several interfaces for managing the objects it creates. Also forges its own XMLHttpRequestand ActiveXObject. Helps with testing queries executed with XHR.

To use XHR with IE, you need to install sinon-ie-1.17.2 immediately after installing sinon-1.17.2.js. The

fake server and XHR can be used completely autonomously, provided that sinon-server-1.17.2 is loaded. When using a fake server in IE, you also need sinon-1.17.2. Download it after sinon-server-1.17.2.

 { setUp: function () { this.xhr = sinon.useFakeXMLHttpRequest(); var requests = this.requests = []; this.xhr.onCreate = function (xhr) { requests.push(xhr); }; }, tearDown: function () { this.xhr.restore(); }, "test should fetch comments from server" : function () { var callback = sinon.spy(); myLib.getCommentsFor("/some/article", callback); assertEquals(1, this.requests.length); this.requests[0].respond(200, { "Content-Type": "application/json" }, '[{ "id": 12, "comment": "Hey there" }]'); assert(callback.calledWith([{ id: 12, comment: "Hey there" }])); } } 

sinon.useFakeXMLHttpRequest
var xhr = sinon.useFakeXMLHttpRequest();
Sinon XMLHttpRequest , . , ActiveXObject , progID XMLHTTP. progID, XMLDOM, .

XMLHttpRequest sinon.xhr. XMLHttpRequest.

xhr.onCreate = function (xhr) {};
onCreate useFakeXMLHttpRequest ()</code. FakeXMLHttpRequest . API XHR. , jQuery.ajax ( /)
xhr.restore();
().

FakeXMLHttpRequest
String request.url
URL- .
String request.method
.
Object request.requestHeaders
, ..:

 { "Accept": "text/html, */*", "Connection": "keep-alive" } 

String request.requestBody
.
int request.status
. , .
String request.statusText
, .
boolean request.async
.
String request.username
, .
String request.password
, .
Document request.responseXML
, .
String request.getResponseHeader(header);
, .
Object request.getAllResponseHeaders();
.


Sinon.JS / , . FakeXMLHttpRequest ( Sinon 1.3.0) :

FakeXMLHttpRequest.useFilters
. Sinon , .
FakeXMLHttpRequest.addFilter(fn)
, , — . , xhr.open (, URL, , , ). , .


request.setResponseHeaders(object);
, , { "Content-Type": "text/html", /* ... */ } , readyState onreadystatechange .
request.setResponseBody(body);
, readyState onreadystatechange . responseXML , .
request.respond(status, headers, body);
statusText . , sinon. FakeXMLHttpRequest.statusCodes .
Boolean request.autoRespond
, -.- 10 , autoRespondAfter .

, , .

Number request.autoRespondAfter
, . 10 .

FakeServe


API FakeXMLHttpRequest

 { setUp: function () { this.server = sinon.fakeServer.create(); }, tearDown: function () { this.server.restore(); }, "test should fetch comments from server" : function () { this.server.respondWith("GET", "/some/article/comments.json", [200, { "Content-Type": "application/json" }, '[{ "id": 12, "comment": "Hey there" }]']); var callback = sinon.spy(); myLib.getCommentsFor("/some/article", callback); this.server.respond(); sinon.assert.calledWith(callback, [{ id: 12, comment: "Hey there" }]); } } 

API FakeServe
var server = sinon.fakeServer.create([config]);
. sinon.useFakeXMLHttpRequest () . / .
var server = sinon.fakeServerWithClock.create();
, . XHR, , , jQuery 1.3.x onreadystatechange . jQuery 1.3.x ,
server.configure(config)
. .
server.respondWith(response);
:

  • ,
  • , , , [200, { "Content-Type": "text/html", "Content-Length": 2 }, "OK"]
  • .


200 . [404, {}, ""]

, . , .

server.respondWith(url, response);
URL, , /posts/1
server.respondWith(method, url, response);
URL . — HTTP.
server.respondWith(urlRegExp, response);
URL , , /\/post\//\d+ . XMLHttpRequest .
 server.respondWith(/\/todo-items\/(\d+)/, function (xhr, id) { xhr.respond(200, { ?Content-Type?: ?application/json? }, ?[{ ?id?: ? + id + ? }]?); }); 

server.respondWith(method, urlRegExp, response);
URL-, .
server.respond();
, . respondWith , [404, {}, ""] . , , respondWith . , respondWith .
server.autoRespond = true;
— -. - 10 , autoRespondAfter . , . respondImmediately .
server.autoRespondAfter = ms;
-.
server.respondImmediately = true;
, . . , . server.autoRespond server.autoRespondAfter.
Boolean `server.fakeHTTPMethods`
, method POST . , Ruby Rails. HTTP server.getHTTPMethod.
server.getHTTPMethod(request)
, HTTP, . request.method . server.fakeHTTPMethods , _method , POST. .
server.restore();
XHR .

:

 server.autoRespond = true 

fakeServer.create .configure .

boolean autoRespond
-. - 10 , autoRespondAfter . , , . .
int autoRespondAfter (ms)
-.
boolean respondImmediately
. , . , . server.autoRespond server.autoRespondAfter .
boolean fakeHTTPMethods
_method POST . , Ruby Rails. HTTP server.getHTTPMethod .

JSON-P


JSON-P Ajax, . JSON-P . . — jQuery .

 sinon.stub(jQuery, "ajax"); sinon.assert.calledOnce(jQuery.ajax); 

, , JQuery jQuery.ajax , JSON-P. .

Assertions


Sinon.JS , . , .

, , sinon.assert.fail sinon.assert.failException sinon.assert.expose sinon.assert.pass .

, .

 "test should call subscribers with message as first argument" : function () { var message = "an example message"; var spy = sinon.spy(); PubSub.subscribe(message, spy); PubSub.publishSync(message, "some payload"); sinon.assert.calledOnce(spy); sinon.assert.calledWith(spy, message); } 

Assertions API
sinon.assert.fail(message)
, . sinon.assert.failException . , , .
sinon.assert.failException
.
sinon.assert.pass(assertion)
, . .
sinon.assert.notCalled(spy)
, .
sinon.assert.called(spy)
, .
sinon.assert.calledOnce(spy)
, .
sinon.assert.calledTwice()
, .
sinon.assert.calledThrice()
, .
sinon.assert.callCount(spy, num)
.
sinon.assert.callOrder(spy1, spy2, ...)
, .
sinon.assert.calledOn(spy, obj)
, this.
sinon.assert.alwaysCalledOn(spy, obj)
, this.
sinon.assert.calledWith(spy, arg1, arg2, ...)
, ( ).
sinon.assert.alwaysCalledWith(spy, arg1, arg2, ...)
, ( ).
sinon.assert.neverCalledWith(spy, arg1, arg2, ...)
, .
sinon.assert.calledWithExactly(spy, arg1, arg2, ...)
, .
sinon.assert.alwaysCalledWithExactly(spy, arg1, arg2, ...)
, .
sinon.assert.calledWithMatch(spy, arg1, arg2, ...)
, . sinon.assert.calledWith(spy, sinon.match(arg1), sinon.match(arg2), ...) .
sinon.assert.alwaysCalledWithMatch(spy, arg1, arg2, ...)
, . sinon.assert.alwaysCalledWith(spy, sinon.match(arg1), sinon.match(arg2), ...) .
sinon.assert.neverCalledWithMatch(spy, arg1, arg2, ...)
, . sinon.assert.neverCalledWith(spy, sinon.match(arg1), sinon.match(arg2), ...) .
sinon.assert.threw(spy, exception)
, (). , , . , .
sinon.assert.alwaysThrew(spy, exception)
, .
sinon.assert.expose(object, options)
. , JsTestDriver , Sinon.JS :
 sinon.assert.expose(this); 

assertCalled(spy),assertCallOrder(spy1, spy2, ...) ..

. Prefix — , . assert , sinon.assert.called target.assertCalled . , target.called . , includeFail failException .

Matchers


spy.calledWith , spy.returned sinon.assert spy.withArgs .
, .

 "test should assert fuzzy": function () { var book = { pages: 42, author: "cjno" }; var spy = sinon.spy(); spy(book); sinon.assert.calledWith(spy, sinon.match({ author: "cjno" })); sinon.assert.calledWith(spy, sinon.match.has("pages", 42)); } 

 "test should stub method differently based on argument types": function () { var callback = sinon.stub(); callback.withArgs(sinon.match.string).returns(true); callback.withArgs(sinon.match.number).throws("TypeError"); callback("abc"); // Returns true callback(123); // Throws TypeError } 

Matchers API
sinon.match(number)
, .
sinon.match(string)
, .
sinon.match(regexp)
, .
sinon.match(object)
, . .
sinon.match(function)
.
sinon.match.any
.
sinon.match.defined
, .
sinon.match.truthy
, .
sinon.match.falsy
, .
sinon.match.bool
, .
sinon.match.number
, .
sinon.match.string
.
sinon.match.object
.
sinon.match.func
, .
sinon.match.array
, .
sinon.match.regexp
, .
sinon.match.date
, .
sinon.match.same(ref)
, ref
sinon.match.typeOf(type)
, , undefined , null , boolean , number , string , object , function , array , regexp date .
sinon.match.instanceOf(type)
, .
sinon.match.has(property[, expectation])
, . .
sinon.match.hasOwn(property[, expectation])
sinon.match.has, . .


«» «». . , () () .

 var stringOrNumber = sinon.match.string.or(sinon.match.number); var bookWithPages = sinon.match.instanceOf(Book).and(sinon.match.has("pages")); 


sinon.match , . , true , 'false' — . , , .

 var trueIsh = sinon.match(function (value) { return !!value; }, "trueIsh"); 

Sandboxes


, / . , XHR, / , , .

 "test using sinon.test sandbox": sinon.test(function () { var myAPI = { method: function () {} }; this.mock(myAPI).expects("method").once(); PubSub.subscribe("message", myAPI.method); PubSub.publishSync("message", undefined); }) 

Sandbox API
var sandbox = sinon.sandbox.create();
sandbox.
var sandbox = sinon.sandbox.create(config);
'sinon.sandbox.create (config)' — , Sinon.JS , , .

sandbox. sandbox , , . :

 sinon.defaultConfig = { // ... injectInto: null, properties: ["spy", "stub", "mock", "clock", "server", "requests"], useFakeTimers: true, useFakeServer: true } 

  • injectInto
    sandbox . «injectInto» . `sinon.test` , ` this` .
  • properties
    . , «server» . '', , 'useFakeServer' .
  • useFakeTimers
    'true', sandbox ''. .
  • useFakeServer
    `true`, `server` `requests` sandbox. . — 'sinon.fakeServer', jQuery 1.3.x , 'onreadystatechange' XHR , :

     sinon.config = { useFakeServer: sinon.fakeServerWithClock }; 


sandbox.spy();
sinon.spy , sandbox.restore () .
sandbox.stub();
sinon.stub , sandbox.restore () . sandbox stub , . , , .
sandbox.mock();
sinon.mock , sandbox.restore () .
sandbox.useFakeTimers();
sandbox, , sandbox.restore (). sandbox.clock.
sandbox.useFakeXMLHttpRequest();
XHR sandbox , , sandbox.restore () . sandbox.requests .
sandbox.useFakeServer();
XHR sandbox , , sandbox.restore () . sandbox.requests sandbox.server .
sandbox.restore();
sandbox.


sinon.test Sinon.JS sandbox . sinon.config .
var wrappedFn = sinon.test(fn);

wrappedFn . , sandbox , . spy , stub mock sandbox , , this.spy () (stub mock) , sandbox.spy () (stub mock) , .

 { injectIntoThis: true, injectInto: null, properties: ["spy", "stub", "mock", "clock", "server", "requests"], useFakeTimers: true, useFakeServer: true } 

sinon.config , , :

 sinon.config = { useFakeTimers: false, useFakeServer: false } 

. , sandbox .
sinon.config

Sinon sinon.test . :

Boolean injectIntoThis
, . .
Object injectInto
, . null ( ) injectIntoThis false ( ), .
Array properties
. — : [spy, stub, mock, clock, server, requests] . , — .
Boolean useFakeTimers
, . .
Boolean useFakeServer
XHR , . — .


sinon.test , sinon.testCase , , sinon.test :` setUp tearDown .

var obj = sinon.testCase({});

Sinon.JS


Sinon.JS , . , API.

API
sinon.createStubInstance(constructor)
, . . API .
sinon.format(object)
. .
sinon.log(string)
, .

, .

Source
(function (root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define('sinon', [], function () {
return (root.sinon = factory());
});
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.sinon = factory();
}
}(this, function () {
'use strict';
var samsam, formatio, lolex;
(function () {
function define(mod, deps, fn) {
if (mod == «samsam») {
samsam = deps();
} else if (typeof deps === «function» && mod.length === 0) {
lolex = deps();
} else if (typeof fn === «function») {
formatio = fn(samsam);
}
}
define.amd = {};
((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, eg «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 (getClass(object) === 'Arguments') { return true; }
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, ie 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 (typeof(matcher) === «undefined») {
return typeof(object) === «undefined»;
}

if (matcher === null) {
return object === null;
}

if (getClass(object) === «Array» && getClass(matcher) === «Array») {
return arrayContains(object, matcher);
}

if (matcher && typeof matcher === «object») {
if (matcher === object) {
return true;
}
var prop;
for (prop in matcher) {
var value = object[prop];
if (typeof value === «undefined» &&
typeof object.getAttribute === «function») {
value = object.getAttribute(prop);
}
if (matcher[prop] === null || typeof matcher[prop] === 'undefined') {
if (value !== matcher[prop]) {
return false;
}
} else if (typeof value === «undefined» || !match(value, 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,
limitChildrenCount: 0
};

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 pieces = [];
var i, l;
l = (this.limitChildrenCount > 0)?
Math.min(this.limitChildrenCount, array.length): array.length;

for (i = 0; i < l; ++i) {
pieces.push(ascii(this, array[i], processed));
}

if(l < array.length)
pieces.push("[… " + (array.length — l) + " more elements]");

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, k, l;
l = (this.limitChildrenCount > 0)?
Math.min(this.limitChildrenCount, properties.length): properties.length;

for (i = 0; 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, k = indent; i < k; ++i) { is += " "; }

if(l < properties.length)
pieces.push("[… " + (properties.length — l) + " more elements]");

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;
});
!function(e){if(«object»==typeof exports&&«undefined»!=typeof module)module.exports=e();else if(«function»==typeof define&&define.amd)define([],e);else{var f;«undefined»!=typeof window?f=window:«undefined»!=typeof global?f=global:«undefined»!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==«function»&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(«Cannot find module '»+o+"'");throw f.code=«MODULE_NOT_FOUND»,f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==«function»&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (global){
/*global global, window*/
/**
* author Christian Johansen (christian@cjohansen.no) and contributors
* @license BSD
*
* Copyright © 2010-2014 Christian Johansen
*/

(function (global) {

// Make properties writable in IE, as per
// www.adequatelygood.com/Replacing-setTimeout-Globally.html
// JSLint being anal
var glbl = global;

global.setTimeout = glbl.setTimeout;
global.clearTimeout = glbl.clearTimeout;
global.setInterval = glbl.setInterval;
global.clearInterval = glbl.clearInterval;
global.Date = glbl.Date;

// setImmediate is not a standard function
// avoid adding the prop to the window object if not present
if('setImmediate' in global) {
global.setImmediate = glbl.setImmediate;
global.clearImmediate = glbl.clearImmediate;
}

// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
// browsers, a number.
// see github.com/cjohansen/Sinon.JS/pull/436

var NOOP = function () { return undefined; };
var timeoutResult = setTimeout(NOOP, 0);
var addTimerReturnsObject = typeof timeoutResult === «object»;
clearTimeout(timeoutResult);

var NativeDate = Date;
var uniqueTimerId = 1;

/**
* Parse strings like «01:10:00» (meaning 1 hour, 10 minutes, 0 seconds) into
* number of milliseconds. This is used to support human-readable strings passed
* to clock.tick()
*/
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;
}

/**
* Used to grok the `now` parameter to createClock.
*/
function getEpoch(epoch) {
if (!epoch) { return 0; }
if (typeof epoch.getTime === «function») { return epoch.getTime(); }
if (typeof epoch === «number») { return epoch; }
throw new TypeError(«now should be milliseconds since UNIX epoch»);
}

function inRange(from, to, timer) {
return timer && timer.callAt >= from && timer.callAt <= to;
}

function mirrorDateProperties(target, source) {
var prop;
for (prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}

// set special now implementation
if (source.now) {
target.now = function now() {
return target.clock.now;
};
} else {
delete target.now;
}

// set special toSource implementation
if (source.toSource) {
target.toSource = function toSource() {
return source.toSource();
};
} else {
delete target.toSource;
}

// set special toString implementation
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;

return target;
}

function createDate() {
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 addTimer(clock, timer) {
if (timer.func === undefined) {
throw new Error(«Callback must be provided to timer calls»);
}

if (!clock.timers) {
clock.timers = {};
}

timer.id = uniqueTimerId++;
timer.createdAt = clock.now;
timer.callAt = clock.now + (timer.delay || (clock.duringTick? 1: 0));

clock.timers[timer.id] = timer;

if (addTimerReturnsObject) {
return {
id: timer.id,
ref: NOOP,
unref: NOOP
};
}

return timer.id;
}

function compareTimers(a, b) {
// Sort first by absolute timing
if (a.callAt < b.callAt) {
return -1;
}
if (a.callAt > b.callAt) {
return 1;
}

// Sort next by immediate, immediate timers take precedence
if (a.immediate && !b.immediate) {
return -1;
}
if (!a.immediate && b.immediate) {
return 1;
}

// Sort next by creation time, earlier-created timers take precedence
if (a.createdAt < b.createdAt) {
return -1;
}
if (a.createdAt > b.createdAt) {
return 1;
}

// Sort next by id, lower-id timers take precedence
if (a.id < b.id) {
return -1;
}
if (a.id > b.id) {
return 1;
}

// As timer ids are unique, no fallback `0` is necessary
}

function firstTimerInRange(clock, from, to) {
var timers = clock.timers,
timer = null,
id,
isInRange;

for (id in timers) {
if (timers.hasOwnProperty(id)) {
isInRange = inRange(from, to, timers[id]);

if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) {
timer = timers[id];
}
}
}

return timer;
}

function callTimer(clock, timer) {
var exception;

if (typeof timer.interval === «number») {
clock.timers[timer.id].callAt += timer.interval;
} else {
delete clock.timers[timer.id];
}

try {
if (typeof timer.func === «function») {
timer.func.apply(null, timer.args);
} else {
eval(timer.func);
}
} catch (e) {
exception = e;
}

if (!clock.timers[timer.id]) {
if (exception) {
throw exception;
}
return;
}

if (exception) {
throw exception;
}
}

function timerType(timer) {
if (timer.immediate) {
return «Immediate»;
} else if (typeof timer.interval !== «undefined») {
return «Interval»;
} else {
return «Timeout»;
}
}

function clearTimer(clock, timerId, ttype) {
if (!timerId) {
// null appears to be allowed in most browsers, and appears to be
// relied upon by some libraries, like Bootstrap carousel
return;
}

if (!clock.timers) {
clock.timers = [];
}

// in Node, timerId is an object with .ref()/.unref(), and
// its .id field is the actual timer id.
if (typeof timerId === «object») {
timerId = timerId.id;
}

if (clock.timers.hasOwnProperty(timerId)) {
// check that the ID matches a timer of the correct type
var timer = clock.timers[timerId];
if (timerType(timer) === ttype) {
delete clock.timers[timerId];
} else {
throw new Error(«Cannot clear timer: timer created with set» + ttype + "() but cleared with clear" + timerType(timer) + "()");
}
}
}

function uninstall(clock, target) {
var method,
i,
l;

for (i = 0, l = clock.methods.length; i < l; i++) {
method = clock.methods[i];

if (target[method].hadOwnProperty) {
target[method] = clock["_" + method];
} else {
try {
delete target[method];
} catch (ignore) {}
}
}

// Prevent multiple executions which will completely remove these props
clock.methods = [];
}

function hijackMethod(target, method, clock) {
var prop;

clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
clock["_" + method] = target[method];

if (method === «Date») {
var date = mirrorDateProperties(clock[method], target[method]);
target[method] = date;
} else {
target[method] = function () {
return clock[method].apply(clock, arguments);
};

for (prop in clock[method]) {
if (clock[method].hasOwnProperty(prop)) {
target[method][prop] = clock[method][prop];
}
}
}

target[method].clock = clock;
}

var timers = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setImmediate: global.setImmediate,
clearImmediate: global.clearImmediate,
setInterval: setInterval,
clearInterval: clearInterval,
Date: Date
};

var keys = Object.keys || function (obj) {
var ks = [],
key;

for (key in obj) {
if (obj.hasOwnProperty(key)) {
ks.push(key);
}
}

return ks;
};

exports.timers = timers;

function createClock(now) {
var clock = {
now: getEpoch(now),
timeouts: {},
Date: createDate()
};

clock.Date.clock = clock;

clock.setTimeout = function setTimeout(func, timeout) {
return addTimer(clock, {
func: func,
args: Array.prototype.slice.call(arguments, 2),
delay: timeout
});
};

clock.clearTimeout = function clearTimeout(timerId) {
return clearTimer(clock, timerId, «Timeout»);
};

clock.setInterval = function setInterval(func, timeout) {
return addTimer(clock, {
func: func,
args: Array.prototype.slice.call(arguments, 2),
delay: timeout,
interval: timeout
});
};

clock.clearInterval = function clearInterval(timerId) {
return clearTimer(clock, timerId, «Interval»);
};

clock.setImmediate = function setImmediate(func) {
return addTimer(clock, {
func: func,
args: Array.prototype.slice.call(arguments, 1),
immediate: true
});
};

clock.clearImmediate = function clearImmediate(timerId) {
return clearTimer(clock, timerId, «Immediate»);
};

clock.tick = function tick(ms) {
ms = typeof ms === «number»? ms: parseTime(ms);
var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
var timer = firstTimerInRange(clock, tickFrom, tickTo);
var oldNow;

clock.duringTick = true;

var firstException;
while (timer && tickFrom <= tickTo) {
if (clock.timers[timer.id]) {
tickFrom = clock.now = timer.callAt;
try {
oldNow = clock.now;
callTimer(clock, timer);
// compensate for any setSystemTime() call during timer callback
if (oldNow !== clock.now) {
tickFrom += clock.now — oldNow;
tickTo += clock.now — oldNow;
previous += clock.now — oldNow;
}
} catch (e) {
firstException = firstException || e;
}
}

timer = firstTimerInRange(clock, previous, tickTo);
previous = tickFrom;
}

clock.duringTick = false;
clock.now = tickTo;

if (firstException) {
throw firstException;
}

return clock.now;
};

clock.reset = function reset() {
clock.timers = {};
};

clock.setSystemTime = function setSystemTime(now) {
// determine time difference
var newNow = getEpoch(now);
var difference = newNow — clock.now;

// update 'system clock'
clock.now = newNow;

// update timers and intervals to keep them stable
for (var id in clock.timers) {
if (clock.timers.hasOwnProperty(id)) {
var timer = clock.timers[id];
timer.createdAt += difference;
timer.callAt += difference;
}
}
};

return clock;
}
exports.createClock = createClock;

exports.install = function install(target, now, toFake) {
var i,
l;

if (typeof target === «number») {
toFake = now;
now = target;
target = null;
}

if (!target) {
target = global;
}

var clock = createClock(now);

clock.uninstall = function () {
uninstall(clock, target);
};

clock.methods = toFake || [];

if (clock.methods.length === 0) {
clock.methods = keys(timers);
}

for (i = 0, l = clock.methods.length; i < l; i++) {
hijackMethod(target, clock.methods[i], clock);
}

return clock;
};

}(global || this));

}).call(this,typeof global !== «undefined»? global: typeof self !== «undefined»? self: typeof window !== «undefined»? window: {})
},{}]},{},[1])(1)
});
}) ();
var define;
/**
* Sinon core utilities. For internal use only.
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
var sinon = (function () {
«use strict»;
// eslint-disable-line no-unused-vars

var sinonModule;
var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
sinonModule = module.exports = require("./sinon/util/core");
require("./sinon/extend");
require("./sinon/walk");
require("./sinon/typeOf");
require("./sinon/times_in_words");
require("./sinon/spy");
require("./sinon/call");
require("./sinon/behavior");
require("./sinon/stub");
require("./sinon/mock");
require("./sinon/collection");
require("./sinon/assert");
require("./sinon/sandbox");
require("./sinon/test");
require("./sinon/test_case");
require("./sinon/match");
require("./sinon/format");
require("./sinon/log_error");
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require, module.exports, module);
sinonModule = module.exports;
} else {
sinonModule = {};
}

return sinonModule;
}());

/**
* depend ../../sinon.js
*/
/**
* Sinon core utilities. For internal use only.
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

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;
}

// Cheap way to detect if we have ES5 support.
var hasES5Support = «keys» in Object;

function makeApi(sinon) {
sinon.wrapMethod = function wrapMethod(object, property, method) {
if (!object) {
throw new TypeError(«Should wrap property of object»);
}

if (typeof method !== «function» && typeof method !== «object») {
throw new TypeError(«Method wrapper should be a function or a property descriptor»);
}

function checkWrappedMethod(wrappedMethod) {
var error;

if (!isFunction(wrappedMethod)) {
error = new TypeError(«Attempted to wrap » + (typeof wrappedMethod) + " property " +
property + " as function");
} else if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
error = new TypeError(«Attempted to wrap » + property + " which is already wrapped");
} else 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 && wrappedMethod.stackTrace) {
error.stack += "\n--------------\n" + wrappedMethod.stackTrace;
}
throw error;
}
}

var error, wrappedMethod, i;

// 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);

if (hasES5Support) {
var methodDesc = (typeof method === «function»)? {value: method}: method;
var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property);

if (!wrappedMethodDesc) {
error = new TypeError(«Attempted to wrap » + (typeof wrappedMethod) + " property " +
property + " as function");
} else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) {
error = new TypeError(«Attempted to wrap » + property + " which is already wrapped");
}
if (error) {
if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) {
error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace;
}
throw error;
}

var types = sinon.objectKeys(methodDesc);
for (i = 0; i < types.length; i++) {
wrappedMethod = wrappedMethodDesc[types[i]];
checkWrappedMethod(wrappedMethod);
}

mirrorProperties(methodDesc, wrappedMethodDesc);
for (i = 0; i < types.length; i++) {
mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]);
}
Object.defineProperty(object, property, methodDesc);
} else {
wrappedMethod = object[property];
checkWrappedMethod(wrappedMethod);
object[property] = method;
method.displayName = property;
}

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.stackTrace = (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) {
// In some cases `delete` may throw an error
try {
delete object[property];
} catch (e) {} // eslint-disable-line no-empty
// For native code functions `delete` fails without throwing an error
// on Chrome < 43, PhantomJS, etc.
} else if (hasES5Support) {
Object.defineProperty(object, property, wrappedMethodDesc);
}

// Use strict equality comparison to check failures then force a reset
// via direct assignment.
if (object[property] === method) {
object[property] = wrappedMethod;
}
};

method.restore.sinon = true;

if (!hasES5Support) {
mirrorProperties(method, wrappedMethod);
}

return method;
};

sinon.create = function create(proto) {
var F = function () {};
F.prototype = proto;
return new F();
};

sinon.deepEqual = function deepEqual(a, b) {
if (sinon.match && sinon.match.isMatcher(a)) {
return a.test(b);
}

if (typeof a !== «object» || typeof b !== «object») {
return isReallyNaN(a) && isReallyNaN(b) || 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;
var aLength = 0;
var bLength = 0;

if (aString === "[object Array]" && a.length !== b.length) {
return false;
}

for (prop in a) {
if (a.hasOwnProperty(prop)) {
aLength += 1;

if (!(prop in b)) {
return false;
}

if (!deepEqual(a[prop], b[prop])) {
return false;
}
}
}

for (prop in b) {
if (b.hasOwnProperty(prop)) {
bLength += 1;
}
}

return aLength === bLength;
};

sinon.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
// (ie toString will say 'spy' rather than 'myFunc').
if (!name) {
var matches = func.toString().match(/function ([^\s\(]+)/);
name = matches && matches[1];
}

return name;
};

sinon.functionToString = function toString() {
if (this.getCall && this.callCount) {
var thisValue,
prop;
var 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»;
};

sinon.objectKeys = function objectKeys(obj) {
if (obj !== Object(obj)) {
throw new TypeError(«sinon.objectKeys called on a non-object»);
}

var keys = [];
var key;
for (key in obj) {
if (hasOwn.call(obj, key)) {
keys.push(key);
}
}

return keys;
};

sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) {
var proto = object;
var descriptor;

while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
proto = Object.getPrototypeOf(proto);
}
return descriptor;
};

sinon.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;
};

sinon.defaultConfig = {
injectIntoThis: true,
injectInto: null,
properties: [«spy», «stub», «mock», «clock», «server», «requests»],
useFakeTimers: true,
useFakeServer: true
};

sinon.timesInWords = function timesInWords(count) {
return count === 1 && «once» ||
count === 2 && «twice» ||
count === 3 && «thrice» ||
(count || 0) + " times";
};

sinon.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;
};

sinon.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;
});
};

sinon.createStubInstance = function (constructor) {
if (typeof constructor !== «function») {
throw new TypeError(«The constructor should be a function.»);
}
return sinon.stub(sinon.create(constructor.prototype));
};

sinon.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();
}
};

return sinon;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports) {
makeApi(exports);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
*/
(function (sinonGlobal) {

function makeApi(sinon) {

// Adapted from developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
var hasDontEnumBug = (function () {
var obj = {
constructor: function () {
return «0»;
},
toString: function () {
return «1»;
},
valueOf: function () {
return «2»;
},
toLocaleString: function () {
return «3»;
},
prototype: function () {
return «4»;
},
isPrototypeOf: function () {
return «5»;
},
propertyIsEnumerable: function () {
return «6»;
},
hasOwnProperty: function () {
return «7»;
},
length: function () {
return «8»;
},
unique: function () {
return «9»;
}
};

var result = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
result.push(obj[prop]());
}
}
return result.join("") !== «0123456789»;
}) ();

/* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will
* override properties in previous sources.
*
* target — The Object to extend
* sources — Objects to copy properties from.
*
* Returns the extended target
*/
function extend(target /*, sources */) {
var sources = Array.prototype.slice.call(arguments, 1);
var source, i, prop;

for (i = 0; i < sources.length; i++) {
source = sources[i];

for (prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}

// Make sure we copy (own) toString method even when in JScript with DontEnum bug
// See developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
if (hasDontEnumBug && source.hasOwnProperty(«toString») && source.toString !== target.toString) {
target.toString = source.toString;
}
}

return target;
}

sinon.extend = extend;
return sinon.extend;
}

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
module.exports = makeApi(sinon);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
*/
(function (sinonGlobal) {

function makeApi(sinon) {

function timesInWords(count) {
switch (count) {
case 1:
return «once»;
case 2:
return «twice»;
case 3:
return «thrice»;
default:
return (count || 0) + " times";
}
}

sinon.timesInWords = timesInWords;
return sinon.timesInWords;
}

function loadDependencies(require, exports, module) {
var core = require("./util/core");
module.exports = makeApi(core);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
*/
/**
* Format functions
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2014 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
function typeOf(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();
}

sinon.typeOf = typeOf;
return sinon.typeOf;
}

function loadDependencies(require, exports, module) {
var core = require("./util/core");
module.exports = makeApi(core);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend typeOf.js
*/
/*jslint eqeqeq: false, onevar: false, plusplus: false*/
/*global module, require, sinon*/
/**
* Match functions
*
* author Maximilian Antoni (mail@maxantoni.de)
* @license BSD
*
* Copyright © 2012 Maximilian Antoni
*/
(function (sinonGlobal) {

function makeApi(sinon) {
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 (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;
}

function match(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) {
// we need type coercion here
return expectation == actual; // eslint-disable-line eqeqeq
};
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;
}

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;
};

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»);

sinon.match = match;
return match;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./typeOf");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
*/
/**
* Format functions
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2014 Christian Johansen
*/
(function (sinonGlobal, formatio) {

function makeApi(sinon) {
function valueFormatter(value) {
return "" + value;
}

function getFormatioFormatter() {
var formatter = formatio.configure({
quoteStrings: false,
limitChildrenCount: 250
});

function format() {
return formatter.ascii.apply(formatter, arguments);
}

return format;
}

function getNodeFormatter() {
try {
var util = require(«util»);
} catch (e) {
/* Node, but no util module — would be very old, but better safe than sorry */
}

function format(v) {
var isObjectWithNativeToString = typeof v === «object» && v.toString === Object.prototype.toString;
return isObjectWithNativeToString? util.inspect(v): v;
}

return util? format: valueFormatter;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var formatter;

if (isNode) {
try {
formatio = require(«formatio»);
}
catch (e) {} // eslint-disable-line no-empty
}

if (formatio) {
formatter = getFormatioFormatter();
} else if (isNode) {
formatter = getNodeFormatter();
} else {
formatter = valueFormatter;
}

sinon.format = formatter;
return sinon.format;
}

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
module.exports = makeApi(sinon);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon, // eslint-disable-line no-undef
typeof formatio === «object» && formatio // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend match.js
* depend format.js
*/
/**
* Spy calls
*
* author Christian Johansen (christian@cjohansen.no)
* author Maximilian Antoni (mail@maxantoni.de)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
* Copyright © 2013 Maximilian Antoni
*/
(function (sinonGlobal) {

var slice = Array.prototype.slice;

function makeApi(sinon) {
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 callProto = {
calledOn: function calledOn(thisValue) {
if (sinon.match && sinon.match.isMatcher(thisValue)) {
return thisValue.test(this.thisValue);
}
return this.thisValue === thisValue;
},

calledWith: function calledWith() {
var l = arguments.length;
if (l > this.args.length) {
return false;
}
for (var i = 0; i < l; i += 1) {
if (!sinon.deepEqual(arguments[i], this.args[i])) {
return false;
}
}

return true;
},

calledWithMatch: function calledWithMatch() {
var l = arguments.length;
if (l > this.args.length) {
return false;
}
for (var i = 0; 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);
},

getStackFrames: function () {
// Omit the error message and the two top stack frames in sinon itself:
return this.stack && this.stack.split("\n").slice(3);
},

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 + ")";
}
}
if (this.stack) {
callStr += this.getStackFrames()[0].replace(/^\s*(?:at\s+|@)?/, " at ");

}

return callStr;
}
};

callProto.invokeCallback = callProto.yield;

function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) {
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;
proxyCall.stack = stack;

return proxyCall;
}
createSpyCall.toString = callProto.toString; // used by mocks

sinon.spyCall = createSpyCall;
return createSpyCall;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./match");
require("./format");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend times_in_words.js
* depend util/core.js
* depend extend.js
* depend call.js
* depend format.js
*/
/**
* Spy functions
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
var push = Array.prototype.push;
var slice = Array.prototype.slice;
var callId = 0;

function spy(object, property, types) {
if (!property && typeof object === «function») {
return spy.create(object);
}

if (!object && !property) {
return spy.create(function () { });
}

if (types) {
var methodDesc = sinon.getPropertyDescriptor(object, property);
for (var i = 0; i < types.length; i++) {
methodDesc[types[i]] = spy.create(methodDesc[types[i]]);
}
return sinon.wrapMethod(object, property, methodDesc);
}

return sinon.wrapMethod(object, property, spy.create(object[property]));
}

function matchingFake(fakes, args, strict) {
if (!fakes) {
return undefined;
}

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, proxyLength) {
// Retain the function length:
var p;
if (proxyLength) {
eval(«p = (function proxy(» + vars.substring(0, proxyLength * 2 — 1) + // eslint-disable-line no-eval
") { return p.invoke(func, this, slice.call(arguments)); });");
} else {
p = function proxy() {
return p.invoke(func, this, slice.call(arguments));
};
}
p.isSinonProxy = true;
return p;
}

var uuid = 0;

// Public API
var spyApi = {
reset: function () {
if (this.invoking) {
var err = new Error(«Cannot reset Sinon function while invoking it. » +
«Move the call to .reset outside of the callback.»);
err.name = «InvalidResetException»;
throw err;
}

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 = [];
this.stacks = [];
if (this.fakes) {
for (var i = 0; i < this.fakes.length; i++) {
this.fakes[i].reset();
}
}

return this;
},

create: function create(func, spyLength) {
var name;

if (typeof func !== «function») {
func = function () { };
} else {
name = sinon.functionName(func);
}

if (!spyLength) {
spyLength = func.length;
}

var proxy = createProxy(func, spyLength);

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.instantiateFake = 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++);

// Make call properties available from within the spied function:
createCallProperties.call(this);

try {
this.invoking = true;

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;
} finally {
delete this.invoking;
}

push.call(this.exceptions, exception);
push.call(this.returnValues, returnValue);
push.call(this.stacks, new Error().stack);

// Make return value and exception available in the calls:
createCallProperties.call(this);

if (exception !== undefined) {
throw exception;
}

return returnValue;
},

named: function named(name) {
this.displayName = name;
return this;
},

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], this.stacks[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.instantiateFake();
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 spyInstance = 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, spyInstance, 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 (spyInstance) {
return sinon.timesInWords(spyInstance.callCount);
},

n: function (spyInstance) {
return spyInstance.toString();
},

C: function (spyInstance) {
var calls = [];

for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
var stringifiedCall = " " + spyInstance.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 (spyInstance) {
var objects = [];

for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
push.call(objects, sinon.format(spyInstance.thisValues[i]));
}

return objects.join(", ");
},

"*": function (spyInstance, 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;
sinon.spy = spy;

return spy;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var core = require("./util/core");
require("./call");
require("./extend");
require("./times_in_words");
require("./format");
module.exports = makeApi(core);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend extend.js
*/
/**
* Stub behavior
*
* author Christian Johansen (christian@cjohansen.no)
* author Tim Fischbach (mail@timfischbach.de)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

var slice = Array.prototype.slice;
var join = Array.prototype.join;
var useLeftMostCallback = -1;
var useRightMostCallback = -2;

var nextTick = (function () {
if (typeof process === «object» && typeof process.nextTick === «function») {
return process.nextTick;
}

if (typeof setImmediate === «function») {
return setImmediate;
}

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) {
return args[callArgAt];
}

var argumentList;

if (callArgAt === useLeftMostCallback) {
argumentList = args;
}

if (callArgAt === useRightMostCallback) {
argumentList = slice.call(args).reverse();
}

var callArgProp = behavior.callArgProp;

for (var i = 0, l = argumentList.length; i < l; ++i) {
if (!callArgProp && typeof argumentList[i] === «function») {
return argumentList[i];
}

if (callArgProp && argumentList[i] &&
typeof argumentList[i][callArgProp] === «function») {
return argumentList[i][callArgProp];
}
}

return null;
}

function makeApi(sinon) {
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);
}
}
}

var proto = {
create: function create(stub) {
var behavior = sinon.extend({}, sinon.behavior);
delete behavior.create;
behavior.stub = stub;

return behavior;
},

isPresent: function isPresent() {
return (typeof this.callArgAt === «number» ||
this.exception ||
typeof this.returnArgAt === «number» ||
this.returnThis ||
this.returnValueDefined);
},

invoke: function invoke(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 onCall(index) {
return this.stub.onCall(index);
},

onFirstCall: function onFirstCall() {
return this.stub.onFirstCall();
},

onSecondCall: function onSecondCall() {
return this.stub.onSecondCall();
},

onThirdCall: function onThirdCall() {
return this.stub.onThirdCall();
},

withArgs: function withArgs(/* 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 = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 0);
this.callbackContext = undefined;
this.callArgProp = undefined;
this.callbackAsync = false;

return this;
},

yieldsRight: function () {
this.callArgAt = useRightMostCallback;
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 = useLeftMostCallback;
this.callbackArguments = slice.call(arguments, 1);
this.callbackContext = context;
this.callArgProp = undefined;
this.callbackAsync = false;

return this;
},

yieldsTo: function (prop) {
this.callArgAt = useLeftMostCallback;
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 = useLeftMostCallback;
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;
this.exception = undefined;

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;
}
};

function createAsyncVersion(syncFnName) {
return function () {
var result = this[syncFnName].apply(this, arguments);
this.callbackAsync = true;
return result;
};
}

// 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»] = createAsyncVersion(method);
}
}

sinon.behavior = proto;
return proto;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./extend");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
*/
(function (sinonGlobal) {

function makeApi(sinon) {
function walkInternal(obj, iterator, context, originalObj, seen) {
var proto, prop;

if (typeof Object.getOwnPropertyNames !== «function») {
// We explicitly want to enumerate through all of the prototype's properties
// in this case, therefore we deliberately leave out an own property check.
/* eslint-disable guard-for-in */
for (prop in obj) {
iterator.call(context, obj[prop], prop, obj);
}
/* eslint-enable guard-for-in */

return;
}

Object.getOwnPropertyNames(obj).forEach(function (k) {
if (!seen[k]) {
seen[k] = true;
var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === «function»?
originalObj: obj;
iterator.call(context, target[k], k, target);
}
});

proto = Object.getPrototypeOf(obj);
if (proto) {
walkInternal(proto, iterator, context, originalObj, seen);
}
}

/* Public: walks the prototype chain of an object and iterates over every own property
* name encountered. The iterator is called in the same fashion that Array.prototype.forEach
* works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional
* argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will
* default to using a simple for..in loop.
*
* obj — The object to walk the prototype chain for.
* iterator — The function to be called on each pass of the walk.
* context — (Optional) When given, the iterator will be called with this object as the receiver.
*/
function walk(obj, iterator, context) {
return walkInternal(obj, iterator, context, obj, {});
}

sinon.walk = walk;
return sinon.walk;
}

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
module.exports = makeApi(sinon);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend extend.js
* depend spy.js
* depend behavior.js
* depend walk.js
*/
/**
* Stub functions
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
function stub(object, property, func) {
if (!!func && typeof func !== «function» && typeof func !== «object») {
throw new TypeError(«Custom stub should be a function or a property descriptor»);
}

var wrapper;

if (func) {
if (typeof func === «function») {
wrapper = sinon.spy && sinon.spy.create? sinon.spy.create(func): func;
} else {
wrapper = func;
if (sinon.spy && sinon.spy.create) {
var types = sinon.objectKeys(wrapper);
for (var i = 0; i < types.length; i++) {
wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]);
}
}
}
} else {
var stubLength = 0;
if (typeof object === «object» && typeof object[property] === «function») {
stubLength = object[property].length;
}
wrapper = stub.create(stubLength);
}

if (!object && typeof property === «undefined») {
return sinon.stub.create();
}

if (typeof property === «undefined» && typeof object === «object») {
sinon.walk(object || {}, function (value, prop, propOwner) {
// we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object
// is not Object.prototype
if (
propOwner !== Object.prototype &&
prop !== «constructor» &&
typeof sinon.getPropertyDescriptor(propOwner, prop).value === «function»
) {
stub(object, prop);
}
});

return object;
}

return sinon.wrapMethod(object, property, wrapper);
}

/*eslint-disable no-use-before-define*/
function getParentBehaviour(stubInstance) {
return (stubInstance.parent && getCurrentBehavior(stubInstance.parent));
}

function getDefaultBehavior(stubInstance) {
return stubInstance.defaultBehavior ||
getParentBehaviour(stubInstance) ||
sinon.behavior.create(stubInstance);
}

function getCurrentBehavior(stubInstance) {
var behavior = stubInstance.behaviors[stubInstance.callCount — 1];
return behavior && behavior.isPresent()? behavior: getDefaultBehavior(stubInstance);
}
/*eslint-enable no-use-before-define*/

var uuid = 0;

var proto = {
create: function create(stubLength) {
var functionStub = function () {
return getCurrentBehavior(functionStub).invoke(this, arguments);
};

functionStub.id = «stub#» + uuid++;
var orig = functionStub;
functionStub = sinon.spy.create(functionStub, stubLength);
functionStub.func = orig;

sinon.extend(functionStub, stub);
functionStub.instantiateFake = 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 onCall(index) {
if (!this.behaviors[index]) {
this.behaviors[index] = sinon.behavior.create(this);
}

return this.behaviors[index];
},

onFirstCall: function onFirstCall() {
return this.onCall(0);
},

onSecondCall: function onSecondCall() {
return this.onCall(1);
},

onThirdCall: function onThirdCall() {
return this.onCall(2);
}
};

function createBehavior(behaviorMethod) {
return function () {
this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);
this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
return this;
};
}

for (var method in sinon.behavior) {
if (sinon.behavior.hasOwnProperty(method) &&
!proto.hasOwnProperty(method) &&
method !== «create» &&
method !== «withArgs» &&
method !== «invoke») {
proto[method] = createBehavior(method);
}
}

sinon.extend(stub, proto);
sinon.stub = stub;

return stub;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var core = require("./util/core");
require("./behavior");
require("./spy");
require("./extend");
module.exports = makeApi(core);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend times_in_words.js
* depend util/core.js
* depend call.js
* depend extend.js
* depend match.js
* depend spy.js
* depend stub.js
* depend format.js
*/
/**
* Mock functions.
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
var push = [].push;
var match = sinon.match;

function mock(object) {
// if (typeof console !== undefined && console.warn) {
// console.warn(«mock will be removed from Sinon.JS v2.0»);
// }

if (!object) {
return sinon.expectation.create(«Anonymous mock»);
}

return mock.create(object);
}

function each(collection, callback) {
if (!collection) {
return;
}

for (var i = 0, l = collection.length; i < l; i += 1) {
callback(collection[i]);
}
}

function arrayEquals(arr1, arr2, compareLength) {
if (compareLength && (arr1.length !== arr2.length)) {
return false;
}

for (var i = 0, l = arr1.length; i < l; i++) {
if (!sinon.deepEqual(arr1[i], arr2[i])) {
return false;
}
}
return true;
}

sinon.extend(mock, {
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 = [];
var 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 if (met.length > 0) {
sinon.expectation.pass(messages.concat(met).join("\n"));
}

return true;
},

invokeMethod: function invokeMethod(method, thisValue, args) {
var expectations = this.expectations && this.expectations[method]? this.expectations[method]: [];
var expectationsWithMatchingArgs = [];
var currentArgs = args || [];
var i, available;

for (i = 0; i < expectations.length; i += 1) {
var expectedArgs = expectations[i].expectedArguments || [];
if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) {
expectationsWithMatchingArgs.push(expectations[i]);
}
}

for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) {
if (!expectationsWithMatchingArgs[i].met() &&
expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {
return expectationsWithMatchingArgs[i].apply(thisValue, args);
}
}

var messages = [];
var exhausted = 0;

for (i = 0; i < expectationsWithMatchingArgs.length; i += 1) {
if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {
available = available || expectationsWithMatchingArgs[i];
} else {
exhausted += 1;
}
}

if (available && exhausted === 0) {
return available.apply(thisValue, args);
}

for (i = 0; i < expectations.length; i += 1) {
push.call(messages, " " + expectations[i].toString());
}

messages.unshift(«Unexpected call: » + sinon.spyCall.toString.call({
proxy: method,
args: args
}));

sinon.expectation.fail(messages.join("\n"));
}
});

var times = sinon.timesInWords;
var slice = Array.prototype.slice;

function callCountInWords(callCount) {
if (callCount === 0) {
return «never called»;
}

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) {
var isMatcher = match && match.isMatcher(possibleMatcher);

return isMatcher && possibleMatcher.test(arg) || true;
}

sinon.expectation = {
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 sinon.spy.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 pass(message) {
sinon.assert.pass(message);
},

fail: function fail(message) {
var exception = new Error(message);
exception.name = «ExpectationError»;

throw exception;
}
};

sinon.mock = mock;
return mock;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./times_in_words");
require("./call");
require("./extend");
require("./match");
require("./spy");
require("./stub");
require("./format");

module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend spy.js
* depend stub.js
* depend mock.js
*/
/**
* Collections of stubs, spies and mocks.
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

var push = [].push;
var hasOwnProperty = Object.prototype.hasOwnProperty;

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);
}
}

function makeApi(sinon) {
var collection = {
verify: function resolve() {
each(this, «verify»);
},

restore: function restore() {
each(this, «restore»);
compact(this);
},

reset: function restore() {
each(this, «reset»);
},

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;
}
};

sinon.collection = collection;
return collection;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./mock");
require("./spy");
require("./stub");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* Fake timer API
* setTimeout
* setInterval
* clearTimeout
* clearInterval
* tick
* reset
* Date
*
* Inspired by jsUnitMockTimeOut from JsUnit
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function () {

function makeApi(s, lol) {
/*global lolex */
var llx = typeof lolex !== «undefined»? lolex: lol;

s.useFakeTimers = function () {
var now;
var methods = Array.prototype.slice.call(arguments);

if (typeof methods[0] === «string») {
now = 0;
} else {
now = methods.shift();
}

var clock = llx.install(now || 0, methods);
clock.restore = clock.uninstall;
return clock;
};

s.clock = {
create: function (now) {
return llx.createClock(now);
}
};

s.timers = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setImmediate: (typeof setImmediate !== «undefined»? setImmediate: undefined),
clearImmediate: (typeof clearImmediate !== «undefined»? clearImmediate: undefined),
setInterval: setInterval,
clearInterval: clearInterval,
Date: Date
};
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, epxorts, module, lolex) {
var core = require("./core");
makeApi(core, lolex);
module.exports = core;
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require, module.exports, module, require(«lolex»));
} else {
makeApi(sinon); // eslint-disable-line no-undef
}
}());

/**
* Minimal Event interface implementation
*
* Original implementation by Sven Fuchs: 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 © 2011 Sven Fuchs, Christian Johansen
*/
if (typeof sinon === «undefined») {
this.sinon = {};
}

(function () {

var push = [].push;

function makeApi(sinon) {
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;
this.lengthComputable = !!progressEventRaw.total;
};

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;
}
};
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require) {
var sinon = require("./core");
makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require);
} else {
makeApi(sinon); // eslint-disable-line no-undef
}
}());

/**
* depend util/core.js
*/
/**
* Logs errors
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2014 Christian Johansen
*/
(function (sinonGlobal) {

// cache a reference to setTimeout, so that our reference won't be stubbed out
// when using fake timers and errors will still get logged
// github.com/cjohansen/Sinon.JS/issues/381
var realSetTimeout = setTimeout;

function makeApi(sinon) {

function log() {}

function logError(label, err) {
var msg = label + " threw exception: ";

function throwLoggedError() {
err.message = msg + err.message;
throw err;
}

sinon.log(msg + "[" + err.name + "] " + err.message);

if (err.stack) {
sinon.log(err.stack);
}

if (logError.useImmediateExceptions) {
throwLoggedError();
} else {
logError.setTimeout(throwLoggedError, 0);
}
}

// When set to true, any errors logged will be thrown immediately;
// If set to false, the errors will be thrown in separate execution frame.
logError.useImmediateExceptions = false;

// wrap realSetTimeout with something we can stub in tests
logError.setTimeout = function (func, timeout) {
realSetTimeout(func, timeout);
};

var exports = {};
exports.log = sinon.log = log;
exports.logError = sinon.logError = logError;

return exports;
}

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
module.exports = makeApi(sinon);
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend core.js
* depend ../extend.js
* depend event.js
* depend ../log_error.js
*/
/**
* Fake XDomainRequest object
*/
if (typeof sinon === «undefined») {
this.sinon = {};
}

// wrapper for global
(function (global) {

var xdr = { XDomainRequest: global.XDomainRequest };
xdr.GlobalXDomainRequest = global.XDomainRequest;
xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== «undefined»;
xdr.workingXDR = xdr.supportsXDR? xdr.GlobalXDomainRequest: false;

function makeApi(sinon) {
sinon.xdr = xdr;

function FakeXDomainRequest() {
this.readyState = FakeXDomainRequest.UNSENT;
this.requestBody = null;
this.requestHeaders = {};
this.status = 0;
this.timeout = null;

if (typeof FakeXDomainRequest.onCreate === «function») {
FakeXDomainRequest.onCreate(this);
}
}

function verifyState(x) {
if (x.readyState !== FakeXDomainRequest.OPENED) {
throw new Error(«INVALID_STATE_ERR»);
}

if (x.sendFlag) {
throw new Error(«INVALID_STATE_ERR»);
}
}

function verifyRequestSent(x) {
if (x.readyState === FakeXDomainRequest.UNSENT) {
throw new Error(«Request not sent»);
}
if (x.readyState === FakeXDomainRequest.DONE) {
throw new Error(«Request done»);
}
}

function verifyResponseBodyType(body) {
if (typeof body !== «string») {
var error = new Error(«Attempted to respond to fake XDomainRequest with » +
body + ", which is not a string.");
error.name = «InvalidBodyException»;
throw error;
}
}

sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, {
open: function open(method, url) {
this.method = method;
this.url = url;

this.responseText = null;
this.sendFlag = false;

this.readyStateChange(FakeXDomainRequest.OPENED);
},

readyStateChange: function readyStateChange(state) {
this.readyState = state;
var eventName = "";
switch (this.readyState) {
case FakeXDomainRequest.UNSENT:
break;
case FakeXDomainRequest.OPENED:
break;
case FakeXDomainRequest.LOADING:
if (this.sendFlag) {
//raise the progress event
eventName = «onprogress»;
}
break;
case FakeXDomainRequest.DONE:
if (this.isTimeout) {
eventName = «ontimeout»;
} else if (this.errorFlag || (this.status < 200 || this.status > 299)) {
eventName = «onerror»;
} else {
eventName = «onload»;
}
break;
}

// raising event (if defined)
if (eventName) {
if (typeof this[eventName] === «function») {
try {
this[eventName]();
} catch (e) {
sinon.logError(«Fake XHR » + eventName + " handler", e);
}
}
}
},

send: function send(data) {
verifyState(this);

if (!/^(get|head)$/i.test(this.method)) {
this.requestBody = data;
}
this.requestHeaders[«Content-Type»] = «text/plain;charset=utf-8»;

this.errorFlag = false;
this.sendFlag = true;
this.readyStateChange(FakeXDomainRequest.OPENED);

if (typeof this.onSend === «function») {
this.onSend(this);
}
},

abort: function abort() {
this.aborted = true;
this.responseText = null;
this.errorFlag = true;

if (this.readyState > sinon.FakeXDomainRequest.UNSENT && this.sendFlag) {
this.readyStateChange(sinon.FakeXDomainRequest.DONE);
this.sendFlag = false;
}
},

setResponseBody: function setResponseBody(body) {
verifyRequestSent(this);
verifyResponseBodyType(body);

var chunkSize = this.chunkSize || 10;
var index = 0;
this.responseText = "";

do {
this.readyStateChange(FakeXDomainRequest.LOADING);
this.responseText += body.substring(index, index + chunkSize);
index += chunkSize;
} while (index < body.length);

this.readyStateChange(FakeXDomainRequest.DONE);
},

respond: function respond(status, contentType, body) {
// content-type ignored, since XDomainRequest does not carry this
// we keep the same syntax for respond(...) as for FakeXMLHttpRequest to ease
// test integration across browsers
this.status = typeof status === «number»? status: 200;
this.setResponseBody(body || "");
},

simulatetimeout: function simulatetimeout() {
this.status = 0;
this.isTimeout = true;
// Access to this should actually throw an error
this.responseText = undefined;
this.readyStateChange(FakeXDomainRequest.DONE);
}
});

sinon.extend(FakeXDomainRequest, {
UNSENT: 0,
OPENED: 1,
LOADING: 3,
DONE: 4
});

sinon.useFakeXDomainRequest = function useFakeXDomainRequest() {
sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) {
if (xdr.supportsXDR) {
global.XDomainRequest = xdr.GlobalXDomainRequest;
}

delete sinon.FakeXDomainRequest.restore;

if (keepOnCreate !== true) {
delete sinon.FakeXDomainRequest.onCreate;
}
};
if (xdr.supportsXDR) {
global.XDomainRequest = sinon.FakeXDomainRequest;
}
return sinon.FakeXDomainRequest;
};

sinon.FakeXDomainRequest = FakeXDomainRequest;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./core");
require("../extend");
require("./event");
require("../log_error");
makeApi(sinon);
module.exports = sinon;
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require, module.exports, module);
} else {
makeApi(sinon); // eslint-disable-line no-undef
}
})(typeof global !== «undefined»? global: self);

/**
* depend core.js
* depend ../extend.js
* depend event.js
* depend ../log_error.js
*/
/**
* Fake XMLHttpRequest object
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal, global) {

function getWorkingXHR(globalScope) {
var supportsXHR = typeof globalScope.XMLHttpRequest !== «undefined»;
if (supportsXHR) {
return globalScope.XMLHttpRequest;
}

var supportsActiveX = typeof globalScope.ActiveXObject !== «undefined»;
if (supportsActiveX) {
return function () {
return new globalScope.ActiveXObject(«MSXML2.XMLHTTP.3.0»);
};
}

return false;
}

var supportsProgress = typeof ProgressEvent !== «undefined»;
var supportsCustomEvent = typeof CustomEvent !== «undefined»;
var supportsFormData = typeof FormData !== «undefined»;
var supportsArrayBuffer = typeof ArrayBuffer !== «undefined»;
var supportsBlob = typeof Blob === «function»;
var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest };
sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
sinonXhr.GlobalActiveXObject = global.ActiveXObject;
sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== «undefined»;
sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== «undefined»;
sinonXhr.workingXHR = getWorkingXHR(global);
sinonXhr.supportsCORS = sinonXhr.supportsXHR && «withCredentials» in (new sinonXhr.GlobalXMLHttpRequest());

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
};

// 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 addEventListener(event, listener) {
this.eventListeners[event].push(listener);
};

UploadProgress.prototype.removeEventListener = function removeEventListener(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 dispatchEvent(event) {
var listeners = this.eventListeners[event.type] || [];

for (var i = 0, listener; (listener = listeners[i]) != null; i++) {
listener(event);
}
};

// Note that for FakeXMLHttpRequest to work pre ES5
// we lose some of the alignment with the spec.
// To ensure as close a match as possible,
// set responseType before calling open, send or respond;
function FakeXMLHttpRequest() {
this.readyState = FakeXMLHttpRequest.UNSENT;
this.requestHeaders = {};
this.requestBody = null;
this.status = 0;
this.statusText = "";
this.upload = new UploadProgress();
this.responseType = "";
this.response = "";
if (sinonXhr.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);
}
}

function verifyState(xhr) {
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
throw new Error(«INVALID_STATE_ERR»);
}

if (xhr.sendFlag) {
throw new Error(«INVALID_STATE_ERR»);
}
}

function getHeader(headers, header) {
header = header.toLowerCase();

for (var h in headers) {
if (h.toLowerCase() === header) {
return h;
}
}

return null;
}

// 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 addFilter(fn) {
this.filters.push(fn);
};
var IE6Re = /MSIE 6/;
FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap

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 stateChange() {
fakeXhr.readyState = xhr.readyState;
if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
copyAttrs([«status», «statusText»]);
}
if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
copyAttrs([«responseText», «response»]);
}
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)) {

/*eslint-disable no-loop-func*/
each(fakeXhr.eventListeners[event], function (handler) {
xhr.addEventListener(event, handler);
});
/*eslint-enable no-loop-func*/
}
}
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;
}
}

function convertToArrayBuffer(body) {
var buffer = new ArrayBuffer(body.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < body.length; i++) {
var charCode = body.charCodeAt(i);
if (charCode >= 256) {
throw new TypeError(«arraybuffer or blob responseTypes require binary string, » +
«invalid character » + body[i] + " found.");
}
view[i] = charCode;
}
return buffer;
}

function isXmlContentType(contentType) {
return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType);
}

function convertResponseBody(responseType, contentType, body) {
if (responseType === "" || responseType === «text») {
return body;
} else if (supportsArrayBuffer && responseType === «arraybuffer») {
return convertToArrayBuffer(body);
} else if (responseType === «json») {
try {
return JSON.parse(body);
} catch (e) {
// Return parsing failure as null
return null;
}
} else if (supportsBlob && responseType === «blob») {
var blobOptions = {};
if (contentType) {
blobOptions.type = contentType;
}
return new Blob([convertToArrayBuffer(body)], blobOptions);
} else if (responseType === «document») {
if (isXmlContentType(contentType)) {
return FakeXMLHttpRequest.parseXML(body);
}
return null;
}
throw new Error(«Invalid responseType » + responseType);
}

function clearResponse(xhr) {
if (xhr.responseType === "" || xhr.responseType === «text») {
xhr.response = xhr.responseText = "";
} else {
xhr.response = xhr.responseText = null;
}
xhr.responseXML = null;
}

FakeXMLHttpRequest.parseXML = function parseXML(text) {
// Treat empty string as parsing failure
if (text !== "") {
try {
if (typeof DOMParser !== «undefined») {
var parser = new DOMParser();
return parser.parseFromString(text, «text/xml»);
}
var xmlDoc = new window.ActiveXObject(«Microsoft.XMLDOM»);
xmlDoc.async = «false»;
xmlDoc.loadXML(text);
return xmlDoc;
} catch (e) {
// Unable to parse XML — no biggie
}
}

return null;
};

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»,
207: «Multi-Status»,
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»
};

function makeApi(sinon) {
sinon.xhr = sinonXhr;

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;
clearResponse(this);
this.requestHeaders = {};
this.sendFlag = false;

if (FakeXMLHttpRequest.useFilters === true) {
var xhrArgs = arguments;
var defake = some(FakeXMLHttpRequest.filters, function (filter) {
return filter.apply(this, xhrArgs);
});
if (defake) {
return FakeXMLHttpRequest.defake(this, arguments);
}
}
this.readyStateChange(FakeXMLHttpRequest.OPENED);
},

readyStateChange: function readyStateChange(state) {
this.readyState = state;

var readyStateChangeEvent = new sinon.Event(«readystatechange», false, false, this);

if (typeof this.onreadystatechange === «function») {
try {
this.onreadystatechange(readyStateChangeEvent);
} catch (e) {
sinon.logError(«Fake XHR onreadystatechange handler», e);
}
}

switch (this.readyState) {
case FakeXMLHttpRequest.DONE:
if (supportsProgress) {
this.upload.dispatchEvent(new sinon.ProgressEvent(«progress», {loaded: 100, total: 100}));
this.dispatchEvent(new sinon.ProgressEvent(«progress», {loaded: 100, total: 100}));
}
this.upload.dispatchEvent(new sinon.Event(«load», false, false, this));
this.dispatchEvent(new sinon.Event(«load», false, false, this));
this.dispatchEvent(new sinon.Event(«loadend», false, false, this));
break;
}

this.dispatchEvent(readyStateChangeEvent);
},

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 (ie no Document)
send: function send(data) {
verifyState(this);

if (!/^(get|head)$/i.test(this.method)) {
var contentType = getHeader(this.requestHeaders, «Content-Type»);
if (this.requestHeaders[contentType]) {
var value = this.requestHeaders[contentType].split(";");
this.requestHeaders[contentType] = value[0] + ";charset=utf-8";
} else if (supportsFormData && !(data instanceof FormData)) {
this.requestHeaders[«Content-Type»] = «text/plain;charset=utf-8»;
}

this.requestBody = data;
}

this.errorFlag = false;
this.sendFlag = this.async;
clearResponse(this);
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;
clearResponse(this);
this.errorFlag = true;
this.requestHeaders = {};
this.responseHeaders = {};

if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) {
this.readyStateChange(FakeXMLHttpRequest.DONE);
this.sendFlag = false;
}

this.readyState = 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 = getHeader(this.responseHeaders, header);

return this.responseHeaders[header] || 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 contentType = this.getResponseHeader(«Content-Type»);

var isTextResponse = this.responseType === "" || this.responseType === «text»;
clearResponse(this);
if (this.async) {
var chunkSize = this.chunkSize || 10;
var index = 0;

do {
this.readyStateChange(FakeXMLHttpRequest.LOADING);

if (isTextResponse) {
this.responseText = this.response += body.substring(index, index + chunkSize);
}
index += chunkSize;
} while (index < body.length);
}

this.response = convertResponseBody(this.responseType, contentType, body);
if (isTextResponse) {
this.responseText = this.response;
}

if (this.responseType === «document») {
this.responseXML = this.response;
} else if (this.responseType === "" && isXmlContentType(contentType)) {
this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
}
this.readyStateChange(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));
}
},

downloadProgress: function downloadProgress(progressEventRaw) {
if (supportsProgress) {
this.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
});

sinon.useFakeXMLHttpRequest = function () {
FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
if (sinonXhr.supportsXHR) {
global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest;
}

if (sinonXhr.supportsActiveX) {
global.ActiveXObject = sinonXhr.GlobalActiveXObject;
}

delete FakeXMLHttpRequest.restore;

if (keepOnCreate !== true) {
delete FakeXMLHttpRequest.onCreate;
}
};
if (sinonXhr.supportsXHR) {
global.XMLHttpRequest = FakeXMLHttpRequest;
}

if (sinonXhr.supportsActiveX) {
global.ActiveXObject = function ActiveXObject(objId) {
if (objId === «Microsoft.XMLHTTP» || /^Msxml2\.XMLHTTP/i.test(objId)) {

return new FakeXMLHttpRequest();
}

return new sinonXhr.GlobalActiveXObject(objId);
};
}

return FakeXMLHttpRequest;
};

sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./core");
require("../extend");
require("./event");
require("../log_error");
makeApi(sinon);
module.exports = sinon;
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon, // eslint-disable-line no-undef
typeof global !== «undefined»? global: self
));

/**
* depend fake_xdomain_request.js
* depend fake_xml_http_request.js
* depend ../format.js
* depend ../log_error.js
*/
/**
* 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 © 2010-2013 Christian Johansen
*/
(function () {

var push = [].push;

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 makeApi(sinon) {
sinon.fakeServer = {
create: function (config) {
var server = sinon.create(this);
server.configure(config);
if (!sinon.xhr.supportsCORS) {
this.xhr = sinon.useFakeXDomainRequest();
} else {
this.xhr = sinon.useFakeXMLHttpRequest();
}
server.requests = [];

this.xhr.onCreate = function (xhrObj) {
server.addRequest(xhrObj);
};

return server;
},
configure: function (config) {
var whitelist = {
«autoRespond»: true,
«autoRespondAfter»: true,
«respondImmediately»: true,
«fakeHTTPMethods»: true
};
var setting;

config = config || {};
for (setting in config) {
if (whitelist.hasOwnProperty(setting) && config.hasOwnProperty(setting)) {
this[setting] = config[setting];
}
}
},
addRequest: function addRequest(xhrObj) {
var server = this;
push.call(this.requests, xhrObj);

xhrObj.onSend = function () {
server.handleRequest(this);

if (server.respondImmediately) {
server.respond();
} else 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);
}
},

log: 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);
},

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, queue.length);

for (var i = 0; i < requests.length; i++) {
this.processRequest(requests[i]);
}
},

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) {
this.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);
}
};
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./core");
require("./fake_xdomain_request");
require("./fake_xml_http_request");
require("../format");
makeApi(sinon);
module.exports = sinon;
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require, module.exports, module);
} else {
makeApi(sinon); // eslint-disable-line no-undef
}
}());

/**
* depend fake_server.js
* depend fake_timers.js
*/
/**
* 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 © 2010-2013 Christian Johansen
*/
(function () {

function makeApi(sinon) {
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);
};
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require) {
var sinon = require("./core");
require("./fake_server");
require("./fake_timers");
makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require);
} else {
makeApi(sinon); // eslint-disable-line no-undef
}
}());

/**
* depend util/core.js
* depend extend.js
* depend collection.js
* depend util/fake_timers.js
* depend util/fake_server_with_clock.js
*/
/**
* 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 © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
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;
}

obj.match = sinon.match;

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;
var 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;
},

match: sinon.match
});

sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;

return sinon.sandbox;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./extend");
require("./util/fake_server_with_clock");
require("./util/fake_timers");
require("./collection");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend util/core.js
* depend sandbox.js
*/
/**
* Test function, sandboxes fakes
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

function makeApi(sinon) {
var slice = Array.prototype.slice;

function test(callback) {
var type = typeof callback;

if (type !== «function») {
throw new TypeError(«sinon.test needs to wrap a test function, got » + type);
}

function sinonSandboxedTest() {
var config = sinon.getConfig(sinon.config);
config.injectInto = config.injectIntoThis && this || config.injectInto;
var sandbox = sinon.sandbox.create(config);
var args = slice.call(arguments);
var oldDone = args.length && args[args.length — 1];
var exception, result;

if (typeof oldDone === «function») {
args[args.length — 1] = function sinonDone(res) {
if (res) {
sandbox.restore();
} else {
sandbox.verifyAndRestore();
}
oldDone(res);
};
}

try {
result = callback.apply(this, args.concat(sandbox.args));
} catch (e) {
exception = e;
}

if (typeof oldDone !== «function») {
if (typeof exception !== «undefined») {
sandbox.restore();
throw exception;
} else {
sandbox.verifyAndRestore();
}
}

return result;
}

if (callback.length) {
return function sinonAsyncSandboxedTest(done) { // eslint-disable-line no-unused-vars
return sinonSandboxedTest.apply(this, arguments);
};
}

return sinonSandboxedTest;
}

test.config = {
injectIntoThis: true,
injectInto: null,
properties: [«spy», «stub», «mock», «clock», «server», «requests»],
useFakeTimers: true,
useFakeServer: true
};

sinon.test = test;
return test;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var core = require("./util/core");
require("./sandbox");
module.exports = makeApi(core);
}

if (isAMD) {
define(loadDependencies);
} else if (isNode) {
loadDependencies(require, module.exports, module);
} else if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(typeof sinon === «object» && sinon || null)); // eslint-disable-line no-undef

/**
* depend util/core.js
* depend test.js
*/
/**
* Test case, sandboxes all test functions
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal) {

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 makeApi(sinon) {
function testCase(tests, prefix) {
if (!tests || typeof tests !== «object») {
throw new TypeError(«sinon.testCase needs an object with test functions»);
}

prefix = prefix || «test»;
var rPrefix = new RegExp("^" + prefix);
var methods = {};
var setUp = tests.setUp;
var tearDown = tests.tearDown;
var testName,
property,
method;

for (testName in tests) {
if (tests.hasOwnProperty(testName) && !/^(setUp|tearDown)$/.test(testName)) {
property = tests[testName];

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;
}

sinon.testCase = testCase;
return testCase;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var core = require("./util/core");
require("./test");
module.exports = makeApi(core);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon // eslint-disable-line no-undef
));

/**
* depend times_in_words.js
* depend util/core.js
* depend match.js
* depend format.js
*/
/**
* Assertions matching the test spy retrieval interface.
*
* author Christian Johansen (christian@cjohansen.no)
* @license BSD
*
* Copyright © 2010-2013 Christian Johansen
*/
(function (sinonGlobal, global) {

var slice = Array.prototype.slice;

function makeApi(sinon) {
var assert;

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 (method.proxy && method.proxy.isSinonProxy) {
verifyIsStub(method.proxy);
} else {
if (typeof method !== «function») {
assert.fail(method + " is not a function");
}

if (typeof method.getCall !== «function») {
assert.fail(method + " is not stubbed");
}
}

}
}

function 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 || fake.proxy.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() {},

callOrder: function assertCallOrder() {
verifyIsStub.apply(null, arguments);
var expected = "";
var 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 !== «expose» && (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");

sinon.assert = assert;
return assert;
}

var isNode = typeof module !== «undefined» && module.exports && typeof require === «function»;
var isAMD = typeof define === «function» && typeof define.amd === «object» && define.amd;

function loadDependencies(require, exports, module) {
var sinon = require("./util/core");
require("./match");
require("./format");
module.exports = makeApi(sinon);
}

if (isAMD) {
define(loadDependencies);
return;
}

if (isNode) {
loadDependencies(require, module.exports, module);
return;
}

if (sinonGlobal) {
makeApi(sinonGlobal);
}
}(
typeof sinon === «object» && sinon, // eslint-disable-line no-undef
typeof global !== «undefined»? global: self
));

return sinon;
}));


.

Source: https://habr.com/ru/post/274317/


All Articles