{"id":271428,"date":"2015-12-30T18:30:09","date_gmt":"2015-12-30T15:30:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=271428"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=271428","title":{"rendered":"Sinon.js \u2014 mock-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f JavaScript"},"content":{"rendered":"<p>       Sinon.js \u2014 \u044d\u0442\u043e mock-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f JavaScript, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441 \u043b\u044e\u0431\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c. \u041e\u043d\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u044d\u043c\u0443\u043b\u044f\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0432 JavaScript. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0442\u0440\u0438 \u0432\u0438\u0434\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 spy, stub \u0438 mock. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u043e API Sinon.js \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043a\u0440\u0430\u0442\u043a\u0438\u043c \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0432 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. <\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/65f\/f58\/f12\/65ff58f123fa431a99aa5b19f416ea04.jpg\"\/><br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h4>Spy. <\/h4>\n<p>  Spy(\u0448\u043f\u0438\u043e\u043d) \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438 \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 (\u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u044b\u0435 \u0431\u044b\u043b\u0438) \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432. \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0448\u043f\u0438\u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0438\u043b\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u043f\u043e\u0432\u0435\u0440\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. <\/p>\n<p>  \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0448\u043f\u0438\u043e\u043d\u044b \u043f\u043e\u043b\u0435\u0437\u043d\u044b, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0438 \u043a\u0430\u043a \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\/\u043c\u0435\u0442\u043e\u0434\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0442\u0435\u0441\u0442\u043e\u0432. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0448\u043f\u0438\u043e\u043d\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430: <\/p>\n<pre><code class=\"javascript\">&quot;test should call subscribers on publish&quot;: function () {     var callback = sinon.spy();     PubSub.subscribe(&quot;message&quot;, callback);      PubSub.publishSync(&quot;message&quot;);      assertTrue(callback.called); } <\/code><\/pre>\n<p>  Sinon.spy \u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438. \u041f\u0440\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u0435\u0441\u0442\u0438 \u0441\u0435\u0431\u044f \u0441\u0442\u043e\u043b\u044c \u0436\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e, \u043d\u043e \u0443 \u0412\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0434\u0430\u043d\u043d\u044b\u043c \u043f\u0440\u043e \u0432\u0441\u0435 \u0432\u044b\u0437\u043e\u0432\u044b. \u041d\u0438\u0436\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440. <\/p>\n<pre><code class=\"javascript\">{     setUp: function () {         sinon.spy(jQuery, &quot;ajax&quot;);     },      tearDown: function () {         jQuery.ajax.restore(); \/\/ Unwraps the spy     },      &quot;test should inspect jQuery.getJSON's usage of jQuery.ajax&quot;: function () {         jQuery.getJSON(&quot;\/some\/resource&quot;);          assert(jQuery.ajax.calledOnce);         assertEquals(&quot;\/some\/resource&quot;, jQuery.ajax.getCall(0).args[0].url);         assertEquals(&quot;json&quot;, jQuery.ajax.getCall(0).args[0].dataType);     } } <\/code><\/pre>\n<p>  <\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0448\u043f\u0438\u043e\u043d\u043e\u0432: sinon.spy ()<\/h4>\n<p>  <b><u><code>var spy = sinon.spy();<\/code><\/u><\/b><br \/>  \u0417\u0430\u0434\u0430\u0435\u0442 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u0440\u043e \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<br \/>  <b><u><code>var spy = sinon.spy(myFunc);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0448\u043f\u0438\u043e\u043d\u0430 \u043f\u043e\u0432\u0435\u0440\u0445 \u0433\u043e\u0442\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. <br \/>  <b><u><code>var spy = sinon.spy(object, &quot;method&quot;);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0448\u043f\u0438\u043e\u043d\u0430 \u0434\u043b\u044f <code>object.method<\/code> \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434. \u041e\u043d \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u043e \u0432\u0441\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445. \u041f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>object.method.restore ()<\/code>.<\/p>\n<h4>Spy API<\/h4>\n<p>  Spy \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0442 \u0448\u0438\u0440\u043e\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0438\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f. \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0438 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>calledOnce<\/code>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043c\u0435\u0442\u043e\u0434 <code>getCall<\/code> \u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u0415\u0441\u0442\u044c \u0442\u0440\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432. <\/p>\n<p>  \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 <code>calledWith<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0438\u0437\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0412\u0430\u0448 \u0442\u0435\u0441\u0442 \u043e\u0442 \u0438\u0437\u043b\u0438\u0448\u043d\u0435\u0439 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u043a\u0438. \u0412\u0430\u043c \u0432\u0435\u0434\u044c \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0437\u043d\u0430\u0442\u044c \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u0441\u044f \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0438\u043d\u043e\u0433\u0434\u0430 \u0431\u044b\u0432\u0430\u0435\u0442 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u0430\u043d\u043d\u044b\u0445 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. <\/p>\n<pre><code class=\"javascript\">&quot;test should call subscribers with message as first argument&quot; : function () {     var message = 'an example message';     var spy = sinon.spy();      PubSub.subscribe(message, spy);     PubSub.publishSync(message, &quot;some payload&quot;);      assert(spy.calledWith(message)); } <\/code><\/pre>\n<p>  \u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u043a\u0438, \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u043f\u043e\u043c\u043e\u0433\u0443\u0442 \u0412\u0430\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e:<\/p>\n<pre><code class=\"javascript\">&quot;test should call subscribers with message as first argument&quot; : function () {     var message = 'an example message';     var spy = sinon.spy();      PubSub.subscribe(message, spy);     PubSub.publishSync(message, &quot;some payload&quot;);      assertEquals(message, spy.args[0][0]); } <\/code><\/pre>\n<pre><code class=\"javascript\">&quot;test should call subscribers with message as first argument&quot; : function () {     var message = 'an example message';     var spy = sinon.spy();      PubSub.subscribe(message, spy);     PubSub.publishSync(message, &quot;some payload&quot;);      assertEquals(message, spy.getCall(0).args[0]); } <\/code><\/pre>\n<p>  \u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043d\u0430 \u0448\u043f\u0438\u043e\u043d\u0435, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0437\u043e\u0432\u0430, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u0435\u0433\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0443. \u041a\u0430\u043a\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u2014 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u043c \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0435\u043d\u0438\u044f, \u043d\u043e \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u0445\u043e\u0434 <code>spy.calledWith (arg1, arg2...)<\/code>, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435\u0441\u0442\u044b \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u043c.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Spy API<\/b><\/p>\n<div class=\"spoiler_text\">Spy-\u043e\u0431\u044a\u0435\u043a\u0442 \u0438\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u044b\u0439 \u0438\u0437 <code>sinon.spy()<\/code>. \u041f\u0440\u0438 \u0441\u043b\u0435\u0436\u0435\u043d\u0438\u0438 \u0437\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0441 <code>sinon.spy (object, method)<\/code>, \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430 <code>object.method<\/code>.<\/p>\n<p>  <b><u><code>spy.withArgs(arg1[, arg2, ...]);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0448\u043f\u0438\u043e\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u044b\u0437\u043e\u0432\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u043e\u0433\u0434\u0430, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u043c \u0434\u043b\u044f <code>withArgs<\/code>.<\/p>\n<pre><code class=\"javascript\">&quot;should call method once with each argument&quot;: function () {     var object = { method: function () {} };     var spy = sinon.spy(object, &quot;method&quot;);     spy.withArgs(42);     spy.withArgs(1);      object.method(42);     object.method(1);      assert(spy.withArgs(42).calledOnce);     assert(spy.withArgs(1).calledOnce); } <\/code><\/pre>\n<p>  <b><u><code>spy.callCount<\/code><\/u><\/b><br \/>  \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0447\u0438\u0441\u043b\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<br \/>  <b><u><code>spy.called<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0445\u043e\u0442\u044f \u0431\u044b \u0440\u0430\u0437. <br \/>  <b><u><code>spy.calledOnce<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437. <br \/>  <b><u><code>spy.calledTwice<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0430 \u0440\u0430\u0437.<br \/>  <b><u><code>spy.calledThrice<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0440\u0438 \u0440\u0430\u0437\u0430.<br \/>  <b><u><code>spy.firstCall<\/code><\/u><\/b><br \/>  \u041f\u0435\u0440\u0432\u044b\u0439 \u0432\u044b\u0437\u043e\u0432. <br \/>  <b><u><code>spy.secondCall<\/code><\/u><\/b><br \/>  \u0412\u0442\u043e\u0440\u043e\u0439 \u0432\u044b\u0437\u043e\u0432.<br \/>  <b><u><code>spy.thirdCall<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0442\u0438\u0439 \u0432\u044b\u0437\u043e\u0432.<br \/>  <b><u><code>spy.lastCall<\/code><\/u><\/b><br \/>  \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0432\u044b\u0437\u043e\u0432.<br \/>  <b><u><code>spy.calledBefore(anotherSpy);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c <code>anotherSpy<\/code>.<br \/>  <b><u><code>spy.calledAfter(anotherSpy);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u043f\u043e\u0441\u043b\u0435 <code>anotherSpy<\/code>.<br \/>  <b><u><code>spy.calledOn(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0445\u043e\u0442\u044f \u0431\u044b \u0440\u0430\u0437 \u0441 obj \u043a\u0430\u043a this. <br \/>  <b><u><code>spy.alwaysCalledOn(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 obj \u043a\u0430\u043a this. <br \/>  <b><u><code>spy.calledWith(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u0432\u0430\u043d, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435 \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u041c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d \u0434\u0430\u0436\u0435 \u043f\u0440\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u043c \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0438, Sinon \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u0440\u043e\u0442\u0438\u0432 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0432 \u0442\u0435\u0445 \u0436\u0435 \u043c\u0435\u0441\u0442\u0430\u0445) \u0432\u044b\u0434\u0430\u0441\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435. <br \/>  <b><u><code>spy.alwaysCalledWith(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438).<br \/>  <b><u><code>spy.alwaysCalledWithExactly(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043f\u043e\u043b\u043d\u043e\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0432\u0438\u0438 \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>spy.calledWithMatch(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438). \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>spy.calledWith(sinon.match(arg1), sinon.match(arg2), ...)<\/code>.<br \/>  <b><u><code>spy.alwaysCalledWithMatch(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>spy.alwaysCalledWith(sinon.match(arg1), sinon.match(arg2), ...)<\/code>.<br \/>  <b><u><code>spy.calledWithNew();<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy\/stub \u0431\u044b\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u044b \u043d\u043e\u0432\u044b\u043c \u043e\u043f\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u043c. \u041f\u043e\u043c\u043d\u0438\u0442\u0435, \u0447\u0442\u043e \u044d\u0442\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0448\u043f\u0438\u043e\u043d\u0430, \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0434\u0430\u0442\u044c \u043b\u043e\u0436\u043d\u043e\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0435\u0441\u043b\u0438 \u0432\u044b \u0430\u043a\u0442\u0438\u0432\u043d\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u0432\u0438\u0434 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<br \/>  <b><u><code>spy.neverCalledWith(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy\/stub \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>spy.neverCalledWithMatch(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 spy\/stub \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>spy.neverCalledWith(sinon.match(arg1), sinon.match(arg2), ...).<\/code>.<br \/>  <b><u><code>spy.threw();<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435, \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<br \/>  <b><u><code>spy.threw(&quot;TypeError&quot;);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0434\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430, \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<br \/>  <b><u><code>spy.threw(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0434\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435 \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<br \/>  <b><u><code>spy.alwaysThrew();<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435.<br \/>  <b><u><code>spy.alwaysThrew(&quot;TypeError&quot;);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.<br \/>  <b><u><code>spy.alwaysThrew(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<br \/>  <b><u><code>spy.returned(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435, \u043e\u0434\u0438\u043d \u0440\u0430\u0437. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0435 \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u0432. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 spy.returned (sinon.match.same (obj)) \u0434\u043b\u044f \u0441\u0442\u0440\u043e\u0433\u043e\u0433\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f.<br \/>  <b><u><code>spy.alwaysReturned(obj);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.<br \/>  <b><u><code>var spyCall = spy.getCall(n);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u044d\u043d\u043d\u044b\u0439 [call](#spycall). \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u0430\u043c, \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u0434\u0438\u043d \u0440\u0430\u0437. \u041f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"javascript\">sinon.spy(jQuery, &quot;ajax&quot;); jQuery.ajax(&quot;\/stuffs&quot;); var spyCall = jQuery.ajax.getCall(0);  assertEquals(&quot;\/stuffs&quot;, spyCall.args[0]); <\/code><\/pre>\n<p>  <b><u><code>spy.thisValues<\/code><\/u><\/b><br \/>  \u0412 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>spy.thisValues[0]<\/code> \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.<br \/>  <b><u><code>spy.args<\/code><\/u><\/b><br \/>  \u041c\u0430\u0441\u0441\u0438\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 <code>spy.args [0]<\/code> \u2014 \u044d\u0442\u043e \u043c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u043f\u0435\u0440\u0432\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435.<br \/>  <b><u><code>spy.exceptions<\/code><\/u><\/b><br \/>  \u0412 \u043c\u0430\u0441\u0441\u0438\u0432\u0435 \u0434\u043b\u044f \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 <code>spy.exceptions[0]<\/code> \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f, \u0432\u044b\u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0435\u0440\u0432\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c. \u0415\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043d\u0435 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443, \u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u043f\u043e\u043b\u0435 <code>.exceptions<\/code> \u0431\u0443\u0434\u0435\u0442 <code>\u2018undefined\u2019<\/code>.<br \/>  <b><u><code>spy.returnValues <\/code><\/u><\/b><br \/>  \u041c\u0430\u0441\u0441\u0438\u0432 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, <code>spy.returnValues[0]<\/code> \u2014 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. \u0415\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u0432\u0435\u0440\u043d\u0443\u043b \u043d\u0435 \u0442\u043e\u0447\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044f \u0432 <code>.returnValues<\/code> \u0431\u0443\u0434\u0435\u0442 <code>\u2018undefined\u2019<\/code>. <br \/>  <b><u><code>spy.reset()<\/code><\/u><\/b><br \/>  \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0448\u043f\u0438\u043e\u043d\u0430.<br \/>  <b><u><code>spy.printf(format string&quot;, [arg1, arg2, ...])`<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0437\u0430\u043c\u0435\u043d:   <\/p>\n<ul>\n<li>#n: \u0438\u043c\u044f \u0448\u043f\u0438\u043e\u043d\u0430 (\u00abspy\u00bb \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e) <\/li>\n<li>#\u0441: \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u0432\u0430\u043b\u0438 \u0432 \u0441\u043b\u043e\u0432\u0430\u0445 (\u201conce\u201d, \u201ctwice\u201d, \u0438 \u0442.\u0434.)<\/li>\n<li>#\u0421: \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u0448\u043f\u0438\u043e\u043d\u0430, \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u043f\u043e\u043c\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c \u043d\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u0447\u0435\u0442\u044b\u0440\u044c\u043c\u044f \u043f\u0440\u043e\u0431\u0435\u043b\u0430\u043c\u0438 <\/li>\n<li>#t: \u0440\u0430\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u044f\u0442\u044b\u043c\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d \u0448\u043f\u0438\u043e\u043d <\/li>\n<li>#: n \u043e\u0442\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u044d\u043d\u043d\u043e\u0433\u043e \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0433\u043e printf<\/li>\n<li>#*: \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u044f\u0442\u044b\u043c\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0437 (\u043d\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438) \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0445 printf<\/li>\n<\/ul>\n<p>  <\/p>\n<h4>\u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0448\u043f\u0438\u043e\u043d\u0430<\/h4>\n<p>  <b><u><code>var spyCall = spy.getCall(n)<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u044d\u043d\u043d\u044b\u0439 <code>[call](#spycall)<\/code>. \u0414\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u0430\u043c \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u043e\u0439 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f, \u043a\u043e\u0433\u0434\u0430 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437. \u041f\u0440\u0438\u043c\u0435\u0440: <\/p>\n<pre><code class=\"javascript\">sinon.spy(jQuery, &quot;ajax&quot;); jQuery.ajax(&quot;\/stuffs&quot;); var spyCall = jQuery.ajax.getCall(0);  assertEquals(&quot;\/stuffs&quot;, spyCall.args[0]); <\/code><\/pre>\n<p>  <b><u><code>spyCall.calledOn(obj);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0431\u044b\u043b \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. <br \/>  <b><u><code>spyCall.calledWith(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435).<br \/>  <b><u><code>spyCall.calledWithExactly(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u0435 \u0434\u0440\u0443\u0433\u0438\u0435. <br \/>  <b><u><code>spyCall.calledWithMatch(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435). \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>spyCall.calledWith (sinon.match (arg1), sinon.match (arg2)...).<\/code><br \/>  <b><u><code>spyCall.notCalledWith(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b.<br \/>  <b><u><code>spyCall.notCalledWithMatch(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>spyCall.notCalledWith(sinon.match(arg1), sinon.match(arg2), ...).;<\/code><br \/>  <b><u><code>spyCall.threw();<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u0432\u044b\u0434\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443.<br \/>  <b><u><code>spyCall.threw(TypeError&quot;);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u0432\u044b\u0434\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430. <br \/>  <b><u><code>spyCall.threw(obj);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u0432\u044b\u0434\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430. <br \/>  <b><u><code>spyCall.thisValue<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u043e\u0432\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>this<\/code>. <br \/>  <b><u><code>spyCall.args<\/code><\/u><\/b><br \/>  \u041c\u0430\u0441\u0441\u0438\u0432 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432.<br \/>  <b><u><code>spyCall.exception<\/code><\/u><\/b><br \/>  \u0412\u044b\u0434\u0430\u043d\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 (\u043e\u0448\u0438\u0431\u043a\u0430), \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u044b \u0438\u043c\u0435\u044e\u0442\u0441\u044f.<br \/>  <b><u><code>spyCall.returnValue<\/code><\/u><\/b><br \/>  \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>Stubs<\/h4>\n<p>  Stubs (\u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438) \u2014 \u044d\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c. \u041e\u043d\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 API \u0448\u043f\u0438\u043e\u043d\u043e\u0432 \u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u043c\u0435\u0442\u043e\u0434\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f stubs.<\/p>\n<p>  \u041a\u0430\u043a \u0438 \u0448\u043f\u0438\u043e\u043d\u044b, \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u044b\u043c\u0438 \u0438\u043b\u0438 \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041f\u0440\u0438 \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u043e\u0439, \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f. <\/p>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u0435\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435:   <\/p>\n<ol>\n<li>\u0423\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0442\u0435\u0441\u0442\u0430 \u0438 \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u0434 \u0432\u044b\u0434\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u044b\u0434\u0430\u0447\u0438 \u043e\u0448\u0438\u0431\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0435\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443. <\/li>\n<li>\u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0438\u0437-\u0437\u0430 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u044d\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0435\u0436\u0435\u043b\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>XMLHttpRequest<\/code> \u0438\u043b\u0438 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e\u0435).<\/li>\n<\/ol>\n<p>  \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u2014 \u044d\u0442\u043e \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0442\u0435\u0441\u0442 PubSubJS \u043e\u0442 \u041c\u043e\u0440\u0433\u0430\u043d\u0430 \u0420\u043e\u0434\u0435\u0440\u0438\u043a\u0430 (Morgan Roderick), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443, \u0432\u044b\u0434\u0430\u044e\u0449\u0443\u044e \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435.<\/p>\n<pre><code class=\"javascript\">&quot;test should call all subscribers, even if there are exceptions&quot; : 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)); } <\/code><\/pre>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0442\u0430\u043a\u0436\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0448\u043f\u0438\u043e\u043d\u0430. \u0422\u0435\u0441\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0431\u044b\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u044b \u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u043f\u0435\u0440\u0435\u0434 \u043d\u043e\u0432\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c. <\/p>\n<h5>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 stubs \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u0430\u0445<\/h5>\n<p>  \u041c\u043d\u043e\u0433\u043e\u043a\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f, \u0442\u0430\u043a\u0438\u0445 \u043a\u0430\u043a <code>returns<\/code> \u0438\u043b\u0438 <code>throws<\/code> \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. \u041d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u0432\u0435\u0440\u0441\u0438\u0438 Sinon 1.8 \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 &lt;codeonCall&lt;\/code, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u0443\u044e \u0440\u0435\u0430\u043a\u0446\u0438\u044e \u043e\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u043f\u0440\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u0430\u0445. <\/p>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432 Sinon \u0432\u0435\u0440\u0441\u0438\u0438 1.5 \u0438 1.7 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u0434\u043b\u044f <code>yields*<\/code> \u0438 <code>callsArg*<\/code> \u2014 \u044d\u0442\u043e \u0441\u0435\u043c\u0435\u0439\u0441\u0442\u0432\u043e \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0440\u044f\u0434\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432. <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Stub API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var stub = sinon.stub();<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438.<br \/>  <b><u><code>var stub = sinon.stub(object, &quot;method&quot;);<\/code><\/u><\/b><br \/>  \u0417\u0430\u043c\u0435\u043d\u044f\u0435\u0442 <code>object.method<\/code> \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. \u0418\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430, \u0432\u044b\u0437\u0432\u0430\u0432 <code>object.method.restore ();<\/code> \u0438\u043b\u0438 <code>stub.restore ();<\/code>. \u041e\u0448\u0438\u0431\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0434\u0430\u043d\u0430, \u0435\u0441\u043b\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043e\u0442\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u2014 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u0434\u043b\u044f \u0438\u0437\u0431\u0435\u0436\u0430\u043d\u0438\u044f \u043e\u043f\u0435\u0447\u0430\u0442\u043e\u043a \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. <br \/>  <b><u><code>var stub = sinon.stub(object, &quot;method&quot;, func);<\/code><\/u><\/b><br \/>  \u0417\u0430\u043c\u0435\u043d\u044f\u0435\u0442 <code>object.method<\/code> \u0438 <code>func<\/code>, \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044b\u0435 \u0448\u043f\u0438\u043e\u043d\u043e\u043c. \u041e\u0431\u044b\u0447\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>object.method.restore ();<\/code> \u0434\u043b\u044f \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430.<br \/>  <b><u><code>var stub = sinon.stub(obj);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a \u2014 \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0430\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430 \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0412\u044b \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435 \u0438\u043b\u0438 \u0442\u0435\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442 \u0432\u0441\u0435\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438). \u041c\u0435\u0442\u043e\u0434 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0431\u043e\u043b\u0435\u0435 \u0442\u043e\u0447\u043d\u043e \u0438 \u043c\u0435\u043d\u0435\u0435 \u0432\u043e\u0441\u043f\u0440\u0438\u0438\u043c\u0447\u0438\u0432\u043e \u043a \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043a\u043e\u0434\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c stub-\u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f <code>MyConstructor<\/code>, \u043d\u043e \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u044d\u0442\u0443 \u0441\u043b\u0443\u0436\u0435\u0431\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e:  <\/p>\n<pre><code class=\"javascript\">var stub = sinon.createStubInstance(MyConstructor) <\/code><\/pre>\n<p>  <b><u><code>stub.withArgs(arg1[, arg2, ...]);<\/code><\/u><\/b><br \/>  \u041c\u0435\u0442\u043e\u0434 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0432\u044b\u0440\u0430\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0432\u0430\u0448\u0438\u0445 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f\u0445, \u0433\u0434\u0435 \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0448\u043f\u0438\u043e\u043d\u0443 \u0441 \u0442\u0435\u043c \u0436\u0435 \u0432\u044b\u0437\u043e\u0432\u043e\u043c. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0430\u0447\u0435, \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b. <\/p>\n<pre><code class=\"javascript\">&quot;test should stub method differently based on arguments&quot;: function () {     var callback = sinon.stub();     callback.withArgs(42).returns(1);     callback.withArgs(1).throws(&quot;TypeError&quot;);      callback(); \/\/ No return value, no exception     callback(42); \/\/ Returns 1     callback(1); \/\/ Throws TypeError } <\/code><\/pre>\n<p>  <b><u><code>stub.onCall(n);<\/code><\/u><\/b><br \/>  <i>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0432 Sinon.JS 1.8.<\/i><br \/>  \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u043d\u0430 \u044d\u043d\u043d\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435. \u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<\/p>\n<pre><code class=\"javascript\">&quot;test should stub method differently on consecutive calls&quot;: 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 } <\/code><\/pre>\n<p>  \u0415\u0441\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431\u044b \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a <code>onFirstCall, onSecondCall, onThirdCall<\/code>, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a \u0431\u043e\u043b\u0435\u0435 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c. <br \/>  <code>`onCall`<\/code> \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0438\u043c\u0438 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0432 \u044d\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435. \u0412 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 <code>'withArgs'<\/code>.<\/p>\n<pre><code class=\"actionscript\">&quot;test should stub method differently on consecutive calls with certain argument&quot;: 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 } <\/code><\/pre>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u043a\u0430\u043a \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0434\u043b\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 42 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0437\u043e\u0432\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0431\u044b\u043b\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b. <br \/>  <b><u><code>stub.onFirstCall();<\/code><\/u><\/b><br \/>  \u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c \u0434\u043b\u044f stub.onCall (0);<br \/>  <b><u><code>stub.onSecondCall();<\/code><\/u><\/b><br \/>  \u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c \u0434\u043b\u044f stub.onCall (1);<br \/>  <b><u><code>stub.onThirdCall();<\/code><\/u><\/b><br \/>  \u041f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c \u0434\u043b\u044f stub.onCall (2);<br \/>  <b><u><code>stub.returns(obj);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442.<br \/>  <b><u><code>stub.returnsArg(index);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u043c. <code>stub.callsArg (0);<\/code> \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442.<br \/>  <b><u><code>stub.returnsThis();<\/code><\/u><\/b><br \/>  \u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <code>this<\/code>. <br \/>  <b><u><code>stub.throws();<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 (\u041e\u0448\u0438\u0431\u043a\u0443).<br \/>  <b><u><code>stub.throws(&quot;TypeError&quot;);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0437\u0430\u0434\u0430\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.<br \/>  <b><u><code>stub.throws(obj);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0434\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<br \/>  <b><u><code>stub.callsArg(index);<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441\u0443, \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. <code>stub.callsArg (0);<\/code> \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442, \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432. <br \/>  <b><u><code>stub.callsArgOn(index, context);<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>stub.callsArg(index);<\/code>, \u043d\u043e \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 <code>this<\/code> \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430.<br \/>  <b><u><code>stub.callsArgWith(index, arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 callsArg, \u043d\u043e \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430. <br \/>  <b><u><code>stub.callsArgOnWith(index, context, arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>stub.callsArgWith(index, arg1, arg2, ...);<\/code>, \u043d\u043e \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 <code>this<\/code> \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430.<br \/>  <b><u><code>stub.yields([arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u043f\u043e\u0447\u0442\u0438 \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a callsArg. \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043d \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u044b\u0435 \u0438\u043c\u0435\u044e\u0442\u0441\u044f). \u0415\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c callsArg, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0438\u043c\u0435\u043b\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0435 \u043e\u0442 \u043f\u0435\u0440\u0432\u043e\u0433\u043e. <br \/>  <b><u><code>stub.yieldsOn(context, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>stub.yieldsOn(context, [arg1, arg2, ...]);<\/code>, \u043d\u043e \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 <code>this<\/code> \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430.<br \/>  <b><u><code>stub.yieldsTo(property, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u0430\u043a \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0448\u043f\u0438\u043e\u043d\u0443. \u0422\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>yields, yieldsTo<\/code> \u0437\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442, \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441 (\u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438) \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>stub.yieldsToOn(property, context, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>stub.yieldsTo(property, [arg1, arg2, ...]);<\/code>, \u043d\u043e \u0441 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 <code>this<\/code> \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430.<\/p>\n<pre><code class=\"javascript\">&quot;test should fake successful ajax request&quot;: function () {     sinon.stub(jQuery, &quot;ajax&quot;).yieldsTo(&quot;success&quot;, [1, 2, 3]);      jQuery.ajax({         success: function (data) {             assertEquals([1, 2, 3], data);         }     }); } <\/code><\/pre>\n<p>  <b><u><code>stub.yield([arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u043a \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, <code>yield<\/code> \u0432\u044b\u0434\u0430\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u0441 \u043f\u0441\u0435\u0432\u0434\u043e\u043d\u0438\u043c\u043e\u043c <code>invokeCallback<\/code>. <br \/>  <b><u><code>stub.yieldTo(callback, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0432\u044b\u0437\u043e\u0432\u044b, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u0430\u043a \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0448\u043f\u0438\u043e\u043d\u0443. \u0422\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>yields, yieldsTo<\/code> \u0437\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442, \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441 (\u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438) \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>stub.callArg(argNum)<\/code><\/u><\/b><br \/>  \u0422\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>yield<\/code>, \u043d\u043e \u0441 \u0442\u043e\u0447\u043d\u044b\u043c \u043d\u043e\u043c\u0435\u0440\u043e\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0438\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0432\u044b\u0437\u043e\u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c. \u041f\u043e\u043b\u0435\u0437\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u0435 \u0447\u0435\u043c \u0441 \u043e\u0434\u043d\u0438\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c, \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f. <\/p>\n<pre><code class=\"javascript\">&quot;calling the last callback&quot;: function () {     var callback = sinon.stub();     callback(function () {         console.log(&quot;Success!&quot;);     }, function () {         console.log(&quot;Oh noes!&quot;);     });      callback.callArg(1); \/\/ Logs &quot;Oh noes!&quot; } <\/code><\/pre>\n<p>  <b><u><code>stub.callArgWith(argNum, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u041f\u043e\u0434\u043e\u0431\u043d\u043e <code>stub.callArg(argNum)<\/code>, \u043d\u043e \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>stub.callsArgAsync(index);<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u0447\u0442\u043e \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442, \u043d\u043e \u0441 \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435 \u0441\u0440\u0430\u0437\u0443, \u0430 \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430 \u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u201c\u043f\u043e\u0442\u043e\u043a\u0435\u201d). <\/p>\n<p>  <b><u><code>stub.callsArgAsync(index);<\/code><\/u><\/b><br \/>  <b><u><code>stub.callsArgOnAsync(index, context);<\/code><\/u><\/b><br \/>  <b><u><code>stub.callsArgWithAsync(index, arg1, arg2, ...);<\/code><\/u><\/b><br \/>  <b><u><code>stub.callsArgOnWithAsync(index, context, arg1, arg2, ...);<\/code><\/u><\/b><br \/>  <b><u><code>stub.yieldsAsync([arg1, arg2, ...])<\/code><\/u><\/b><br \/>  <b><u><code>stub.yieldsOnAsync(context, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  <b><u><code>stub.yieldsToAsync(property, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  <b><u><code>stub.yieldsToOnAsync(property, context, [arg1, arg2, ...])<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 \u0438\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b, \u043d\u043e \u0441 \u043e\u0442\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c (\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0430 \u0441\u0440\u0430\u0437\u0443, \u043d\u043e \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0433\u043e \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430 \u0438 \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u201c\u043f\u043e\u0442\u043e\u043a\u0435\u201d)<\/p><\/div>\n<\/div>\n<p>  <\/p>\n<h4>Mocks<\/h4>\n<p>  Mocks (\u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044f) \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 (\u043a\u0430\u043a \u0448\u043f\u0438\u043e\u043d\u044b) \u0441 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c (\u043a\u0430\u043a \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430), \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435\u043c. \u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0442\u0435\u0441\u0442, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0430 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a, \u043a\u0430\u043a \u043e\u0436\u0438\u0434\u0430\u043b\u043e\u0441\u044c. <\/p>\n<p>  \u0418\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 \u043f\u043e\u0434 \u0442\u0435\u0441\u0442. \u0412 \u043a\u0430\u0436\u0434\u043e\u043c \u044e\u043d\u0438\u0442-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0431\u043b\u043e\u043a \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0427\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0439 \u0431\u043b\u043e\u043a \u0438 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u0442\u0444\u0430\u043a\u0442\u0443\u043c), \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e.<\/p>\n<p>  \u0418\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0443\u044e\u0442\u0441\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0412\u0430\u043c\u0438 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f\u043c\u0438 \u043e\u0442 \u0442\u0435\u0441\u0442\u0430, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u0438\u0432\u0435\u0441\u0442\u0438 \u043a \u043f\u0440\u043e\u0432\u0430\u043b\u0443 \u0432\u0441\u0435\u0433\u043e \u0442\u0435\u0441\u0442\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0442\u0435\u0441\u0442 \u043d\u0430\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0434\u0435\u0442\u0430\u043b\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f. \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e: \u0435\u0441\u043b\u0438 \u0412\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0439\u0442\u0435 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0438\u0445. \u041b\u0443\u0447\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443. \u0412 \u043b\u044e\u0431\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0438 \u0443 \u0412\u0430\u0441 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0439 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f\u043c\u0438) \u0432 \u043e\u0434\u043d\u043e\u043c \u0442\u0435\u0441\u0442\u0435. <\/p>\n<p>  \u041e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c API \u043a\u0430\u043a \u0448\u043f\u0438\u043e\u043d\u0430 \u0442\u0430\u043a \u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. <\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0432 Sinon.JS, \u0432\u043e\u0442 \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 PubSubJS. \u041d\u0430 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0438 \u043a\u0430\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0435\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435: <\/p>\n<pre><code class=\"javascript\">&quot;test should call all subscribers when exceptions&quot;: function () {     var myAPI = { method: function () {} };      var spy = sinon.spy();     var mock = sinon.mock(myAPI);     mock.expects(&quot;method&quot;).once().throws();      PubSub.subscribe(&quot;message&quot;, myAPI.method);     PubSub.subscribe(&quot;message&quot;, spy);     PubSub.publishSync(&quot;message&quot;, undefined);      mock.verify();     assert(spy.calledOnce); } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Mocks API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var mock = sinon.mock(obj);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u0421\u0430\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u043d\u043e \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u0435\u0442 mock-\u043e\u0431\u0435\u043a\u0442 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0439 \u043d\u0430 \u043c\u0435\u0442\u043e\u0434\u0430\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<br \/>  <b><u><code>var expectation = mock.expects(&quot;method&quot;);<\/code><\/u><\/b><br \/>  \u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 <code>obj.method<\/code> \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0435\u0433\u043e.<br \/>  <b><u><code>mock.restore();<\/code><\/u><\/b><br \/>  \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438.<br \/>  <b><u><code>mock.verify();<\/code><\/u><\/b><br \/>  \u041f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043f\u043e\u0438\u0441\u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043d\u0435 \u0431\u044b\u043b\u0430 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0430, \u0432\u044b\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u043e \u043e\u0448\u0438\u0431\u043a\u0443. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438.   <\/div>\n<\/div>\n<p>  <\/p>\n<h4>Expectations<\/h4>\n<p>  \u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b expectations (\u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435) \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0438\u0445 \u0432 \u0446\u0435\u043f\u043e\u0447\u043a\u0443. \u0422\u0438\u043f\u0438\u0447\u043d\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435:  <\/p>\n<pre><code class=\"javascript\">sinon.mock(jQuery).expects(&quot;ajax&quot;).atLeast(2).atMost(5); jQuery.ajax.verify(); <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Expectations API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var expectation = sinon.expectation.create([methodName]);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0431\u0435\u0437 mock=\u043e\u0431\u044a\u0435\u043a\u0442\u0430, \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u044d\u0442\u043e \u0430\u043d\u043e\u043d\u0438\u043c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438. \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u0430 \u044d\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u0445 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0445 \u0431\u043e\u043b\u0435\u0435 \u0447\u0438\u0442\u0430\u0435\u043c\u044b\u043c\u0438. <br \/>  <b><u><code>var expectation = sinon.mock();<\/code><\/u><\/b><br \/>  \u0422\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 <code>var expectation = sinon.expectation.create([methodName]);<\/code><br \/>  <b><u><code>expectation.atLeast(number);<\/code><\/u><\/b><br \/>  \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<br \/>  <b><u><code>expectation.atMost(number);<\/code><\/u><\/b><br \/>  \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432.<br \/>  <b><u><code>expectation.never();<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d.<br \/>  <b><u><code>expectation.once();<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<br \/>  <b><u><code>expectation.twice();<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0434\u0432\u0430 \u0440\u0430\u0437\u0430.<br \/>  <b><u><code>expectation.thrice();<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u0440\u0438 \u0440\u0430\u0437\u0430.<br \/>  <b><u><code>expectation.exactly(number);<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0442\u043e\u0447\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437.<br \/>  <b><u><code>expectation.withArgs(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c\u0438).<br \/>  <b><u><code>expectation.withExactArgs(arg1, arg2, ...);<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u043c\u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<br \/>  <b><u><code>expectation.on(obj);<\/code><\/u><\/b><br \/>  \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0441 <code>obj<\/code> \u043a\u0430\u043a <code>this<\/code>.<br \/>  <b><u><code>expectation.verify();<\/code><\/u><\/b><br \/>  \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0432\u0441\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0438 \u0432\u044b\u0434\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u043d\u0435 \u0431\u044b\u043b\u043e \u043d\u0430\u0439\u0434\u0435\u043d\u043e.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>Fake timers<\/h4>\n<p>  \u041f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0430\u0439\u043c\u0435\u0440\u044b \u2014 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f setTimeout \u0438 \u043f\u043e\u043c\u043e\u0449\u043d\u0438\u043a\u043e\u0432 Sinon.JS, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442 \u0432\u0430\u043c \u0431\u043e\u043b\u0435\u0435 \u043b\u0435\u0433\u043a\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0438\u0445. <\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0430\u0439\u043c\u0435\u0440\u044b \u0441 IE \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c sinon-ie-1.17.2 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 sinon-1.17.2.js.<\/p>\n<p>  \u0414\u043b\u044f \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0442\u0430\u0439\u043c\u0435\u0440\u043e\u0432 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c lolex \u043f\u0430\u043a\u0435\u0442. \u042d\u0442\u043e \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u0442 \u0436\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0438 \u0431\u044b\u043b \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d \u0440\u0430\u043d\u0435\u0435 \u0438\u0437 Sinon.JS. <\/p>\n<pre><code class=\"javascript\">{     setUp: function () {         this.clock = sinon.useFakeTimers();     },      tearDown: function () {         this.clock.restore();     },      &quot;test should animate element over 500ms&quot; : function(){         var el = jQuery(&quot;&lt;div&gt;&lt;\/div&gt;&quot;);         el.appendTo(document.body);          el.animate({ height: &quot;200px&quot;, width: &quot;200px&quot; });         this.clock.tick(510);          assertEquals(&quot;200px&quot;, el.css(&quot;height&quot;));         assertEquals(&quot;200px&quot;, el.css(&quot;width&quot;));     } } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Fake timers API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var clock = sinon.useFakeTimers();<\/code><\/u><\/b><br \/>  Sinon \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 <code>setTimeout, setInterval, clearTimeout, clearInterval<\/code> \u0438 \u0434\u0430\u0442\u044b \u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u043c \u0447\u0430\u0441\u043e\u0432\u044b\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c. \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0447\u0430\u0441\u044b \u0432 \u044d\u043f\u043e\u0445\u0443 UNIX \u0441 \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<br \/>  <b><u><code>var clock = sinon.useFakeTimers(now);<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u0432\u0441\u0435 \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435, \u0447\u0442\u043e \u0438 \u043c\u0435\u0442\u043e\u0434 \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435, \u043d\u043e \u0432\u043c\u0435\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0447\u0430\u0441\u043e\u0432 \u0441 \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u043e\u0442\u043c\u0435\u0442\u043a\u043e\u0439 \u0447\u0430\u0441\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442\u0441\u044f \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u043e\u0442\u043c\u0435\u0442\u043a\u043e\u0439 \u0432 \u043f\u043e\u043b\u0435 <code>(now)<\/code>.<br \/>  <b><u><code>var clock = sinon.useFakeTimers([now, ]prop1, prop2, ...);<\/code><\/u><\/b><br \/>  \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043e\u0442\u043c\u0435\u0442\u043a\u0443 \u0447\u0430\u0441\u043e\u0432 \u0438 \u0438\u043c\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u2014 <code>setTimeout, clearTimeout, setInterval<\/code>, <code>clearInterval<\/code>, \u0438 \u0434\u0430\u0442\u0430. \u041c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0431\u044b\u0442\u044c \u0432\u044b\u0437\u0432\u0430\u043d \u0431\u0435\u0437 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u0435\u0442\u043a\u0438.<br \/>  <b><u><code>clock.tick(ms);<\/code><\/u><\/b><br \/>  \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u0442\u043c\u0435\u0442\u043a\u0430 \u0432\u044b\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0434 <code>ms<\/code> \u0438 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445. \u0412\u0441\u0435 \u0442\u0430\u0439\u043c\u0435\u0440\u044b \u0441 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0441\u0442\u0430\u0440\u0442\u0430, \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0449\u0438\u043c \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0437\u0432\u0430\u043d\u044b. <br \/>  <b><u><code>clock.restore();<\/code><\/u><\/b><br \/>  \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b. \u0412\u044b\u0437\u0432\u0430\u0442\u044c \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>tearDown<\/code>  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>Fake XMLHttpRequest<\/h4>\n<p>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <code>XMLHttpRequest<\/code> \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u044b\u043c\u0438 \u0438\u043c. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u043b\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 <code>XMLHttpRequest<\/code> \u0438 <code>ActiveXObject<\/code>. \u041f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0441 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u0445 \u0441 XHR.<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c XHR \u0441 IE \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c sinon-ie-1.17.2 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 sinon-1.17.2.js<\/p>\n<p>  \u041f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 XHR \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e \u043f\u0440\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0438, \u0447\u0442\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430 sinon-server-1.17.2. \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 IE \u0432\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u0435\u043d sinon-1.17.2. \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0435\u0433\u043e \u043f\u043e\u0441\u043b\u0435 sinon-server-1.17.2.<\/p>\n<pre><code class=\"javascript\">{     setUp: function () {         this.xhr = sinon.useFakeXMLHttpRequest();         var requests = this.requests = [];          this.xhr.onCreate = function (xhr) {             requests.push(xhr);         };     },      tearDown: function () {         this.xhr.restore();     },      &quot;test should fetch comments from server&quot; : function () {         var callback = sinon.spy();         myLib.getCommentsFor(&quot;\/some\/article&quot;, callback);         assertEquals(1, this.requests.length);          this.requests[0].respond(200, { &quot;Content-Type&quot;: &quot;application\/json&quot; },                                  '[{ &quot;id&quot;: 12, &quot;comment&quot;: &quot;Hey there&quot; }]');         assert(callback.calledWith([{ id: 12, comment: &quot;Hey there&quot; }]));     } } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">sinon.useFakeXMLHttpRequest<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var xhr = sinon.useFakeXMLHttpRequest();<\/code><\/u><\/b><br \/>  Sinon \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442 \u0440\u043e\u0434\u043d\u043e\u0439 XMLHttpRequest \u043e\u0431\u0435\u043a\u0442 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0437\u0430\u043f\u0440\u043e\u0441. \u0412 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442 <code>ActiveXObject<\/code>, \u0434\u0430\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0434\u043b\u044f progID XMLHTTP. \u0414\u0440\u0443\u0433\u0438\u0435 progID, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a XMLDOM, \u043e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u043d\u0435\u0442\u0440\u043e\u043d\u0443\u0442\u044b\u043c\u0438.<\/p>\n<p>  \u0420\u043e\u0434\u043d\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 XMLHttpRequest \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 sinon.xhr. XMLHttpRequest.<\/p>\n<p>  <b><u><code>xhr.onCreate = function (xhr) {};<\/code><\/u><\/b><br \/>  \u041f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e onCreate \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043e\u0442 <code>useFakeXMLHttpRequest ()&lt;\/code. \u042d\u0442\u043e \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 &lt;code&gt;FakeXMLHttpRequest<\/code> \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0412\u0430\u043c\u0438. \u041d\u0438\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u0441\u044f \u0441 API \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 XHR. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0434\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u043e\u0437\u0440\u0435\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0412\u044b \u0432\u0441\u0435 \u0435\u0449\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u043e\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 jQuery.ajax (\u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0446\u0438\u0438\/\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438) <br \/>  <b><u><code>xhr.restore();<\/code><\/u><\/b><br \/>  \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e(\u0438).   <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">FakeXMLHttpRequest<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>String request.url<\/code><\/u><\/b><br \/>  URL-\u0430\u0434\u0440\u0435\u0441 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430.<br \/>  <b><u><code>String request.method<\/code><\/u><\/b><br \/>  \u041c\u0435\u0442\u043e\u0434 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a\u0430\u043a \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<br \/>  <b><u><code>Object request.requestHeaders<\/code><\/u><\/b><br \/>  \u0412\u044b\u0448\u0435\u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0442.\u0435.:<\/p>\n<pre><code class=\"javascript\">{     &quot;Accept&quot;: &quot;text\/html, *\/*&quot;,     &quot;Connection&quot;: &quot;keep-alive&quot; } <\/code><\/pre>\n<p>  <b><u><code>String request.requestBody<\/code><\/u><\/b><br \/>  \u0417\u0430\u043f\u0440\u043e\u0441 \u0442\u0435\u043b\u0430.<br \/>  <b><u><code>int request.status<\/code><\/u><\/b><br \/>  \u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0411\u0443\u0434\u0435\u0442 \u043d\u0435 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0431\u044b\u043b \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d. <br \/>  <b><u><code>String request.statusText<\/code><\/u><\/b><br \/>  \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u044b\u043b \u0432\u044b\u0437\u0432\u0430\u043d. <br \/>  <b><u><code>boolean request.async<\/code><\/u><\/b><br \/>  \u0411\u0443\u0434\u0435\u0442 \u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u043c \u0438\u043b\u0438 \u043d\u0435\u0442. <br \/>  <b><u><code>String request.username<\/code><\/u><\/b><br \/>  \u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u043e\u0435 \u0438\u043c\u0435\u0435\u0442\u0441\u044f.<br \/>  <b><u><code>String request.password<\/code><\/u><\/b><br \/>  \u041f\u0430\u0440\u043e\u043b\u044c, \u0435\u0441\u043b\u0438 \u0442\u0430\u043a\u043e\u0432\u043e\u0439 \u0438\u043c\u0435\u0435\u0442\u0441\u044f.<br \/>  <b><u><code>Document request.responseXML<\/code><\/u><\/b><br \/>  \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0442\u0432\u0435\u0442\u0430, \u0434\u0430\u043d\u043d\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0441 \u0430\u043d\u0430\u043b\u0438\u0437\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442. <br \/>  <b><u><code>String request.getResponseHeader(header);<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u043a \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u043c, \u0435\u0441\u043b\u0438 \u0443 \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u044b\u043b \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u043a\u0430\u043a \u0438 \u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. <br \/>  <b><u><code>Object request.getAllResponseHeaders();<\/code><\/u><\/b><br \/>  \u0412\u0441\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432 \u0432\u0438\u0434\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0424\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h4>\n<p>  \u041f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 Sinon.JS \u0434\u043b\u044f \u043c\u0430\u043a\u0435\u0442\u043e\u0432 \u0438\u043b\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e\/\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0412\u044b \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0434\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 \u044d\u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u0434\u0440\u0443\u0433\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c \u0438\u0434\u0442\u0438 \u043d\u0430 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438 <code> FakeXMLHttpRequest<\/code> (\u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 Sinon 1.3.0) \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u044b: <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041c\u0435\u0442\u043e\u0434\u044b<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>FakeXMLHttpRequest.useFilters<\/code><\/u><\/b><br \/>  \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043b\u043e\u0436\u043d\u044b\u0435. Sinon \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043d\u0435 \u043b\u043e\u0436\u043d\u044b\u043c \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0444\u0438\u043b\u044c\u0442\u0440. <br \/>  <b><u><code>FakeXMLHttpRequest.addFilter(fn)<\/code><\/u><\/b><br \/>  \u0422\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0437\u0430\u043f\u0440\u043e\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0435\u0448\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u043c \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0438\u043b\u0438 \u043d\u0435\u0442. \u0424\u0438\u043b\u044c\u0442\u0440 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u0433\u0434\u0430, \u043a\u043e\u0433\u0434\u0430 <code>xhr.open<\/code> \u0432\u044b\u0437\u0432\u0430\u043d \u0441 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 (\u043c\u0435\u0442\u043e\u0434, URL, \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0441\u0442\u044c, \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043f\u0430\u0440\u043e\u043b\u044c). \u0415\u0441\u043b\u0438 \u0444\u0438\u043b\u044c\u0442\u0440 \u0432\u044b\u0434\u0430\u0441\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0435\u043d \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u043c.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/h4>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0418\u043c\u0438\u0442\u0430\u0446\u0438\u044f<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>request.setResponseHeaders(object);<\/code><\/u><\/b><br \/>  \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>{ &quot;Content-Type&quot;: &quot;text\/html&quot;, \/* ... *\/ }<\/code>, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>readyState<\/code> \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 <code>onreadystatechange<\/code>.<br \/>  <b><u><code>request.setResponseBody(body);<\/code><\/u><\/b><br \/>  \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0442\u0435\u043b\u0430, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>readyState<\/code> \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 <code>onreadystatechange<\/code>. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442 <code>responseXML<\/code> \u0441 \u043f\u0440\u043e\u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u043c, \u0435\u0441\u043b\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u044e\u0442. <br \/>  <b><u><code>request.respond(status, headers, body);<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0434\u0432\u0430 \u0432\u044b\u0448\u0435\u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430 \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f <code>statusText<\/code>. \u0421\u0442\u0430\u0442\u0443\u0441 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u043c, \u043f\u043e\u0438\u0441\u043a \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0434\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>sinon. FakeXMLHttpRequest.statusCodes<\/code>.<br \/>  <b><u><code>Boolean request.autoRespond<\/code><\/u><\/b><br \/>  \u041a\u043e\u0433\u0434\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441, \u044d\u0442\u043e \u0432\u044b\u043d\u0443\u0436\u0434\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u0430\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430.\u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 10 \u043c\u0441, \u043d\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>autoRespondAfter<\/code>.<\/p>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u043e\u0447\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u0430\u043a\u0435\u0442\u0430 \u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445.<\/p>\n<p>  <b><u><code>Number request.autoRespondAfter<\/code><\/u><\/b><br \/>  \u041a\u043e\u0433\u0434\u0430 \u0430\u0432\u0442\u043e\u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d, \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0430\u043d \u043f\u043e\u0441\u043b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0447\u0438\u0441\u043b\u0430 \u043c\u0438\u043b\u043b\u0438\u0441\u0435\u043a\u0443\u043d\u0434. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0440\u0430\u0432\u043d\u043e 10 \u043c\u0441.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>FakeServe<\/h4>\n<p>  API \u0432\u044b\u0441\u043e\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f \u0434\u043b\u044f \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u043c\u0438 <code>FakeXMLHttpRequest<\/code><\/p>\n<pre><code class=\"javascript\">{     setUp: function () {         this.server = sinon.fakeServer.create();     },      tearDown: function () {         this.server.restore();     },      &quot;test should fetch comments from server&quot; : function () {         this.server.respondWith(&quot;GET&quot;, &quot;\/some\/article\/comments.json&quot;,             [200, { &quot;Content-Type&quot;: &quot;application\/json&quot; },              '[{ &quot;id&quot;: 12, &quot;comment&quot;: &quot;Hey there&quot; }]']);          var callback = sinon.spy();         myLib.getCommentsFor(&quot;\/some\/article&quot;, callback);         this.server.respond();          sinon.assert.calledWith(callback, [{ id: 12, comment: &quot;Hey there&quot; }]);     } } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">API FakeServe<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var server = sinon.fakeServer.create([config]);<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440. \u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0442\u0430\u043a\u0436\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 <code>sinon.useFakeXMLHttpRequest ()<\/code>. \u0421\u043e\u0437\u0434\u0430\u0435\u0442\/\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043b\u043e\u0436\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440.<br \/>  <b><u><code>var server = sinon.fakeServerWithClock.create();<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0442\u0430\u0439\u043c\u0435\u0440\u0430\u043c\u0438. \u042d\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e \u043f\u0440\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 XHR, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, jQuery 1.3.x \u0430 \u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u044b\u0439 <code>onreadystatechange<\/code>. jQuery 1.3.x \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0442\u0430\u0439\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f<br \/>  <b><u><code>server.configure(config)<\/code><\/u><\/b><br \/>  \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440. \u041d\u0438\u0436\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.<br \/>  <b><u><code>server.respondWith(response);<\/code><\/u><\/b><br \/>  \u041e\u0442\u0432\u0435\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0442\u0440\u0435\u0445 \u0442\u0438\u043f\u043e\u0432:<\/p>\n<ul>\n<li>\u0421\u0442\u0440\u043e\u043a\u0430, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0430\u044f \u0442\u0435\u043b\u043e \u043e\u0442\u0432\u0435\u0442\u0430<\/li>\n<li>\u041c\u0430\u0441\u0441\u0438\u0432 \u0441 \u0441\u0442\u0430\u0442\u0443\u0441\u0430\u043c\u0438, \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c\u0438 \u0438 \u0442\u0435\u043b\u043e\u043c \u043e\u0442\u0432\u0435\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>[200, { &quot;Content-Type&quot;: &quot;text\/html&quot;, &quot;Content-Length&quot;: 2 }, &quot;OK&quot;]<\/code><\/li>\n<li>\u0424\u0443\u043d\u043a\u0446\u0438\u044f.<\/li>\n<\/ul>\n<p>  \u0421\u0442\u0430\u0442\u0443\u0441 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0437\u0430\u0434\u0430\u043d 200 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043d\u0435 \u0437\u0430\u0434\u0430\u043d\u044b \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e. \u041e\u0431\u0449\u0438\u0439 \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <code>[404, {}, &quot;&quot;]<\/code><\/p>\n<p>  \u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439, \u0442\u043e \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u0434\u0430\u043d \u043e\u0431\u044a\u0435\u043a\u0442\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441. <\/p>\n<p>  <b><u><code>server.respondWith(url, response);<\/code><\/u><\/b><br \/>  \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a \u0434\u0430\u043d\u043d\u043e\u043c\u0443 URL, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>\/posts\/1<\/code><br \/>  <b><u><code>server.respondWith(method, url, response);<\/code><\/u><\/b><br \/>  \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043c\u0435\u0442\u043e\u0434\u0430 \u043a \u0434\u0430\u043d\u043d\u043e\u043c\u0443 URL \u0441 \u0434\u0430\u043d\u043d\u044b\u043c \u043e\u0442\u0432\u0435\u0442\u043e\u043c. \u041c\u0435\u0442\u043e\u0434 \u2014 \u0433\u043b\u0430\u0433\u043e\u043b HTTP.<br \/>  <b><u><code>server.respondWith(urlRegExp, response);<\/code><\/u><\/b><br \/>  URL \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <code>\/\\\/post\\\/\/\\d+<\/code>. \u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u043d \u0432 \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0432\u043c\u0435\u0441\u0442\u0438 \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c <code>XMLHttpRequest<\/code>.   <\/p>\n<pre><code class=\"javascript\">server.respondWith(\/\\\/todo-items\\\/(\\d+)\/, function (xhr, id) {  xhr.respond(200, { ?Content-Type?: ?application\/json? }, ?[{ ?id?: ? + id + ? }]?); }); <\/code><\/pre>\n<p>  <b><u><code>server.respondWith(method, urlRegExp, response);<\/code><\/u><\/b><br \/>  \u041e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a URL-\u0430\u0434\u0440\u0435\u0441\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c. <br \/>  <b><u><code>server.respond();<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441 \u043e\u0447\u0435\u0440\u0435\u0434\u044f\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442. \u0415\u0441\u043b\u0438 \u043d\u0438 \u043e\u0434\u0438\u043d \u0438\u0437 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043d\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d \u0447\u0435\u0440\u0435\u0437 <code>respondWith<\/code> \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0432\u0435\u043d\u043d\u043e, \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <code>[404, {}, &quot;&quot;]<\/code>. \u041d\u0430 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0442\u0432\u0435\u0442 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0441\u0440\u0430\u0437\u0443, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044c\u0442\u0435\u0441\u044c, \u0447\u0442\u043e <code>respondWith<\/code> \u0432\u044b\u0437\u0432\u0430\u043b\u0438 \u0437\u0430\u0440\u0430\u043d\u0435\u0435. \u0415\u0441\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d \u0441 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438, <code>respondWith<\/code> \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u0441 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441. <br \/>  <b><u><code>server.autoRespond = true;<\/code><\/u><\/b><br \/>  \u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0438\u0442 \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430. \u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 10 \u043c\u0441, \u043d\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>autoRespondAfter<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430 \u043f\u043e\u043c\u043e\u0433\u0430\u0442\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u0430\u043a\u0435\u0442\u0430 \u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445. \u0414\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 respondImmediately \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e.<br \/>  <b><u><code>server.autoRespondAfter = ms;<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430.<br \/>  <b><u><code>server.respondImmediately = true;<\/code><\/u><\/b><br \/>  \u0415\u0441\u043b\u0438 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e, \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u0432\u0435\u0442\u0438\u0442 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u0440\u0430\u0437\u0443 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0438 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043e\u043d \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u0438\u043b\u0438 \u043c\u0430\u043a\u0435\u0442\u0430\u0445. \u0427\u0442\u043e\u0431\u044b \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u0441 \u043d\u0435\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c\u0438 \u0440\u0435\u0430\u043a\u0446\u0438\u044f\u043c\u0438, \u0441\u043c. server.autoRespond \u0438 server.autoRespondAfter. <br \/>  <b><u><code>Boolean `server.fakeHTTPMethods`<\/code><\/u><\/b><br \/>  \u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435, \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u0439\u0434\u0435\u0442 <code>method<\/code> \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u0442\u0435\u043b\u0435 POST \u0438 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0435\u0442 \u0435\u0433\u043e \u043a\u0430\u043a \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043c\u0435\u0442\u043e\u0434. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0437\u0446\u044b, \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 Ruby \u0438 Rails. \u0414\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 HTTP \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 server.getHTTPMethod.<br \/>  <b><u><code>server.getHTTPMethod(request)<\/code><\/u><\/b><br \/>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043c\u0435\u0442\u043e\u0434 HTTP, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u044d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>request.method<\/code>. \u041a\u043e\u0433\u0434\u0430 <code>server.fakeHTTPMethods<\/code> \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d, \u043c\u0435\u0442\u043e\u0434 \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0438\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 <code>_method<\/code>, \u0435\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 \u0432 POST. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0434\u043b\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f.<br \/>  <b><u><code>server.restore();<\/code><\/u><\/b><br \/>  \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0440\u043e\u0434\u043d\u043e\u0439 XHR \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440.   <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/b><\/p>\n<div class=\"spoiler_text\">\u042d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043d\u0430:<\/p>\n<pre><code class=\"javascript\">server.autoRespond = true <\/code><\/pre>\n<p>  \u0412\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441 \u0431\u0443\u043a\u0432\u0430\u043b\u044c\u043d\u044b\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u0432 <code>fakeServer.create<\/code> \u0438 <code>.configure<\/code>.<\/p>\n<p>  <b><u><code>boolean autoRespond<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u043b\u044e\u0431\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430. \u0422\u0430\u0439\u043c-\u0430\u0443\u0442 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 10 \u043c\u0441, \u043d\u043e \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>autoRespondAfter<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u043e\u0447\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u0430\u043a\u0435\u0442\u0430 \u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u0442\u0435\u0441\u0442\u0430\u0445. \u0414\u043b\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0432\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e.<br \/>  <b><u><code>int autoRespondAfter (ms)<\/code><\/u><\/b><br \/>  \u0417\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430 \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043f\u043e\u0441\u043b\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0430\u0439\u043c-\u0430\u0443\u0442\u0430.<br \/>  <b><u><code>boolean respondImmediately<\/code><\/u><\/b><br \/>  \u0421\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u0432\u0435\u0442\u0438\u0442 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u0440\u0430\u0437\u0443 \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u044d\u0442\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043e\u043d \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0438 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445 \u0438\u043b\u0438 \u043c\u0430\u043a\u0435\u0442\u0430\u0445. \u0427\u0442\u043e\u0431\u044b \u043c\u043e\u0434\u0435\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u0443\u044e \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0443 \u0441 \u043d\u0435\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u043c\u0438 \u0440\u0435\u0430\u043a\u0446\u0438\u044f\u043c\u0438, \u0441\u043c. <code>server.autoRespond<\/code> \u0438 <code>server.autoRespondAfter<\/code>. <br \/>  <b><u><code>boolean fakeHTTPMethods<\/code><\/u><\/b><br \/>  \u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u0439\u0434\u0435\u0442 _method \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u0442\u0435\u043b\u0435 POST \u0438 \u0440\u0430\u0441\u043f\u043e\u0437\u043d\u0430\u0435\u0442 \u044d\u0442\u043e \u043a\u0430\u043a \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043c\u0435\u0442\u043e\u0434. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043e\u0431\u0440\u0430\u0437\u0446\u044b, \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439 Ruby \u0438 Rails. \u0414\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 HTTP \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441 <code>server.getHTTPMethod<\/code>.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>JSON-P<\/h4>\n<p>  JSON-P \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b Ajax, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0417\u0430\u043f\u0440\u043e\u0441 JSON-P \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f \u0438 \u0432\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0437\u0443\u043c\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438. \u0421\u0430\u043c\u044b\u0439 \u043b\u0443\u0447\u0448\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u2014 \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0443 \u0434\u043b\u044f jQuery \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435. <\/p>\n<pre><code class=\"javascript\">sinon.stub(jQuery, &quot;ajax&quot;); sinon.assert.calledOnce(jQuery.ajax); <\/code><\/pre>\n<p>  \u0412 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043b \u0431\u044b \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a JQuery \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u043b \u043b\u044e\u0431\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a <code>jQuery.ajax<\/code>, \u043a\u043e\u0433\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f JSON-P. \u041d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0449\u0435 \u0438 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0438\u0436\u0435. <\/p>\n<h4>Assertions<\/h4>\n<p>  Sinon.JS \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0448\u043f\u0438\u043e\u043d\u043e\u0432 \u0438 \u0437\u0430\u0433\u043b\u0443\u0448\u0435\u043a. \u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0439 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u043d\u0435\u0443\u0434\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044f \u043d\u0430 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u0445 \u0438 \u0448\u043f\u0438\u043e\u043d\u0430\u0445 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u044b \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u043e\u0442\u043a\u0430\u0437\u044b \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u043c\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0430\u0445.<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0443\u0431\u0435\u0434\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0441 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0431\u0430\u0437\u044b, \u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043b\u0438\u0431\u043e <code>sinon.assert.fail<\/code> \u0438\u043b\u0438 <code>sinon.assert.failException<\/code> \u0438 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0440\u043e <code>sinon.assert.expose<\/code> \u0438 <code>sinon.assert.pass<\/code>.<\/p>\n<p>  \u0423\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043b\u0438 \u0441\u043e \u0448\u043f\u0438\u043e\u043d\u0430\u043c\u0438, \u0438\u043b\u0438 \u0441 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0430\u043c\u0438.<\/p>\n<pre><code class=\"javascript\">&quot;test should call subscribers with message as first argument&quot; : function () {     var message = &quot;an example message&quot;;     var spy = sinon.spy();      PubSub.subscribe(message, spy);     PubSub.publishSync(message, &quot;some payload&quot;);      sinon.assert.calledOnce(spy);     sinon.assert.calledWith(spy, message); } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Assertions API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>sinon.assert.fail(message)<\/code><\/u><\/b><br \/>  \u041f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430, \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u0432\u044b\u0434\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d \u0432\u044b\u0434\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u0442\u0438\u043f\u0430 <code>sinon.assert.failException<\/code>. \u0415\u0441\u043b\u0438 \u0412\u0430\u0448\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0449\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0438 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0439, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u044f \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0438\u0434 \u0432\u044b\u0434\u0430\u043d\u043d\u043e\u0439 \u043e\u0448\u0438\u0431\u043a\u0438. <br \/>  <b><u><code>sinon.assert.failException<\/code><\/u><\/b><br \/>  \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043b\u044f \u043e\u0448\u0438\u0431\u043e\u043a \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f.<br \/>  <b><u><code>sinon.assert.pass(assertion)<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0435\u0442.<br \/>  <b><u><code>sinon.assert.notCalled(spy)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438.<br \/>  <b><u><code>sinon.assert.called(spy)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437.<br \/>  <b><u><code>sinon.assert.calledOnce(spy)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437. <br \/>  <b><u><code>sinon.assert.calledTwice()<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0430 \u0440\u0430\u0437\u0430. <br \/>  <b><u><code>sinon.assert.calledThrice()<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0440\u0438 \u0440\u0430\u0437\u0430. <br \/>  <b><u><code>sinon.assert.callCount(spy, num)<\/code><\/u><\/b><br \/>  \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430 \u0448\u043f\u0438\u043e\u043d\u0430 \u043d\u0430\u0437\u0432\u0430\u043b\u0438 \u0440\u043e\u0432\u043d\u043e \u043f \u0440\u0430\u0437.<br \/>  <b><u><code>sinon.assert.callOrder(spy1, spy2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u0448\u043f\u0438\u043e\u043d\u044b \u0431\u044b\u043b\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u044b \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435.<br \/>  <b><u><code>sinon.assert.calledOn(spy, obj)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u043a\u0430\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 this.<br \/>  <b><u><code>sinon.assert.alwaysCalledOn(spy, obj)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u043a\u0430\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 this.<br \/>  <b><u><code>sinon.assert.calledWith(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438). <br \/>  <b><u><code>sinon.assert.alwaysCalledWith(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 (\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438). <br \/>  <b><u><code>sinon.assert.neverCalledWith(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438.<br \/>  <b><u><code>sinon.assert.calledWithExactly(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0442\u043e\u0447\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u043c\u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<br \/>  <b><u><code>sinon.assert.alwaysCalledWithExactly(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0442\u043e\u0447\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438 \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u043c\u0438 \u0434\u0440\u0443\u0433\u0438\u043c\u0438.<br \/>  <b><u><code>sinon.assert.calledWithMatch(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>sinon.assert.calledWith(spy, sinon.match(arg1), sinon.match(arg2), ...)<\/code>.<br \/>  <b><u><code>sinon.assert.alwaysCalledWithMatch(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>sinon.assert.alwaysCalledWith(spy, sinon.match(arg1), sinon.match(arg2), ...)<\/code>.<br \/>  <b><u><code>sinon.assert.neverCalledWithMatch(spy, arg1, arg2, ...)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0441\u044f \u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u043c\u0438. \u0412\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>sinon.assert.neverCalledWith(spy, sinon.match(arg1), sinon.match(arg2), ...)<\/code>.<br \/>  <b><u><code>sinon.assert.threw(spy, exception)<\/code><\/u><\/b><br \/>  \u0421\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 (\u043e\u0448\u0438\u0431\u043a\u0443). \u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u043e\u0439, \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u044e\u0449\u0435\u0439 \u0435\u0433\u043e \u0442\u0438\u043f, \u0438\u043b\u0438 \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043e\u0431\u044a\u0435\u043a\u0442. \u0423\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0435\u0441\u043b\u0438 \u0443\u043a\u0430\u0437\u0430\u043d \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0438 \u0448\u043f\u0438\u043e\u043d \u0445\u043e\u0442\u044f \u0431\u044b \u0440\u0430\u0437 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435. <br \/>  <b><u><code>sinon.assert.alwaysThrew(spy, exception)<\/code><\/u><\/b><br \/>  \u0422\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u043e\u0435 \u0432\u044b\u0448\u0435, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0448\u043f\u0438\u043e\u043d \u0432\u0441\u0435\u0433\u0434\u0430 \u0432\u044b\u0434\u0430\u0432\u0430\u043b \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435. <br \/>  <b><u><code>sinon.assert.expose(object, options)<\/code><\/u><\/b><br \/>  \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u0432 \u0432\u0438\u0434\u0435 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, JsTestDriver \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f Sinon.JS \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c\u0438 \u0441 \u043d\u0438\u043c \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c:  <\/p>\n<pre><code class=\"javascript\">sinon.assert.expose(this); <\/code><\/pre>\n<p>  \u041f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0412\u0430\u043c assertCalled(spy),assertCallOrder(spy1, spy2, &#8230;) \u0438 \u0442.\u0434.<\/p>\n<p>  \u041c\u0435\u0442\u043e\u0434 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u0432\u044b\u0448\u0435\u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0435 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0434\u0432\u0443\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u0445. Prefix \u2014 \u043f\u0440\u0435\u0444\u0438\u043a\u0441, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u044d\u0442\u043e <code>assert<\/code>, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c <code>sinon.assert.called<\/code> \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f <code>target.assertCalled<\/code>. \u041f\u0440\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0435 \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u0431\u0443\u0434\u0435\u0442 <code>target.called<\/code>. \u0412\u0442\u043e\u0440\u0430\u044f \u043e\u043f\u0446\u0438\u044f, <code>includeFail<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0441\u0442\u0438\u043d\u043e\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438 \u043a\u043e\u043f\u0438\u0440\u0443\u0435\u0442 \u0441\u0431\u043e\u0438 \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 <code>failException<\/code>.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>Matchers<\/h4>\n<p>  \u0412\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 <code>spy.calledWith<\/code>, <code>spy.returned<\/code> \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c <code>sinon.assert<\/code> \u0438 <code>spy.withArgs<\/code>. <br \/>  \u0412\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u0430\u043a \u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u0443\u044e, \u0442\u0430\u043a \u0438 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043f\u0440\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. <\/p>\n<pre><code class=\"javascript\">&quot;test should assert fuzzy&quot;: function () {     var book = {         pages: 42,         author: &quot;cjno&quot;     };     var spy = sinon.spy();      spy(book);      sinon.assert.calledWith(spy, sinon.match({ author: &quot;cjno&quot; }));     sinon.assert.calledWith(spy, sinon.match.has(&quot;pages&quot;, 42)); } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"javascript\">&quot;test should stub method differently based on argument types&quot;: function () {     var callback = sinon.stub();     callback.withArgs(sinon.match.string).returns(true);     callback.withArgs(sinon.match.number).throws(&quot;TypeError&quot;);      callback(&quot;abc&quot;); \/\/ Returns true     callback(123); \/\/ Throws TypeError } <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Matchers API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>sinon.match(number)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0440\u0430\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0447\u0438\u0441\u043b\u0443.<br \/>  <b><u><code>sinon.match(string)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u043a\u043e\u0439.<br \/>  <b><u><code>sinon.match(regexp)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u043c\u0443 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044e.<br \/>  <b><u><code>sinon.match(object)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043d\u0435 \u043d\u0443\u043b\u0435\u043c \u0438\u043b\u0438 \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u043d\u043e \u0438\u043c\u0435\u043b\u043e \u0442\u0435 \u0436\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0436\u0438\u0434\u0430\u043b\u0438\u0441\u044c. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0432\u0441\u0442\u0430\u0432\u043a\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439.<br \/>  <b><u><code>sinon.match(function)<\/code><\/u><\/b><br \/>  \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0438.<br \/>  <b><u><code>sinon.match.any<\/code><\/u><\/b><br \/>  \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u043c\u0443 \u043b\u0438\u0431\u043e.<br \/>  <b><u><code>sinon.match.defined<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e.<br \/>  <b><u><code>sinon.match.truthy<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u0440\u0430\u0432\u0434\u0438\u0432\u044b\u043c. <br \/>  <b><u><code>sinon.match.falsy<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0444\u0430\u043b\u044c\u0448\u0438\u0432\u044b\u043c.<br \/>  <b><u><code>sinon.match.bool<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439. <br \/>  <b><u><code>sinon.match.number<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0447\u0438\u0441\u043b\u043e\u043c.<br \/>  <b><u><code>sinon.match.string<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e.<br \/>  <b><u><code>sinon.match.object<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c.<br \/>  <b><u><code>sinon.match.func<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439.<br \/>  <b><u><code>sinon.match.array<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u043c.<br \/>  <b><u><code>sinon.match.regexp<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435\u043c.<br \/>  <b><u><code>sinon.match.date<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u0434\u0430\u0442\u044b.<br \/>  <b><u><code>sinon.match.same(ref)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0442\u0440\u043e\u0433\u043e \u0440\u0430\u0432\u043d\u043e <code>ref<\/code><br \/>  <b><u><code>sinon.match.typeOf(type)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0438\u043c\u0435\u043b\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0442\u0438\u043f, \u0433\u0434\u0435 \u0442\u0438\u043f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u043d\u0438\u043c \u0438\u0437 <code>undefined<\/code>, <code>null<\/code>, <code>boolean<\/code>, <code>number<\/code>, <code>string<\/code>, <code>object<\/code>, <code>function<\/code>, <code>array<\/code>, <code>regexp<\/code> \u0438\u043b\u0438 <code>date<\/code>.<br \/>  <b><u><code>sinon.match.instanceOf(type)<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442, \u0447\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0442\u0438\u043f\u0430.<br \/>  <b><u><code>sinon.match.has(property[, expectation])<\/code><\/u><\/b><br \/>  \u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u043e \u0434\u0430\u043d\u043d\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e. \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0443\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u043e\u0432. <br \/>  <b><u><code>sinon.match.hasOwn(property[, expectation])<\/code><\/u><\/b><br \/>  \u0414\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438 sinon.match.has, \u043d\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e \u0441\u0430\u043c\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c. \u0423\u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u0421\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439<\/h4>\n<p>  \u0412\u0441\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439 \u00ab\u0438\u00bb \u0438 \u00ab\u0438\u043b\u0438\u00bb. \u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043d\u043e\u0432\u044b\u0439 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0431\u043e\u0438\u0445 (\u0438) \u0438\u043b\u0438 \u043e\u0434\u0438\u043d \u0438\u0437 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0435\u0439 (\u0438\u043b\u0438) \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435.<\/p>\n<pre><code class=\"javascript\">var stringOrNumber = sinon.match.string.or(sinon.match.number);  var bookWithPages = sinon.match.instanceOf(Book).and(sinon.match.has(&quot;pages&quot;)); <\/code><\/pre>\n<p>  <\/p>\n<h4>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0438<\/h4>\n<p>  \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0441 <code>sinon.match<\/code> \u0444\u0430\u043a\u0442\u043e\u0440\u043e\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435. \u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>true<\/code>, \u0435\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044e \u0438 &#8216;false&#8217; \u2014 \u0435\u0441\u043b\u0438 \u0438\u043d\u0430\u0447\u0435. \u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u043a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u044e.<\/p>\n<pre><code class=\"javascript\">var trueIsh = sinon.match(function (value) {     return !!value; }, &quot;trueIsh&quot;); <\/code><\/pre>\n<p>  <\/p>\n<h4>Sandboxes<\/h4>\n<p>  \u041f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u044b \u0443\u043f\u0440\u043e\u0449\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0438\/\u0438\u043b\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u044b. \u0415\u0441\u043b\u0438 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0430\u0439\u043c\u0435\u0440\u044b, \u0444\u0430\u043b\u044c\u0441\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442\u0435 XHR, \u0438\u043b\u0438 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438\/\u0448\u043f\u0438\u043e\u043d\u044b \u043d\u0430 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u0445, \u0412\u0430\u043c \u043b\u0443\u0447\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0441\u043e\u0447\u043d\u0438\u0446\u0443, \u0447\u0442\u043e\u0431\u044b \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u043e\u0447\u0438\u0441\u0442\u043a\u0443.<\/p>\n<pre><code class=\"javascript\">&quot;test using sinon.test sandbox&quot;: sinon.test(function () {     var myAPI = { method: function () {} };     this.mock(myAPI).expects(&quot;method&quot;).once();      PubSub.subscribe(&quot;message&quot;, myAPI.method);     PubSub.publishSync(&quot;message&quot;, undefined); }) <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Sandbox API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>var sandbox = sinon.sandbox.create();<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 sandbox.<br \/>  <b><u><code>var sandbox = sinon.sandbox.create(config);<\/code><\/u><\/b><br \/>  &#8216;sinon.sandbox.create (config)&#8217; \u043c\u0435\u0442\u043e\u0434 \u2014 \u0433\u043b\u0430\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438, \u0438 \u043a\u0430\u043a \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e Sinon.JS \u0412\u0430\u043c, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u043e\u043d \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u0435\u043d.<\/p>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 sandbox. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0434\u0430\u0442\u044c sandbox \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u0430\u0439\u043c\u0435\u0440\u044b, \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044e \u0441 \u043d\u0438\u043c\u0438. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0442\u0430\u043a: <\/p>\n<pre><code class=\"javascript\">sinon.defaultConfig = {     \/\/ ...     injectInto: null,     properties: [&quot;spy&quot;, &quot;stub&quot;, &quot;mock&quot;, &quot;clock&quot;, &quot;server&quot;, &quot;requests&quot;],     useFakeTimers: true,     useFakeServer: true } <\/code><\/pre>\n<p>  <\/p>\n<ul>\n<li><b><u><code>injectInto<\/code><\/u><\/b> <br \/>  \u041c\u0435\u0442\u043e\u0434\u044b sandbox \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0432\u0432\u0435\u0434\u0435\u043d\u044b \u0432 \u0434\u0440\u0443\u0433\u043e\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u043e\u043f\u0446\u0438\u044f \u00abinjectInto\u00bb \u043c\u043e\u0436\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043e\u0431\u044a\u0435\u043a\u0442\u0443. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0434\u043b\u044f `sinon.test` \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e \u043e\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f` this` \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. <\/li>\n<li><b><u><code>properties<\/code><\/u><\/b><br \/>  \u041a\u0430\u043a\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0432\u0432\u0435\u0441\u0442\u0438. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u00abserver\u00bb \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u0427\u0442\u043e\u0431\u044b \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e &#8216;\u0441\u0435\u0440\u0432\u0435\u0440\u0430&#8217;, \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0432 \u0446\u0435\u043b\u0435\u0432\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0435, \u0412\u0430\u043c \u0442\u0430\u043a\u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c &#8216;useFakeServer&#8217; \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f.<\/li>\n<li><b><u><code>useFakeTimers<\/code><\/u><\/b><br \/>  \u0415\u0441\u043b\u0438 &#8216;true&#8217;, \u0443 sandbox \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e &#8216;\u0447\u0430\u0441\u043e\u0432&#8217;. \u041c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0431\u044b\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0442\u0430\u0439\u043c\u0435\u0440\u0430 \u0434\u043b\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438.<\/li>\n<li><b><u><code>useFakeServer<\/code><\/u><\/b><br \/>  \u0415\u0441\u043b\u0438 `true`, `server` \u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 `requests` \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043a sandbox. \u041c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0431\u044b\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u2014 &#8216;sinon.fakeServer&#8217;, \u043d\u043e \u0435\u0441\u043b\u0438 \u0412\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 jQuery 1.3.x \u0438\u043b\u0438 \u043a\u0430\u043a\u0443\u044e \u0442\u043e \u0434\u0440\u0443\u0433\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043d\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 &#8216;onreadystatechange&#8217; \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a XHR \u043f\u043e \u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:\n<pre><code class=\"javascript\">sinon.config = {     useFakeServer: sinon.fakeServerWithClock }; <\/code><\/pre>\n<p>  <\/li>\n<\/ul>\n<p>  <b><u><code>sandbox.spy();<\/code><\/u><\/b><br \/>  \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u0447\u043d\u043e \u043a\u0430\u043a <code>sinon.spy<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u043e\u0433\u043e \u0448\u043f\u0438\u043e\u043d\u0430 \u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c\u0443 \u043d\u0430\u0431\u043e\u0440\u0443 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0439 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 <code>sandbox.restore ()<\/code>. <br \/>  <b><u><code>sandbox.stub();<\/code><\/u><\/b><br \/>  \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e\u0447\u0442\u0438 \u0442\u043e\u0447\u043d\u043e \u043a\u0430\u043a <code>sinon.stub<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c\u0443 \u043d\u0430\u0431\u043e\u0440\u0443 \u0444\u0430\u043b\u044c\u0448\u0438\u0432\u043e\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 <code>sandbox.restore ()<\/code>. \u041c\u0435\u0442\u043e\u0434 <code>sandbox stub<\/code> \u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0433\u043b\u0443\u0448\u0438\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043b\u044e\u0431\u043e\u0433\u043e \u0432\u0438\u0434\u0430. \u042d\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e, \u0435\u0441\u043b\u0438 \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u0442\u0435\u0441\u0442\u0430, \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043a\u043e\u0433\u0434\u0430 \u0442\u0435\u0441\u0442 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d.<br \/>  <b><u><code>sandbox.mock();<\/code><\/u><\/b><br \/>  \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u0447\u043d\u043e \u043a\u0430\u043a <code>sinon.mock<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u043d\u0443\u044e \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u044e \u043a \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435\u043c\u0443 \u043d\u0430\u0431\u043e\u0440\u0443 \u0444\u0430\u043b\u044c\u0448\u0438\u0432\u043e\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 <code>sandbox.restore ()<\/code>.<br \/>  <b><u><code>sandbox.useFakeTimers();<\/code><\/u><\/b><br \/>  \u0421\u0432\u044f\u0437\u044b\u0432\u0430\u044e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0447\u0430\u0441\u043e\u0432 \u0441 sandbox, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 sandbox.restore (). \u0414\u043e\u0441\u0442\u0443\u043f \u0447\u0435\u0440\u0435\u0437 sandbox.clock.<br \/>  <b><u><code>sandbox.useFakeXMLHttpRequest();<\/code><\/u><\/b><br \/>  \u0418\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442 XHR \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 <code>sandbox<\/code>, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 <code>sandbox.restore ()<\/code>. \u0414\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 <code>sandbox.requests<\/code>.<br \/>  <b><u><code>sandbox.useFakeServer();<\/code><\/u><\/b><br \/>  \u0418\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442 XHR \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 <code>sandbox<\/code>, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 <code>sandbox.restore ()<\/code>. \u0414\u043e\u0441\u0442\u0443\u043f \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 <code>sandbox.requests<\/code> \u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 <code>sandbox.server<\/code>.<br \/>  <b><u><code>sandbox.restore();<\/code><\/u><\/b><br \/>  \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0441\u0435 \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u0447\u0435\u0440\u0435\u0437 sandbox.  <\/div>\n<\/div>\n<p>  <\/p>\n<h4>\u041c\u0435\u0442\u043e\u0434 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/h4>\n<p>  \u041c\u0435\u0442\u043e\u0434 \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f \u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0439 \u0432 <code>sinon.test<\/code> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 Sinon.JS \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c <code>sandbox<\/code>. \u041f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u0447\u0435\u0440\u0435\u0437 <code>sinon.config<\/code>.<br \/>  <b><u><code>var wrappedFn = sinon.test(fn);<\/code><\/u><\/b> <\/p>\n<p>  \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u044b\u044f <code>wrappedFn<\/code> \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0438\u0441\u0445\u043e\u0434\u043d\u0430\u044f. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043e\u0431\u044a\u0435\u043a\u0442 sandbox \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u043a\u043e\u0433\u0434\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0437\u0430\u043a\u0430\u043d\u0447\u0438\u0432\u0430\u0435\u0442 \u0432\u044b\u0437\u043e\u0432. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e <code>spy<\/code>, <code>stub<\/code> \u0438 <code>mock<\/code> \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 sandbox \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u043b\u044e\u0431\u044b\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c \u043d\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0442\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>this.spy () (stub \u0438 mock)<\/code>, \u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a <code>sandbox.spy () (stub \u0438 mock)<\/code>, \u0442\u0430\u043a \u0436\u0435 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"javascript\">{     injectIntoThis: true,     injectInto: null,     properties: [&quot;spy&quot;, &quot;stub&quot;, &quot;mock&quot;, &quot;clock&quot;, &quot;server&quot;, &quot;requests&quot;],     useFakeTimers: true,     useFakeServer: true } <\/code><\/pre>\n<p>  \u041f\u0440\u043e\u0441\u0442\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 <code>sinon.config<\/code>, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u0438\u0437 \u043d\u0438\u0445, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"javascript\">sinon.config = {     useFakeTimers: false,     useFakeServer: false } <\/code><\/pre>\n<p>  \u0412 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u043b\u044f \u043d\u0435\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0441\u0432\u043e\u0439\u0441\u0442\u0432. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, sandbox \u0438 \u0442\u0435\u0441\u0442\u044b \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0442\u0430\u0439\u043c\u0435\u0440\u0430\u043c \u0438 \u043f\u043e\u0434\u0434\u0435\u043b\u044c\u043d\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u044d\u0442\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438.  <\/p>\n<h5>sinon.config<\/h5>\n<p>  \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0442\u0435\u043c \u043a\u0430\u043a Sinon \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 <code>sinon.test<\/code>. \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430:<\/p>\n<p>  <b><u><code>Boolean injectIntoThis<\/code><\/u><\/b><br \/>  \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u0432\u0435\u0434\u0435\u043d\u043e \u0432 \u044d\u0442\u043e\u043c \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e.<br \/>  <b><u><code>Object injectInto<\/code><\/u><\/b><br \/>  \u041e\u0431\u044a\u0435\u043a\u0442, \u0447\u0442\u043e\u0431\u044b \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430. \u0415\u0441\u043b\u0438 \u044d\u0442\u043e <code>null<\/code> (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e) \u0438 <code>injectIntoThis<\/code> <code>false<\/code> (\u043d\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e), \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<br \/>  <b><u><code>Array properties<\/code><\/u><\/b><br \/>  \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u2014 \u0432\u0441\u0435: <code>[spy, stub, mock, clock, server, requests]<\/code>. \u041e\u0434\u043d\u0430\u043a\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0442\u0440\u0438 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u2014 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u044b.<br \/>  <b><u><code>Boolean useFakeTimers<\/code><\/u><\/b><br \/>  \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u0442 \u0442\u0430\u0439\u043c\u0435\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0438\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0443 \u0447\u0430\u0441\u043e\u0432 \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c. \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e. <br \/>  <b><u><code>Boolean useFakeServer<\/code><\/u><\/b><br \/>  \u0418\u043c\u0438\u0442\u0438\u0440\u0443\u0435\u0442 XHR \u0438 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c\u044b\u043c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u043c \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438. \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u2014 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u043e. <\/p>\n<h4>\u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439<\/h4>\n<p>  \u0415\u0441\u043b\u0438 \u0412\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 <code>sinon.test<\/code> \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>sinon.testCase<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0435\u0434\u0435\u0442 \u0441\u0435\u0431\u044f \u0442\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0443\u043f\u0430\u043a\u043e\u0432\u043a\u0430 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430 \u0432 <code>sinon.test<\/code> \u0441 \u043e\u0434\u043d\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c:` \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 <code>setUp<\/code> \u0438 <code>tearDown<\/code> \u043c\u043e\u0433\u0443\u0442 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438. <\/p>\n<p>  <b><u><code>var obj = sinon.testCase({});<\/code><\/u><\/b><\/p>\n<h4>Sinon.JS \u0443\u0442\u0438\u043b\u0438\u0442\u044b<\/h4>\n<p>  Sinon.JS \u0438\u043c\u0435\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0443\u0442\u0438\u043b\u0438\u0442, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0435. \u0415\u0441\u043b\u0438 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u043d\u0435 \u0437\u0430\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d \u0437\u0434\u0435\u0441\u044c, \u0435\u0433\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u044c\u044e \u043e\u0431\u0449\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0433\u043e API. <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0423\u0442\u0438\u043b\u0438\u0442\u044b API<\/b><\/p>\n<div class=\"spoiler_text\"><b><u><code>sinon.createStubInstance(constructor)<\/code><\/u><\/b><br \/>  \u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439, \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u0438 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439. \u0423\u043a\u0430\u0437\u0430\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0430 \u043d\u0435 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f. \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435 \u0442\u0430\u043a \u0436\u0435 API \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438. <br \/>  <b><u><code>sinon.format(object)<\/code><\/u><\/b><br \/>  \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0430\u0434\u0435\u043a\u0432\u0430\u0442\u043d\u043e\u0439 \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u0438 \u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u0445 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435. \u041c\u0435\u0442\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<br \/>  <b><u><code>sinon.log(string)<\/code><\/u><\/b><br \/>  \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u0442 \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043a\u0438, \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438.  <\/div>\n<\/div>\n<p>  \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438, \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043d\u0430\u0439\u0442\u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u044f \u043f\u043e <a href=\"http:\/\/sinonjs.org\/releases\/sinon-1.17.2.js\">\u0441\u0441\u044b\u043b\u043a\u0435<\/a> \u0438\u043b\u0438 \u0432 \u0441\u043f\u043e\u0439\u043b\u0435\u0440\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c \u043d\u0438\u0436\u0435.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434.<\/b><\/p>\n<div class=\"spoiler_text\">(function (root, factory) {<br \/>   &#8216;use strict&#8217;;<br \/>   if (typeof define === &#8216;function&#8217; &#038;&#038; define.amd) {<br \/>   define(&#8216;sinon&#8217;, [], function () {<br \/>   return (root.sinon = factory());<br \/>   });<br \/>   } else if (typeof exports === &#8216;object&#8217;) {<br \/>   module.exports = factory();<br \/>   } else {<br \/>   root.sinon = factory();<br \/>   }<br \/>  }(this, function () {<br \/>   &#8216;use strict&#8217;;<br \/>   var samsam, formatio, lolex;<br \/>   (function () {<br \/>   function define(mod, deps, fn) {<br \/>   if (mod == \u00absamsam\u00bb) {<br \/>   samsam = deps();<br \/>   } else if (typeof deps === \u00abfunction\u00bb &#038;&#038; mod.length === 0) {<br \/>   lolex = deps();<br \/>   } else if (typeof fn === \u00abfunction\u00bb) {<br \/>   formatio = fn(samsam);<br \/>   }<br \/>   }<br \/>   define.amd = {};<br \/>  ((typeof define === \u00abfunction\u00bb &#038;&#038; define.amd &#038;&#038; function (m) { define(\u00absamsam\u00bb, m); }) ||<br \/>   (typeof module === \u00abobject\u00bb &#038;&#038;<br \/>   function (m) { module.exports = m(); }) || \/\/ Node<br \/>   function (m) { this.samsam = m(); } \/\/ Browser globals<br \/>  )(function () {<br \/>   var o = Object.prototype;<br \/>   var div = typeof document !== \u00abundefined\u00bb &#038;&#038; document.createElement(\u00abdiv\u00bb);<\/p>\n<p>  function isNaN(value) {<br \/>   \/\/ Unlike global isNaN, this avoids type coercion<br \/>   \/\/ typeof check avoids IE host object issues, hat tip to<br \/>   \/\/ lodash<br \/>   var val = value; \/\/ JsLint thinks value !== value is \u00abweird\u00bb<br \/>   return typeof value === \u00abnumber\u00bb &#038;&#038; value !== val;<br \/>   }<\/p>\n<p>  function getClass(value) {<br \/>   \/\/ Returns the internal [[Class]] by calling Object.prototype.toString<br \/>   \/\/ with the provided value as this. Return value is a string, naming the<br \/>   \/\/ internal class, e.g. \u00abArray\u00bb<br \/>   return o.toString.call(value).split(\/[ \\]]\/)[1];<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.isArguments<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object object<br \/>   *<br \/>   * Returns &#171;true&#171; if &#171;object&#171; is an &#171;arguments&#171; object,<br \/>   * &#171;false&#171; otherwise.<br \/>   *\/<br \/>   function isArguments(object) {<br \/>   if (getClass(object) === &#8216;Arguments&#8217;) { return true; }<br \/>   if (typeof object !== \u00abobject\u00bb || typeof object.length !== \u00abnumber\u00bb ||<br \/>   getClass(object) === \u00abArray\u00bb) {<br \/>   return false;<br \/>   }<br \/>   if (typeof object.callee == \u00abfunction\u00bb) { return true; }<br \/>   try {<br \/>   object[object.length] = 6;<br \/>   delete object[object.length];<br \/>   } catch (e) {<br \/>   return true;<br \/>   }<br \/>   return false;<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.isElement<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object object<br \/>   *<br \/>   * Returns &#171;true&#171; if &#171;object&#171; is a DOM element node. Unlike<br \/>   * Underscore.js\/lodash, this function will return &#171;false&#171; if &#171;object&#171;<br \/>   * is an *element-like* object, i.e. a regular object with a &#171;nodeType&#171;<br \/>   * property that holds the value &#171;1&#171;.<br \/>   *\/<br \/>   function isElement(object) {<br \/>   if (!object || object.nodeType !== 1 || !div) { return false; }<br \/>   try {<br \/>   object.appendChild(div);<br \/>   object.removeChild(div);<br \/>   } catch (e) {<br \/>   return false;<br \/>   }<br \/>   return true;<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.keys<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object object<br \/>   *<br \/>   * Return an array of own property names.<br \/>   *\/<br \/>   function keys(object) {<br \/>   var ks = [], prop;<br \/>   for (prop in object) {<br \/>   if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); }<br \/>   }<br \/>   return ks;<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.isDate<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object value<br \/>   *<br \/>   * Returns true if the object is a &#171;Date&#171;, or *date-like*. Duck typing<br \/>   * of date objects work by checking that the object has a &#171;getTime&#171;<br \/>   * function whose return value equals the return value from the object&#8217;s<br \/>   * &#171;valueOf&#171;.<br \/>   *\/<br \/>   function isDate(value) {<br \/>   return typeof value.getTime == \u00abfunction\u00bb &#038;&#038;<br \/>   value.getTime() == value.valueOf();<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.isNegZero<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object value<br \/>   *<br \/>   * Returns &#171;true&#171; if &#171;value&#171; is &#171;-0&#171;.<br \/>   *\/<br \/>   function isNegZero(value) {<br \/>   return value === 0 &#038;&#038; 1 \/ value === -Infinity;<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.equal<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object obj1<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object obj2<br \/>   *<br \/>   * Returns &#171;true&#171; if two objects are strictly equal. Compared to<br \/>   * &#171;===&#171; there are two exceptions:<br \/>   *<br \/>   * \u2014 NaN is considered equal to NaN<br \/>   * \u2014 -0 and +0 are not considered equal<br \/>   *\/<br \/>   function identical(obj1, obj2) {<br \/>   if (obj1 === obj2 || (isNaN(obj1) &#038;&#038; isNaN(obj2))) {<br \/>   return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2);<br \/>   }<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.deepEqual<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object obj1<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object obj2<br \/>   *<br \/>   * Deep equal comparison. Two values are \u00abdeep equal\u00bb if:<br \/>   *<br \/>   * \u2014 They are equal, according to samsam.identical<br \/>   * \u2014 They are both date objects representing the same time<br \/>   * \u2014 They are both arrays containing elements that are all deepEqual<br \/>   * \u2014 They are objects with the same set of properties, and each property<br \/>   * in &#171;obj1&#171; is deepEqual to the corresponding property in &#171;obj2&#171;<br \/>   *<br \/>   * Supports cyclic objects.<br \/>   *\/<br \/>   function deepEqualCyclic(obj1, obj2) {<\/p>\n<p>  \/\/ used for cyclic comparison<br \/>   \/\/ contain already visited objects<br \/>   var objects1 = [],<br \/>   objects2 = [],<br \/>   \/\/ contain pathes (position in the object structure)<br \/>   \/\/ of the already visited objects<br \/>   \/\/ indexes same as in objects arrays<br \/>   paths1 = [],<br \/>   paths2 = [],<br \/>   \/\/ contains combinations of already compared objects<br \/>   \/\/ in the manner: { &quot;$1[&#8216;ref&#8217;]$2[&#8216;ref&#8217;]&quot;: true }<br \/>   compared = {};<\/p>\n<p>  \/**<br \/>   * used to check, if the value of a property is an object<br \/>   * (cyclic logic is only needed for objects)<br \/>   * only needed for cyclic logic<br \/>   *\/<br \/>   function isObject(value) {<\/p>\n<p>  if (typeof value === &#8216;object&#8217; &#038;&#038; value !== null &#038;&#038;<br \/>   !(value instanceof Boolean) &#038;&#038;<br \/>   !(value instanceof Date) &#038;&#038;<br \/>   !(value instanceof Number) &#038;&#038;<br \/>   !(value instanceof RegExp) &#038;&#038;<br \/>   !(value instanceof String)) {<\/p>\n<p>  return true;<br \/>   }<\/p>\n<p>  return false;<br \/>   }<\/p>\n<p>  \/**<br \/>   * returns the index of the given object in the<br \/>   * given objects array, -1 if not contained<br \/>   * only needed for cyclic logic<br \/>   *\/<br \/>   function getIndex(objects, obj) {<\/p>\n<p>  var i;<br \/>   for (i = 0; i &lt; objects.length; i++) {<br \/>   if (objects[i] === obj) {<br \/>   return i;<br \/>   }<br \/>   }<\/p>\n<p>  return -1;<br \/>   }<\/p>\n<p>  \/\/ does the recursion for the deep equal check<br \/>   return (function deepEqual(obj1, obj2, path1, path2) {<br \/>   var type1 = typeof obj1;<br \/>   var type2 = typeof obj2;<\/p>\n<p>  \/\/ == null also matches undefined<br \/>   if (obj1 === obj2 ||<br \/>   isNaN(obj1) || isNaN(obj2) ||<br \/>   obj1 == null || obj2 == null ||<br \/>   type1 !== \u00abobject\u00bb || type2 !== \u00abobject\u00bb) {<\/p>\n<p>  return identical(obj1, obj2);<br \/>   }<\/p>\n<p>  \/\/ Elements are only equal if identical(expected, actual)<br \/>   if (isElement(obj1) || isElement(obj2)) { return false; }<\/p>\n<p>  var isDate1 = isDate(obj1), isDate2 = isDate(obj2);<br \/>   if (isDate1 || isDate2) {<br \/>   if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  if (obj1 instanceof RegExp &#038;&#038; obj2 instanceof RegExp) {<br \/>   if (obj1.toString() !== obj2.toString()) { return false; }<br \/>   }<\/p>\n<p>  var class1 = getClass(obj1);<br \/>   var class2 = getClass(obj2);<br \/>   var keys1 = keys(obj1);<br \/>   var keys2 = keys(obj2);<\/p>\n<p>  if (isArguments(obj1) || isArguments(obj2)) {<br \/>   if (obj1.length !== obj2.length) { return false; }<br \/>   } else {<br \/>   if (type1 !== type2 || class1 !== class2 ||<br \/>   keys1.length !== keys2.length) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  var key, i, l,<br \/>   \/\/ following vars are used for the cyclic logic<br \/>   value1, value2,<br \/>   isObject1, isObject2,<br \/>   index1, index2,<br \/>   newPath1, newPath2;<\/p>\n<p>  for (i = 0, l = keys1.length; i &lt; l; i++) {<br \/>   key = keys1[i];<br \/>   if (!o.hasOwnProperty.call(obj2, key)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  \/\/ Start of the cyclic logic<\/p>\n<p>  value1 = obj1[key];<br \/>   value2 = obj2[key];<\/p>\n<p>  isObject1 = isObject(value1);<br \/>   isObject2 = isObject(value2);<\/p>\n<p>  \/\/ determine, if the objects were already visited<br \/>   \/\/ (it&#8217;s faster to check for isObject first, than to<br \/>   \/\/ get -1 from getIndex for non objects)<br \/>   index1 = isObject1? getIndex(objects1, value1): -1;<br \/>   index2 = isObject2? getIndex(objects2, value2): -1;<\/p>\n<p>  \/\/ determine the new pathes of the objects<br \/>   \/\/ \u2014 for non cyclic objects the current path will be extended<br \/>   \/\/ by current property name<br \/>   \/\/ \u2014 for cyclic objects the stored path is taken<br \/>   newPath1 = index1 !== -1<br \/>  ? paths1[index1]<br \/>  : path1 + &#8216;[&#8216; + JSON.stringify(key) + &#8216;]&#8217;;<br \/>   newPath2 = index2 !== -1<br \/>  ? paths2[index2]<br \/>  : path2 + &#8216;[&#8216; + JSON.stringify(key) + &#8216;]&#8217;;<\/p>\n<p>  \/\/ stop recursion if current objects are already compared<br \/>   if (compared[newPath1 + newPath2]) {<br \/>   return true;<br \/>   }<\/p>\n<p>  \/\/ remember the current objects and their pathes<br \/>   if (index1 === -1 &#038;&#038; isObject1) {<br \/>   objects1.push(value1);<br \/>   paths1.push(newPath1);<br \/>   }<br \/>   if (index2 === -1 &#038;&#038; isObject2) {<br \/>   objects2.push(value2);<br \/>   paths2.push(newPath2);<br \/>   }<\/p>\n<p>  \/\/ remember that the current objects are already compared<br \/>   if (isObject1 &#038;&#038; isObject2) {<br \/>   compared[newPath1 + newPath2] = true;<br \/>   }<\/p>\n<p>  \/\/ End of cyclic logic<\/p>\n<p>  \/\/ neither value1 nor value2 is a cycle<br \/>   \/\/ continue with next level<br \/>   if (!deepEqual(value1, value2, newPath1, newPath2)) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  return true;<\/p>\n<p>  }(obj1, obj2, &#8216;$1&#8217;, &#8216;$2&#8217;));<br \/>   }<\/p>\n<p>  var match;<\/p>\n<p>  function arrayContains(array, subset) {<br \/>   if (subset.length === 0) { return true; }<br \/>   var i, l, j, k;<br \/>   for (i = 0, l = array.length; i &lt; l; ++i) {<br \/>   if (match(array[i], subset[0])) {<br \/>   for (j = 0, k = subset.length; j &lt; k; ++j) {<br \/>   if (!match(array[i + j], subset[j])) { return false; }<br \/>   }<br \/>   return true;<br \/>   }<br \/>   }<br \/>   return false;<br \/>   }<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/name\/\" class=\"user_link\">name<\/a> samsam.match<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object object<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/param\/\" class=\"user_link\">param<\/a> Object matcher<br \/>   *<br \/>   * Compare arbitrary value &#171;object&#171; with matcher.<br \/>   *\/<br \/>   match = function match(object, matcher) {<br \/>   if (matcher &#038;&#038; typeof matcher.test === \u00abfunction\u00bb) {<br \/>   return matcher.test(object);<br \/>   }<\/p>\n<p>  if (typeof matcher === \u00abfunction\u00bb) {<br \/>   return matcher(object) === true;<br \/>   }<\/p>\n<p>  if (typeof matcher === \u00abstring\u00bb) {<br \/>   matcher = matcher.toLowerCase();<br \/>   var notNull = typeof object === \u00abstring\u00bb || !!object;<br \/>   return notNull &#038;&#038;<br \/>   (String(object)).toLowerCase().indexOf(matcher) &gt;= 0;<br \/>   }<\/p>\n<p>  if (typeof matcher === \u00abnumber\u00bb) {<br \/>   return matcher === object;<br \/>   }<\/p>\n<p>  if (typeof matcher === \u00abboolean\u00bb) {<br \/>   return matcher === object;<br \/>   }<\/p>\n<p>  if (typeof(matcher) === \u00abundefined\u00bb) {<br \/>   return typeof(object) === \u00abundefined\u00bb;<br \/>   }<\/p>\n<p>  if (matcher === null) {<br \/>   return object === null;<br \/>   }<\/p>\n<p>  if (getClass(object) === \u00abArray\u00bb &#038;&#038; getClass(matcher) === \u00abArray\u00bb) {<br \/>   return arrayContains(object, matcher);<br \/>   }<\/p>\n<p>  if (matcher &#038;&#038; typeof matcher === \u00abobject\u00bb) {<br \/>   if (matcher === object) {<br \/>   return true;<br \/>   }<br \/>   var prop;<br \/>   for (prop in matcher) {<br \/>   var value = object[prop];<br \/>   if (typeof value === \u00abundefined\u00bb &#038;&#038;<br \/>   typeof object.getAttribute === \u00abfunction\u00bb) {<br \/>   value = object.getAttribute(prop);<br \/>   }<br \/>   if (matcher[prop] === null || typeof matcher[prop] === &#8216;undefined&#8217;) {<br \/>   if (value !== matcher[prop]) {<br \/>   return false;<br \/>   }<br \/>   } else if (typeof value === \u00abundefined\u00bb || !match(value, matcher[prop])) {<br \/>   return false;<br \/>   }<br \/>   }<br \/>   return true;<br \/>   }<\/p>\n<p>  throw new Error(\u00abMatcher was not a string, a number, a \u00bb +<br \/>   \u00abfunction, a boolean or an object\u00bb);<br \/>   };<\/p>\n<p>  return {<br \/>   isArguments: isArguments,<br \/>   isElement: isElement,<br \/>   isDate: isDate,<br \/>   isNegZero: isNegZero,<br \/>   identical: identical,<br \/>   deepEqual: deepEqualCyclic,<br \/>   match: match,<br \/>   keys: keys<br \/>   };<br \/>  });<br \/>  ((typeof define === \u00abfunction\u00bb &#038;&#038; define.amd &#038;&#038; function (m) {<br \/>   define(\u00abformatio\u00bb, [\u00absamsam\u00bb], m);<br \/>  }) || (typeof module === \u00abobject\u00bb &#038;&#038; function (m) {<br \/>   module.exports = m(require(\u00absamsam\u00bb));<br \/>  }) || function (m) { this.formatio = m(this.samsam); }<br \/>  )(function (samsam) {<\/p>\n<p>   var formatio = {<br \/>   excludeConstructors: [\u00abObject\u00bb, \/^.$\/],<br \/>   quoteStrings: true,<br \/>   limitChildrenCount: 0<br \/>   };<\/p>\n<p>  var hasOwn = Object.prototype.hasOwnProperty;<\/p>\n<p>  var specialObjects = [];<br \/>   if (typeof global !== \u00abundefined\u00bb) {<br \/>   specialObjects.push({ object: global, value: &quot;[object global]&quot; });<br \/>   }<br \/>   if (typeof document !== \u00abundefined\u00bb) {<br \/>   specialObjects.push({<br \/>   object: document,<br \/>   value: &quot;[object HTMLDocument]&quot;<br \/>   });<br \/>   }<br \/>   if (typeof window !== \u00abundefined\u00bb) {<br \/>   specialObjects.push({ object: window, value: &quot;[object Window]&quot; });<br \/>   }<\/p>\n<p>  function functionName(func) {<br \/>   if (!func) { return &quot;&quot;; }<br \/>   if (func.displayName) { return func.displayName; }<br \/>   if (func.name) { return func.name; }<br \/>   var matches = func.toString().match(\/function\\s+([^\\(]+)\/m);<br \/>   return (matches &#038;&#038; matches[1]) || &quot;&quot;;<br \/>   }<\/p>\n<p>  function constructorName(f, object) {<br \/>   var name = functionName(object &#038;&#038; object.constructor);<br \/>   var excludes = f.excludeConstructors ||<br \/>   formatio.excludeConstructors || [];<\/p>\n<p>  var i, l;<br \/>   for (i = 0, l = excludes.length; i &lt; l; ++i) {<br \/>   if (typeof excludes[i] === \u00abstring\u00bb &#038;&#038; excludes[i] === name) {<br \/>   return &quot;&quot;;<br \/>   } else if (excludes[i].test &#038;&#038; excludes[i].test(name)) {<br \/>   return &quot;&quot;;<br \/>   }<br \/>   }<\/p>\n<p>  return name;<br \/>   }<\/p>\n<p>  function isCircular(object, objects) {<br \/>   if (typeof object !== \u00abobject\u00bb) { return false; }<br \/>   var i, l;<br \/>   for (i = 0, l = objects.length; i &lt; l; ++i) {<br \/>   if (objects[i] === object) { return true; }<br \/>   }<br \/>   return false;<br \/>   }<\/p>\n<p>  function ascii(f, object, processed, indent) {<br \/>   if (typeof object === \u00abstring\u00bb) {<br \/>   var qs = f.quoteStrings;<br \/>   var quote = typeof qs !== \u00abboolean\u00bb || qs;<br \/>   return processed || quote? &#8216;&quot;&#8217; + object + &#8216;&quot;&#8217;: object;<br \/>   }<\/p>\n<p>  if (typeof object === \u00abfunction\u00bb &#038;&#038; !(object instanceof RegExp)) {<br \/>   return ascii.func(object);<br \/>   }<\/p>\n<p>  processed = processed || [];<\/p>\n<p>  if (isCircular(object, processed)) { return &quot;[Circular]&quot;; }<\/p>\n<p>  if (Object.prototype.toString.call(object) === &quot;[object Array]&quot;) {<br \/>   return ascii.array.call(f, object, processed);<br \/>   }<\/p>\n<p>  if (!object) { return String((1\/object) === -Infinity? &quot;-0&quot;: object); }<br \/>   if (samsam.isElement(object)) { return ascii.element(object); }<\/p>\n<p>  if (typeof object.toString === \u00abfunction\u00bb &#038;&#038;<br \/>   object.toString !== Object.prototype.toString) {<br \/>   return object.toString();<br \/>   }<\/p>\n<p>  var i, l;<br \/>   for (i = 0, l = specialObjects.length; i &lt; l; i++) {<br \/>   if (object === specialObjects[i].object) {<br \/>   return specialObjects[i].value;<br \/>   }<br \/>   }<\/p>\n<p>  return ascii.object.call(f, object, processed, indent);<br \/>   }<\/p>\n<p>  ascii.func = function (func) {<br \/>   return \u00abfunction \u00bb + functionName(func) + &quot;() {}&quot;;<br \/>   };<\/p>\n<p>  ascii.array = function (array, processed) {<br \/>   processed = processed || [];<br \/>   processed.push(array);<br \/>   var pieces = [];<br \/>   var i, l;<br \/>   l = (this.limitChildrenCount &gt; 0)? <br \/>   Math.min(this.limitChildrenCount, array.length): array.length;<\/p>\n<p>  for (i = 0; i &lt; l; ++i) {<br \/>   pieces.push(ascii(this, array[i], processed));<br \/>   }<\/p>\n<p>  if(l &lt; array.length)<br \/>   pieces.push(&quot;[\u2026 &quot; + (array.length \u2014 l) + &quot; more elements]&quot;);<\/p>\n<p>  return &quot;[&quot; + pieces.join(&quot;, &quot;) + &quot;]&quot;;<br \/>   };<\/p>\n<p>  ascii.object = function (object, processed, indent) {<br \/>   processed = processed || [];<br \/>   processed.push(object);<br \/>   indent = indent || 0;<br \/>   var pieces = [], properties = samsam.keys(object).sort();<br \/>   var length = 3;<br \/>   var prop, str, obj, i, k, l;<br \/>   l = (this.limitChildrenCount &gt; 0)? <br \/>   Math.min(this.limitChildrenCount, properties.length): properties.length;<\/p>\n<p>  for (i = 0; i &lt; l; ++i) {<br \/>   prop = properties[i];<br \/>   obj = object[prop];<\/p>\n<p>  if (isCircular(obj, processed)) {<br \/>   str = &quot;[Circular]&quot;;<br \/>   } else {<br \/>   str = ascii(this, obj, processed, indent + 2);<br \/>   }<\/p>\n<p>  str = (\/\\s\/.test(prop)? &#8216;&quot;&#8217; + prop + &#8216;&quot;&#8217;: prop) + &quot;: &quot; + str;<br \/>   length += str.length;<br \/>   pieces.push(str);<br \/>   }<\/p>\n<p>  var cons = constructorName(this, object);<br \/>   var prefix = cons? &quot;[&quot; + cons + &quot;] &quot;: &quot;&quot;;<br \/>   var is = &quot;&quot;;<br \/>   for (i = 0, k = indent; i &lt; k; ++i) { is += &quot; &quot;; }<\/p>\n<p>  if(l &lt; properties.length)<br \/>   pieces.push(&quot;[\u2026 &quot; + (properties.length \u2014 l) + &quot; more elements]&quot;);<\/p>\n<p>  if (length + indent &gt; 80) {<br \/>   return prefix + &quot;{\\n &quot; + is + pieces.join(&quot;,\\n &quot; + is) + &quot;\\n&quot; +<br \/>   is + &quot;}&quot;;<br \/>   }<br \/>   return prefix + &quot;{ &quot; + pieces.join(&quot;, &quot;) + &quot; }&quot;;<br \/>   };<\/p>\n<p>  ascii.element = function (element) {<br \/>   var tagName = element.tagName.toLowerCase();<br \/>   var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;<\/p>\n<p>  for (i = 0, l = attrs.length; i &lt; l; ++i) {<br \/>   attr = attrs.item(i);<br \/>   attrName = attr.nodeName.toLowerCase().replace(\u00abhtml:\u00bb, &quot;&quot;);<br \/>   val = attr.nodeValue;<br \/>   if (attrName !== \u00abcontenteditable\u00bb || val !== \u00abinherit\u00bb) {<br \/>   if (!!val) { pairs.push(attrName + &quot;=\\&quot;&quot; + val + &quot;\\&quot;&quot;); }<br \/>   }<br \/>   }<\/p>\n<p>  var formatted = &quot;&lt;&quot; + tagName + (pairs.length &gt; 0? &quot; &quot;: &quot;&quot;);<br \/>   var content = element.innerHTML;<\/p>\n<p>  if (content.length &gt; 20) {<br \/>   content = content.substr(0, 20) + &quot;[&#8230;]&quot;;<br \/>   }<\/p>\n<p>  var res = formatted + pairs.join(&quot; &quot;) + &quot;&gt;&quot; + content +<br \/>   &quot;&lt;\/&quot; + tagName + &quot;&gt;&quot;;<\/p>\n<p>  return res.replace(\/ contentEditable=\u00abinherit\u00bb\/, &quot;&quot;);<br \/>   };<\/p>\n<p>  function Formatio(options) {<br \/>   for (var opt in options) {<br \/>   this[opt] = options[opt];<br \/>   }<br \/>   }<\/p>\n<p>  Formatio.prototype = {<br \/>   functionName: functionName,<\/p>\n<p>  configure: function (options) {<br \/>   return new Formatio(options);<br \/>   },<\/p>\n<p>  constructorName: function (object) {<br \/>   return constructorName(this, object);<br \/>   },<\/p>\n<p>  ascii: function (object, processed, indent) {<br \/>   return ascii(this, object, processed, indent);<br \/>   }<br \/>   };<\/p>\n<p>  return Formatio.prototype;<br \/>  });<br \/>  !function(e){if(\u00abobject\u00bb==typeof exports&#038;&#038;\u00abundefined\u00bb!=typeof module)module.exports=e();else if(\u00abfunction\u00bb==typeof define&#038;&#038;define.amd)define([],e);else{var f;\u00abundefined\u00bb!=typeof window?f=window:\u00abundefined\u00bb!=typeof global?f=global:\u00abundefined\u00bb!=typeof self&#038;&#038;(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==\u00abfunction\u00bb&#038;&require;if(!u&#038;&#038;a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\u00abCannot find module &#8216;\u00bb+o+&quot;&#8217;&quot;);throw f.code=\u00abMODULE_NOT_FOUND\u00bb,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==\u00abfunction\u00bb&#038;&require;for(var o=0;o&lt;r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){<br \/>  (function (global){<br \/>  \/*global global, window*\/<br \/>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no) and contributors<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2014 Christian Johansen<br \/>   *\/<\/p>\n<p>  (function (global) {<\/p>\n<p>   \/\/ Make properties writable in IE, as per<br \/>   \/\/ <a href=\"http:\/\/www.adequatelygood.com\/Replacing-setTimeout-Globally.html\">www.adequatelygood.com\/Replacing-setTimeout-Globally.html<\/a><br \/>   \/\/ JSLint being anal<br \/>   var glbl = global;<\/p>\n<p>  global.setTimeout = glbl.setTimeout;<br \/>   global.clearTimeout = glbl.clearTimeout;<br \/>   global.setInterval = glbl.setInterval;<br \/>   global.clearInterval = glbl.clearInterval;<br \/>   global.Date = glbl.Date;<\/p>\n<p>  \/\/ setImmediate is not a standard function<br \/>   \/\/ avoid adding the prop to the window object if not present<br \/>   if(&#8216;setImmediate&#8217; in global) {<br \/>   global.setImmediate = glbl.setImmediate;<br \/>   global.clearImmediate = glbl.clearImmediate;<br \/>   }<\/p>\n<p>  \/\/ node expects setTimeout\/setInterval to return a fn object w\/ .ref()\/.unref()<br \/>   \/\/ browsers, a number.<br \/>   \/\/ see <a href=\"https:\/\/github.com\/cjohansen\/Sinon.JS\/pull\/436\">github.com\/cjohansen\/Sinon.JS\/pull\/436<\/a><\/p>\n<p>  var NOOP = function () { return undefined; };<br \/>   var timeoutResult = setTimeout(NOOP, 0);<br \/>   var addTimerReturnsObject = typeof timeoutResult === \u00abobject\u00bb;<br \/>   clearTimeout(timeoutResult);<\/p>\n<p>  var NativeDate = Date;<br \/>   var uniqueTimerId = 1;<\/p>\n<p>  \/**<br \/>   * Parse strings like \u00ab01:10:00\u00bb (meaning 1 hour, 10 minutes, 0 seconds) into<br \/>   * number of milliseconds. This is used to support human-readable strings passed<br \/>   * to clock.tick()<br \/>   *\/<br \/>   function parseTime(str) {<br \/>   if (!str) {<br \/>   return 0;<br \/>   }<\/p>\n<p>  var strings = str.split(&quot;:&quot;);<br \/>   var l = strings.length, i = l;<br \/>   var ms = 0, parsed;<\/p>\n<p>  if (l &gt; 3 || !\/^(\\d\\d:){0,2}\\d\\d?$\/.test(str)) {<br \/>   throw new Error(\u00abtick only understands numbers and &#8216;h:m:s&#8217;\u00bb);<br \/>   }<\/p>\n<p>  while (i&#8212;) {<br \/>   parsed = parseInt(strings[i], 10);<\/p>\n<p>  if (parsed &gt;= 60) {<br \/>   throw new Error(\u00abInvalid time \u00bb + str);<br \/>   }<\/p>\n<p>  ms += parsed * Math.pow(60, (l \u2014 i \u2014 1));<br \/>   }<\/p>\n<p>  return ms * 1000;<br \/>   }<\/p>\n<p>  \/**<br \/>   * Used to grok the `now` parameter to createClock.<br \/>   *\/<br \/>   function getEpoch(epoch) {<br \/>   if (!epoch) { return 0; }<br \/>   if (typeof epoch.getTime === \u00abfunction\u00bb) { return epoch.getTime(); }<br \/>   if (typeof epoch === \u00abnumber\u00bb) { return epoch; }<br \/>   throw new TypeError(\u00abnow should be milliseconds since UNIX epoch\u00bb);<br \/>   }<\/p>\n<p>  function inRange(from, to, timer) {<br \/>   return timer &#038;&#038; timer.callAt &gt;= from &#038;&#038; timer.callAt &lt;= to;<br \/>   }<\/p>\n<p>  function mirrorDateProperties(target, source) {<br \/>   var prop;<br \/>   for (prop in source) {<br \/>   if (source.hasOwnProperty(prop)) {<br \/>   target[prop] = source[prop];<br \/>   }<br \/>   }<\/p>\n<p>  \/\/ set special now implementation<br \/>   if (source.now) {<br \/>   target.now = function now() {<br \/>   return target.clock.now;<br \/>   };<br \/>   } else {<br \/>   delete target.now;<br \/>   }<\/p>\n<p>  \/\/ set special toSource implementation<br \/>   if (source.toSource) {<br \/>   target.toSource = function toSource() {<br \/>   return source.toSource();<br \/>   };<br \/>   } else {<br \/>   delete target.toSource;<br \/>   }<\/p>\n<p>  \/\/ set special toString implementation<br \/>   target.toString = function toString() {<br \/>   return source.toString();<br \/>   };<\/p>\n<p>  target.prototype = source.prototype;<br \/>   target.parse = source.parse;<br \/>   target.UTC = source.UTC;<br \/>   target.prototype.toUTCString = source.prototype.toUTCString;<\/p>\n<p>  return target;<br \/>   }<\/p>\n<p>  function createDate() {<br \/>   function ClockDate(year, month, date, hour, minute, second, ms) {<br \/>   \/\/ Defensive and verbose to avoid potential harm in passing<br \/>   \/\/ explicit undefined when user does not pass argument<br \/>   switch (arguments.length) {<br \/>   case 0:<br \/>   return new NativeDate(ClockDate.clock.now);<br \/>   case 1:<br \/>   return new NativeDate(year);<br \/>   case 2:<br \/>   return new NativeDate(year, month);<br \/>   case 3:<br \/>   return new NativeDate(year, month, date);<br \/>   case 4:<br \/>   return new NativeDate(year, month, date, hour);<br \/>   case 5:<br \/>   return new NativeDate(year, month, date, hour, minute);<br \/>   case 6:<br \/>   return new NativeDate(year, month, date, hour, minute, second);<br \/>   default:<br \/>   return new NativeDate(year, month, date, hour, minute, second, ms);<br \/>   }<br \/>   }<\/p>\n<p>  return mirrorDateProperties(ClockDate, NativeDate);<br \/>   }<\/p>\n<p>  function addTimer(clock, timer) {<br \/>   if (timer.func === undefined) {<br \/>   throw new Error(\u00abCallback must be provided to timer calls\u00bb);<br \/>   }<\/p>\n<p>  if (!clock.timers) {<br \/>   clock.timers = {};<br \/>   }<\/p>\n<p>  timer.id = uniqueTimerId++;<br \/>   timer.createdAt = clock.now;<br \/>   timer.callAt = clock.now + (timer.delay || (clock.duringTick? 1: 0));<\/p>\n<p>  clock.timers[timer.id] = timer;<\/p>\n<p>  if (addTimerReturnsObject) {<br \/>   return {<br \/>   id: timer.id,<br \/>   ref: NOOP,<br \/>   unref: NOOP<br \/>   };<br \/>   }<\/p>\n<p>  return timer.id;<br \/>   }<\/p>\n<p>  function compareTimers(a, b) {<br \/>   \/\/ Sort first by absolute timing<br \/>   if (a.callAt &lt; b.callAt) {<br \/>   return -1;<br \/>   }<br \/>   if (a.callAt &gt; b.callAt) {<br \/>   return 1;<br \/>   }<\/p>\n<p>  \/\/ Sort next by immediate, immediate timers take precedence<br \/>   if (a.immediate &#038;&#038; !b.immediate) {<br \/>   return -1;<br \/>   }<br \/>   if (!a.immediate &#038;&#038; b.immediate) {<br \/>   return 1;<br \/>   }<\/p>\n<p>  \/\/ Sort next by creation time, earlier-created timers take precedence<br \/>   if (a.createdAt &lt; b.createdAt) {<br \/>   return -1;<br \/>   }<br \/>   if (a.createdAt &gt; b.createdAt) {<br \/>   return 1;<br \/>   }<\/p>\n<p>  \/\/ Sort next by id, lower-id timers take precedence<br \/>   if (a.id &lt; b.id) {<br \/>   return -1;<br \/>   }<br \/>   if (a.id &gt; b.id) {<br \/>   return 1;<br \/>   }<\/p>\n<p>  \/\/ As timer ids are unique, no fallback `0` is necessary<br \/>   }<\/p>\n<p>  function firstTimerInRange(clock, from, to) {<br \/>   var timers = clock.timers,<br \/>   timer = null,<br \/>   id,<br \/>   isInRange;<\/p>\n<p>  for (id in timers) {<br \/>   if (timers.hasOwnProperty(id)) {<br \/>   isInRange = inRange(from, to, timers[id]);<\/p>\n<p>  if (isInRange &#038;&#038; (!timer || compareTimers(timer, timers[id]) === 1)) {<br \/>   timer = timers[id];<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  return timer;<br \/>   }<\/p>\n<p>  function callTimer(clock, timer) {<br \/>   var exception;<\/p>\n<p>  if (typeof timer.interval === \u00abnumber\u00bb) {<br \/>   clock.timers[timer.id].callAt += timer.interval;<br \/>   } else {<br \/>   delete clock.timers[timer.id];<br \/>   }<\/p>\n<p>  try {<br \/>   if (typeof timer.func === \u00abfunction\u00bb) {<br \/>   timer.func.apply(null, timer.args);<br \/>   } else {<br \/>   eval(timer.func);<br \/>   }<br \/>   } catch (e) {<br \/>   exception = e;<br \/>   }<\/p>\n<p>  if (!clock.timers[timer.id]) {<br \/>   if (exception) {<br \/>   throw exception;<br \/>   }<br \/>   return;<br \/>   }<\/p>\n<p>  if (exception) {<br \/>   throw exception;<br \/>   }<br \/>   }<\/p>\n<p>  function timerType(timer) {<br \/>   if (timer.immediate) {<br \/>   return \u00abImmediate\u00bb;<br \/>   } else if (typeof timer.interval !== \u00abundefined\u00bb) {<br \/>   return \u00abInterval\u00bb;<br \/>   } else {<br \/>   return \u00abTimeout\u00bb;<br \/>   }<br \/>   }<\/p>\n<p>  function clearTimer(clock, timerId, ttype) {<br \/>   if (!timerId) {<br \/>   \/\/ null appears to be allowed in most browsers, and appears to be<br \/>   \/\/ relied upon by some libraries, like Bootstrap carousel<br \/>   return;<br \/>   }<\/p>\n<p>  if (!clock.timers) {<br \/>   clock.timers = [];<br \/>   }<\/p>\n<p>  \/\/ in Node, timerId is an object with .ref()\/.unref(), and<br \/>   \/\/ its .id field is the actual timer id.<br \/>   if (typeof timerId === \u00abobject\u00bb) {<br \/>   timerId = timerId.id;<br \/>   }<\/p>\n<p>  if (clock.timers.hasOwnProperty(timerId)) {<br \/>   \/\/ check that the ID matches a timer of the correct type<br \/>   var timer = clock.timers[timerId];<br \/>   if (timerType(timer) === ttype) {<br \/>   delete clock.timers[timerId];<br \/>   } else {<br \/>   throw new Error(\u00abCannot clear timer: timer created with set\u00bb + ttype + &quot;() but cleared with clear&quot; + timerType(timer) + &quot;()&quot;);<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  function uninstall(clock, target) {<br \/>   var method,<br \/>   i,<br \/>   l;<\/p>\n<p>  for (i = 0, l = clock.methods.length; i &lt; l; i++) {<br \/>   method = clock.methods[i];<\/p>\n<p>  if (target[method].hadOwnProperty) {<br \/>   target[method] = clock[&quot;_&quot; + method];<br \/>   } else {<br \/>   try {<br \/>   delete target[method];<br \/>   } catch (ignore) {}<br \/>   }<br \/>   }<\/p>\n<p>  \/\/ Prevent multiple executions which will completely remove these props<br \/>   clock.methods = [];<br \/>   }<\/p>\n<p>  function hijackMethod(target, method, clock) {<br \/>   var prop;<\/p>\n<p>  clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);<br \/>   clock[&quot;_&quot; + method] = target[method];<\/p>\n<p>  if (method === \u00abDate\u00bb) {<br \/>   var date = mirrorDateProperties(clock[method], target[method]);<br \/>   target[method] = date;<br \/>   } else {<br \/>   target[method] = function () {<br \/>   return clock[method].apply(clock, arguments);<br \/>   };<\/p>\n<p>  for (prop in clock[method]) {<br \/>   if (clock[method].hasOwnProperty(prop)) {<br \/>   target[method][prop] = clock[method][prop];<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  target[method].clock = clock;<br \/>   }<\/p>\n<p>  var timers = {<br \/>   setTimeout: setTimeout,<br \/>   clearTimeout: clearTimeout,<br \/>   setImmediate: global.setImmediate,<br \/>   clearImmediate: global.clearImmediate,<br \/>   setInterval: setInterval,<br \/>   clearInterval: clearInterval,<br \/>   Date: Date<br \/>   };<\/p>\n<p>  var keys = Object.keys || function (obj) {<br \/>   var ks = [],<br \/>   key;<\/p>\n<p>  for (key in obj) {<br \/>   if (obj.hasOwnProperty(key)) {<br \/>   ks.push(key);<br \/>   }<br \/>   }<\/p>\n<p>  return ks;<br \/>   };<\/p>\n<p>  exports.timers = timers;<\/p>\n<p>  function createClock(now) {<br \/>   var clock = {<br \/>   now: getEpoch(now),<br \/>   timeouts: {},<br \/>   Date: createDate()<br \/>   };<\/p>\n<p>  clock.Date.clock = clock;<\/p>\n<p>  clock.setTimeout = function setTimeout(func, timeout) {<br \/>   return addTimer(clock, {<br \/>   func: func,<br \/>   args: Array.prototype.slice.call(arguments, 2),<br \/>   delay: timeout<br \/>   });<br \/>   };<\/p>\n<p>  clock.clearTimeout = function clearTimeout(timerId) {<br \/>   return clearTimer(clock, timerId, \u00abTimeout\u00bb);<br \/>   };<\/p>\n<p>  clock.setInterval = function setInterval(func, timeout) {<br \/>   return addTimer(clock, {<br \/>   func: func,<br \/>   args: Array.prototype.slice.call(arguments, 2),<br \/>   delay: timeout,<br \/>   interval: timeout<br \/>   });<br \/>   };<\/p>\n<p>  clock.clearInterval = function clearInterval(timerId) {<br \/>   return clearTimer(clock, timerId, \u00abInterval\u00bb);<br \/>   };<\/p>\n<p>  clock.setImmediate = function setImmediate(func) {<br \/>   return addTimer(clock, {<br \/>   func: func,<br \/>   args: Array.prototype.slice.call(arguments, 1),<br \/>   immediate: true<br \/>   });<br \/>   };<\/p>\n<p>  clock.clearImmediate = function clearImmediate(timerId) {<br \/>   return clearTimer(clock, timerId, \u00abImmediate\u00bb);<br \/>   };<\/p>\n<p>  clock.tick = function tick(ms) {<br \/>   ms = typeof ms === \u00abnumber\u00bb? ms: parseTime(ms);<br \/>   var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;<br \/>   var timer = firstTimerInRange(clock, tickFrom, tickTo);<br \/>   var oldNow;<\/p>\n<p>  clock.duringTick = true;<\/p>\n<p>  var firstException;<br \/>   while (timer &#038;&#038; tickFrom &lt;= tickTo) {<br \/>   if (clock.timers[timer.id]) {<br \/>   tickFrom = clock.now = timer.callAt;<br \/>   try {<br \/>   oldNow = clock.now;<br \/>   callTimer(clock, timer);<br \/>   \/\/ compensate for any setSystemTime() call during timer callback<br \/>   if (oldNow !== clock.now) {<br \/>   tickFrom += clock.now \u2014 oldNow;<br \/>   tickTo += clock.now \u2014 oldNow;<br \/>   previous += clock.now \u2014 oldNow;<br \/>   }<br \/>   } catch (e) {<br \/>   firstException = firstException || e;<br \/>   }<br \/>   }<\/p>\n<p>  timer = firstTimerInRange(clock, previous, tickTo);<br \/>   previous = tickFrom;<br \/>   }<\/p>\n<p>  clock.duringTick = false;<br \/>   clock.now = tickTo;<\/p>\n<p>  if (firstException) {<br \/>   throw firstException;<br \/>   }<\/p>\n<p>  return clock.now;<br \/>   };<\/p>\n<p>  clock.reset = function reset() {<br \/>   clock.timers = {};<br \/>   };<\/p>\n<p>  clock.setSystemTime = function setSystemTime(now) {<br \/>   \/\/ determine time difference<br \/>   var newNow = getEpoch(now);<br \/>   var difference = newNow \u2014 clock.now;<\/p>\n<p>  \/\/ update &#8216;system clock&#8217;<br \/>   clock.now = newNow;<\/p>\n<p>  \/\/ update timers and intervals to keep them stable<br \/>   for (var id in clock.timers) {<br \/>   if (clock.timers.hasOwnProperty(id)) {<br \/>   var timer = clock.timers[id];<br \/>   timer.createdAt += difference;<br \/>   timer.callAt += difference;<br \/>   }<br \/>   }<br \/>   };<\/p>\n<p>  return clock;<br \/>   }<br \/>   exports.createClock = createClock;<\/p>\n<p>  exports.install = function install(target, now, toFake) {<br \/>   var i,<br \/>   l;<\/p>\n<p>  if (typeof target === \u00abnumber\u00bb) {<br \/>   toFake = now;<br \/>   now = target;<br \/>   target = null;<br \/>   }<\/p>\n<p>  if (!target) {<br \/>   target = global;<br \/>   }<\/p>\n<p>  var clock = createClock(now);<\/p>\n<p>  clock.uninstall = function () {<br \/>   uninstall(clock, target);<br \/>   };<\/p>\n<p>  clock.methods = toFake || [];<\/p>\n<p>  if (clock.methods.length === 0) {<br \/>   clock.methods = keys(timers);<br \/>   }<\/p>\n<p>  for (i = 0, l = clock.methods.length; i &lt; l; i++) {<br \/>   hijackMethod(target, clock.methods[i], clock);<br \/>   }<\/p>\n<p>  return clock;<br \/>   };<\/p>\n<p>  }(global || this));<\/p>\n<p>  }).call(this,typeof global !== \u00abundefined\u00bb? global: typeof self !== \u00abundefined\u00bb? self: typeof window !== \u00abundefined\u00bb? window: {})<br \/>  },{}]},{},[1])(1)<br \/>  });<br \/>   })();<br \/>   var define;<br \/>  \/**<br \/>   * Sinon core utilities. For internal use only.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  var sinon = (function () {<br \/>  \u00abuse strict\u00bb;<br \/>   \/\/ eslint-disable-line no-unused-vars<\/p>\n<p>   var sinonModule;<br \/>   var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   sinonModule = module.exports = require(&quot;.\/sinon\/util\/core&quot;);<br \/>   require(&quot;.\/sinon\/extend&quot;);<br \/>   require(&quot;.\/sinon\/walk&quot;);<br \/>   require(&quot;.\/sinon\/typeOf&quot;);<br \/>   require(&quot;.\/sinon\/times_in_words&quot;);<br \/>   require(&quot;.\/sinon\/spy&quot;);<br \/>   require(&quot;.\/sinon\/call&quot;);<br \/>   require(&quot;.\/sinon\/behavior&quot;);<br \/>   require(&quot;.\/sinon\/stub&quot;);<br \/>   require(&quot;.\/sinon\/mock&quot;);<br \/>   require(&quot;.\/sinon\/collection&quot;);<br \/>   require(&quot;.\/sinon\/assert&quot;);<br \/>   require(&quot;.\/sinon\/sandbox&quot;);<br \/>   require(&quot;.\/sinon\/test&quot;);<br \/>   require(&quot;.\/sinon\/test_case&quot;);<br \/>   require(&quot;.\/sinon\/match&quot;);<br \/>   require(&quot;.\/sinon\/format&quot;);<br \/>   require(&quot;.\/sinon\/log_error&quot;);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   sinonModule = module.exports;<br \/>   } else {<br \/>   sinonModule = {};<br \/>   }<\/p>\n<p>  return sinonModule;<br \/>  }());<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/..\/sinon.js<br \/>   *\/<br \/>  \/**<br \/>   * Sinon core utilities. For internal use only.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   var div = typeof document !== \u00abundefined\u00bb &#038;&#038; document.createElement(\u00abdiv\u00bb);<br \/>   var hasOwn = Object.prototype.hasOwnProperty;<\/p>\n<p>  function isDOMNode(obj) {<br \/>   var success = false;<\/p>\n<p>  try {<br \/>   obj.appendChild(div);<br \/>   success = div.parentNode === obj;<br \/>   } catch (e) {<br \/>   return false;<br \/>   } finally {<br \/>   try {<br \/>   obj.removeChild(div);<br \/>   } catch (e) {<br \/>   \/\/ Remove failed, not much we can do about that<br \/>   }<br \/>   }<\/p>\n<p>  return success;<br \/>   }<\/p>\n<p>  function isElement(obj) {<br \/>   return div &#038;&#038; obj &#038;&#038; obj.nodeType === 1 &#038;&#038; isDOMNode(obj);<br \/>   }<\/p>\n<p>  function isFunction(obj) {<br \/>   return typeof obj === \u00abfunction\u00bb || !!(obj &#038;&#038; obj.constructor &#038;&#038; obj.call &#038;&#038; obj.apply);<br \/>   }<\/p>\n<p>  function isReallyNaN(val) {<br \/>   return typeof val === \u00abnumber\u00bb &#038;&#038; isNaN(val);<br \/>   }<\/p>\n<p>  function mirrorProperties(target, source) {<br \/>   for (var prop in source) {<br \/>   if (!hasOwn.call(target, prop)) {<br \/>   target[prop] = source[prop];<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  function isRestorable(obj) {<br \/>   return typeof obj === \u00abfunction\u00bb &#038;&#038; typeof obj.restore === \u00abfunction\u00bb &#038;&#038; obj.restore.sinon;<br \/>   }<\/p>\n<p>  \/\/ Cheap way to detect if we have ES5 support.<br \/>   var hasES5Support = \u00abkeys\u00bb in Object;<\/p>\n<p>  function makeApi(sinon) {<br \/>   sinon.wrapMethod = function wrapMethod(object, property, method) {<br \/>   if (!object) {<br \/>   throw new TypeError(\u00abShould wrap property of object\u00bb);<br \/>   }<\/p>\n<p>  if (typeof method !== \u00abfunction\u00bb &#038;&#038; typeof method !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abMethod wrapper should be a function or a property descriptor\u00bb);<br \/>   }<\/p>\n<p>  function checkWrappedMethod(wrappedMethod) {<br \/>   var error;<\/p>\n<p>  if (!isFunction(wrappedMethod)) {<br \/>   error = new TypeError(\u00abAttempted to wrap \u00bb + (typeof wrappedMethod) + &quot; property &quot; +<br \/>   property + &quot; as function&quot;);<br \/>   } else if (wrappedMethod.restore &#038;&#038; wrappedMethod.restore.sinon) {<br \/>   error = new TypeError(\u00abAttempted to wrap \u00bb + property + &quot; which is already wrapped&quot;);<br \/>   } else if (wrappedMethod.calledBefore) {<br \/>   var verb = wrappedMethod.returns? \u00abstubbed\u00bb: \u00abspied on\u00bb;<br \/>   error = new TypeError(\u00abAttempted to wrap \u00bb + property + &quot; which is already &quot; + verb);<br \/>   }<\/p>\n<p>  if (error) {<br \/>   if (wrappedMethod &#038;&#038; wrappedMethod.stackTrace) {<br \/>   error.stack += &quot;\\n&#8212;&#8212;&#8212;&#8212;&#8212;\\n&quot; + wrappedMethod.stackTrace;<br \/>   }<br \/>   throw error;<br \/>   }<br \/>   }<\/p>\n<p>  var error, wrappedMethod, i;<\/p>\n<p>  \/\/ IE 8 does not support hasOwnProperty on the window object and Firefox has a problem<br \/>   \/\/ when using hasOwn.call on objects from other frames.<br \/>   var owned = object.hasOwnProperty? object.hasOwnProperty(property): hasOwn.call(object, property);<\/p>\n<p>  if (hasES5Support) {<br \/>   var methodDesc = (typeof method === \u00abfunction\u00bb)? {value: method}: method;<br \/>   var wrappedMethodDesc = sinon.getPropertyDescriptor(object, property);<\/p>\n<p>  if (!wrappedMethodDesc) {<br \/>   error = new TypeError(\u00abAttempted to wrap \u00bb + (typeof wrappedMethod) + &quot; property &quot; +<br \/>   property + &quot; as function&quot;);<br \/>   } else if (wrappedMethodDesc.restore &#038;&#038; wrappedMethodDesc.restore.sinon) {<br \/>   error = new TypeError(\u00abAttempted to wrap \u00bb + property + &quot; which is already wrapped&quot;);<br \/>   }<br \/>   if (error) {<br \/>   if (wrappedMethodDesc &#038;&#038; wrappedMethodDesc.stackTrace) {<br \/>   error.stack += &quot;\\n&#8212;&#8212;&#8212;&#8212;&#8212;\\n&quot; + wrappedMethodDesc.stackTrace;<br \/>   }<br \/>   throw error;<br \/>   }<\/p>\n<p>  var types = sinon.objectKeys(methodDesc);<br \/>   for (i = 0; i &lt; types.length; i++) {<br \/>   wrappedMethod = wrappedMethodDesc[types[i]];<br \/>   checkWrappedMethod(wrappedMethod);<br \/>   }<\/p>\n<p>  mirrorProperties(methodDesc, wrappedMethodDesc);<br \/>   for (i = 0; i &lt; types.length; i++) {<br \/>   mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]);<br \/>   }<br \/>   Object.defineProperty(object, property, methodDesc);<br \/>   } else {<br \/>   wrappedMethod = object[property];<br \/>   checkWrappedMethod(wrappedMethod);<br \/>   object[property] = method;<br \/>   method.displayName = property;<br \/>   }<\/p>\n<p>  method.displayName = property;<\/p>\n<p>  \/\/ Set up a stack trace which can be used later to find what line of<br \/>   \/\/ code the original method was created on.<br \/>   method.stackTrace = (new Error(\u00abStack Trace for original\u00bb)).stack;<\/p>\n<p>  method.restore = function () {<br \/>   \/\/ For prototype properties try to reset by delete first.<br \/>   \/\/ If this fails (ex: localStorage on mobile safari) then force a reset<br \/>   \/\/ via direct assignment.<br \/>   if (!owned) {<br \/>   \/\/ In some cases `delete` may throw an error<br \/>   try {<br \/>   delete object[property];<br \/>   } catch (e) {} \/\/ eslint-disable-line no-empty<br \/>   \/\/ For native code functions `delete` fails without throwing an error<br \/>   \/\/ on Chrome &lt; 43, PhantomJS, etc.<br \/>   } else if (hasES5Support) {<br \/>   Object.defineProperty(object, property, wrappedMethodDesc);<br \/>   }<\/p>\n<p>  \/\/ Use strict equality comparison to check failures then force a reset<br \/>   \/\/ via direct assignment.<br \/>   if (object[property] === method) {<br \/>   object[property] = wrappedMethod;<br \/>   }<br \/>   };<\/p>\n<p>  method.restore.sinon = true;<\/p>\n<p>  if (!hasES5Support) {<br \/>   mirrorProperties(method, wrappedMethod);<br \/>   }<\/p>\n<p>  return method;<br \/>   };<\/p>\n<p>  sinon.create = function create(proto) {<br \/>   var F = function () {};<br \/>   F.prototype = proto;<br \/>   return new F();<br \/>   };<\/p>\n<p>  sinon.deepEqual = function deepEqual(a, b) {<br \/>   if (sinon.match &#038;&#038; sinon.match.isMatcher(a)) {<br \/>   return a.test(b);<br \/>   }<\/p>\n<p>  if (typeof a !== \u00abobject\u00bb || typeof b !== \u00abobject\u00bb) {<br \/>   return isReallyNaN(a) &#038;&#038; isReallyNaN(b) || a === b;<br \/>   }<\/p>\n<p>  if (isElement(a) || isElement(b)) {<br \/>   return a === b;<br \/>   }<\/p>\n<p>  if (a === b) {<br \/>   return true;<br \/>   }<\/p>\n<p>  if ((a === null &#038;&#038; b !== null) || (a !== null &#038;&#038; b === null)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (a instanceof RegExp &#038;&#038; b instanceof RegExp) {<br \/>   return (a.source === b.source) &#038;&#038; (a.global === b.global) &#038;&#038;<br \/>   (a.ignoreCase === b.ignoreCase) &#038;&#038; (a.multiline === b.multiline);<br \/>   }<\/p>\n<p>  var aString = Object.prototype.toString.call(a);<br \/>   if (aString !== Object.prototype.toString.call(b)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (aString === &quot;[object Date]&quot;) {<br \/>   return a.valueOf() === b.valueOf();<br \/>   }<\/p>\n<p>  var prop;<br \/>   var aLength = 0;<br \/>   var bLength = 0;<\/p>\n<p>  if (aString === &quot;[object Array]&quot; &#038;&#038; a.length !== b.length) {<br \/>   return false;<br \/>   }<\/p>\n<p>  for (prop in a) {<br \/>   if (a.hasOwnProperty(prop)) {<br \/>   aLength += 1;<\/p>\n<p>  if (!(prop in b)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (!deepEqual(a[prop], b[prop])) {<br \/>   return false;<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  for (prop in b) {<br \/>   if (b.hasOwnProperty(prop)) {<br \/>   bLength += 1;<br \/>   }<br \/>   }<\/p>\n<p>  return aLength === bLength;<br \/>   };<\/p>\n<p>  sinon.functionName = function functionName(func) {<br \/>   var name = func.displayName || func.name;<\/p>\n<p>  \/\/ Use function decomposition as a last resort to get function<br \/>   \/\/ name. Does not rely on function decomposition to work \u2014 if it<br \/>   \/\/ doesn&#8217;t debugging will be slightly less informative<br \/>   \/\/ (i.e. toString will say &#8216;spy&#8217; rather than &#8216;myFunc&#8217;).<br \/>   if (!name) {<br \/>   var matches = func.toString().match(\/function ([^\\s\\(]+)\/);<br \/>   name = matches &#038;&#038; matches[1];<br \/>   }<\/p>\n<p>  return name;<br \/>   };<\/p>\n<p>  sinon.functionToString = function toString() {<br \/>   if (this.getCall &#038;&#038; this.callCount) {<br \/>   var thisValue,<br \/>   prop;<br \/>   var i = this.callCount;<\/p>\n<p>  while (i&#8212;) {<br \/>   thisValue = this.getCall(i).thisValue;<\/p>\n<p>  for (prop in thisValue) {<br \/>   if (thisValue[prop] === this) {<br \/>   return prop;<br \/>   }<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  return this.displayName || \u00absinon fake\u00bb;<br \/>   };<\/p>\n<p>  sinon.objectKeys = function objectKeys(obj) {<br \/>   if (obj !== Object(obj)) {<br \/>   throw new TypeError(\u00absinon.objectKeys called on a non-object\u00bb);<br \/>   }<\/p>\n<p>  var keys = [];<br \/>   var key;<br \/>   for (key in obj) {<br \/>   if (hasOwn.call(obj, key)) {<br \/>   keys.push(key);<br \/>   }<br \/>   }<\/p>\n<p>  return keys;<br \/>   };<\/p>\n<p>  sinon.getPropertyDescriptor = function getPropertyDescriptor(object, property) {<br \/>   var proto = object;<br \/>   var descriptor;<\/p>\n<p>  while (proto &#038;&#038; !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {<br \/>   proto = Object.getPrototypeOf(proto);<br \/>   }<br \/>   return descriptor;<br \/>   };<\/p>\n<p>  sinon.getConfig = function (custom) {<br \/>   var config = {};<br \/>   custom = custom || {};<br \/>   var defaults = sinon.defaultConfig;<\/p>\n<p>  for (var prop in defaults) {<br \/>   if (defaults.hasOwnProperty(prop)) {<br \/>   config[prop] = custom.hasOwnProperty(prop)? custom[prop]: defaults[prop];<br \/>   }<br \/>   }<\/p>\n<p>  return config;<br \/>   };<\/p>\n<p>  sinon.defaultConfig = {<br \/>   injectIntoThis: true,<br \/>   injectInto: null,<br \/>   properties: [\u00abspy\u00bb, \u00abstub\u00bb, \u00abmock\u00bb, \u00abclock\u00bb, \u00abserver\u00bb, \u00abrequests\u00bb],<br \/>   useFakeTimers: true,<br \/>   useFakeServer: true<br \/>   };<\/p>\n<p>  sinon.timesInWords = function timesInWords(count) {<br \/>   return count === 1 &#038;&#038; \u00abonce\u00bb ||<br \/>   count === 2 &#038;&#038; \u00abtwice\u00bb ||<br \/>   count === 3 &#038;&#038; \u00abthrice\u00bb ||<br \/>   (count || 0) + &quot; times&quot;;<br \/>   };<\/p>\n<p>  sinon.calledInOrder = function (spies) {<br \/>   for (var i = 1, l = spies.length; i &lt; l; i++) {<br \/>   if (!spies[i \u2014 1].calledBefore(spies[i]) || !spies[i].called) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  return true;<br \/>   };<\/p>\n<p>  sinon.orderByFirstCall = function (spies) {<br \/>   return spies.sort(function (a, b) {<br \/>   \/\/ uuid, won&#8217;t ever be equal<br \/>   var aCall = a.getCall(0);<br \/>   var bCall = b.getCall(0);<br \/>   var aId = aCall &#038;&#038; aCall.callId || -1;<br \/>   var bId = bCall &#038;&#038; bCall.callId || -1;<\/p>\n<p>  return aId &lt; bId? -1: 1;<br \/>   });<br \/>   };<\/p>\n<p>  sinon.createStubInstance = function (constructor) {<br \/>   if (typeof constructor !== \u00abfunction\u00bb) {<br \/>   throw new TypeError(\u00abThe constructor should be a function.\u00bb);<br \/>   }<br \/>   return sinon.stub(sinon.create(constructor.prototype));<br \/>   };<\/p>\n<p>  sinon.restore = function (object) {<br \/>   if (object !== null &#038;&#038; typeof object === \u00abobject\u00bb) {<br \/>   for (var prop in object) {<br \/>   if (isRestorable(object[prop])) {<br \/>   object[prop].restore();<br \/>   }<br \/>   }<br \/>   } else if (isRestorable(object)) {<br \/>   object.restore();<br \/>   }<br \/>   };<\/p>\n<p>  return sinon;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports) {<br \/>   makeApi(exports);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<\/p>\n<p>  \/\/ Adapted from <a href=\"https:\/\/developer.mozilla.org\/en\/docs\/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug\">developer.mozilla.org\/en\/docs\/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug<\/a><br \/>   var hasDontEnumBug = (function () {<br \/>   var obj = {<br \/>   constructor: function () {<br \/>   return \u00ab0\u00bb;<br \/>   },<br \/>   toString: function () {<br \/>   return \u00ab1\u00bb;<br \/>   },<br \/>   valueOf: function () {<br \/>   return \u00ab2\u00bb;<br \/>   },<br \/>   toLocaleString: function () {<br \/>   return \u00ab3\u00bb;<br \/>   },<br \/>   prototype: function () {<br \/>   return \u00ab4\u00bb;<br \/>   },<br \/>   isPrototypeOf: function () {<br \/>   return \u00ab5\u00bb;<br \/>   },<br \/>   propertyIsEnumerable: function () {<br \/>   return \u00ab6\u00bb;<br \/>   },<br \/>   hasOwnProperty: function () {<br \/>   return \u00ab7\u00bb;<br \/>   },<br \/>   length: function () {<br \/>   return \u00ab8\u00bb;<br \/>   },<br \/>   unique: function () {<br \/>   return \u00ab9\u00bb;<br \/>   }<br \/>   };<\/p>\n<p>  var result = [];<br \/>   for (var prop in obj) {<br \/>   if (obj.hasOwnProperty(prop)) {<br \/>   result.push(obj[prop]());<br \/>   }<br \/>   }<br \/>   return result.join(&quot;&quot;) !== \u00ab0123456789\u00bb;<br \/>   })();<\/p>\n<p>  \/* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will<br \/>   * override properties in previous sources.<br \/>   *<br \/>   * target \u2014 The Object to extend<br \/>   * sources \u2014 Objects to copy properties from.<br \/>   *<br \/>   * Returns the extended target<br \/>   *\/<br \/>   function extend(target \/*, sources *\/) {<br \/>   var sources = Array.prototype.slice.call(arguments, 1);<br \/>   var source, i, prop;<\/p>\n<p>  for (i = 0; i &lt; sources.length; i++) {<br \/>   source = sources[i];<\/p>\n<p>  for (prop in source) {<br \/>   if (source.hasOwnProperty(prop)) {<br \/>   target[prop] = source[prop];<br \/>   }<br \/>   }<\/p>\n<p>  \/\/ Make sure we copy (own) toString method even when in JScript with DontEnum bug<br \/>   \/\/ See <a href=\"https:\/\/developer.mozilla.org\/en\/docs\/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug\">developer.mozilla.org\/en\/docs\/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug<\/a><br \/>   if (hasDontEnumBug &#038;&#038; source.hasOwnProperty(\u00abtoString\u00bb) &#038;&#038; source.toString !== target.toString) {<br \/>   target.toString = source.toString;<br \/>   }<br \/>   }<\/p>\n<p>  return target;<br \/>   }<\/p>\n<p>  sinon.extend = extend;<br \/>   return sinon.extend;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<\/p>\n<p>  function timesInWords(count) {<br \/>   switch (count) {<br \/>   case 1:<br \/>   return \u00abonce\u00bb;<br \/>   case 2:<br \/>   return \u00abtwice\u00bb;<br \/>   case 3:<br \/>   return \u00abthrice\u00bb;<br \/>   default:<br \/>   return (count || 0) + &quot; times&quot;;<br \/>   }<br \/>   }<\/p>\n<p>  sinon.timesInWords = timesInWords;<br \/>   return sinon.timesInWords;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  \/**<br \/>   * Format functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2014 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function typeOf(value) {<br \/>   if (value === null) {<br \/>   return \u00abnull\u00bb;<br \/>   } else if (value === undefined) {<br \/>   return \u00abundefined\u00bb;<br \/>   }<br \/>   var string = Object.prototype.toString.call(value);<br \/>   return string.substring(8, string.length \u2014 1).toLowerCase();<br \/>   }<\/p>\n<p>  sinon.typeOf = typeOf;<br \/>   return sinon.typeOf;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> typeOf.js<br \/>   *\/<br \/>  \/*jslint eqeqeq: false, onevar: false, plusplus: false*\/<br \/>  \/*global module, require, sinon*\/<br \/>  \/**<br \/>   * Match functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Maximilian Antoni (mail@maxantoni.de)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2012 Maximilian Antoni<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function assertType(value, type, name) {<br \/>   var actual = sinon.typeOf(value);<br \/>   if (actual !== type) {<br \/>   throw new TypeError(\u00abExpected type of \u00bb + name + &quot; to be &quot; +<br \/>   type + &quot;, but was &quot; + actual);<br \/>   }<br \/>   }<\/p>\n<p>  var matcher = {<br \/>   toString: function () {<br \/>   return this.message;<br \/>   }<br \/>   };<\/p>\n<p>  function isMatcher(object) {<br \/>   return matcher.isPrototypeOf(object);<br \/>   }<\/p>\n<p>  function matchObject(expectation, actual) {<br \/>   if (actual === null || actual === undefined) {<br \/>   return false;<br \/>   }<br \/>   for (var key in expectation) {<br \/>   if (expectation.hasOwnProperty(key)) {<br \/>   var exp = expectation[key];<br \/>   var act = actual[key];<br \/>   if (isMatcher(exp)) {<br \/>   if (!exp.test(act)) {<br \/>   return false;<br \/>   }<br \/>   } else if (sinon.typeOf(exp) === \u00abobject\u00bb) {<br \/>   if (!matchObject(exp, act)) {<br \/>   return false;<br \/>   }<br \/>   } else if (!sinon.deepEqual(exp, act)) {<br \/>   return false;<br \/>   }<br \/>   }<br \/>   }<br \/>   return true;<br \/>   }<\/p>\n<p>  function match(expectation, message) {<br \/>   var m = sinon.create(matcher);<br \/>   var type = sinon.typeOf(expectation);<br \/>   switch (type) {<br \/>   case \u00abobject\u00bb:<br \/>   if (typeof expectation.test === \u00abfunction\u00bb) {<br \/>   m.test = function (actual) {<br \/>   return expectation.test(actual) === true;<br \/>   };<br \/>   m.message = \u00abmatch(\u00bb + sinon.functionName(expectation.test) + &quot;)&quot;;<br \/>   return m;<br \/>   }<br \/>   var str = [];<br \/>   for (var key in expectation) {<br \/>   if (expectation.hasOwnProperty(key)) {<br \/>   str.push(key + &quot;: &quot; + expectation[key]);<br \/>   }<br \/>   }<br \/>   m.test = function (actual) {<br \/>   return matchObject(expectation, actual);<br \/>   };<br \/>   m.message = \u00abmatch(\u00bb + str.join(&quot;, &quot;) + &quot;)&quot;;<br \/>   break;<br \/>   case \u00abnumber\u00bb:<br \/>   m.test = function (actual) {<br \/>   \/\/ we need type coercion here<br \/>   return expectation == actual; \/\/ eslint-disable-line eqeqeq<br \/>   };<br \/>   break;<br \/>   case \u00abstring\u00bb:<br \/>   m.test = function (actual) {<br \/>   if (typeof actual !== \u00abstring\u00bb) {<br \/>   return false;<br \/>   }<br \/>   return actual.indexOf(expectation) !== -1;<br \/>   };<br \/>   m.message = \u00abmatch(\\\u00bb&quot; + expectation + &quot;\\&quot;)&quot;;<br \/>   break;<br \/>   case \u00abregexp\u00bb:<br \/>   m.test = function (actual) {<br \/>   if (typeof actual !== \u00abstring\u00bb) {<br \/>   return false;<br \/>   }<br \/>   return expectation.test(actual);<br \/>   };<br \/>   break;<br \/>   case \u00abfunction\u00bb:<br \/>   m.test = expectation;<br \/>   if (message) {<br \/>   m.message = message;<br \/>   } else {<br \/>   m.message = \u00abmatch(\u00bb + sinon.functionName(expectation) + &quot;)&quot;;<br \/>   }<br \/>   break;<br \/>   default:<br \/>   m.test = function (actual) {<br \/>   return sinon.deepEqual(expectation, actual);<br \/>   };<br \/>   }<br \/>   if (!m.message) {<br \/>   m.message = \u00abmatch(\u00bb + expectation + &quot;)&quot;;<br \/>   }<br \/>   return m;<br \/>   }<\/p>\n<p>  matcher.or = function (m2) {<br \/>   if (!arguments.length) {<br \/>   throw new TypeError(\u00abMatcher expected\u00bb);<br \/>   } else if (!isMatcher(m2)) {<br \/>   m2 = match(m2);<br \/>   }<br \/>   var m1 = this;<br \/>   var or = sinon.create(matcher);<br \/>   or.test = function (actual) {<br \/>   return m1.test(actual) || m2.test(actual);<br \/>   };<br \/>   or.message = m1.message + &quot;.or(&quot; + m2.message + &quot;)&quot;;<br \/>   return or;<br \/>   };<\/p>\n<p>  matcher.and = function (m2) {<br \/>   if (!arguments.length) {<br \/>   throw new TypeError(\u00abMatcher expected\u00bb);<br \/>   } else if (!isMatcher(m2)) {<br \/>   m2 = match(m2);<br \/>   }<br \/>   var m1 = this;<br \/>   var and = sinon.create(matcher);<br \/>   and.test = function (actual) {<br \/>   return m1.test(actual) &#038;&#038; m2.test(actual);<br \/>   };<br \/>   and.message = m1.message + &quot;.and(&quot; + m2.message + &quot;)&quot;;<br \/>   return and;<br \/>   };<\/p>\n<p>  match.isMatcher = isMatcher;<\/p>\n<p>  match.any = match(function () {<br \/>   return true;<br \/>   }, \u00abany\u00bb);<\/p>\n<p>  match.defined = match(function (actual) {<br \/>   return actual !== null &#038;&#038; actual !== undefined;<br \/>   }, \u00abdefined\u00bb);<\/p>\n<p>  match.truthy = match(function (actual) {<br \/>   return !!actual;<br \/>   }, \u00abtruthy\u00bb);<\/p>\n<p>  match.falsy = match(function (actual) {<br \/>   return !actual;<br \/>   }, \u00abfalsy\u00bb);<\/p>\n<p>  match.same = function (expectation) {<br \/>   return match(function (actual) {<br \/>   return expectation === actual;<br \/>   }, \u00absame(\u00bb + expectation + &quot;)&quot;);<br \/>   };<\/p>\n<p>  match.typeOf = function (type) {<br \/>   assertType(type, \u00abstring\u00bb, \u00abtype\u00bb);<br \/>   return match(function (actual) {<br \/>   return sinon.typeOf(actual) === type;<br \/>   }, \u00abtypeOf(\\\u00bb&quot; + type + &quot;\\&quot;)&quot;);<br \/>   };<\/p>\n<p>  match.instanceOf = function (type) {<br \/>   assertType(type, \u00abfunction\u00bb, \u00abtype\u00bb);<br \/>   return match(function (actual) {<br \/>   return actual instanceof type;<br \/>   }, \u00abinstanceOf(\u00bb + sinon.functionName(type) + &quot;)&quot;);<br \/>   };<\/p>\n<p>  function createPropertyMatcher(propertyTest, messagePrefix) {<br \/>   return function (property, value) {<br \/>   assertType(property, \u00abstring\u00bb, \u00abproperty\u00bb);<br \/>   var onlyProperty = arguments.length === 1;<br \/>   var message = messagePrefix + &quot;(\\&quot;&quot; + property + &quot;\\&quot;&quot;;<br \/>   if (!onlyProperty) {<br \/>   message += &quot;, &quot; + value;<br \/>   }<br \/>   message += &quot;)&quot;;<br \/>   return match(function (actual) {<br \/>   if (actual === undefined || actual === null ||<br \/>   !propertyTest(actual, property)) {<br \/>   return false;<br \/>   }<br \/>   return onlyProperty || sinon.deepEqual(value, actual[property]);<br \/>   }, message);<br \/>   };<br \/>   }<\/p>\n<p>  match.has = createPropertyMatcher(function (actual, property) {<br \/>   if (typeof actual === \u00abobject\u00bb) {<br \/>   return property in actual;<br \/>   }<br \/>   return actual[property] !== undefined;<br \/>   }, \u00abhas\u00bb);<\/p>\n<p>  match.hasOwn = createPropertyMatcher(function (actual, property) {<br \/>   return actual.hasOwnProperty(property);<br \/>   }, \u00abhasOwn\u00bb);<\/p>\n<p>  match.bool = match.typeOf(\u00abboolean\u00bb);<br \/>   match.number = match.typeOf(\u00abnumber\u00bb);<br \/>   match.string = match.typeOf(\u00abstring\u00bb);<br \/>   match.object = match.typeOf(\u00abobject\u00bb);<br \/>   match.func = match.typeOf(\u00abfunction\u00bb);<br \/>   match.array = match.typeOf(\u00abarray\u00bb);<br \/>   match.regexp = match.typeOf(\u00abregexp\u00bb);<br \/>   match.date = match.typeOf(\u00abdate\u00bb);<\/p>\n<p>  sinon.match = match;<br \/>   return match;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/typeOf&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  \/**<br \/>   * Format functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2014 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal, formatio) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function valueFormatter(value) {<br \/>   return &quot;&quot; + value;<br \/>   }<\/p>\n<p>  function getFormatioFormatter() {<br \/>   var formatter = formatio.configure({<br \/>   quoteStrings: false,<br \/>   limitChildrenCount: 250<br \/>   });<\/p>\n<p>  function format() {<br \/>   return formatter.ascii.apply(formatter, arguments);<br \/>   }<\/p>\n<p>  return format;<br \/>   }<\/p>\n<p>  function getNodeFormatter() {<br \/>   try {<br \/>   var util = require(\u00abutil\u00bb);<br \/>   } catch (e) {<br \/>   \/* Node, but no util module \u2014 would be very old, but better safe than sorry *\/<br \/>   }<\/p>\n<p>  function format(v) {<br \/>   var isObjectWithNativeToString = typeof v === \u00abobject\u00bb &#038;&#038; v.toString === Object.prototype.toString;<br \/>   return isObjectWithNativeToString? util.inspect(v): v;<br \/>   }<\/p>\n<p>  return util? format: valueFormatter;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var formatter;<\/p>\n<p>  if (isNode) {<br \/>   try {<br \/>   formatio = require(\u00abformatio\u00bb);<br \/>   }<br \/>   catch (e) {} \/\/ eslint-disable-line no-empty<br \/>   }<\/p>\n<p>  if (formatio) {<br \/>   formatter = getFormatioFormatter();<br \/>   } else if (isNode) {<br \/>   formatter = getNodeFormatter();<br \/>   } else {<br \/>   formatter = valueFormatter;<br \/>   }<\/p>\n<p>  sinon.format = formatter;<br \/>   return sinon.format;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon, \/\/ eslint-disable-line no-undef<br \/>   typeof formatio === \u00abobject\u00bb &#038;&#038; formatio \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> match.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> format.js<br \/>   *\/<br \/>  \/**<br \/>   * Spy calls<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Maximilian Antoni (mail@maxantoni.de)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   * Copyright  2013 Maximilian Antoni<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   var slice = Array.prototype.slice;<\/p>\n<p>  function makeApi(sinon) {<br \/>   function throwYieldError(proxy, text, args) {<br \/>   var msg = sinon.functionName(proxy) + text;<br \/>   if (args.length) {<br \/>   msg += &quot; Received [&quot; + slice.call(args).join(&quot;, &quot;) + &quot;]&quot;;<br \/>   }<br \/>   throw new Error(msg);<br \/>   }<\/p>\n<p>  var callProto = {<br \/>   calledOn: function calledOn(thisValue) {<br \/>   if (sinon.match &#038;&#038; sinon.match.isMatcher(thisValue)) {<br \/>   return thisValue.test(this.thisValue);<br \/>   }<br \/>   return this.thisValue === thisValue;<br \/>   },<\/p>\n<p>  calledWith: function calledWith() {<br \/>   var l = arguments.length;<br \/>   if (l &gt; this.args.length) {<br \/>   return false;<br \/>   }<br \/>   for (var i = 0; i &lt; l; i += 1) {<br \/>   if (!sinon.deepEqual(arguments[i], this.args[i])) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  return true;<br \/>   },<\/p>\n<p>  calledWithMatch: function calledWithMatch() {<br \/>   var l = arguments.length;<br \/>   if (l &gt; this.args.length) {<br \/>   return false;<br \/>   }<br \/>   for (var i = 0; i &lt; l; i += 1) {<br \/>   var actual = this.args[i];<br \/>   var expectation = arguments[i];<br \/>   if (!sinon.match || !sinon.match(expectation).test(actual)) {<br \/>   return false;<br \/>   }<br \/>   }<br \/>   return true;<br \/>   },<\/p>\n<p>  calledWithExactly: function calledWithExactly() {<br \/>   return arguments.length === this.args.length &#038;&#038;<br \/>   this.calledWith.apply(this, arguments);<br \/>   },<\/p>\n<p>  notCalledWith: function notCalledWith() {<br \/>   return !this.calledWith.apply(this, arguments);<br \/>   },<\/p>\n<p>  notCalledWithMatch: function notCalledWithMatch() {<br \/>   return !this.calledWithMatch.apply(this, arguments);<br \/>   },<\/p>\n<p>  returned: function returned(value) {<br \/>   return sinon.deepEqual(value, this.returnValue);<br \/>   },<\/p>\n<p>  threw: function threw(error) {<br \/>   if (typeof error === \u00abundefined\u00bb || !this.exception) {<br \/>   return !!this.exception;<br \/>   }<\/p>\n<p>  return this.exception === error || this.exception.name === error;<br \/>   },<\/p>\n<p>  calledWithNew: function calledWithNew() {<br \/>   return this.proxy.prototype &#038;&#038; this.thisValue instanceof this.proxy;<br \/>   },<\/p>\n<p>  calledBefore: function (other) {<br \/>   return this.callId &lt; other.callId;<br \/>   },<\/p>\n<p>  calledAfter: function (other) {<br \/>   return this.callId &gt; other.callId;<br \/>   },<\/p>\n<p>  callArg: function (pos) {<br \/>   this.args[pos]();<br \/>   },<\/p>\n<p>  callArgOn: function (pos, thisValue) {<br \/>   this.args[pos].apply(thisValue);<br \/>   },<\/p>\n<p>  callArgWith: function (pos) {<br \/>   this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));<br \/>   },<\/p>\n<p>  callArgOnWith: function (pos, thisValue) {<br \/>   var args = slice.call(arguments, 2);<br \/>   this.args[pos].apply(thisValue, args);<br \/>   },<\/p>\n<p>  \u00abyield\u00bb: function () {<br \/>   this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));<br \/>   },<\/p>\n<p>  yieldOn: function (thisValue) {<br \/>   var args = this.args;<br \/>   for (var i = 0, l = args.length; i &lt; l; ++i) {<br \/>   if (typeof args[i] === \u00abfunction\u00bb) {<br \/>   args[i].apply(thisValue, slice.call(arguments, 1));<br \/>   return;<br \/>   }<br \/>   }<br \/>   throwYieldError(this.proxy, &quot; cannot yield since no callback was passed.&quot;, args);<br \/>   },<\/p>\n<p>  yieldTo: function (prop) {<br \/>   this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));<br \/>   },<\/p>\n<p>  yieldToOn: function (prop, thisValue) {<br \/>   var args = this.args;<br \/>   for (var i = 0, l = args.length; i &lt; l; ++i) {<br \/>   if (args[i] &#038;&#038; typeof args[i][prop] === \u00abfunction\u00bb) {<br \/>   args[i][prop].apply(thisValue, slice.call(arguments, 2));<br \/>   return;<br \/>   }<br \/>   }<br \/>   throwYieldError(this.proxy, &quot; cannot yield to &#8216;&quot; + prop +<br \/>   &quot;&#8217; since no callback was passed.&quot;, args);<br \/>   },<\/p>\n<p>  getStackFrames: function () {<br \/>   \/\/ Omit the error message and the two top stack frames in sinon itself:<br \/>   return this.stack &#038;&#038; this.stack.split(&quot;\\n&quot;).slice(3);<br \/>   },<\/p>\n<p>  toString: function () {<br \/>   var callStr = this.proxy.toString() + &quot;(&quot;;<br \/>   var args = [];<\/p>\n<p>  for (var i = 0, l = this.args.length; i &lt; l; ++i) {<br \/>   args.push(sinon.format(this.args[i]));<br \/>   }<\/p>\n<p>  callStr = callStr + args.join(&quot;, &quot;) + &quot;)&quot;;<\/p>\n<p>  if (typeof this.returnValue !== \u00abundefined\u00bb) {<br \/>   callStr += &quot; =&gt; &quot; + sinon.format(this.returnValue);<br \/>   }<\/p>\n<p>  if (this.exception) {<br \/>   callStr += &quot; !&quot; + this.exception.name;<\/p>\n<p>  if (this.exception.message) {<br \/>   callStr += &quot;(&quot; + this.exception.message + &quot;)&quot;;<br \/>   }<br \/>   }<br \/>   if (this.stack) {<br \/>   callStr += this.getStackFrames()[0].replace(\/^\\s*(?:at\\s+|@)?\/, &quot; at &quot;);<\/p>\n<p>  }<\/p>\n<p>  return callStr;<br \/>   }<br \/>   };<\/p>\n<p>  callProto.invokeCallback = callProto.yield;<\/p>\n<p>  function createSpyCall(spy, thisValue, args, returnValue, exception, id, stack) {<br \/>   if (typeof id !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abCall id is not a number\u00bb);<br \/>   }<br \/>   var proxyCall = sinon.create(callProto);<br \/>   proxyCall.proxy = spy;<br \/>   proxyCall.thisValue = thisValue;<br \/>   proxyCall.args = args;<br \/>   proxyCall.returnValue = returnValue;<br \/>   proxyCall.exception = exception;<br \/>   proxyCall.callId = id;<br \/>   proxyCall.stack = stack;<\/p>\n<p>  return proxyCall;<br \/>   }<br \/>   createSpyCall.toString = callProto.toString; \/\/ used by mocks<\/p>\n<p>  sinon.spyCall = createSpyCall;<br \/>   return createSpyCall;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/match&quot;);<br \/>   require(&quot;.\/format&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> times_in_words.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> call.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> format.js<br \/>   *\/<br \/>  \/**<br \/>   * Spy functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   var push = Array.prototype.push;<br \/>   var slice = Array.prototype.slice;<br \/>   var callId = 0;<\/p>\n<p>  function spy(object, property, types) {<br \/>   if (!property &#038;&#038; typeof object === \u00abfunction\u00bb) {<br \/>   return spy.create(object);<br \/>   }<\/p>\n<p>  if (!object &#038;&#038; !property) {<br \/>   return spy.create(function () { });<br \/>   }<\/p>\n<p>  if (types) {<br \/>   var methodDesc = sinon.getPropertyDescriptor(object, property);<br \/>   for (var i = 0; i &lt; types.length; i++) {<br \/>   methodDesc[types[i]] = spy.create(methodDesc[types[i]]);<br \/>   }<br \/>   return sinon.wrapMethod(object, property, methodDesc);<br \/>   }<\/p>\n<p>  return sinon.wrapMethod(object, property, spy.create(object[property]));<br \/>   }<\/p>\n<p>  function matchingFake(fakes, args, strict) {<br \/>   if (!fakes) {<br \/>   return undefined;<br \/>   }<\/p>\n<p>  for (var i = 0, l = fakes.length; i &lt; l; i++) {<br \/>   if (fakes[i].matches(args, strict)) {<br \/>   return fakes[i];<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  function incrementCallCount() {<br \/>   this.called = true;<br \/>   this.callCount += 1;<br \/>   this.notCalled = false;<br \/>   this.calledOnce = this.callCount === 1;<br \/>   this.calledTwice = this.callCount === 2;<br \/>   this.calledThrice = this.callCount === 3;<br \/>   }<\/p>\n<p>  function createCallProperties() {<br \/>   this.firstCall = this.getCall(0);<br \/>   this.secondCall = this.getCall(1);<br \/>   this.thirdCall = this.getCall(2);<br \/>   this.lastCall = this.getCall(this.callCount \u2014 1);<br \/>   }<\/p>\n<p>  var vars = \u00aba,b,c,d,e,f,g,h,i,j,k,l\u00bb;<br \/>   function createProxy(func, proxyLength) {<br \/>   \/\/ Retain the function length:<br \/>   var p;<br \/>   if (proxyLength) {<br \/>   eval(\u00abp = (function proxy(\u00bb + vars.substring(0, proxyLength * 2 \u2014 1) + \/\/ eslint-disable-line no-eval<br \/>   &quot;) { return p.invoke(func, this, slice.call(arguments)); });&quot;);<br \/>   } else {<br \/>   p = function proxy() {<br \/>   return p.invoke(func, this, slice.call(arguments));<br \/>   };<br \/>   }<br \/>   p.isSinonProxy = true;<br \/>   return p;<br \/>   }<\/p>\n<p>  var uuid = 0;<\/p>\n<p>  \/\/ Public API<br \/>   var spyApi = {<br \/>   reset: function () {<br \/>   if (this.invoking) {<br \/>   var err = new Error(\u00abCannot reset Sinon function while invoking it. \u00bb +<br \/>   \u00abMove the call to .reset outside of the callback.\u00bb);<br \/>   err.name = \u00abInvalidResetException\u00bb;<br \/>   throw err;<br \/>   }<\/p>\n<p>  this.called = false;<br \/>   this.notCalled = true;<br \/>   this.calledOnce = false;<br \/>   this.calledTwice = false;<br \/>   this.calledThrice = false;<br \/>   this.callCount = 0;<br \/>   this.firstCall = null;<br \/>   this.secondCall = null;<br \/>   this.thirdCall = null;<br \/>   this.lastCall = null;<br \/>   this.args = [];<br \/>   this.returnValues = [];<br \/>   this.thisValues = [];<br \/>   this.exceptions = [];<br \/>   this.callIds = [];<br \/>   this.stacks = [];<br \/>   if (this.fakes) {<br \/>   for (var i = 0; i &lt; this.fakes.length; i++) {<br \/>   this.fakes[i].reset();<br \/>   }<br \/>   }<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  create: function create(func, spyLength) {<br \/>   var name;<\/p>\n<p>  if (typeof func !== \u00abfunction\u00bb) {<br \/>   func = function () { };<br \/>   } else {<br \/>   name = sinon.functionName(func);<br \/>   }<\/p>\n<p>  if (!spyLength) {<br \/>   spyLength = func.length;<br \/>   }<\/p>\n<p>  var proxy = createProxy(func, spyLength);<\/p>\n<p>  sinon.extend(proxy, spy);<br \/>   delete proxy.create;<br \/>   sinon.extend(proxy, func);<\/p>\n<p>  proxy.reset();<br \/>   proxy.prototype = func.prototype;<br \/>   proxy.displayName = name || \u00abspy\u00bb;<br \/>   proxy.toString = sinon.functionToString;<br \/>   proxy.instantiateFake = sinon.spy.create;<br \/>   proxy.id = \u00abspy#\u00bb + uuid++;<\/p>\n<p>  return proxy;<br \/>   },<\/p>\n<p>  invoke: function invoke(func, thisValue, args) {<br \/>   var matching = matchingFake(this.fakes, args);<br \/>   var exception, returnValue;<\/p>\n<p>  incrementCallCount.call(this);<br \/>   push.call(this.thisValues, thisValue);<br \/>   push.call(this.args, args);<br \/>   push.call(this.callIds, callId++);<\/p>\n<p>  \/\/ Make call properties available from within the spied function:<br \/>   createCallProperties.call(this);<\/p>\n<p>  try {<br \/>   this.invoking = true;<\/p>\n<p>  if (matching) {<br \/>   returnValue = matching.invoke(func, thisValue, args);<br \/>   } else {<br \/>   returnValue = (this.func || func).apply(thisValue, args);<br \/>   }<\/p>\n<p>  var thisCall = this.getCall(this.callCount \u2014 1);<br \/>   if (thisCall.calledWithNew() &#038;&#038; typeof returnValue !== \u00abobject\u00bb) {<br \/>   returnValue = thisValue;<br \/>   }<br \/>   } catch (e) {<br \/>   exception = e;<br \/>   } finally {<br \/>   delete this.invoking;<br \/>   }<\/p>\n<p>  push.call(this.exceptions, exception);<br \/>   push.call(this.returnValues, returnValue);<br \/>   push.call(this.stacks, new Error().stack);<\/p>\n<p>  \/\/ Make return value and exception available in the calls:<br \/>   createCallProperties.call(this);<\/p>\n<p>  if (exception !== undefined) {<br \/>   throw exception;<br \/>   }<\/p>\n<p>  return returnValue;<br \/>   },<\/p>\n<p>  named: function named(name) {<br \/>   this.displayName = name;<br \/>   return this;<br \/>   },<\/p>\n<p>  getCall: function getCall(i) {<br \/>   if (i &lt; 0 || i &gt;= this.callCount) {<br \/>   return null;<br \/>   }<\/p>\n<p>  return sinon.spyCall(this, this.thisValues[i], this.args[i],<br \/>   this.returnValues[i], this.exceptions[i],<br \/>   this.callIds[i], this.stacks[i]);<br \/>   },<\/p>\n<p>  getCalls: function () {<br \/>   var calls = [];<br \/>   var i;<\/p>\n<p>  for (i = 0; i &lt; this.callCount; i++) {<br \/>   calls.push(this.getCall(i));<br \/>   }<\/p>\n<p>  return calls;<br \/>   },<\/p>\n<p>  calledBefore: function calledBefore(spyFn) {<br \/>   if (!this.called) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (!spyFn.called) {<br \/>   return true;<br \/>   }<\/p>\n<p>  return this.callIds[0] &lt; spyFn.callIds[spyFn.callIds.length \u2014 1];<br \/>   },<\/p>\n<p>  calledAfter: function calledAfter(spyFn) {<br \/>   if (!this.called || !spyFn.called) {<br \/>   return false;<br \/>   }<\/p>\n<p>  return this.callIds[this.callCount \u2014 1] &gt; spyFn.callIds[spyFn.callCount \u2014 1];<br \/>   },<\/p>\n<p>  withArgs: function () {<br \/>   var args = slice.call(arguments);<\/p>\n<p>  if (this.fakes) {<br \/>   var match = matchingFake(this.fakes, args, true);<\/p>\n<p>  if (match) {<br \/>   return match;<br \/>   }<br \/>   } else {<br \/>   this.fakes = [];<br \/>   }<\/p>\n<p>  var original = this;<br \/>   var fake = this.instantiateFake();<br \/>   fake.matchingAguments = args;<br \/>   fake.parent = this;<br \/>   push.call(this.fakes, fake);<\/p>\n<p>  fake.withArgs = function () {<br \/>   return original.withArgs.apply(original, arguments);<br \/>   };<\/p>\n<p>  for (var i = 0; i &lt; this.args.length; i++) {<br \/>   if (fake.matches(this.args[i])) {<br \/>   incrementCallCount.call(fake);<br \/>   push.call(fake.thisValues, this.thisValues[i]);<br \/>   push.call(fake.args, this.args[i]);<br \/>   push.call(fake.returnValues, this.returnValues[i]);<br \/>   push.call(fake.exceptions, this.exceptions[i]);<br \/>   push.call(fake.callIds, this.callIds[i]);<br \/>   }<br \/>   }<br \/>   createCallProperties.call(fake);<\/p>\n<p>  return fake;<br \/>   },<\/p>\n<p>  matches: function (args, strict) {<br \/>   var margs = this.matchingAguments;<\/p>\n<p>  if (margs.length &lt;= args.length &#038;&#038;<br \/>   sinon.deepEqual(margs, args.slice(0, margs.length))) {<br \/>   return !strict || margs.length === args.length;<br \/>   }<br \/>   },<\/p>\n<p>  printf: function (format) {<br \/>   var spyInstance = this;<br \/>   var args = slice.call(arguments, 1);<br \/>   var formatter;<\/p>\n<p>  return (format || &quot;&quot;).replace(\/%(.)\/g, function (match, specifyer) {<br \/>   formatter = spyApi.formatters[specifyer];<\/p>\n<p>  if (typeof formatter === \u00abfunction\u00bb) {<br \/>   return formatter.call(null, spyInstance, args);<br \/>   } else if (!isNaN(parseInt(specifyer, 10))) {<br \/>   return sinon.format(args[specifyer \u2014 1]);<br \/>   }<\/p>\n<p>  return &quot;%&quot; + specifyer;<br \/>   });<br \/>   }<br \/>   };<\/p>\n<p>  function delegateToCalls(method, matchAny, actual, notCalled) {<br \/>   spyApi[method] = function () {<br \/>   if (!this.called) {<br \/>   if (notCalled) {<br \/>   return notCalled.apply(this, arguments);<br \/>   }<br \/>   return false;<br \/>   }<\/p>\n<p>  var currentCall;<br \/>   var matches = 0;<\/p>\n<p>  for (var i = 0, l = this.callCount; i &lt; l; i += 1) {<br \/>   currentCall = this.getCall(i);<\/p>\n<p>  if (currentCall[actual || method].apply(currentCall, arguments)) {<br \/>   matches += 1;<\/p>\n<p>  if (matchAny) {<br \/>   return true;<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  return matches === this.callCount;<br \/>   };<br \/>   }<\/p>\n<p>  delegateToCalls(\u00abcalledOn\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysCalledOn\u00bb, false, \u00abcalledOn\u00bb);<br \/>   delegateToCalls(\u00abcalledWith\u00bb, true);<br \/>   delegateToCalls(\u00abcalledWithMatch\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysCalledWith\u00bb, false, \u00abcalledWith\u00bb);<br \/>   delegateToCalls(\u00abalwaysCalledWithMatch\u00bb, false, \u00abcalledWithMatch\u00bb);<br \/>   delegateToCalls(\u00abcalledWithExactly\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysCalledWithExactly\u00bb, false, \u00abcalledWithExactly\u00bb);<br \/>   delegateToCalls(\u00abneverCalledWith\u00bb, false, \u00abnotCalledWith\u00bb, function () {<br \/>   return true;<br \/>   });<br \/>   delegateToCalls(\u00abneverCalledWithMatch\u00bb, false, \u00abnotCalledWithMatch\u00bb, function () {<br \/>   return true;<br \/>   });<br \/>   delegateToCalls(\u00abthrew\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysThrew\u00bb, false, \u00abthrew\u00bb);<br \/>   delegateToCalls(\u00abreturned\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysReturned\u00bb, false, \u00abreturned\u00bb);<br \/>   delegateToCalls(\u00abcalledWithNew\u00bb, true);<br \/>   delegateToCalls(\u00abalwaysCalledWithNew\u00bb, false, \u00abcalledWithNew\u00bb);<br \/>   delegateToCalls(\u00abcallArg\u00bb, false, \u00abcallArgWith\u00bb, function () {<br \/>   throw new Error(this.toString() + &quot; cannot call arg since it was not yet invoked.&quot;);<br \/>   });<br \/>   spyApi.callArgWith = spyApi.callArg;<br \/>   delegateToCalls(\u00abcallArgOn\u00bb, false, \u00abcallArgOnWith\u00bb, function () {<br \/>   throw new Error(this.toString() + &quot; cannot call arg since it was not yet invoked.&quot;);<br \/>   });<br \/>   spyApi.callArgOnWith = spyApi.callArgOn;<br \/>   delegateToCalls(\u00abyield\u00bb, false, \u00abyield\u00bb, function () {<br \/>   throw new Error(this.toString() + &quot; cannot yield since it was not yet invoked.&quot;);<br \/>   });<br \/>   \/\/ \u00abinvokeCallback\u00bb is an alias for \u00abyield\u00bb since \u00abyield\u00bb is invalid in strict mode.<br \/>   spyApi.invokeCallback = spyApi.yield;<br \/>   delegateToCalls(\u00abyieldOn\u00bb, false, \u00abyieldOn\u00bb, function () {<br \/>   throw new Error(this.toString() + &quot; cannot yield since it was not yet invoked.&quot;);<br \/>   });<br \/>   delegateToCalls(\u00abyieldTo\u00bb, false, \u00abyieldTo\u00bb, function (property) {<br \/>   throw new Error(this.toString() + &quot; cannot yield to &#8216;&quot; + property +<br \/>   &quot;&#8217; since it was not yet invoked.&quot;);<br \/>   });<br \/>   delegateToCalls(\u00abyieldToOn\u00bb, false, \u00abyieldToOn\u00bb, function (property) {<br \/>   throw new Error(this.toString() + &quot; cannot yield to &#8216;&quot; + property +<br \/>   &quot;&#8217; since it was not yet invoked.&quot;);<br \/>   });<\/p>\n<p>  spyApi.formatters = {<br \/>   c: function (spyInstance) {<br \/>   return sinon.timesInWords(spyInstance.callCount);<br \/>   },<\/p>\n<p>  n: function (spyInstance) {<br \/>   return spyInstance.toString();<br \/>   },<\/p>\n<p>  C: function (spyInstance) {<br \/>   var calls = [];<\/p>\n<p>  for (var i = 0, l = spyInstance.callCount; i &lt; l; ++i) {<br \/>   var stringifiedCall = &quot; &quot; + spyInstance.getCall(i).toString();<br \/>   if (\/\\n\/.test(calls[i \u2014 1])) {<br \/>   stringifiedCall = &quot;\\n&quot; + stringifiedCall;<br \/>   }<br \/>   push.call(calls, stringifiedCall);<br \/>   }<\/p>\n<p>  return calls.length &gt; 0? &quot;\\n&quot; + calls.join(&quot;\\n&quot;): &quot;&quot;;<br \/>   },<\/p>\n<p>  t: function (spyInstance) {<br \/>   var objects = [];<\/p>\n<p>  for (var i = 0, l = spyInstance.callCount; i &lt; l; ++i) {<br \/>   push.call(objects, sinon.format(spyInstance.thisValues[i]));<br \/>   }<\/p>\n<p>  return objects.join(&quot;, &quot;);<br \/>   },<\/p>\n<p>  &quot;*&quot;: function (spyInstance, args) {<br \/>   var formatted = [];<\/p>\n<p>  for (var i = 0, l = args.length; i &lt; l; ++i) {<br \/>   push.call(formatted, sinon.format(args[i]));<br \/>   }<\/p>\n<p>  return formatted.join(&quot;, &quot;);<br \/>   }<br \/>   };<\/p>\n<p>  sinon.extend(spy, spyApi);<\/p>\n<p>  spy.spyCall = sinon.spyCall;<br \/>   sinon.spy = spy;<\/p>\n<p>  return spy;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/call&quot;);<br \/>   require(&quot;.\/extend&quot;);<br \/>   require(&quot;.\/times_in_words&quot;);<br \/>   require(&quot;.\/format&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> extend.js<br \/>   *\/<br \/>  \/**<br \/>   * Stub behavior<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Tim Fischbach (mail@timfischbach.de)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   var slice = Array.prototype.slice;<br \/>   var join = Array.prototype.join;<br \/>   var useLeftMostCallback = -1;<br \/>   var useRightMostCallback = -2;<\/p>\n<p>  var nextTick = (function () {<br \/>   if (typeof process === \u00abobject\u00bb &#038;&#038; typeof process.nextTick === \u00abfunction\u00bb) {<br \/>   return process.nextTick;<br \/>   }<\/p>\n<p>  if (typeof setImmediate === \u00abfunction\u00bb) {<br \/>   return setImmediate;<br \/>   }<\/p>\n<p>  return function (callback) {<br \/>   setTimeout(callback, 0);<br \/>   };<br \/>   })();<\/p>\n<p>  function throwsException(error, message) {<br \/>   if (typeof error === \u00abstring\u00bb) {<br \/>   this.exception = new Error(message || &quot;&quot;);<br \/>   this.exception.name = error;<br \/>   } else if (!error) {<br \/>   this.exception = new Error(\u00abError\u00bb);<br \/>   } else {<br \/>   this.exception = error;<br \/>   }<\/p>\n<p>  return this;<br \/>   }<\/p>\n<p>  function getCallback(behavior, args) {<br \/>   var callArgAt = behavior.callArgAt;<\/p>\n<p>  if (callArgAt &gt;= 0) {<br \/>   return args[callArgAt];<br \/>   }<\/p>\n<p>  var argumentList;<\/p>\n<p>  if (callArgAt === useLeftMostCallback) {<br \/>   argumentList = args;<br \/>   }<\/p>\n<p>  if (callArgAt === useRightMostCallback) {<br \/>   argumentList = slice.call(args).reverse();<br \/>   }<\/p>\n<p>  var callArgProp = behavior.callArgProp;<\/p>\n<p>  for (var i = 0, l = argumentList.length; i &lt; l; ++i) {<br \/>   if (!callArgProp &#038;&#038; typeof argumentList[i] === \u00abfunction\u00bb) {<br \/>   return argumentList[i];<br \/>   }<\/p>\n<p>  if (callArgProp &#038;&#038; argumentList[i] &#038;&#038;<br \/>   typeof argumentList[i][callArgProp] === \u00abfunction\u00bb) {<br \/>   return argumentList[i][callArgProp];<br \/>   }<br \/>   }<\/p>\n<p>  return null;<br \/>   }<\/p>\n<p>  function makeApi(sinon) {<br \/>   function getCallbackError(behavior, func, args) {<br \/>   if (behavior.callArgAt &lt; 0) {<br \/>   var msg;<\/p>\n<p>  if (behavior.callArgProp) {<br \/>   msg = sinon.functionName(behavior.stub) +<br \/>   &quot; expected to yield to &#8216;&quot; + behavior.callArgProp +<br \/>   &quot;&#8217;, but no object with such a property was passed.&quot;;<br \/>   } else {<br \/>   msg = sinon.functionName(behavior.stub) +<br \/>   &quot; expected to yield, but no callback was passed.&quot;;<br \/>   }<\/p>\n<p>  if (args.length &gt; 0) {<br \/>   msg += &quot; Received [&quot; + join.call(args, &quot;, &quot;) + &quot;]&quot;;<br \/>   }<\/p>\n<p>  return msg;<br \/>   }<\/p>\n<p>  return \u00abargument at index \u00bb + behavior.callArgAt + &quot; is not a function: &quot; + func;<br \/>   }<\/p>\n<p>  function callCallback(behavior, args) {<br \/>   if (typeof behavior.callArgAt === \u00abnumber\u00bb) {<br \/>   var func = getCallback(behavior, args);<\/p>\n<p>  if (typeof func !== \u00abfunction\u00bb) {<br \/>   throw new TypeError(getCallbackError(behavior, func, args));<br \/>   }<\/p>\n<p>  if (behavior.callbackAsync) {<br \/>   nextTick(function () {<br \/>   func.apply(behavior.callbackContext, behavior.callbackArguments);<br \/>   });<br \/>   } else {<br \/>   func.apply(behavior.callbackContext, behavior.callbackArguments);<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  var proto = {<br \/>   create: function create(stub) {<br \/>   var behavior = sinon.extend({}, sinon.behavior);<br \/>   delete behavior.create;<br \/>   behavior.stub = stub;<\/p>\n<p>  return behavior;<br \/>   },<\/p>\n<p>  isPresent: function isPresent() {<br \/>   return (typeof this.callArgAt === \u00abnumber\u00bb ||<br \/>   this.exception ||<br \/>   typeof this.returnArgAt === \u00abnumber\u00bb ||<br \/>   this.returnThis ||<br \/>   this.returnValueDefined);<br \/>   },<\/p>\n<p>  invoke: function invoke(context, args) {<br \/>   callCallback(this, args);<\/p>\n<p>  if (this.exception) {<br \/>   throw this.exception;<br \/>   } else if (typeof this.returnArgAt === \u00abnumber\u00bb) {<br \/>   return args[this.returnArgAt];<br \/>   } else if (this.returnThis) {<br \/>   return context;<br \/>   }<\/p>\n<p>  return this.returnValue;<br \/>   },<\/p>\n<p>  onCall: function onCall(index) {<br \/>   return this.stub.onCall(index);<br \/>   },<\/p>\n<p>  onFirstCall: function onFirstCall() {<br \/>   return this.stub.onFirstCall();<br \/>   },<\/p>\n<p>  onSecondCall: function onSecondCall() {<br \/>   return this.stub.onSecondCall();<br \/>   },<\/p>\n<p>  onThirdCall: function onThirdCall() {<br \/>   return this.stub.onThirdCall();<br \/>   },<\/p>\n<p>  withArgs: function withArgs(\/* arguments *\/) {<br \/>   throw new Error(<br \/>   \u00abDefining a stub by invoking \\\u00bbstub.onCall(&#8230;).withArgs(&#8230;)\\&quot; &quot; +<br \/>   \u00abis not supported. Use \\\u00bbstub.withArgs(&#8230;).onCall(&#8230;)\\&quot; &quot; +<br \/>   \u00abto define sequential behavior for calls with certain arguments.\u00bb<br \/>   );<br \/>   },<\/p>\n<p>  callsArg: function callsArg(pos) {<br \/>   if (typeof pos !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abargument index is not number\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = pos;<br \/>   this.callbackArguments = [];<br \/>   this.callbackContext = undefined;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  callsArgOn: function callsArgOn(pos, context) {<br \/>   if (typeof pos !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abargument index is not number\u00bb);<br \/>   }<br \/>   if (typeof context !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abargument context is not an object\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = pos;<br \/>   this.callbackArguments = [];<br \/>   this.callbackContext = context;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  callsArgWith: function callsArgWith(pos) {<br \/>   if (typeof pos !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abargument index is not number\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = pos;<br \/>   this.callbackArguments = slice.call(arguments, 1);<br \/>   this.callbackContext = undefined;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  callsArgOnWith: function callsArgWith(pos, context) {<br \/>   if (typeof pos !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abargument index is not number\u00bb);<br \/>   }<br \/>   if (typeof context !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abargument context is not an object\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = pos;<br \/>   this.callbackArguments = slice.call(arguments, 2);<br \/>   this.callbackContext = context;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  yields: function () {<br \/>   this.callArgAt = useLeftMostCallback;<br \/>   this.callbackArguments = slice.call(arguments, 0);<br \/>   this.callbackContext = undefined;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  yieldsRight: function () {<br \/>   this.callArgAt = useRightMostCallback;<br \/>   this.callbackArguments = slice.call(arguments, 0);<br \/>   this.callbackContext = undefined;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  yieldsOn: function (context) {<br \/>   if (typeof context !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abargument context is not an object\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = useLeftMostCallback;<br \/>   this.callbackArguments = slice.call(arguments, 1);<br \/>   this.callbackContext = context;<br \/>   this.callArgProp = undefined;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  yieldsTo: function (prop) {<br \/>   this.callArgAt = useLeftMostCallback;<br \/>   this.callbackArguments = slice.call(arguments, 1);<br \/>   this.callbackContext = undefined;<br \/>   this.callArgProp = prop;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  yieldsToOn: function (prop, context) {<br \/>   if (typeof context !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abargument context is not an object\u00bb);<br \/>   }<\/p>\n<p>  this.callArgAt = useLeftMostCallback;<br \/>   this.callbackArguments = slice.call(arguments, 2);<br \/>   this.callbackContext = context;<br \/>   this.callArgProp = prop;<br \/>   this.callbackAsync = false;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  throws: throwsException,<br \/>   throwsException: throwsException,<\/p>\n<p>  returns: function returns(value) {<br \/>   this.returnValue = value;<br \/>   this.returnValueDefined = true;<br \/>   this.exception = undefined;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  returnsArg: function returnsArg(pos) {<br \/>   if (typeof pos !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(\u00abargument index is not number\u00bb);<br \/>   }<\/p>\n<p>  this.returnArgAt = pos;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  returnsThis: function returnsThis() {<br \/>   this.returnThis = true;<\/p>\n<p>  return this;<br \/>   }<br \/>   };<\/p>\n<p>  function createAsyncVersion(syncFnName) {<br \/>   return function () {<br \/>   var result = this[syncFnName].apply(this, arguments);<br \/>   this.callbackAsync = true;<br \/>   return result;<br \/>   };<br \/>   }<\/p>\n<p>  \/\/ create asynchronous versions of callsArg* and yields* methods<br \/>   for (var method in proto) {<br \/>   \/\/ need to avoid creating anotherasync versions of the newly added async methods<br \/>   if (proto.hasOwnProperty(method) &#038;&#038; method.match(\/^(callsArg|yields)\/) &#038;&#038; !method.match(\/Async\/)) {<br \/>   proto[method + \u00abAsync\u00bb] = createAsyncVersion(method);<br \/>   }<br \/>   }<\/p>\n<p>  sinon.behavior = proto;<br \/>   return proto;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/extend&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function walkInternal(obj, iterator, context, originalObj, seen) {<br \/>   var proto, prop;<\/p>\n<p>  if (typeof Object.getOwnPropertyNames !== \u00abfunction\u00bb) {<br \/>   \/\/ We explicitly want to enumerate through all of the prototype&#8217;s properties<br \/>   \/\/ in this case, therefore we deliberately leave out an own property check.<br \/>   \/* eslint-disable guard-for-in *\/<br \/>   for (prop in obj) {<br \/>   iterator.call(context, obj[prop], prop, obj);<br \/>   }<br \/>   \/* eslint-enable guard-for-in *\/<\/p>\n<p>  return;<br \/>   }<\/p>\n<p>  Object.getOwnPropertyNames(obj).forEach(function (k) {<br \/>   if (!seen[k]) {<br \/>   seen[k] = true;<br \/>   var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === \u00abfunction\u00bb?<br \/>   originalObj: obj;<br \/>   iterator.call(context, target[k], k, target);<br \/>   }<br \/>   });<\/p>\n<p>  proto = Object.getPrototypeOf(obj);<br \/>   if (proto) {<br \/>   walkInternal(proto, iterator, context, originalObj, seen);<br \/>   }<br \/>   }<\/p>\n<p>  \/* Public: walks the prototype chain of an object and iterates over every own property<br \/>   * name encountered. The iterator is called in the same fashion that Array.prototype.forEach<br \/>   * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional<br \/>   * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will<br \/>   * default to using a simple for..in loop.<br \/>   *<br \/>   * obj \u2014 The object to walk the prototype chain for.<br \/>   * iterator \u2014 The function to be called on each pass of the walk.<br \/>   * context \u2014 (Optional) When given, the iterator will be called with this object as the receiver.<br \/>   *\/<br \/>   function walk(obj, iterator, context) {<br \/>   return walkInternal(obj, iterator, context, obj, {});<br \/>   }<\/p>\n<p>  sinon.walk = walk;<br \/>   return sinon.walk;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> spy.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> behavior.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> walk.js<br \/>   *\/<br \/>  \/**<br \/>   * Stub functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function stub(object, property, func) {<br \/>   if (!!func &#038;&#038; typeof func !== \u00abfunction\u00bb &#038;&#038; typeof func !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00abCustom stub should be a function or a property descriptor\u00bb);<br \/>   }<\/p>\n<p>  var wrapper;<\/p>\n<p>  if (func) {<br \/>   if (typeof func === \u00abfunction\u00bb) {<br \/>   wrapper = sinon.spy &#038;&#038; sinon.spy.create? sinon.spy.create(func): func;<br \/>   } else {<br \/>   wrapper = func;<br \/>   if (sinon.spy &#038;&#038; sinon.spy.create) {<br \/>   var types = sinon.objectKeys(wrapper);<br \/>   for (var i = 0; i &lt; types.length; i++) {<br \/>   wrapper[types[i]] = sinon.spy.create(wrapper[types[i]]);<br \/>   }<br \/>   }<br \/>   }<br \/>   } else {<br \/>   var stubLength = 0;<br \/>   if (typeof object === \u00abobject\u00bb &#038;&#038; typeof object[property] === \u00abfunction\u00bb) {<br \/>   stubLength = object[property].length;<br \/>   }<br \/>   wrapper = stub.create(stubLength);<br \/>   }<\/p>\n<p>  if (!object &#038;&#038; typeof property === \u00abundefined\u00bb) {<br \/>   return sinon.stub.create();<br \/>   }<\/p>\n<p>  if (typeof property === \u00abundefined\u00bb &#038;&#038; typeof object === \u00abobject\u00bb) {<br \/>   sinon.walk(object || {}, function (value, prop, propOwner) {<br \/>   \/\/ we don&#8217;t want to stub things like toString(), valueOf(), etc. so we only stub if the object<br \/>   \/\/ is not Object.prototype<br \/>   if (<br \/>   propOwner !== Object.prototype &#038;&#038;<br \/>   prop !== \u00abconstructor\u00bb &#038;&#038;<br \/>   typeof sinon.getPropertyDescriptor(propOwner, prop).value === \u00abfunction\u00bb<br \/>   ) {<br \/>   stub(object, prop);<br \/>   }<br \/>   });<\/p>\n<p>  return object;<br \/>   }<\/p>\n<p>  return sinon.wrapMethod(object, property, wrapper);<br \/>   }<\/p>\n<p>  \/*eslint-disable no-use-before-define*\/<br \/>   function getParentBehaviour(stubInstance) {<br \/>   return (stubInstance.parent &#038;&#038; getCurrentBehavior(stubInstance.parent));<br \/>   }<\/p>\n<p>  function getDefaultBehavior(stubInstance) {<br \/>   return stubInstance.defaultBehavior ||<br \/>   getParentBehaviour(stubInstance) ||<br \/>   sinon.behavior.create(stubInstance);<br \/>   }<\/p>\n<p>  function getCurrentBehavior(stubInstance) {<br \/>   var behavior = stubInstance.behaviors[stubInstance.callCount \u2014 1];<br \/>   return behavior &#038;&#038; behavior.isPresent()? behavior: getDefaultBehavior(stubInstance);<br \/>   }<br \/>   \/*eslint-enable no-use-before-define*\/<\/p>\n<p>  var uuid = 0;<\/p>\n<p>  var proto = {<br \/>   create: function create(stubLength) {<br \/>   var functionStub = function () {<br \/>   return getCurrentBehavior(functionStub).invoke(this, arguments);<br \/>   };<\/p>\n<p>  functionStub.id = \u00abstub#\u00bb + uuid++;<br \/>   var orig = functionStub;<br \/>   functionStub = sinon.spy.create(functionStub, stubLength);<br \/>   functionStub.func = orig;<\/p>\n<p>  sinon.extend(functionStub, stub);<br \/>   functionStub.instantiateFake = sinon.stub.create;<br \/>   functionStub.displayName = \u00abstub\u00bb;<br \/>   functionStub.toString = sinon.functionToString;<\/p>\n<p>  functionStub.defaultBehavior = null;<br \/>   functionStub.behaviors = [];<\/p>\n<p>  return functionStub;<br \/>   },<\/p>\n<p>  resetBehavior: function () {<br \/>   var i;<\/p>\n<p>  this.defaultBehavior = null;<br \/>   this.behaviors = [];<\/p>\n<p>  delete this.returnValue;<br \/>   delete this.returnArgAt;<br \/>   this.returnThis = false;<\/p>\n<p>  if (this.fakes) {<br \/>   for (i = 0; i &lt; this.fakes.length; i++) {<br \/>   this.fakes[i].resetBehavior();<br \/>   }<br \/>   }<br \/>   },<\/p>\n<p>  onCall: function onCall(index) {<br \/>   if (!this.behaviors[index]) {<br \/>   this.behaviors[index] = sinon.behavior.create(this);<br \/>   }<\/p>\n<p>  return this.behaviors[index];<br \/>   },<\/p>\n<p>  onFirstCall: function onFirstCall() {<br \/>   return this.onCall(0);<br \/>   },<\/p>\n<p>  onSecondCall: function onSecondCall() {<br \/>   return this.onCall(1);<br \/>   },<\/p>\n<p>  onThirdCall: function onThirdCall() {<br \/>   return this.onCall(2);<br \/>   }<br \/>   };<\/p>\n<p>  function createBehavior(behaviorMethod) {<br \/>   return function () {<br \/>   this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);<br \/>   this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);<br \/>   return this;<br \/>   };<br \/>   }<\/p>\n<p>  for (var method in sinon.behavior) {<br \/>   if (sinon.behavior.hasOwnProperty(method) &#038;&#038;<br \/>   !proto.hasOwnProperty(method) &#038;&#038;<br \/>   method !== \u00abcreate\u00bb &#038;&#038;<br \/>   method !== \u00abwithArgs\u00bb &#038;&#038;<br \/>   method !== \u00abinvoke\u00bb) {<br \/>   proto[method] = createBehavior(method);<br \/>   }<br \/>   }<\/p>\n<p>  sinon.extend(stub, proto);<br \/>   sinon.stub = stub;<\/p>\n<p>  return stub;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/behavior&quot;);<br \/>   require(&quot;.\/spy&quot;);<br \/>   require(&quot;.\/extend&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> times_in_words.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> call.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> match.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> spy.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> stub.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> format.js<br \/>   *\/<br \/>  \/**<br \/>   * Mock functions.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   var push = [].push;<br \/>   var match = sinon.match;<\/p>\n<p>  function mock(object) {<br \/>   \/\/ if (typeof console !== undefined &#038;&#038; console.warn) {<br \/>   \/\/ console.warn(\u00abmock will be removed from Sinon.JS v2.0\u00bb);<br \/>   \/\/ }<\/p>\n<p>  if (!object) {<br \/>   return sinon.expectation.create(\u00abAnonymous mock\u00bb);<br \/>   }<\/p>\n<p>  return mock.create(object);<br \/>   }<\/p>\n<p>  function each(collection, callback) {<br \/>   if (!collection) {<br \/>   return;<br \/>   }<\/p>\n<p>  for (var i = 0, l = collection.length; i &lt; l; i += 1) {<br \/>   callback(collection[i]);<br \/>   }<br \/>   }<\/p>\n<p>  function arrayEquals(arr1, arr2, compareLength) {<br \/>   if (compareLength &#038;&#038; (arr1.length !== arr2.length)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  for (var i = 0, l = arr1.length; i &lt; l; i++) {<br \/>   if (!sinon.deepEqual(arr1[i], arr2[i])) {<br \/>   return false;<br \/>   }<br \/>   }<br \/>   return true;<br \/>   }<\/p>\n<p>  sinon.extend(mock, {<br \/>   create: function create(object) {<br \/>   if (!object) {<br \/>   throw new TypeError(\u00abobject is null\u00bb);<br \/>   }<\/p>\n<p>  var mockObject = sinon.extend({}, mock);<br \/>   mockObject.object = object;<br \/>   delete mockObject.create;<\/p>\n<p>  return mockObject;<br \/>   },<\/p>\n<p>  expects: function expects(method) {<br \/>   if (!method) {<br \/>   throw new TypeError(\u00abmethod is falsy\u00bb);<br \/>   }<\/p>\n<p>  if (!this.expectations) {<br \/>   this.expectations = {};<br \/>   this.proxies = [];<br \/>   }<\/p>\n<p>  if (!this.expectations[method]) {<br \/>   this.expectations[method] = [];<br \/>   var mockObject = this;<\/p>\n<p>  sinon.wrapMethod(this.object, method, function () {<br \/>   return mockObject.invokeMethod(method, this, arguments);<br \/>   });<\/p>\n<p>  push.call(this.proxies, method);<br \/>   }<\/p>\n<p>  var expectation = sinon.expectation.create(method);<br \/>   push.call(this.expectations[method], expectation);<\/p>\n<p>  return expectation;<br \/>   },<\/p>\n<p>  restore: function restore() {<br \/>   var object = this.object;<\/p>\n<p>  each(this.proxies, function (proxy) {<br \/>   if (typeof object[proxy].restore === \u00abfunction\u00bb) {<br \/>   object[proxy].restore();<br \/>   }<br \/>   });<br \/>   },<\/p>\n<p>  verify: function verify() {<br \/>   var expectations = this.expectations || {};<br \/>   var messages = [];<br \/>   var met = [];<\/p>\n<p>  each(this.proxies, function (proxy) {<br \/>   each(expectations[proxy], function (expectation) {<br \/>   if (!expectation.met()) {<br \/>   push.call(messages, expectation.toString());<br \/>   } else {<br \/>   push.call(met, expectation.toString());<br \/>   }<br \/>   });<br \/>   });<\/p>\n<p>  this.restore();<\/p>\n<p>  if (messages.length &gt; 0) {<br \/>   sinon.expectation.fail(messages.concat(met).join(&quot;\\n&quot;));<br \/>   } else if (met.length &gt; 0) {<br \/>   sinon.expectation.pass(messages.concat(met).join(&quot;\\n&quot;));<br \/>   }<\/p>\n<p>  return true;<br \/>   },<\/p>\n<p>  invokeMethod: function invokeMethod(method, thisValue, args) {<br \/>   var expectations = this.expectations &#038;&#038; this.expectations[method]? this.expectations[method]: [];<br \/>   var expectationsWithMatchingArgs = [];<br \/>   var currentArgs = args || [];<br \/>   var i, available;<\/p>\n<p>  for (i = 0; i &lt; expectations.length; i += 1) {<br \/>   var expectedArgs = expectations[i].expectedArguments || [];<br \/>   if (arrayEquals(expectedArgs, currentArgs, expectations[i].expectsExactArgCount)) {<br \/>   expectationsWithMatchingArgs.push(expectations[i]);<br \/>   }<br \/>   }<\/p>\n<p>  for (i = 0; i &lt; expectationsWithMatchingArgs.length; i += 1) {<br \/>   if (!expectationsWithMatchingArgs[i].met() &#038;&#038;<br \/>   expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {<br \/>   return expectationsWithMatchingArgs[i].apply(thisValue, args);<br \/>   }<br \/>   }<\/p>\n<p>  var messages = [];<br \/>   var exhausted = 0;<\/p>\n<p>  for (i = 0; i &lt; expectationsWithMatchingArgs.length; i += 1) {<br \/>   if (expectationsWithMatchingArgs[i].allowsCall(thisValue, args)) {<br \/>   available = available || expectationsWithMatchingArgs[i];<br \/>   } else {<br \/>   exhausted += 1;<br \/>   }<br \/>   }<\/p>\n<p>  if (available &#038;&#038; exhausted === 0) {<br \/>   return available.apply(thisValue, args);<br \/>   }<\/p>\n<p>  for (i = 0; i &lt; expectations.length; i += 1) {<br \/>   push.call(messages, &quot; &quot; + expectations[i].toString());<br \/>   }<\/p>\n<p>  messages.unshift(\u00abUnexpected call: \u00bb + sinon.spyCall.toString.call({<br \/>   proxy: method,<br \/>   args: args<br \/>   }));<\/p>\n<p>  sinon.expectation.fail(messages.join(&quot;\\n&quot;));<br \/>   }<br \/>   });<\/p>\n<p>  var times = sinon.timesInWords;<br \/>   var slice = Array.prototype.slice;<\/p>\n<p>  function callCountInWords(callCount) {<br \/>   if (callCount === 0) {<br \/>   return \u00abnever called\u00bb;<br \/>   }<\/p>\n<p>  return \u00abcalled \u00bb + times(callCount);<br \/>   }<\/p>\n<p>  function expectedCallCountInWords(expectation) {<br \/>   var min = expectation.minCalls;<br \/>   var max = expectation.maxCalls;<\/p>\n<p>  if (typeof min === \u00abnumber\u00bb &#038;&#038; typeof max === \u00abnumber\u00bb) {<br \/>   var str = times(min);<\/p>\n<p>  if (min !== max) {<br \/>   str = \u00abat least \u00bb + str + &quot; and at most &quot; + times(max);<br \/>   }<\/p>\n<p>  return str;<br \/>   }<\/p>\n<p>  if (typeof min === \u00abnumber\u00bb) {<br \/>   return \u00abat least \u00bb + times(min);<br \/>   }<\/p>\n<p>  return \u00abat most \u00bb + times(max);<br \/>   }<\/p>\n<p>  function receivedMinCalls(expectation) {<br \/>   var hasMinLimit = typeof expectation.minCalls === \u00abnumber\u00bb;<br \/>   return !hasMinLimit || expectation.callCount &gt;= expectation.minCalls;<br \/>   }<\/p>\n<p>  function receivedMaxCalls(expectation) {<br \/>   if (typeof expectation.maxCalls !== \u00abnumber\u00bb) {<br \/>   return false;<br \/>   }<\/p>\n<p>  return expectation.callCount === expectation.maxCalls;<br \/>   }<\/p>\n<p>  function verifyMatcher(possibleMatcher, arg) {<br \/>   var isMatcher = match &#038;&#038; match.isMatcher(possibleMatcher);<\/p>\n<p>  return isMatcher &#038;&#038; possibleMatcher.test(arg) || true;<br \/>   }<\/p>\n<p>  sinon.expectation = {<br \/>   minCalls: 1,<br \/>   maxCalls: 1,<\/p>\n<p>  create: function create(methodName) {<br \/>   var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);<br \/>   delete expectation.create;<br \/>   expectation.method = methodName;<\/p>\n<p>  return expectation;<br \/>   },<\/p>\n<p>  invoke: function invoke(func, thisValue, args) {<br \/>   this.verifyCallAllowed(thisValue, args);<\/p>\n<p>  return sinon.spy.invoke.apply(this, arguments);<br \/>   },<\/p>\n<p>  atLeast: function atLeast(num) {<br \/>   if (typeof num !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(&quot;&#8217;&quot; + num + &quot;&#8217; is not number&quot;);<br \/>   }<\/p>\n<p>  if (!this.limitsSet) {<br \/>   this.maxCalls = null;<br \/>   this.limitsSet = true;<br \/>   }<\/p>\n<p>  this.minCalls = num;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  atMost: function atMost(num) {<br \/>   if (typeof num !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(&quot;&#8217;&quot; + num + &quot;&#8217; is not number&quot;);<br \/>   }<\/p>\n<p>  if (!this.limitsSet) {<br \/>   this.minCalls = null;<br \/>   this.limitsSet = true;<br \/>   }<\/p>\n<p>  this.maxCalls = num;<\/p>\n<p>  return this;<br \/>   },<\/p>\n<p>  never: function never() {<br \/>   return this.exactly(0);<br \/>   },<\/p>\n<p>  once: function once() {<br \/>   return this.exactly(1);<br \/>   },<\/p>\n<p>  twice: function twice() {<br \/>   return this.exactly(2);<br \/>   },<\/p>\n<p>  thrice: function thrice() {<br \/>   return this.exactly(3);<br \/>   },<\/p>\n<p>  exactly: function exactly(num) {<br \/>   if (typeof num !== \u00abnumber\u00bb) {<br \/>   throw new TypeError(&quot;&#8217;&quot; + num + &quot;&#8217; is not a number&quot;);<br \/>   }<\/p>\n<p>  this.atLeast(num);<br \/>   return this.atMost(num);<br \/>   },<\/p>\n<p>  met: function met() {<br \/>   return !this.failed &#038;&#038; receivedMinCalls(this);<br \/>   },<\/p>\n<p>  verifyCallAllowed: function verifyCallAllowed(thisValue, args) {<br \/>   if (receivedMaxCalls(this)) {<br \/>   this.failed = true;<br \/>   sinon.expectation.fail(this.method + &quot; already called &quot; + times(this.maxCalls));<br \/>   }<\/p>\n<p>  if (\u00abexpectedThis\u00bb in this &#038;&#038; this.expectedThis !== thisValue) {<br \/>   sinon.expectation.fail(this.method + &quot; called with &quot; + thisValue + &quot; as thisValue, expected &quot; +<br \/>   this.expectedThis);<br \/>   }<\/p>\n<p>  if (!(\u00abexpectedArguments\u00bb in this)) {<br \/>   return;<br \/>   }<\/p>\n<p>  if (!args) {<br \/>   sinon.expectation.fail(this.method + &quot; received no arguments, expected &quot; +<br \/>   sinon.format(this.expectedArguments));<br \/>   }<\/p>\n<p>  if (args.length &lt; this.expectedArguments.length) {<br \/>   sinon.expectation.fail(this.method + &quot; received too few arguments (&quot; + sinon.format(args) +<br \/>   &quot;), expected &quot; + sinon.format(this.expectedArguments));<br \/>   }<\/p>\n<p>  if (this.expectsExactArgCount &#038;&#038;<br \/>   args.length !== this.expectedArguments.length) {<br \/>   sinon.expectation.fail(this.method + &quot; received too many arguments (&quot; + sinon.format(args) +<br \/>   &quot;), expected &quot; + sinon.format(this.expectedArguments));<br \/>   }<\/p>\n<p>  for (var i = 0, l = this.expectedArguments.length; i &lt; l; i += 1) {<\/p>\n<p>  if (!verifyMatcher(this.expectedArguments[i], args[i])) {<br \/>   sinon.expectation.fail(this.method + &quot; received wrong arguments &quot; + sinon.format(args) +<br \/>   &quot;, didn&#8217;t match &quot; + this.expectedArguments.toString());<br \/>   }<\/p>\n<p>  if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {<br \/>   sinon.expectation.fail(this.method + &quot; received wrong arguments &quot; + sinon.format(args) +<br \/>   &quot;, expected &quot; + sinon.format(this.expectedArguments));<br \/>   }<br \/>   }<br \/>   },<\/p>\n<p>  allowsCall: function allowsCall(thisValue, args) {<br \/>   if (this.met() &#038;&#038; receivedMaxCalls(this)) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (\u00abexpectedThis\u00bb in this &#038;&#038; this.expectedThis !== thisValue) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (!(\u00abexpectedArguments\u00bb in this)) {<br \/>   return true;<br \/>   }<\/p>\n<p>  args = args || [];<\/p>\n<p>  if (args.length &lt; this.expectedArguments.length) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (this.expectsExactArgCount &#038;&#038;<br \/>   args.length !== this.expectedArguments.length) {<br \/>   return false;<br \/>   }<\/p>\n<p>  for (var i = 0, l = this.expectedArguments.length; i &lt; l; i += 1) {<br \/>   if (!verifyMatcher(this.expectedArguments[i], args[i])) {<br \/>   return false;<br \/>   }<\/p>\n<p>  if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {<br \/>   return false;<br \/>   }<br \/>   }<\/p>\n<p>  return true;<br \/>   },<\/p>\n<p>  withArgs: function withArgs() {<br \/>   this.expectedArguments = slice.call(arguments);<br \/>   return this;<br \/>   },<\/p>\n<p>  withExactArgs: function withExactArgs() {<br \/>   this.withArgs.apply(this, arguments);<br \/>   this.expectsExactArgCount = true;<br \/>   return this;<br \/>   },<\/p>\n<p>  on: function on(thisValue) {<br \/>   this.expectedThis = thisValue;<br \/>   return this;<br \/>   },<\/p>\n<p>  toString: function () {<br \/>   var args = (this.expectedArguments || []).slice();<\/p>\n<p>  if (!this.expectsExactArgCount) {<br \/>   push.call(args, &quot;[&#8230;]&quot;);<br \/>   }<\/p>\n<p>  var callStr = sinon.spyCall.toString.call({<br \/>   proxy: this.method || \u00abanonymous mock expectation\u00bb,<br \/>   args: args<br \/>   });<\/p>\n<p>  var message = callStr.replace(&quot;, [&#8230;&quot;, &quot;[, &#8230;&quot;) + &quot; &quot; +<br \/>   expectedCallCountInWords(this);<\/p>\n<p>  if (this.met()) {<br \/>   return \u00abExpectation met: \u00bb + message;<br \/>   }<\/p>\n<p>  return \u00abExpected \u00bb + message + &quot; (&quot; +<br \/>   callCountInWords(this.callCount) + &quot;)&quot;;<br \/>   },<\/p>\n<p>  verify: function verify() {<br \/>   if (!this.met()) {<br \/>   sinon.expectation.fail(this.toString());<br \/>   } else {<br \/>   sinon.expectation.pass(this.toString());<br \/>   }<\/p>\n<p>  return true;<br \/>   },<\/p>\n<p>  pass: function pass(message) {<br \/>   sinon.assert.pass(message);<br \/>   },<\/p>\n<p>  fail: function fail(message) {<br \/>   var exception = new Error(message);<br \/>   exception.name = \u00abExpectationError\u00bb;<\/p>\n<p>  throw exception;<br \/>   }<br \/>   };<\/p>\n<p>  sinon.mock = mock;<br \/>   return mock;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/times_in_words&quot;);<br \/>   require(&quot;.\/call&quot;);<br \/>   require(&quot;.\/extend&quot;);<br \/>   require(&quot;.\/match&quot;);<br \/>   require(&quot;.\/spy&quot;);<br \/>   require(&quot;.\/stub&quot;);<br \/>   require(&quot;.\/format&quot;);<\/p>\n<p>  module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> spy.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> stub.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> mock.js<br \/>   *\/<br \/>  \/**<br \/>   * Collections of stubs, spies and mocks.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   var push = [].push;<br \/>   var hasOwnProperty = Object.prototype.hasOwnProperty;<\/p>\n<p>  function getFakes(fakeCollection) {<br \/>   if (!fakeCollection.fakes) {<br \/>   fakeCollection.fakes = [];<br \/>   }<\/p>\n<p>  return fakeCollection.fakes;<br \/>   }<\/p>\n<p>  function each(fakeCollection, method) {<br \/>   var fakes = getFakes(fakeCollection);<\/p>\n<p>  for (var i = 0, l = fakes.length; i &lt; l; i += 1) {<br \/>   if (typeof fakes[i][method] === \u00abfunction\u00bb) {<br \/>   fakes[i][method]();<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  function compact(fakeCollection) {<br \/>   var fakes = getFakes(fakeCollection);<br \/>   var i = 0;<br \/>   while (i &lt; fakes.length) {<br \/>   fakes.splice(i, 1);<br \/>   }<br \/>   }<\/p>\n<p>  function makeApi(sinon) {<br \/>   var collection = {<br \/>   verify: function resolve() {<br \/>   each(this, \u00abverify\u00bb);<br \/>   },<\/p>\n<p>  restore: function restore() {<br \/>   each(this, \u00abrestore\u00bb);<br \/>   compact(this);<br \/>   },<\/p>\n<p>  reset: function restore() {<br \/>   each(this, \u00abreset\u00bb);<br \/>   },<\/p>\n<p>  verifyAndRestore: function verifyAndRestore() {<br \/>   var exception;<\/p>\n<p>  try {<br \/>   this.verify();<br \/>   } catch (e) {<br \/>   exception = e;<br \/>   }<\/p>\n<p>  this.restore();<\/p>\n<p>  if (exception) {<br \/>   throw exception;<br \/>   }<br \/>   },<\/p>\n<p>  add: function add(fake) {<br \/>   push.call(getFakes(this), fake);<br \/>   return fake;<br \/>   },<\/p>\n<p>  spy: function spy() {<br \/>   return this.add(sinon.spy.apply(sinon, arguments));<br \/>   },<\/p>\n<p>  stub: function stub(object, property, value) {<br \/>   if (property) {<br \/>   var original = object[property];<\/p>\n<p>  if (typeof original !== \u00abfunction\u00bb) {<br \/>   if (!hasOwnProperty.call(object, property)) {<br \/>   throw new TypeError(\u00abCannot stub non-existent own property \u00bb + property);<br \/>   }<\/p>\n<p>  object[property] = value;<\/p>\n<p>  return this.add({<br \/>   restore: function () {<br \/>   object[property] = original;<br \/>   }<br \/>   });<br \/>   }<br \/>   }<br \/>   if (!property &#038;&#038; !!object &#038;&#038; typeof object === \u00abobject\u00bb) {<br \/>   var stubbedObj = sinon.stub.apply(sinon, arguments);<\/p>\n<p>  for (var prop in stubbedObj) {<br \/>   if (typeof stubbedObj[prop] === \u00abfunction\u00bb) {<br \/>   this.add(stubbedObj[prop]);<br \/>   }<br \/>   }<\/p>\n<p>  return stubbedObj;<br \/>   }<\/p>\n<p>  return this.add(sinon.stub.apply(sinon, arguments));<br \/>   },<\/p>\n<p>  mock: function mock() {<br \/>   return this.add(sinon.mock.apply(sinon, arguments));<br \/>   },<\/p>\n<p>  inject: function inject(obj) {<br \/>   var col = this;<\/p>\n<p>  obj.spy = function () {<br \/>   return col.spy.apply(col, arguments);<br \/>   };<\/p>\n<p>  obj.stub = function () {<br \/>   return col.stub.apply(col, arguments);<br \/>   };<\/p>\n<p>  obj.mock = function () {<br \/>   return col.mock.apply(col, arguments);<br \/>   };<\/p>\n<p>  return obj;<br \/>   }<br \/>   };<\/p>\n<p>  sinon.collection = collection;<br \/>   return collection;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/mock&quot;);<br \/>   require(&quot;.\/spy&quot;);<br \/>   require(&quot;.\/stub&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   * Fake timer API<br \/>   * setTimeout<br \/>   * setInterval<br \/>   * clearTimeout<br \/>   * clearInterval<br \/>   * tick<br \/>   * reset<br \/>   * Date<br \/>   *<br \/>   * Inspired by jsUnitMockTimeOut from JsUnit<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function () {<\/p>\n<p>   function makeApi(s, lol) {<br \/>   \/*global lolex *\/<br \/>   var llx = typeof lolex !== \u00abundefined\u00bb? lolex: lol;<\/p>\n<p>  s.useFakeTimers = function () {<br \/>   var now;<br \/>   var methods = Array.prototype.slice.call(arguments);<\/p>\n<p>  if (typeof methods[0] === \u00abstring\u00bb) {<br \/>   now = 0;<br \/>   } else {<br \/>   now = methods.shift();<br \/>   }<\/p>\n<p>  var clock = llx.install(now || 0, methods);<br \/>   clock.restore = clock.uninstall;<br \/>   return clock;<br \/>   };<\/p>\n<p>  s.clock = {<br \/>   create: function (now) {<br \/>   return llx.createClock(now);<br \/>   }<br \/>   };<\/p>\n<p>  s.timers = {<br \/>   setTimeout: setTimeout,<br \/>   clearTimeout: clearTimeout,<br \/>   setImmediate: (typeof setImmediate !== \u00abundefined\u00bb? setImmediate: undefined),<br \/>   clearImmediate: (typeof clearImmediate !== \u00abundefined\u00bb? clearImmediate: undefined),<br \/>   setInterval: setInterval,<br \/>   clearInterval: clearInterval,<br \/>   Date: Date<br \/>   };<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, epxorts, module, lolex) {<br \/>   var core = require(&quot;.\/core&quot;);<br \/>   makeApi(core, lolex);<br \/>   module.exports = core;<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require, module.exports, module, require(\u00ablolex\u00bb));<br \/>   } else {<br \/>   makeApi(sinon); \/\/ eslint-disable-line no-undef<br \/>   }<br \/>  }());<\/p>\n<p>  \/**<br \/>   * Minimal Event interface implementation<br \/>   *<br \/>   * Original implementation by Sven Fuchs: <a href=\"https:\/\/gist.github.com\/995028\">gist.github.com\/995028<\/a><br \/>   * Modifications and tests by Christian Johansen.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Sven Fuchs (svenfuchs@artweb-design.de)<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2011 Sven Fuchs, Christian Johansen<br \/>   *\/<br \/>  if (typeof sinon === \u00abundefined\u00bb) {<br \/>   this.sinon = {};<br \/>  }<\/p>\n<p>  (function () {<\/p>\n<p>   var push = [].push;<\/p>\n<p>  function makeApi(sinon) {<br \/>   sinon.Event = function Event(type, bubbles, cancelable, target) {<br \/>   this.initEvent(type, bubbles, cancelable, target);<br \/>   };<\/p>\n<p>  sinon.Event.prototype = {<br \/>   initEvent: function (type, bubbles, cancelable, target) {<br \/>   this.type = type;<br \/>   this.bubbles = bubbles;<br \/>   this.cancelable = cancelable;<br \/>   this.target = target;<br \/>   },<\/p>\n<p>  stopPropagation: function () {},<\/p>\n<p>  preventDefault: function () {<br \/>   this.defaultPrevented = true;<br \/>   }<br \/>   };<\/p>\n<p>  sinon.ProgressEvent = function ProgressEvent(type, progressEventRaw, target) {<br \/>   this.initEvent(type, false, false, target);<br \/>   this.loaded = progressEventRaw.loaded || null;<br \/>   this.total = progressEventRaw.total || null;<br \/>   this.lengthComputable = !!progressEventRaw.total;<br \/>   };<\/p>\n<p>  sinon.ProgressEvent.prototype = new sinon.Event();<\/p>\n<p>  sinon.ProgressEvent.prototype.constructor = sinon.ProgressEvent;<\/p>\n<p>  sinon.CustomEvent = function CustomEvent(type, customData, target) {<br \/>   this.initEvent(type, false, false, target);<br \/>   this.detail = customData.detail || null;<br \/>   };<\/p>\n<p>  sinon.CustomEvent.prototype = new sinon.Event();<\/p>\n<p>  sinon.CustomEvent.prototype.constructor = sinon.CustomEvent;<\/p>\n<p>  sinon.EventTarget = {<br \/>   addEventListener: function addEventListener(event, listener) {<br \/>   this.eventListeners = this.eventListeners || {};<br \/>   this.eventListeners[event] = this.eventListeners[event] || [];<br \/>   push.call(this.eventListeners[event], listener);<br \/>   },<\/p>\n<p>  removeEventListener: function removeEventListener(event, listener) {<br \/>   var listeners = this.eventListeners &#038;&#038; this.eventListeners[event] || [];<\/p>\n<p>  for (var i = 0, l = listeners.length; i &lt; l; ++i) {<br \/>   if (listeners[i] === listener) {<br \/>   return listeners.splice(i, 1);<br \/>   }<br \/>   }<br \/>   },<\/p>\n<p>  dispatchEvent: function dispatchEvent(event) {<br \/>   var type = event.type;<br \/>   var listeners = this.eventListeners &#038;&#038; this.eventListeners[type] || [];<\/p>\n<p>  for (var i = 0; i &lt; listeners.length; i++) {<br \/>   if (typeof listeners[i] === \u00abfunction\u00bb) {<br \/>   listeners[i].call(this, event);<br \/>   } else {<br \/>   listeners[i].handleEvent(event);<br \/>   }<br \/>   }<\/p>\n<p>  return !!event.defaultPrevented;<br \/>   }<br \/>   };<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require) {<br \/>   var sinon = require(&quot;.\/core&quot;);<br \/>   makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require);<br \/>   } else {<br \/>   makeApi(sinon); \/\/ eslint-disable-line no-undef<br \/>   }<br \/>  }());<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *\/<br \/>  \/**<br \/>   * Logs errors<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2014 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   \/\/ cache a reference to setTimeout, so that our reference won&#8217;t be stubbed out<br \/>   \/\/ when using fake timers and errors will still get logged<br \/>   \/\/ <a href=\"https:\/\/github.com\/cjohansen\/Sinon.JS\/issues\/381\">github.com\/cjohansen\/Sinon.JS\/issues\/381<\/a><br \/>   var realSetTimeout = setTimeout;<\/p>\n<p>  function makeApi(sinon) {<\/p>\n<p>  function log() {}<\/p>\n<p>  function logError(label, err) {<br \/>   var msg = label + &quot; threw exception: &quot;;<\/p>\n<p>  function throwLoggedError() {<br \/>   err.message = msg + err.message;<br \/>   throw err;<br \/>   }<\/p>\n<p>  sinon.log(msg + &quot;[&quot; + err.name + &quot;] &quot; + err.message);<\/p>\n<p>  if (err.stack) {<br \/>   sinon.log(err.stack);<br \/>   }<\/p>\n<p>  if (logError.useImmediateExceptions) {<br \/>   throwLoggedError();<br \/>   } else {<br \/>   logError.setTimeout(throwLoggedError, 0);<br \/>   }<br \/>   }<\/p>\n<p>  \/\/ When set to true, any errors logged will be thrown immediately;<br \/>   \/\/ If set to false, the errors will be thrown in separate execution frame.<br \/>   logError.useImmediateExceptions = false;<\/p>\n<p>  \/\/ wrap realSetTimeout with something we can stub in tests<br \/>   logError.setTimeout = function (func, timeout) {<br \/>   realSetTimeout(func, timeout);<br \/>   };<\/p>\n<p>  var exports = {};<br \/>   exports.log = sinon.log = log;<br \/>   exports.logError = sinon.logError = logError;<\/p>\n<p>  return exports;<br \/>   }<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> event.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/log_error.js<br \/>   *\/<br \/>  \/**<br \/>   * Fake XDomainRequest object<br \/>   *\/<br \/>  if (typeof sinon === \u00abundefined\u00bb) {<br \/>   this.sinon = {};<br \/>  }<\/p>\n<p>  \/\/ wrapper for global<br \/>  (function (global) {<\/p>\n<p>   var xdr = { XDomainRequest: global.XDomainRequest };<br \/>   xdr.GlobalXDomainRequest = global.XDomainRequest;<br \/>   xdr.supportsXDR = typeof xdr.GlobalXDomainRequest !== \u00abundefined\u00bb;<br \/>   xdr.workingXDR = xdr.supportsXDR? xdr.GlobalXDomainRequest: false;<\/p>\n<p>  function makeApi(sinon) {<br \/>   sinon.xdr = xdr;<\/p>\n<p>  function FakeXDomainRequest() {<br \/>   this.readyState = FakeXDomainRequest.UNSENT;<br \/>   this.requestBody = null;<br \/>   this.requestHeaders = {};<br \/>   this.status = 0;<br \/>   this.timeout = null;<\/p>\n<p>  if (typeof FakeXDomainRequest.onCreate === \u00abfunction\u00bb) {<br \/>   FakeXDomainRequest.onCreate(this);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyState(x) {<br \/>   if (x.readyState !== FakeXDomainRequest.OPENED) {<br \/>   throw new Error(\u00abINVALID_STATE_ERR\u00bb);<br \/>   }<\/p>\n<p>  if (x.sendFlag) {<br \/>   throw new Error(\u00abINVALID_STATE_ERR\u00bb);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyRequestSent(x) {<br \/>   if (x.readyState === FakeXDomainRequest.UNSENT) {<br \/>   throw new Error(\u00abRequest not sent\u00bb);<br \/>   }<br \/>   if (x.readyState === FakeXDomainRequest.DONE) {<br \/>   throw new Error(\u00abRequest done\u00bb);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyResponseBodyType(body) {<br \/>   if (typeof body !== \u00abstring\u00bb) {<br \/>   var error = new Error(\u00abAttempted to respond to fake XDomainRequest with \u00bb +<br \/>   body + &quot;, which is not a string.&quot;);<br \/>   error.name = \u00abInvalidBodyException\u00bb;<br \/>   throw error;<br \/>   }<br \/>   }<\/p>\n<p>  sinon.extend(FakeXDomainRequest.prototype, sinon.EventTarget, {<br \/>   open: function open(method, url) {<br \/>   this.method = method;<br \/>   this.url = url;<\/p>\n<p>  this.responseText = null;<br \/>   this.sendFlag = false;<\/p>\n<p>  this.readyStateChange(FakeXDomainRequest.OPENED);<br \/>   },<\/p>\n<p>  readyStateChange: function readyStateChange(state) {<br \/>   this.readyState = state;<br \/>   var eventName = &quot;&quot;;<br \/>   switch (this.readyState) {<br \/>   case FakeXDomainRequest.UNSENT:<br \/>   break;<br \/>   case FakeXDomainRequest.OPENED:<br \/>   break;<br \/>   case FakeXDomainRequest.LOADING:<br \/>   if (this.sendFlag) {<br \/>   \/\/raise the progress event<br \/>   eventName = \u00abonprogress\u00bb;<br \/>   }<br \/>   break;<br \/>   case FakeXDomainRequest.DONE:<br \/>   if (this.isTimeout) {<br \/>   eventName = \u00abontimeout\u00bb;<br \/>   } else if (this.errorFlag || (this.status &lt; 200 || this.status &gt; 299)) {<br \/>   eventName = \u00abonerror\u00bb;<br \/>   } else {<br \/>   eventName = \u00abonload\u00bb;<br \/>   }<br \/>   break;<br \/>   }<\/p>\n<p>  \/\/ raising event (if defined)<br \/>   if (eventName) {<br \/>   if (typeof this[eventName] === \u00abfunction\u00bb) {<br \/>   try {<br \/>   this[eventName]();<br \/>   } catch (e) {<br \/>   sinon.logError(\u00abFake XHR \u00bb + eventName + &quot; handler&quot;, e);<br \/>   }<br \/>   }<br \/>   }<br \/>   },<\/p>\n<p>  send: function send(data) {<br \/>   verifyState(this);<\/p>\n<p>  if (!\/^(get|head)$\/i.test(this.method)) {<br \/>   this.requestBody = data;<br \/>   }<br \/>   this.requestHeaders[\u00abContent-Type\u00bb] = \u00abtext\/plain;charset=utf-8\u00bb;<\/p>\n<p>  this.errorFlag = false;<br \/>   this.sendFlag = true;<br \/>   this.readyStateChange(FakeXDomainRequest.OPENED);<\/p>\n<p>  if (typeof this.onSend === \u00abfunction\u00bb) {<br \/>   this.onSend(this);<br \/>   }<br \/>   },<\/p>\n<p>  abort: function abort() {<br \/>   this.aborted = true;<br \/>   this.responseText = null;<br \/>   this.errorFlag = true;<\/p>\n<p>  if (this.readyState &gt; sinon.FakeXDomainRequest.UNSENT &#038;&#038; this.sendFlag) {<br \/>   this.readyStateChange(sinon.FakeXDomainRequest.DONE);<br \/>   this.sendFlag = false;<br \/>   }<br \/>   },<\/p>\n<p>  setResponseBody: function setResponseBody(body) {<br \/>   verifyRequestSent(this);<br \/>   verifyResponseBodyType(body);<\/p>\n<p>  var chunkSize = this.chunkSize || 10;<br \/>   var index = 0;<br \/>   this.responseText = &quot;&quot;;<\/p>\n<p>  do {<br \/>   this.readyStateChange(FakeXDomainRequest.LOADING);<br \/>   this.responseText += body.substring(index, index + chunkSize);<br \/>   index += chunkSize;<br \/>   } while (index &lt; body.length);<\/p>\n<p>  this.readyStateChange(FakeXDomainRequest.DONE);<br \/>   },<\/p>\n<p>  respond: function respond(status, contentType, body) {<br \/>   \/\/ content-type ignored, since XDomainRequest does not carry this<br \/>   \/\/ we keep the same syntax for respond(&#8230;) as for FakeXMLHttpRequest to ease<br \/>   \/\/ test integration across browsers<br \/>   this.status = typeof status === \u00abnumber\u00bb? status: 200;<br \/>   this.setResponseBody(body || &quot;&quot;);<br \/>   },<\/p>\n<p>  simulatetimeout: function simulatetimeout() {<br \/>   this.status = 0;<br \/>   this.isTimeout = true;<br \/>   \/\/ Access to this should actually throw an error<br \/>   this.responseText = undefined;<br \/>   this.readyStateChange(FakeXDomainRequest.DONE);<br \/>   }<br \/>   });<\/p>\n<p>  sinon.extend(FakeXDomainRequest, {<br \/>   UNSENT: 0,<br \/>   OPENED: 1,<br \/>   LOADING: 3,<br \/>   DONE: 4<br \/>   });<\/p>\n<p>  sinon.useFakeXDomainRequest = function useFakeXDomainRequest() {<br \/>   sinon.FakeXDomainRequest.restore = function restore(keepOnCreate) {<br \/>   if (xdr.supportsXDR) {<br \/>   global.XDomainRequest = xdr.GlobalXDomainRequest;<br \/>   }<\/p>\n<p>  delete sinon.FakeXDomainRequest.restore;<\/p>\n<p>  if (keepOnCreate !== true) {<br \/>   delete sinon.FakeXDomainRequest.onCreate;<br \/>   }<br \/>   };<br \/>   if (xdr.supportsXDR) {<br \/>   global.XDomainRequest = sinon.FakeXDomainRequest;<br \/>   }<br \/>   return sinon.FakeXDomainRequest;<br \/>   };<\/p>\n<p>  sinon.FakeXDomainRequest = FakeXDomainRequest;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/core&quot;);<br \/>   require(&quot;..\/extend&quot;);<br \/>   require(&quot;.\/event&quot;);<br \/>   require(&quot;..\/log_error&quot;);<br \/>   makeApi(sinon);<br \/>   module.exports = sinon;<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   } else {<br \/>   makeApi(sinon); \/\/ eslint-disable-line no-undef<br \/>   }<br \/>  })(typeof global !== \u00abundefined\u00bb? global: self);<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> event.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/log_error.js<br \/>   *\/<br \/>  \/**<br \/>   * Fake XMLHttpRequest object<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal, global) {<\/p>\n<p>   function getWorkingXHR(globalScope) {<br \/>   var supportsXHR = typeof globalScope.XMLHttpRequest !== \u00abundefined\u00bb;<br \/>   if (supportsXHR) {<br \/>   return globalScope.XMLHttpRequest;<br \/>   }<\/p>\n<p>  var supportsActiveX = typeof globalScope.ActiveXObject !== \u00abundefined\u00bb;<br \/>   if (supportsActiveX) {<br \/>   return function () {<br \/>   return new globalScope.ActiveXObject(\u00abMSXML2.XMLHTTP.3.0\u00bb);<br \/>   };<br \/>   }<\/p>\n<p>  return false;<br \/>   }<\/p>\n<p>  var supportsProgress = typeof ProgressEvent !== \u00abundefined\u00bb;<br \/>   var supportsCustomEvent = typeof CustomEvent !== \u00abundefined\u00bb;<br \/>   var supportsFormData = typeof FormData !== \u00abundefined\u00bb;<br \/>   var supportsArrayBuffer = typeof ArrayBuffer !== \u00abundefined\u00bb;<br \/>   var supportsBlob = typeof Blob === \u00abfunction\u00bb;<br \/>   var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest };<br \/>   sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest;<br \/>   sinonXhr.GlobalActiveXObject = global.ActiveXObject;<br \/>   sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== \u00abundefined\u00bb;<br \/>   sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== \u00abundefined\u00bb;<br \/>   sinonXhr.workingXHR = getWorkingXHR(global);<br \/>   sinonXhr.supportsCORS = sinonXhr.supportsXHR &#038;&#038; \u00abwithCredentials\u00bb in (new sinonXhr.GlobalXMLHttpRequest());<\/p>\n<p>  var unsafeHeaders = {<br \/>   \u00abAccept-Charset\u00bb: true,<br \/>   \u00abAccept-Encoding\u00bb: true,<br \/>   Connection: true,<br \/>   \u00abContent-Length\u00bb: true,<br \/>   Cookie: true,<br \/>   Cookie2: true,<br \/>   \u00abContent-Transfer-Encoding\u00bb: true,<br \/>   Date: true,<br \/>   Expect: true,<br \/>   Host: true,<br \/>   \u00abKeep-Alive\u00bb: true,<br \/>   Referer: true,<br \/>   TE: true,<br \/>   Trailer: true,<br \/>   \u00abTransfer-Encoding\u00bb: true,<br \/>   Upgrade: true,<br \/>   \u00abUser-Agent\u00bb: true,<br \/>   Via: true<br \/>   };<\/p>\n<p>  \/\/ An upload object is created for each<br \/>   \/\/ FakeXMLHttpRequest and allows upload<br \/>   \/\/ events to be simulated using uploadProgress<br \/>   \/\/ and uploadError.<br \/>   function UploadProgress() {<br \/>   this.eventListeners = {<br \/>   progress: [],<br \/>   load: [],<br \/>   abort: [],<br \/>   error: []<br \/>   };<br \/>   }<\/p>\n<p>  UploadProgress.prototype.addEventListener = function addEventListener(event, listener) {<br \/>   this.eventListeners[event].push(listener);<br \/>   };<\/p>\n<p>  UploadProgress.prototype.removeEventListener = function removeEventListener(event, listener) {<br \/>   var listeners = this.eventListeners[event] || [];<\/p>\n<p>  for (var i = 0, l = listeners.length; i &lt; l; ++i) {<br \/>   if (listeners[i] === listener) {<br \/>   return listeners.splice(i, 1);<br \/>   }<br \/>   }<br \/>   };<\/p>\n<p>  UploadProgress.prototype.dispatchEvent = function dispatchEvent(event) {<br \/>   var listeners = this.eventListeners[event.type] || [];<\/p>\n<p>  for (var i = 0, listener; (listener = listeners[i]) != null; i++) {<br \/>   listener(event);<br \/>   }<br \/>   };<\/p>\n<p>  \/\/ Note that for FakeXMLHttpRequest to work pre ES5<br \/>   \/\/ we lose some of the alignment with the spec.<br \/>   \/\/ To ensure as close a match as possible,<br \/>   \/\/ set responseType before calling open, send or respond;<br \/>   function FakeXMLHttpRequest() {<br \/>   this.readyState = FakeXMLHttpRequest.UNSENT;<br \/>   this.requestHeaders = {};<br \/>   this.requestBody = null;<br \/>   this.status = 0;<br \/>   this.statusText = &quot;&quot;;<br \/>   this.upload = new UploadProgress();<br \/>   this.responseType = &quot;&quot;;<br \/>   this.response = &quot;&quot;;<br \/>   if (sinonXhr.supportsCORS) {<br \/>   this.withCredentials = false;<br \/>   }<\/p>\n<p>  var xhr = this;<br \/>   var events = [\u00abloadstart\u00bb, \u00abload\u00bb, \u00ababort\u00bb, \u00abloadend\u00bb];<\/p>\n<p>  function addEventListener(eventName) {<br \/>   xhr.addEventListener(eventName, function (event) {<br \/>   var listener = xhr[\u00abon\u00bb + eventName];<\/p>\n<p>  if (listener &#038;&#038; typeof listener === \u00abfunction\u00bb) {<br \/>   listener.call(this, event);<br \/>   }<br \/>   });<br \/>   }<\/p>\n<p>  for (var i = events.length \u2014 1; i &gt;= 0; i&#8212;) {<br \/>   addEventListener(events[i]);<br \/>   }<\/p>\n<p>  if (typeof FakeXMLHttpRequest.onCreate === \u00abfunction\u00bb) {<br \/>   FakeXMLHttpRequest.onCreate(this);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyState(xhr) {<br \/>   if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {<br \/>   throw new Error(\u00abINVALID_STATE_ERR\u00bb);<br \/>   }<\/p>\n<p>  if (xhr.sendFlag) {<br \/>   throw new Error(\u00abINVALID_STATE_ERR\u00bb);<br \/>   }<br \/>   }<\/p>\n<p>  function getHeader(headers, header) {<br \/>   header = header.toLowerCase();<\/p>\n<p>  for (var h in headers) {<br \/>   if (h.toLowerCase() === header) {<br \/>   return h;<br \/>   }<br \/>   }<\/p>\n<p>  return null;<br \/>   }<\/p>\n<p>  \/\/ filtering to enable a white-list version of Sinon FakeXhr,<br \/>   \/\/ where whitelisted requests are passed through to real XHR<br \/>   function each(collection, callback) {<br \/>   if (!collection) {<br \/>   return;<br \/>   }<\/p>\n<p>  for (var i = 0, l = collection.length; i &lt; l; i += 1) {<br \/>   callback(collection[i]);<br \/>   }<br \/>   }<br \/>   function some(collection, callback) {<br \/>   for (var index = 0; index &lt; collection.length; index++) {<br \/>   if (callback(collection[index]) === true) {<br \/>   return true;<br \/>   }<br \/>   }<br \/>   return false;<br \/>   }<br \/>   \/\/ largest arity in XHR is 5 \u2014 XHR#open<br \/>   var apply = function (obj, method, args) {<br \/>   switch (args.length) {<br \/>   case 0: return obj[method]();<br \/>   case 1: return obj[method](args[0]);<br \/>   case 2: return obj[method](args[0], args[1]);<br \/>   case 3: return obj[method](args[0], args[1], args[2]);<br \/>   case 4: return obj[method](args[0], args[1], args[2], args[3]);<br \/>   case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]);<br \/>   }<br \/>   };<\/p>\n<p>  FakeXMLHttpRequest.filters = [];<br \/>   FakeXMLHttpRequest.addFilter = function addFilter(fn) {<br \/>   this.filters.push(fn);<br \/>   };<br \/>   var IE6Re = \/MSIE 6\/;<br \/>   FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {<br \/>   var xhr = new sinonXhr.workingXHR(); \/\/ eslint-disable-line new-cap<\/p>\n<p>  each([<br \/>   \u00abopen\u00bb,<br \/>   \u00absetRequestHeader\u00bb,<br \/>   \u00absend\u00bb,<br \/>   \u00ababort\u00bb,<br \/>   \u00abgetResponseHeader\u00bb,<br \/>   \u00abgetAllResponseHeaders\u00bb,<br \/>   \u00abaddEventListener\u00bb,<br \/>   \u00aboverrideMimeType\u00bb,<br \/>   \u00abremoveEventListener\u00bb<br \/>   ], function (method) {<br \/>   fakeXhr[method] = function () {<br \/>   return apply(xhr, method, arguments);<br \/>   };<br \/>   });<\/p>\n<p>  var copyAttrs = function (args) {<br \/>   each(args, function (attr) {<br \/>   try {<br \/>   fakeXhr[attr] = xhr[attr];<br \/>   } catch (e) {<br \/>   if (!IE6Re.test(navigator.userAgent)) {<br \/>   throw e;<br \/>   }<br \/>   }<br \/>   });<br \/>   };<\/p>\n<p>  var stateChange = function stateChange() {<br \/>   fakeXhr.readyState = xhr.readyState;<br \/>   if (xhr.readyState &gt;= FakeXMLHttpRequest.HEADERS_RECEIVED) {<br \/>   copyAttrs([\u00abstatus\u00bb, \u00abstatusText\u00bb]);<br \/>   }<br \/>   if (xhr.readyState &gt;= FakeXMLHttpRequest.LOADING) {<br \/>   copyAttrs([\u00abresponseText\u00bb, \u00abresponse\u00bb]);<br \/>   }<br \/>   if (xhr.readyState === FakeXMLHttpRequest.DONE) {<br \/>   copyAttrs([\u00abresponseXML\u00bb]);<br \/>   }<br \/>   if (fakeXhr.onreadystatechange) {<br \/>   fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr });<br \/>   }<br \/>   };<\/p>\n<p>  if (xhr.addEventListener) {<br \/>   for (var event in fakeXhr.eventListeners) {<br \/>   if (fakeXhr.eventListeners.hasOwnProperty(event)) {<\/p>\n<p>  \/*eslint-disable no-loop-func*\/<br \/>   each(fakeXhr.eventListeners[event], function (handler) {<br \/>   xhr.addEventListener(event, handler);<br \/>   });<br \/>   \/*eslint-enable no-loop-func*\/<br \/>   }<br \/>   }<br \/>   xhr.addEventListener(\u00abreadystatechange\u00bb, stateChange);<br \/>   } else {<br \/>   xhr.onreadystatechange = stateChange;<br \/>   }<br \/>   apply(xhr, \u00abopen\u00bb, xhrArgs);<br \/>   };<br \/>   FakeXMLHttpRequest.useFilters = false;<\/p>\n<p>  function verifyRequestOpened(xhr) {<br \/>   if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {<br \/>   throw new Error(\u00abINVALID_STATE_ERR \u2014 \u00bb + xhr.readyState);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyRequestSent(xhr) {<br \/>   if (xhr.readyState === FakeXMLHttpRequest.DONE) {<br \/>   throw new Error(\u00abRequest done\u00bb);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyHeadersReceived(xhr) {<br \/>   if (xhr.async &#038;&#038; xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) {<br \/>   throw new Error(\u00abNo headers received\u00bb);<br \/>   }<br \/>   }<\/p>\n<p>  function verifyResponseBodyType(body) {<br \/>   if (typeof body !== \u00abstring\u00bb) {<br \/>   var error = new Error(\u00abAttempted to respond to fake XMLHttpRequest with \u00bb +<br \/>   body + &quot;, which is not a string.&quot;);<br \/>   error.name = \u00abInvalidBodyException\u00bb;<br \/>   throw error;<br \/>   }<br \/>   }<\/p>\n<p>  function convertToArrayBuffer(body) {<br \/>   var buffer = new ArrayBuffer(body.length);<br \/>   var view = new Uint8Array(buffer);<br \/>   for (var i = 0; i &lt; body.length; i++) {<br \/>   var charCode = body.charCodeAt(i);<br \/>   if (charCode &gt;= 256) {<br \/>   throw new TypeError(\u00abarraybuffer or blob responseTypes require binary string, \u00bb +<br \/>   \u00abinvalid character \u00bb + body[i] + &quot; found.&quot;);<br \/>   }<br \/>   view[i] = charCode;<br \/>   }<br \/>   return buffer;<br \/>   }<\/p>\n<p>  function isXmlContentType(contentType) {<br \/>   return !contentType || \/(text\\\/xml)|(application\\\/xml)|(\\+xml)\/.test(contentType);<br \/>   }<\/p>\n<p>  function convertResponseBody(responseType, contentType, body) {<br \/>   if (responseType === &quot;&quot; || responseType === \u00abtext\u00bb) {<br \/>   return body;<br \/>   } else if (supportsArrayBuffer &#038;&#038; responseType === \u00abarraybuffer\u00bb) {<br \/>   return convertToArrayBuffer(body);<br \/>   } else if (responseType === \u00abjson\u00bb) {<br \/>   try {<br \/>   return JSON.parse(body);<br \/>   } catch (e) {<br \/>   \/\/ Return parsing failure as null<br \/>   return null;<br \/>   }<br \/>   } else if (supportsBlob &#038;&#038; responseType === \u00abblob\u00bb) {<br \/>   var blobOptions = {};<br \/>   if (contentType) {<br \/>   blobOptions.type = contentType;<br \/>   }<br \/>   return new Blob([convertToArrayBuffer(body)], blobOptions);<br \/>   } else if (responseType === \u00abdocument\u00bb) {<br \/>   if (isXmlContentType(contentType)) {<br \/>   return FakeXMLHttpRequest.parseXML(body);<br \/>   }<br \/>   return null;<br \/>   }<br \/>   throw new Error(\u00abInvalid responseType \u00bb + responseType);<br \/>   }<\/p>\n<p>  function clearResponse(xhr) {<br \/>   if (xhr.responseType === &quot;&quot; || xhr.responseType === \u00abtext\u00bb) {<br \/>   xhr.response = xhr.responseText = &quot;&quot;;<br \/>   } else {<br \/>   xhr.response = xhr.responseText = null;<br \/>   }<br \/>   xhr.responseXML = null;<br \/>   }<\/p>\n<p>  FakeXMLHttpRequest.parseXML = function parseXML(text) {<br \/>   \/\/ Treat empty string as parsing failure<br \/>   if (text !== &quot;&quot;) {<br \/>   try {<br \/>   if (typeof DOMParser !== \u00abundefined\u00bb) {<br \/>   var parser = new DOMParser();<br \/>   return parser.parseFromString(text, \u00abtext\/xml\u00bb);<br \/>   }<br \/>   var xmlDoc = new window.ActiveXObject(\u00abMicrosoft.XMLDOM\u00bb);<br \/>   xmlDoc.async = \u00abfalse\u00bb;<br \/>   xmlDoc.loadXML(text);<br \/>   return xmlDoc;<br \/>   } catch (e) {<br \/>   \/\/ Unable to parse XML \u2014 no biggie<br \/>   }<br \/>   }<\/p>\n<p>  return null;<br \/>   };<\/p>\n<p>  FakeXMLHttpRequest.statusCodes = {<br \/>   100: \u00abContinue\u00bb,<br \/>   101: \u00abSwitching Protocols\u00bb,<br \/>   200: \u00abOK\u00bb,<br \/>   201: \u00abCreated\u00bb,<br \/>   202: \u00abAccepted\u00bb,<br \/>   203: \u00abNon-Authoritative Information\u00bb,<br \/>   204: \u00abNo Content\u00bb,<br \/>   205: \u00abReset Content\u00bb,<br \/>   206: \u00abPartial Content\u00bb,<br \/>   207: \u00abMulti-Status\u00bb,<br \/>   300: \u00abMultiple Choice\u00bb,<br \/>   301: \u00abMoved Permanently\u00bb,<br \/>   302: \u00abFound\u00bb,<br \/>   303: \u00abSee Other\u00bb,<br \/>   304: \u00abNot Modified\u00bb,<br \/>   305: \u00abUse Proxy\u00bb,<br \/>   307: \u00abTemporary Redirect\u00bb,<br \/>   400: \u00abBad Request\u00bb,<br \/>   401: \u00abUnauthorized\u00bb,<br \/>   402: \u00abPayment Required\u00bb,<br \/>   403: \u00abForbidden\u00bb,<br \/>   404: \u00abNot Found\u00bb,<br \/>   405: \u00abMethod Not Allowed\u00bb,<br \/>   406: \u00abNot Acceptable\u00bb,<br \/>   407: \u00abProxy Authentication Required\u00bb,<br \/>   408: \u00abRequest Timeout\u00bb,<br \/>   409: \u00abConflict\u00bb,<br \/>   410: \u00abGone\u00bb,<br \/>   411: \u00abLength Required\u00bb,<br \/>   412: \u00abPrecondition Failed\u00bb,<br \/>   413: \u00abRequest Entity Too Large\u00bb,<br \/>   414: \u00abRequest-URI Too Long\u00bb,<br \/>   415: \u00abUnsupported Media Type\u00bb,<br \/>   416: \u00abRequested Range Not Satisfiable\u00bb,<br \/>   417: \u00abExpectation Failed\u00bb,<br \/>   422: \u00abUnprocessable Entity\u00bb,<br \/>   500: \u00abInternal Server Error\u00bb,<br \/>   501: \u00abNot Implemented\u00bb,<br \/>   502: \u00abBad Gateway\u00bb,<br \/>   503: \u00abService Unavailable\u00bb,<br \/>   504: \u00abGateway Timeout\u00bb,<br \/>   505: \u00abHTTP Version Not Supported\u00bb<br \/>   };<\/p>\n<p>  function makeApi(sinon) {<br \/>   sinon.xhr = sinonXhr;<\/p>\n<p>  sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {<br \/>   async: true,<\/p>\n<p>  open: function open(method, url, async, username, password) {<br \/>   this.method = method;<br \/>   this.url = url;<br \/>   this.async = typeof async === \u00abboolean\u00bb? async: true;<br \/>   this.username = username;<br \/>   this.password = password;<br \/>   clearResponse(this);<br \/>   this.requestHeaders = {};<br \/>   this.sendFlag = false;<\/p>\n<p>  if (FakeXMLHttpRequest.useFilters === true) {<br \/>   var xhrArgs = arguments;<br \/>   var defake = some(FakeXMLHttpRequest.filters, function (filter) {<br \/>   return filter.apply(this, xhrArgs);<br \/>   });<br \/>   if (defake) {<br \/>   return FakeXMLHttpRequest.defake(this, arguments);<br \/>   }<br \/>   }<br \/>   this.readyStateChange(FakeXMLHttpRequest.OPENED);<br \/>   },<\/p>\n<p>  readyStateChange: function readyStateChange(state) {<br \/>   this.readyState = state;<\/p>\n<p>  var readyStateChangeEvent = new sinon.Event(\u00abreadystatechange\u00bb, false, false, this);<\/p>\n<p>  if (typeof this.onreadystatechange === \u00abfunction\u00bb) {<br \/>   try {<br \/>   this.onreadystatechange(readyStateChangeEvent);<br \/>   } catch (e) {<br \/>   sinon.logError(\u00abFake XHR onreadystatechange handler\u00bb, e);<br \/>   }<br \/>   }<\/p>\n<p>  switch (this.readyState) {<br \/>   case FakeXMLHttpRequest.DONE:<br \/>   if (supportsProgress) {<br \/>   this.upload.dispatchEvent(new sinon.ProgressEvent(\u00abprogress\u00bb, {loaded: 100, total: 100}));<br \/>   this.dispatchEvent(new sinon.ProgressEvent(\u00abprogress\u00bb, {loaded: 100, total: 100}));<br \/>   }<br \/>   this.upload.dispatchEvent(new sinon.Event(\u00abload\u00bb, false, false, this));<br \/>   this.dispatchEvent(new sinon.Event(\u00abload\u00bb, false, false, this));<br \/>   this.dispatchEvent(new sinon.Event(\u00abloadend\u00bb, false, false, this));<br \/>   break;<br \/>   }<\/p>\n<p>  this.dispatchEvent(readyStateChangeEvent);<br \/>   },<\/p>\n<p>  setRequestHeader: function setRequestHeader(header, value) {<br \/>   verifyState(this);<\/p>\n<p>  if (unsafeHeaders[header] || \/^(Sec-|Proxy-)\/.test(header)) {<br \/>   throw new Error(\u00abRefused to set unsafe header \\\u00bb&quot; + header + &quot;\\&quot;&quot;);<br \/>   }<\/p>\n<p>  if (this.requestHeaders[header]) {<br \/>   this.requestHeaders[header] += &quot;,&quot; + value;<br \/>   } else {<br \/>   this.requestHeaders[header] = value;<br \/>   }<br \/>   },<\/p>\n<p>  \/\/ Helps testing<br \/>   setResponseHeaders: function setResponseHeaders(headers) {<br \/>   verifyRequestOpened(this);<br \/>   this.responseHeaders = {};<\/p>\n<p>  for (var header in headers) {<br \/>   if (headers.hasOwnProperty(header)) {<br \/>   this.responseHeaders[header] = headers[header];<br \/>   }<br \/>   }<\/p>\n<p>  if (this.async) {<br \/>   this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);<br \/>   } else {<br \/>   this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;<br \/>   }<br \/>   },<\/p>\n<p>  \/\/ Currently treats ALL data as a DOMString (i.e. no Document)<br \/>   send: function send(data) {<br \/>   verifyState(this);<\/p>\n<p>  if (!\/^(get|head)$\/i.test(this.method)) {<br \/>   var contentType = getHeader(this.requestHeaders, \u00abContent-Type\u00bb);<br \/>   if (this.requestHeaders[contentType]) {<br \/>   var value = this.requestHeaders[contentType].split(&quot;;&quot;);<br \/>   this.requestHeaders[contentType] = value[0] + &quot;;charset=utf-8&quot;;<br \/>   } else if (supportsFormData &#038;&#038; !(data instanceof FormData)) {<br \/>   this.requestHeaders[\u00abContent-Type\u00bb] = \u00abtext\/plain;charset=utf-8\u00bb;<br \/>   }<\/p>\n<p>  this.requestBody = data;<br \/>   }<\/p>\n<p>  this.errorFlag = false;<br \/>   this.sendFlag = this.async;<br \/>   clearResponse(this);<br \/>   this.readyStateChange(FakeXMLHttpRequest.OPENED);<\/p>\n<p>  if (typeof this.onSend === \u00abfunction\u00bb) {<br \/>   this.onSend(this);<br \/>   }<\/p>\n<p>  this.dispatchEvent(new sinon.Event(\u00abloadstart\u00bb, false, false, this));<br \/>   },<\/p>\n<p>  abort: function abort() {<br \/>   this.aborted = true;<br \/>   clearResponse(this);<br \/>   this.errorFlag = true;<br \/>   this.requestHeaders = {};<br \/>   this.responseHeaders = {};<\/p>\n<p>  if (this.readyState &gt; FakeXMLHttpRequest.UNSENT &#038;&#038; this.sendFlag) {<br \/>   this.readyStateChange(FakeXMLHttpRequest.DONE);<br \/>   this.sendFlag = false;<br \/>   }<\/p>\n<p>  this.readyState = FakeXMLHttpRequest.UNSENT;<\/p>\n<p>  this.dispatchEvent(new sinon.Event(\u00ababort\u00bb, false, false, this));<\/p>\n<p>  this.upload.dispatchEvent(new sinon.Event(\u00ababort\u00bb, false, false, this));<\/p>\n<p>  if (typeof this.onerror === \u00abfunction\u00bb) {<br \/>   this.onerror();<br \/>   }<br \/>   },<\/p>\n<p>  getResponseHeader: function getResponseHeader(header) {<br \/>   if (this.readyState &lt; FakeXMLHttpRequest.HEADERS_RECEIVED) {<br \/>   return null;<br \/>   }<\/p>\n<p>  if (\/^Set-Cookie2?$\/i.test(header)) {<br \/>   return null;<br \/>   }<\/p>\n<p>  header = getHeader(this.responseHeaders, header);<\/p>\n<p>  return this.responseHeaders[header] || null;<br \/>   },<\/p>\n<p>  getAllResponseHeaders: function getAllResponseHeaders() {<br \/>   if (this.readyState &lt; FakeXMLHttpRequest.HEADERS_RECEIVED) {<br \/>   return &quot;&quot;;<br \/>   }<\/p>\n<p>  var headers = &quot;&quot;;<\/p>\n<p>  for (var header in this.responseHeaders) {<br \/>   if (this.responseHeaders.hasOwnProperty(header) &#038;&#038;<br \/>   !\/^Set-Cookie2?$\/i.test(header)) {<br \/>   headers += header + &quot;: &quot; + this.responseHeaders[header] + &quot;\\r\\n&quot;;<br \/>   }<br \/>   }<\/p>\n<p>  return headers;<br \/>   },<\/p>\n<p>  setResponseBody: function setResponseBody(body) {<br \/>   verifyRequestSent(this);<br \/>   verifyHeadersReceived(this);<br \/>   verifyResponseBodyType(body);<br \/>   var contentType = this.getResponseHeader(\u00abContent-Type\u00bb);<\/p>\n<p>  var isTextResponse = this.responseType === &quot;&quot; || this.responseType === \u00abtext\u00bb;<br \/>   clearResponse(this);<br \/>   if (this.async) {<br \/>   var chunkSize = this.chunkSize || 10;<br \/>   var index = 0;<\/p>\n<p>  do {<br \/>   this.readyStateChange(FakeXMLHttpRequest.LOADING);<\/p>\n<p>  if (isTextResponse) {<br \/>   this.responseText = this.response += body.substring(index, index + chunkSize);<br \/>   }<br \/>   index += chunkSize;<br \/>   } while (index &lt; body.length);<br \/>   }<\/p>\n<p>  this.response = convertResponseBody(this.responseType, contentType, body);<br \/>   if (isTextResponse) {<br \/>   this.responseText = this.response;<br \/>   }<\/p>\n<p>  if (this.responseType === \u00abdocument\u00bb) {<br \/>   this.responseXML = this.response;<br \/>   } else if (this.responseType === &quot;&quot; &#038;&#038; isXmlContentType(contentType)) {<br \/>   this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);<br \/>   }<br \/>   this.readyStateChange(FakeXMLHttpRequest.DONE);<br \/>   },<\/p>\n<p>  respond: function respond(status, headers, body) {<br \/>   this.status = typeof status === \u00abnumber\u00bb? status: 200;<br \/>   this.statusText = FakeXMLHttpRequest.statusCodes[this.status];<br \/>   this.setResponseHeaders(headers || {});<br \/>   this.setResponseBody(body || &quot;&quot;);<br \/>   },<\/p>\n<p>  uploadProgress: function uploadProgress(progressEventRaw) {<br \/>   if (supportsProgress) {<br \/>   this.upload.dispatchEvent(new sinon.ProgressEvent(\u00abprogress\u00bb, progressEventRaw));<br \/>   }<br \/>   },<\/p>\n<p>  downloadProgress: function downloadProgress(progressEventRaw) {<br \/>   if (supportsProgress) {<br \/>   this.dispatchEvent(new sinon.ProgressEvent(\u00abprogress\u00bb, progressEventRaw));<br \/>   }<br \/>   },<\/p>\n<p>  uploadError: function uploadError(error) {<br \/>   if (supportsCustomEvent) {<br \/>   this.upload.dispatchEvent(new sinon.CustomEvent(\u00aberror\u00bb, {detail: error}));<br \/>   }<br \/>   }<br \/>   });<\/p>\n<p>  sinon.extend(FakeXMLHttpRequest, {<br \/>   UNSENT: 0,<br \/>   OPENED: 1,<br \/>   HEADERS_RECEIVED: 2,<br \/>   LOADING: 3,<br \/>   DONE: 4<br \/>   });<\/p>\n<p>  sinon.useFakeXMLHttpRequest = function () {<br \/>   FakeXMLHttpRequest.restore = function restore(keepOnCreate) {<br \/>   if (sinonXhr.supportsXHR) {<br \/>   global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest;<br \/>   }<\/p>\n<p>  if (sinonXhr.supportsActiveX) {<br \/>   global.ActiveXObject = sinonXhr.GlobalActiveXObject;<br \/>   }<\/p>\n<p>  delete FakeXMLHttpRequest.restore;<\/p>\n<p>  if (keepOnCreate !== true) {<br \/>   delete FakeXMLHttpRequest.onCreate;<br \/>   }<br \/>   };<br \/>   if (sinonXhr.supportsXHR) {<br \/>   global.XMLHttpRequest = FakeXMLHttpRequest;<br \/>   }<\/p>\n<p>  if (sinonXhr.supportsActiveX) {<br \/>   global.ActiveXObject = function ActiveXObject(objId) {<br \/>   if (objId === \u00abMicrosoft.XMLHTTP\u00bb || \/^Msxml2\\.XMLHTTP\/i.test(objId)) {<\/p>\n<p>  return new FakeXMLHttpRequest();<br \/>   }<\/p>\n<p>  return new sinonXhr.GlobalActiveXObject(objId);<br \/>   };<br \/>   }<\/p>\n<p>  return FakeXMLHttpRequest;<br \/>   };<\/p>\n<p>  sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/core&quot;);<br \/>   require(&quot;..\/extend&quot;);<br \/>   require(&quot;.\/event&quot;);<br \/>   require(&quot;..\/log_error&quot;);<br \/>   makeApi(sinon);<br \/>   module.exports = sinon;<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon, \/\/ eslint-disable-line no-undef<br \/>   typeof global !== \u00abundefined\u00bb? global: self<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> fake_xdomain_request.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> fake_xml_http_request.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/format.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> ..\/log_error.js<br \/>   *\/<br \/>  \/**<br \/>   * The Sinon \u00abserver\u00bb mimics a web server that receives requests from<br \/>   * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,<br \/>   * both synchronously and asynchronously. To respond synchronuously, canned<br \/>   * answers have to be provided upfront.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function () {<\/p>\n<p>   var push = [].push;<\/p>\n<p>  function responseArray(handler) {<br \/>   var response = handler;<\/p>\n<p>  if (Object.prototype.toString.call(handler) !== &quot;[object Array]&quot;) {<br \/>   response = [200, {}, handler];<br \/>   }<\/p>\n<p>  if (typeof response[2] !== \u00abstring\u00bb) {<br \/>   throw new TypeError(\u00abFake server response body should be string, but was \u00bb +<br \/>   typeof response[2]);<br \/>   }<\/p>\n<p>  return response;<br \/>   }<\/p>\n<p>  var wloc = typeof window !== \u00abundefined\u00bb? window.location: {};<br \/>   var rCurrLoc = new RegExp(&quot;^&quot; + wloc.protocol + &quot;\/\/&quot; + wloc.host);<\/p>\n<p>  function matchOne(response, reqMethod, reqUrl) {<br \/>   var rmeth = response.method;<br \/>   var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase();<br \/>   var url = response.url;<br \/>   var matchUrl = !url || url === reqUrl || (typeof url.test === \u00abfunction\u00bb &#038;&#038; url.test(reqUrl));<\/p>\n<p>  return matchMethod &#038;&#038; matchUrl;<br \/>   }<\/p>\n<p>  function match(response, request) {<br \/>   var requestUrl = request.url;<\/p>\n<p>  if (!\/^https?:\\\/\\\/\/.test(requestUrl) || rCurrLoc.test(requestUrl)) {<br \/>   requestUrl = requestUrl.replace(rCurrLoc, &quot;&quot;);<br \/>   }<\/p>\n<p>  if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {<br \/>   if (typeof response.response === \u00abfunction\u00bb) {<br \/>   var ru = response.url;<br \/>   var args = [request].concat(ru &#038;&#038; typeof ru.exec === \u00abfunction\u00bb? ru.exec(requestUrl).slice(1): []);<br \/>   return response.response.apply(response, args);<br \/>   }<\/p>\n<p>  return true;<br \/>   }<\/p>\n<p>  return false;<br \/>   }<\/p>\n<p>  function makeApi(sinon) {<br \/>   sinon.fakeServer = {<br \/>   create: function (config) {<br \/>   var server = sinon.create(this);<br \/>   server.configure(config);<br \/>   if (!sinon.xhr.supportsCORS) {<br \/>   this.xhr = sinon.useFakeXDomainRequest();<br \/>   } else {<br \/>   this.xhr = sinon.useFakeXMLHttpRequest();<br \/>   }<br \/>   server.requests = [];<\/p>\n<p>  this.xhr.onCreate = function (xhrObj) {<br \/>   server.addRequest(xhrObj);<br \/>   };<\/p>\n<p>  return server;<br \/>   },<br \/>   configure: function (config) {<br \/>   var whitelist = {<br \/>   \u00abautoRespond\u00bb: true,<br \/>   \u00abautoRespondAfter\u00bb: true,<br \/>   \u00abrespondImmediately\u00bb: true,<br \/>   \u00abfakeHTTPMethods\u00bb: true<br \/>   };<br \/>   var setting;<\/p>\n<p>  config = config || {};<br \/>   for (setting in config) {<br \/>   if (whitelist.hasOwnProperty(setting) &#038;&#038; config.hasOwnProperty(setting)) {<br \/>   this[setting] = config[setting];<br \/>   }<br \/>   }<br \/>   },<br \/>   addRequest: function addRequest(xhrObj) {<br \/>   var server = this;<br \/>   push.call(this.requests, xhrObj);<\/p>\n<p>  xhrObj.onSend = function () {<br \/>   server.handleRequest(this);<\/p>\n<p>  if (server.respondImmediately) {<br \/>   server.respond();<br \/>   } else if (server.autoRespond &#038;&#038; !server.responding) {<br \/>   setTimeout(function () {<br \/>   server.responding = false;<br \/>   server.respond();<br \/>   }, server.autoRespondAfter || 10);<\/p>\n<p>  server.responding = true;<br \/>   }<br \/>   };<br \/>   },<\/p>\n<p>  getHTTPMethod: function getHTTPMethod(request) {<br \/>   if (this.fakeHTTPMethods &#038;&#038; \/post\/i.test(request.method)) {<br \/>   var matches = (request.requestBody || &quot;&quot;).match(\/_method=([^\\b;]+)\/);<br \/>   return matches? matches[1]: request.method;<br \/>   }<\/p>\n<p>  return request.method;<br \/>   },<\/p>\n<p>  handleRequest: function handleRequest(xhr) {<br \/>   if (xhr.async) {<br \/>   if (!this.queue) {<br \/>   this.queue = [];<br \/>   }<\/p>\n<p>  push.call(this.queue, xhr);<br \/>   } else {<br \/>   this.processRequest(xhr);<br \/>   }<br \/>   },<\/p>\n<p>  log: function log(response, request) {<br \/>   var str;<\/p>\n<p>  str = \u00abRequest:\\n\u00bb + sinon.format(request) + &quot;\\n\\n&quot;;<br \/>   str += \u00abResponse:\\n\u00bb + sinon.format(response) + &quot;\\n\\n&quot;;<\/p>\n<p>  sinon.log(str);<br \/>   },<\/p>\n<p>  respondWith: function respondWith(method, url, body) {<br \/>   if (arguments.length === 1 &#038;&#038; typeof method !== \u00abfunction\u00bb) {<br \/>   this.response = responseArray(method);<br \/>   return;<br \/>   }<\/p>\n<p>  if (!this.responses) {<br \/>   this.responses = [];<br \/>   }<\/p>\n<p>  if (arguments.length === 1) {<br \/>   body = method;<br \/>   url = method = null;<br \/>   }<\/p>\n<p>  if (arguments.length === 2) {<br \/>   body = url;<br \/>   url = method;<br \/>   method = null;<br \/>   }<\/p>\n<p>  push.call(this.responses, {<br \/>   method: method,<br \/>   url: url,<br \/>   response: typeof body === \u00abfunction\u00bb? body: responseArray(body)<br \/>   });<br \/>   },<\/p>\n<p>  respond: function respond() {<br \/>   if (arguments.length &gt; 0) {<br \/>   this.respondWith.apply(this, arguments);<br \/>   }<\/p>\n<p>  var queue = this.queue || [];<br \/>   var requests = queue.splice(0, queue.length);<\/p>\n<p>  for (var i = 0; i &lt; requests.length; i++) {<br \/>   this.processRequest(requests[i]);<br \/>   }<br \/>   },<\/p>\n<p>  processRequest: function processRequest(request) {<br \/>   try {<br \/>   if (request.aborted) {<br \/>   return;<br \/>   }<\/p>\n<p>  var response = this.response || [404, {}, &quot;&quot;];<\/p>\n<p>  if (this.responses) {<br \/>   for (var l = this.responses.length, i = l \u2014 1; i &gt;= 0; i&#8212;) {<br \/>   if (match.call(this, this.responses[i], request)) {<br \/>   response = this.responses[i].response;<br \/>   break;<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  if (request.readyState !== 4) {<br \/>   this.log(response, request);<\/p>\n<p>  request.respond(response[0], response[1], response[2]);<br \/>   }<br \/>   } catch (e) {<br \/>   sinon.logError(\u00abFake server request processing\u00bb, e);<br \/>   }<br \/>   },<\/p>\n<p>  restore: function restore() {<br \/>   return this.xhr.restore &#038;&#038; this.xhr.restore.apply(this.xhr, arguments);<br \/>   }<br \/>   };<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/core&quot;);<br \/>   require(&quot;.\/fake_xdomain_request&quot;);<br \/>   require(&quot;.\/fake_xml_http_request&quot;);<br \/>   require(&quot;..\/format&quot;);<br \/>   makeApi(sinon);<br \/>   module.exports = sinon;<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   } else {<br \/>   makeApi(sinon); \/\/ eslint-disable-line no-undef<br \/>   }<br \/>  }());<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> fake_server.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> fake_timers.js<br \/>   *\/<br \/>  \/**<br \/>   * Add-on for sinon.fakeServer that automatically handles a fake timer along with<br \/>   * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery<br \/>   * 1.3.x, which does not use xhr object&#8217;s onreadystatehandler at all \u2014 instead,<br \/>   * it polls the object for completion with setInterval. Dispite the direct<br \/>   * motivation, there is nothing jQuery-specific in this file, so it can be used<br \/>   * in any environment where the ajax implementation depends on setInterval or<br \/>   * setTimeout.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function () {<\/p>\n<p>   function makeApi(sinon) {<br \/>   function Server() {}<br \/>   Server.prototype = sinon.fakeServer;<\/p>\n<p>  sinon.fakeServerWithClock = new Server();<\/p>\n<p>  sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {<br \/>   if (xhr.async) {<br \/>   if (typeof setTimeout.clock === \u00abobject\u00bb) {<br \/>   this.clock = setTimeout.clock;<br \/>   } else {<br \/>   this.clock = sinon.useFakeTimers();<br \/>   this.resetClock = true;<br \/>   }<\/p>\n<p>  if (!this.longestTimeout) {<br \/>   var clockSetTimeout = this.clock.setTimeout;<br \/>   var clockSetInterval = this.clock.setInterval;<br \/>   var server = this;<\/p>\n<p>  this.clock.setTimeout = function (fn, timeout) {<br \/>   server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);<\/p>\n<p>  return clockSetTimeout.apply(this, arguments);<br \/>   };<\/p>\n<p>  this.clock.setInterval = function (fn, timeout) {<br \/>   server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);<\/p>\n<p>  return clockSetInterval.apply(this, arguments);<br \/>   };<br \/>   }<br \/>   }<\/p>\n<p>  return sinon.fakeServer.addRequest.call(this, xhr);<br \/>   };<\/p>\n<p>  sinon.fakeServerWithClock.respond = function respond() {<br \/>   var returnVal = sinon.fakeServer.respond.apply(this, arguments);<\/p>\n<p>  if (this.clock) {<br \/>   this.clock.tick(this.longestTimeout || 0);<br \/>   this.longestTimeout = 0;<\/p>\n<p>  if (this.resetClock) {<br \/>   this.clock.restore();<br \/>   this.resetClock = false;<br \/>   }<br \/>   }<\/p>\n<p>  return returnVal;<br \/>   };<\/p>\n<p>  sinon.fakeServerWithClock.restore = function restore() {<br \/>   if (this.clock) {<br \/>   this.clock.restore();<br \/>   }<\/p>\n<p>  return sinon.fakeServer.restore.apply(this, arguments);<br \/>   };<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require) {<br \/>   var sinon = require(&quot;.\/core&quot;);<br \/>   require(&quot;.\/fake_server&quot;);<br \/>   require(&quot;.\/fake_timers&quot;);<br \/>   makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require);<br \/>   } else {<br \/>   makeApi(sinon); \/\/ eslint-disable-line no-undef<br \/>   }<br \/>  }());<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> extend.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> collection.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/fake_timers.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/fake_server_with_clock.js<br \/>   *\/<br \/>  \/**<br \/>   * Manages fake collections as well as fake utilities such as Sinon&#8217;s<br \/>   * timers and fake XHR implementation in one convenient object.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   var push = [].push;<\/p>\n<p>  function exposeValue(sandbox, config, key, value) {<br \/>   if (!value) {<br \/>   return;<br \/>   }<\/p>\n<p>  if (config.injectInto &#038;&#038; !(key in config.injectInto)) {<br \/>   config.injectInto[key] = value;<br \/>   sandbox.injectedKeys.push(key);<br \/>   } else {<br \/>   push.call(sandbox.args, value);<br \/>   }<br \/>   }<\/p>\n<p>  function prepareSandboxFromConfig(config) {<br \/>   var sandbox = sinon.create(sinon.sandbox);<\/p>\n<p>  if (config.useFakeServer) {<br \/>   if (typeof config.useFakeServer === \u00abobject\u00bb) {<br \/>   sandbox.serverPrototype = config.useFakeServer;<br \/>   }<\/p>\n<p>  sandbox.useFakeServer();<br \/>   }<\/p>\n<p>  if (config.useFakeTimers) {<br \/>   if (typeof config.useFakeTimers === \u00abobject\u00bb) {<br \/>   sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);<br \/>   } else {<br \/>   sandbox.useFakeTimers();<br \/>   }<br \/>   }<\/p>\n<p>  return sandbox;<br \/>   }<\/p>\n<p>  sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {<br \/>   useFakeTimers: function useFakeTimers() {<br \/>   this.clock = sinon.useFakeTimers.apply(sinon, arguments);<\/p>\n<p>  return this.add(this.clock);<br \/>   },<\/p>\n<p>  serverPrototype: sinon.fakeServer,<\/p>\n<p>  useFakeServer: function useFakeServer() {<br \/>   var proto = this.serverPrototype || sinon.fakeServer;<\/p>\n<p>  if (!proto || !proto.create) {<br \/>   return null;<br \/>   }<\/p>\n<p>  this.server = proto.create();<br \/>   return this.add(this.server);<br \/>   },<\/p>\n<p>  inject: function (obj) {<br \/>   sinon.collection.inject.call(this, obj);<\/p>\n<p>  if (this.clock) {<br \/>   obj.clock = this.clock;<br \/>   }<\/p>\n<p>  if (this.server) {<br \/>   obj.server = this.server;<br \/>   obj.requests = this.server.requests;<br \/>   }<\/p>\n<p>  obj.match = sinon.match;<\/p>\n<p>  return obj;<br \/>   },<\/p>\n<p>  restore: function () {<br \/>   sinon.collection.restore.apply(this, arguments);<br \/>   this.restoreContext();<br \/>   },<\/p>\n<p>  restoreContext: function () {<br \/>   if (this.injectedKeys) {<br \/>   for (var i = 0, j = this.injectedKeys.length; i &lt; j; i++) {<br \/>   delete this.injectInto[this.injectedKeys[i]];<br \/>   }<br \/>   this.injectedKeys = [];<br \/>   }<br \/>   },<\/p>\n<p>  create: function (config) {<br \/>   if (!config) {<br \/>   return sinon.create(sinon.sandbox);<br \/>   }<\/p>\n<p>  var sandbox = prepareSandboxFromConfig(config);<br \/>   sandbox.args = sandbox.args || [];<br \/>   sandbox.injectedKeys = [];<br \/>   sandbox.injectInto = config.injectInto;<br \/>   var prop,<br \/>   value;<br \/>   var exposed = sandbox.inject({});<\/p>\n<p>  if (config.properties) {<br \/>   for (var i = 0, l = config.properties.length; i &lt; l; i++) {<br \/>   prop = config.properties[i];<br \/>   value = exposed[prop] || prop === \u00absandbox\u00bb &#038;&#038; sandbox;<br \/>   exposeValue(sandbox, config, prop, value);<br \/>   }<br \/>   } else {<br \/>   exposeValue(sandbox, config, \u00absandbox\u00bb, value);<br \/>   }<\/p>\n<p>  return sandbox;<br \/>   },<\/p>\n<p>  match: sinon.match<br \/>   });<\/p>\n<p>  sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;<\/p>\n<p>  return sinon.sandbox;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/extend&quot;);<br \/>   require(&quot;.\/util\/fake_server_with_clock&quot;);<br \/>   require(&quot;.\/util\/fake_timers&quot;);<br \/>   require(&quot;.\/collection&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> sandbox.js<br \/>   *\/<br \/>  \/**<br \/>   * Test function, sandboxes fakes<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function makeApi(sinon) {<br \/>   var slice = Array.prototype.slice;<\/p>\n<p>  function test(callback) {<br \/>   var type = typeof callback;<\/p>\n<p>  if (type !== \u00abfunction\u00bb) {<br \/>   throw new TypeError(\u00absinon.test needs to wrap a test function, got \u00bb + type);<br \/>   }<\/p>\n<p>  function sinonSandboxedTest() {<br \/>   var config = sinon.getConfig(sinon.config);<br \/>   config.injectInto = config.injectIntoThis &#038;&#038; this || config.injectInto;<br \/>   var sandbox = sinon.sandbox.create(config);<br \/>   var args = slice.call(arguments);<br \/>   var oldDone = args.length &#038;&#038; args[args.length \u2014 1];<br \/>   var exception, result;<\/p>\n<p>  if (typeof oldDone === \u00abfunction\u00bb) {<br \/>   args[args.length \u2014 1] = function sinonDone(res) {<br \/>   if (res) {<br \/>   sandbox.restore();<br \/>   } else {<br \/>   sandbox.verifyAndRestore();<br \/>   }<br \/>   oldDone(res);<br \/>   };<br \/>   }<\/p>\n<p>  try {<br \/>   result = callback.apply(this, args.concat(sandbox.args));<br \/>   } catch (e) {<br \/>   exception = e;<br \/>   }<\/p>\n<p>  if (typeof oldDone !== \u00abfunction\u00bb) {<br \/>   if (typeof exception !== \u00abundefined\u00bb) {<br \/>   sandbox.restore();<br \/>   throw exception;<br \/>   } else {<br \/>   sandbox.verifyAndRestore();<br \/>   }<br \/>   }<\/p>\n<p>  return result;<br \/>   }<\/p>\n<p>  if (callback.length) {<br \/>   return function sinonAsyncSandboxedTest(done) { \/\/ eslint-disable-line no-unused-vars<br \/>   return sinonSandboxedTest.apply(this, arguments);<br \/>   };<br \/>   }<\/p>\n<p>  return sinonSandboxedTest;<br \/>   }<\/p>\n<p>  test.config = {<br \/>   injectIntoThis: true,<br \/>   injectInto: null,<br \/>   properties: [\u00abspy\u00bb, \u00abstub\u00bb, \u00abmock\u00bb, \u00abclock\u00bb, \u00abserver\u00bb, \u00abrequests\u00bb],<br \/>   useFakeTimers: true,<br \/>   useFakeServer: true<br \/>   };<\/p>\n<p>  sinon.test = test;<br \/>   return test;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/sandbox&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   } else if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   } else if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(typeof sinon === \u00abobject\u00bb &#038;&#038; sinon || null)); \/\/ eslint-disable-line no-undef<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> test.js<br \/>   *\/<br \/>  \/**<br \/>   * Test case, sandboxes all test functions<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal) {<\/p>\n<p>   function createTest(property, setUp, tearDown) {<br \/>   return function () {<br \/>   if (setUp) {<br \/>   setUp.apply(this, arguments);<br \/>   }<\/p>\n<p>  var exception, result;<\/p>\n<p>  try {<br \/>   result = property.apply(this, arguments);<br \/>   } catch (e) {<br \/>   exception = e;<br \/>   }<\/p>\n<p>  if (tearDown) {<br \/>   tearDown.apply(this, arguments);<br \/>   }<\/p>\n<p>  if (exception) {<br \/>   throw exception;<br \/>   }<\/p>\n<p>  return result;<br \/>   };<br \/>   }<\/p>\n<p>  function makeApi(sinon) {<br \/>   function testCase(tests, prefix) {<br \/>   if (!tests || typeof tests !== \u00abobject\u00bb) {<br \/>   throw new TypeError(\u00absinon.testCase needs an object with test functions\u00bb);<br \/>   }<\/p>\n<p>  prefix = prefix || \u00abtest\u00bb;<br \/>   var rPrefix = new RegExp(&quot;^&quot; + prefix);<br \/>   var methods = {};<br \/>   var setUp = tests.setUp;<br \/>   var tearDown = tests.tearDown;<br \/>   var testName,<br \/>   property,<br \/>   method;<\/p>\n<p>  for (testName in tests) {<br \/>   if (tests.hasOwnProperty(testName) &#038;&#038; !\/^(setUp|tearDown)$\/.test(testName)) {<br \/>   property = tests[testName];<\/p>\n<p>  if (typeof property === \u00abfunction\u00bb &#038;&#038; rPrefix.test(testName)) {<br \/>   method = property;<\/p>\n<p>  if (setUp || tearDown) {<br \/>   method = createTest(property, setUp, tearDown);<br \/>   }<\/p>\n<p>  methods[testName] = sinon.test(method);<br \/>   } else {<br \/>   methods[testName] = tests[testName];<br \/>   }<br \/>   }<br \/>   }<\/p>\n<p>  return methods;<br \/>   }<\/p>\n<p>  sinon.testCase = testCase;<br \/>   return testCase;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var core = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/test&quot;);<br \/>   module.exports = makeApi(core);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon \/\/ eslint-disable-line no-undef<br \/>  ));<\/p>\n<p>  \/**<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> times_in_words.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> util\/core.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> match.js<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/depend\/\" class=\"user_link\">depend<\/a> format.js<br \/>   *\/<br \/>  \/**<br \/>   * Assertions matching the test spy retrieval interface.<br \/>   *<br \/>   *  <a href=\"http:\/\/habrahabr.ru\/users\/author\/\" class=\"user_link\">author<\/a> Christian Johansen (christian@cjohansen.no)<br \/>   *  @license BSD<br \/>   *<br \/>   * Copyright  2010-2013 Christian Johansen<br \/>   *\/<br \/>  (function (sinonGlobal, global) {<\/p>\n<p>   var slice = Array.prototype.slice;<\/p>\n<p>  function makeApi(sinon) {<br \/>   var assert;<\/p>\n<p>  function verifyIsStub() {<br \/>   var method;<\/p>\n<p>  for (var i = 0, l = arguments.length; i &lt; l; ++i) {<br \/>   method = arguments[i];<\/p>\n<p>  if (!method) {<br \/>   assert.fail(\u00abfake is not a spy\u00bb);<br \/>   }<\/p>\n<p>  if (method.proxy &#038;&#038; method.proxy.isSinonProxy) {<br \/>   verifyIsStub(method.proxy);<br \/>   } else {<br \/>   if (typeof method !== \u00abfunction\u00bb) {<br \/>   assert.fail(method + &quot; is not a function&quot;);<br \/>   }<\/p>\n<p>  if (typeof method.getCall !== \u00abfunction\u00bb) {<br \/>   assert.fail(method + &quot; is not stubbed&quot;);<br \/>   }<br \/>   }<\/p>\n<p>  }<br \/>   }<\/p>\n<p>  function failAssertion(object, msg) {<br \/>   object = object || global;<br \/>   var failMethod = object.fail || assert.fail;<br \/>   failMethod.call(object, msg);<br \/>   }<\/p>\n<p>  function mirrorPropAsAssertion(name, method, message) {<br \/>   if (arguments.length === 2) {<br \/>   message = method;<br \/>   method = name;<br \/>   }<\/p>\n<p>  assert[name] = function (fake) {<br \/>   verifyIsStub(fake);<\/p>\n<p>  var args = slice.call(arguments, 1);<br \/>   var failed = false;<\/p>\n<p>  if (typeof method === \u00abfunction\u00bb) {<br \/>   failed = !method(fake);<br \/>   } else {<br \/>   failed = typeof fake[method] === \u00abfunction\u00bb?<br \/>   !fake[method].apply(fake, args): !fake[method];<br \/>   }<\/p>\n<p>  if (failed) {<br \/>   failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args)));<br \/>   } else {<br \/>   assert.pass(name);<br \/>   }<br \/>   };<br \/>   }<\/p>\n<p>  function exposedName(prefix, prop) {<br \/>   return !prefix || \/^fail\/.test(prop)? prop:<br \/>   prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);<br \/>   }<\/p>\n<p>  assert = {<br \/>   failException: \u00abAssertError\u00bb,<\/p>\n<p>  fail: function fail(message) {<br \/>   var error = new Error(message);<br \/>   error.name = this.failException || assert.failException;<\/p>\n<p>  throw error;<br \/>   },<\/p>\n<p>  pass: function pass() {},<\/p>\n<p>  callOrder: function assertCallOrder() {<br \/>   verifyIsStub.apply(null, arguments);<br \/>   var expected = &quot;&quot;;<br \/>   var actual = &quot;&quot;;<\/p>\n<p>  if (!sinon.calledInOrder(arguments)) {<br \/>   try {<br \/>   expected = [].join.call(arguments, &quot;, &quot;);<br \/>   var calls = slice.call(arguments);<br \/>   var i = calls.length;<br \/>   while (i) {<br \/>   if (!calls[&#8212;i].called) {<br \/>   calls.splice(i, 1);<br \/>   }<br \/>   }<br \/>   actual = sinon.orderByFirstCall(calls).join(&quot;, &quot;);<br \/>   } catch (e) {<br \/>   \/\/ If this fails, we&#8217;ll just fall back to the blank string<br \/>   }<\/p>\n<p>  failAssertion(this, \u00abexpected \u00bb + expected + &quot; to be &quot; +<br \/>   \u00abcalled in order but were called as \u00bb + actual);<br \/>   } else {<br \/>   assert.pass(\u00abcallOrder\u00bb);<br \/>   }<br \/>   },<\/p>\n<p>  callCount: function assertCallCount(method, count) {<br \/>   verifyIsStub(method);<\/p>\n<p>  if (method.callCount !== count) {<br \/>   var msg = \u00abexpected %n to be called \u00bb + sinon.timesInWords(count) +<br \/>   &quot; but was called %c%C&quot;;<br \/>   failAssertion(this, method.printf(msg));<br \/>   } else {<br \/>   assert.pass(\u00abcallCount\u00bb);<br \/>   }<br \/>   },<\/p>\n<p>  expose: function expose(target, options) {<br \/>   if (!target) {<br \/>   throw new TypeError(\u00abtarget is null or undefined\u00bb);<br \/>   }<\/p>\n<p>  var o = options || {};<br \/>   var prefix = typeof o.prefix === \u00abundefined\u00bb &#038;&#038; \u00abassert\u00bb || o.prefix;<br \/>   var includeFail = typeof o.includeFail === \u00abundefined\u00bb || !!o.includeFail;<\/p>\n<p>  for (var method in this) {<br \/>   if (method !== \u00abexpose\u00bb &#038;&#038; (includeFail || !\/^(fail)\/.test(method))) {<br \/>   target[exposedName(prefix, method)] = this[method];<br \/>   }<br \/>   }<\/p>\n<p>  return target;<br \/>   },<\/p>\n<p>  match: function match(actual, expectation) {<br \/>   var matcher = sinon.match(expectation);<br \/>   if (matcher.test(actual)) {<br \/>   assert.pass(\u00abmatch\u00bb);<br \/>   } else {<br \/>   var formatted = [<br \/>   \u00abexpected value to match\u00bb,<br \/>   &quot; expected = &quot; + sinon.format(expectation),<br \/>   &quot; actual = &quot; + sinon.format(actual)<br \/>   ];<\/p>\n<p>  failAssertion(this, formatted.join(&quot;\\n&quot;));<br \/>   }<br \/>   }<br \/>   };<\/p>\n<p>  mirrorPropAsAssertion(\u00abcalled\u00bb, \u00abexpected %n to have been called at least once but was never called\u00bb);<br \/>   mirrorPropAsAssertion(\u00abnotCalled\u00bb, function (spy) {<br \/>   return !spy.called;<br \/>   }, \u00abexpected %n to not have been called but was called %c%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledOnce\u00bb, \u00abexpected %n to be called once but was called %c%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledTwice\u00bb, \u00abexpected %n to be called twice but was called %c%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledThrice\u00bb, \u00abexpected %n to be called thrice but was called %c%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledOn\u00bb, \u00abexpected %n to be called with %1 as this but was called with %t\u00bb);<br \/>   mirrorPropAsAssertion(<br \/>   \u00abalwaysCalledOn\u00bb,<br \/>   \u00abexpected %n to always be called with %1 as this but was called with %t\u00bb<br \/>   );<br \/>   mirrorPropAsAssertion(\u00abcalledWithNew\u00bb, \u00abexpected %n to be called with new\u00bb);<br \/>   mirrorPropAsAssertion(\u00abalwaysCalledWithNew\u00bb, \u00abexpected %n to always be called with new\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledWith\u00bb, \u00abexpected %n to be called with arguments %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledWithMatch\u00bb, \u00abexpected %n to be called with match %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abalwaysCalledWith\u00bb, \u00abexpected %n to always be called with arguments %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abalwaysCalledWithMatch\u00bb, \u00abexpected %n to always be called with match %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abcalledWithExactly\u00bb, \u00abexpected %n to be called with exact arguments %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abalwaysCalledWithExactly\u00bb, \u00abexpected %n to always be called with exact arguments %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abneverCalledWith\u00bb, \u00abexpected %n to never be called with arguments %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abneverCalledWithMatch\u00bb, \u00abexpected %n to never be called with match %*%C\u00bb);<br \/>   mirrorPropAsAssertion(\u00abthrew\u00bb, &quot;%n did not throw exception%C&quot;);<br \/>   mirrorPropAsAssertion(\u00abalwaysThrew\u00bb, &quot;%n did not always throw exception%C&quot;);<\/p>\n<p>  sinon.assert = assert;<br \/>   return assert;<br \/>   }<\/p>\n<p>  var isNode = typeof module !== \u00abundefined\u00bb &#038;&#038; module.exports &#038;&#038; typeof require === \u00abfunction\u00bb;<br \/>   var isAMD = typeof define === \u00abfunction\u00bb &#038;&#038; typeof define.amd === \u00abobject\u00bb &#038;&#038; define.amd;<\/p>\n<p>  function loadDependencies(require, exports, module) {<br \/>   var sinon = require(&quot;.\/util\/core&quot;);<br \/>   require(&quot;.\/match&quot;);<br \/>   require(&quot;.\/format&quot;);<br \/>   module.exports = makeApi(sinon);<br \/>   }<\/p>\n<p>  if (isAMD) {<br \/>   define(loadDependencies);<br \/>   return;<br \/>   }<\/p>\n<p>  if (isNode) {<br \/>   loadDependencies(require, module.exports, module);<br \/>   return;<br \/>   }<\/p>\n<p>  if (sinonGlobal) {<br \/>   makeApi(sinonGlobal);<br \/>   }<br \/>  }(<br \/>   typeof sinon === \u00abobject\u00bb &#038;&#038; sinon, \/\/ eslint-disable-line no-undef<br \/>   typeof global !== \u00abundefined\u00bb? global: self<br \/>  ));<\/p>\n<p>  return sinon;<br \/>  }));<\/p><\/div>\n<\/div>\n<p>  \u041f\u0435\u0440\u0432\u043e\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0441\u043f\u0440\u044f\u0442\u0430\u043d <a href=\"http:\/\/sinonjs.org\/docs\/#respond\">\u0442\u0443\u0442<\/a>.               <\/p>\n<div class=\"clear\"><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/274317\/\"> http:\/\/habrahabr.ru\/post\/274317\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>       Sinon.js \u2014 \u044d\u0442\u043e mock-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043b\u044f JavaScript, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441 \u043b\u044e\u0431\u044b\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u043c \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u043c. \u041e\u043d\u0430 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u044d\u043c\u0443\u043b\u044f\u0446\u0438\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u043c\u043e\u0433\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0432 JavaScript. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0442\u0440\u0438 \u0432\u0438\u0434\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0441 spy, stub \u0438 mock. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u043e\u0441\u0442\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043f\u043e API Sinon.js \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043a\u0440\u0430\u0442\u043a\u0438\u043c \u0432\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0432 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0434\u0430\u043d\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. <\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/65f\/f58\/f12\/65ff58f123fa431a99aa5b19f416ea04.jpg\"\/>  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-271428","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/271428","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=271428"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/271428\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=271428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=271428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=271428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}