Backed out changeset 4b280518c7b1 (bug 1532514) for Browser-chrome failures in browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Tue, 12 Mar 2019 03:59:56 +0200
changeset 521470 ee1ceae6688873b2750e84fa189d030612516e0d
parent 521469 fddb40ccfe5accf8d5039924eacd6abcc49b46cd
child 521471 0a3a2f8340e311ade4d7776b85ba8214eae74eb1
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1532514
milestone67.0a1
backs out4b280518c7b1798e846ef99af00ba60867dac20c
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 4b280518c7b1 (bug 1532514) for Browser-chrome failures in browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js. CLOSED TREE
.eslintignore
browser/base/content/test/general/browser_contentAreaClick.js
browser/base/content/test/pageActions/browser_page_action_menu.js
browser/base/content/test/pageActions/browser_page_action_menu_share_mac.js
browser/base/content/test/pageActions/browser_page_action_menu_share_win.js
browser/base/content/test/sync/head.js
browser/components/extensions/test/browser/browser_ExtensionControlledPopup.js
browser/components/extensions/test/xpcshell/test_ext_distribution_popup.js
browser/components/payments/test/mochitest/mochitest.ini
browser/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html
browser/components/payments/test/mochitest/test_PaymentsStore.html
browser/components/payments/test/mochitest/test_accepted_cards.html
browser/components/payments/test/mochitest/test_address_form.html
browser/components/payments/test/mochitest/test_basic_card_form.html
browser/components/payments/test/mochitest/test_payment_dialog.html
browser/components/payments/test/unit/head.js
browser/components/places/tests/browser/browser_bookmarkProperties_cancel.js
browser/components/places/tests/browser/browser_controller_onDrop_tagFolder.js
browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
browser/components/preferences/in-content/tests/browser_sync_pairing.js
browser/components/preferences/in-content/tests/browser_sync_sanitize.js
browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
browser/components/syncedtabs/test/browser/head.js
browser/components/syncedtabs/test/xpcshell/head.js
browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
browser/components/syncedtabs/test/xpcshell/test_SyncedTabsListStore.js
browser/components/tests/unit/head.js
browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
browser/components/urlbar/tests/browser/browser_UrlbarLoadRace.js
browser/components/urlbar/tests/browser/browser_remotetab.js
browser/components/urlbar/tests/browser/head.js
browser/components/urlbar/tests/unit/head.js
browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
browser/components/urlbar/tests/unit/test_UrlbarUtils_addToUrlbarHistory.js
browser/extensions/formautofill/test/unit/head.js
browser/extensions/formautofill/test/unit/test_activeStatus.js
browser/extensions/formautofill/test/unit/test_addressDataLoader.js
browser/extensions/formautofill/test/unit/test_autofillFormFields.js
browser/extensions/formautofill/test/unit/test_getRecords.js
browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
browser/extensions/formautofill/test/unit/test_savedFieldNames.js
browser/modules/test/browser/browser_UsageTelemetry_urlbar_remotetab.js
devtools/client/shared/components/test/mochitest/test_accordion.html
devtools/shared/adb/test/xpcshell-head.js
modules/libpref/init/all.js
services/common/tests/unit/test_async_iterator.js
services/fxaccounts/tests/xpcshell/head.js
services/fxaccounts/tests/xpcshell/test_profile.js
services/sync/tests/unit/head_helpers.js
services/sync/tests/unit/test_clients_engine.js
services/sync/tests/unit/test_disconnect_shutdown.js
services/sync/tests/unit/test_tab_store.js
services/sync/tests/unit/test_uistate.js
testing/modules/Sinon.jsm
testing/modules/moz.build
testing/modules/sinon-2.3.2.js
testing/modules/sinon-7.2.7.js
toolkit/components/featuregates/test/unit/head.js
toolkit/components/normandy/test/browser/browser_ClientEnvironment.js
toolkit/components/normandy/test/browser/browser_RecipeRunner.js
toolkit/components/normandy/test/browser/browser_actions_PreferenceExperimentAction.js
toolkit/components/normandy/test/browser/head.js
toolkit/components/normandy/test/unit/head_xpc.js
toolkit/components/normandy/test/unit/test_NormandyApi.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -318,17 +318,17 @@ testing/marionette/harness
 
 # other testing/ exclusions
 # third party modules
 testing/mochitest/tests/Harness_sanity/**
 testing/mochitest/MochiKit/**
 testing/mochitest/tests/MochiKit-1.4.2/**
 testing/mochitest/tests/SimpleTest/**
 testing/modules/ajv-4.1.1.js
-testing/modules/sinon-7.2.7.js
+testing/modules/sinon-2.3.2.js
 # octothorpe used for pref file comment causes parsing error
 testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
 testing/talos/talos/scripts/jszip.min.js
 testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
 testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js
 testing/talos/talos/tests/devtools/addon/content/pages/**
 testing/talos/talos/tests/dromaeo/**
 testing/talos/talos/tests/v8_7/**
--- a/browser/base/content/test/general/browser_contentAreaClick.js
+++ b/browser/base/content/test/general/browser_contentAreaClick.js
@@ -9,18 +9,16 @@
  * The test opens a new browser window, then replaces browser.js methods invoked
  * by contentAreaClick with a mock function that tracks which methods have been
  * called.
  * Each sub-test synthesizes a mouse click event on links injected in content,
  * the event is collected by a click handler that ensures that contentAreaClick
  * correctly prevent default events, and follows the correct code path.
  */
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
-
 var gTests = [
 
   {
     desc: "Simple left click",
     setup() {},
     clean() {},
     event: {},
     targets: [ "commonlink", "mathxlink", "svgxlink", "maplink" ],
@@ -178,21 +176,28 @@ function getStub(replacedMethod) {
 }
 
 // Reference to the new window.
 var gTestWin = null;
 
 // The test currently running.
 var gCurrentTest = null;
 
+var sandbox;
+
 function test() {
   waitForExplicitFinish();
 
+  /* global sinon */
+  Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+  sandbox = sinon.sandbox.create();
+
   registerCleanupFunction(function() {
-    sinon.restore();
+    sandbox.restore();
+    delete window.sinon;
   });
 
   gTestWin = openDialog(location, "", "chrome,all,dialog=no", "about:blank");
   whenDelayedStartupFinished(gTestWin, function() {
     info("Browser window opened");
     waitForFocus(function() {
       info("Browser window focused");
       waitForFocus(function() {
@@ -240,17 +245,17 @@ var gClickHandler = {
 
 function setupTestBrowserWindow() {
   // Steal click events and don't propagate them.
   gTestWin.addEventListener("click", gClickHandler, true);
 
   // Replace methods.
   gReplacedMethods.forEach(function(methodName) {
     let targetObj = methodName == "getShortcutOrURIAndPostData" ? UrlbarUtils : gTestWin;
-    sinon.stub(targetObj, methodName).returnsArg(0);
+    sandbox.stub(targetObj, methodName).returnsArg(0);
   });
 
   // Inject links in content.
   let doc = gTestWin.content.document;
   let mainDiv = doc.createElement("div");
   mainDiv.innerHTML =
     '<p><a id="commonlink" href="http://mochi.test/moz/">Common link</a></p>' +
     '<p><a id="panellink" href="http://mochi.test/moz/">Panel link</a></p>' +
@@ -276,17 +281,17 @@ function runNextTest() {
       gCurrentTest.setup();
     } else {
       finishTest();
       return;
     }
   }
 
   // Move to next target.
-  sinon.resetHistory();
+  sandbox.resetHistory();
   let target = gCurrentTest.targets.shift();
 
   info(gCurrentTest.desc + ": testing " + target);
 
   // Fire click event.
   let targetElt = gTestWin.content.document.getElementById(target);
   ok(targetElt, gCurrentTest.desc + ": target is valid (" + targetElt.id + ")");
   EventUtils.synthesizeMouseAtCenter(targetElt, gCurrentTest.event, gTestWin.content);
--- a/browser/base/content/test/pageActions/browser_page_action_menu.js
+++ b/browser/base/content/test/pageActions/browser_page_action_menu.js
@@ -1,12 +1,16 @@
 "use strict";
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
-/* global UIState */
+/* global sinon, UIState */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+registerCleanupFunction(function() {
+  delete window.sinon;
+});
 
 const lastModifiedFixture = 1507655615.87; // Approx Oct 10th 2017
 const mockTargets = [
   { id: "0", name: "foo", type: "phone", clientRecord: {id: "cli0", serverLastModified: lastModifiedFixture, type: "phone"} },
   { id: "1", name: "bar", type: "desktop", clientRecord: {id: "cli1", serverLastModified: lastModifiedFixture, type: "desktop"} },
   { id: "2", name: "baz", type: "phone", clientRecord: {id: "cli2", serverLastModified: lastModifiedFixture, type: "phone"} },
   { id: "3", name: "no client record device", type: "phone" },
 ];
@@ -246,17 +250,17 @@ add_task(async function sendToDevice_non
     Assert.equal(urlbarButton, null, "The urlbar button shouldn't exist");
   });
 });
 
 add_task(async function sendToDevice_syncNotReady_other_states() {
   // Open a tab that's sendable.
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
-    const sandbox = sinon.createSandbox();
+    const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => false);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_NOT_VERIFIED });
     sandbox.stub(gSync, "isSendableURI").returns(true);
 
     let cleanUp = () => {
       sandbox.restore();
     };
     registerCleanupFunction(cleanUp);
@@ -302,17 +306,17 @@ add_task(async function sendToDevice_syn
     cleanUp();
   });
 });
 
 add_task(async function sendToDevice_syncNotReady_configured() {
   // Open a tab that's sendable.
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
-    const sandbox = sinon.createSandbox();
+    const sandbox = sinon.sandbox.create();
     const syncReady = sandbox.stub(gSync, "syncReady").get(() => false);
     const hasSyncedThisSession = sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => false);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
 
     sandbox.stub(Weave.Service, "sync").callsFake(() => {
       syncReady.get(() => true);
       hasSyncedThisSession.get(() => true);
@@ -446,17 +450,17 @@ add_task(async function sendToDevice_not
     await hiddenPromise;
   });
 });
 
 add_task(async function sendToDevice_noDevices() {
   // Open a tab that's sendable.
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
-    const sandbox = sinon.createSandbox();
+    const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "fxaDevices").get(() => []);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockTargets.find(c => c.clientRecord && c.clientRecord.id == id).clientRecord.type);
 
     let cleanUp = () => {
@@ -512,17 +516,17 @@ add_task(async function sendToDevice_noD
     await UIState.reset();
   });
 });
 
 add_task(async function sendToDevice_devices() {
   // Open a tab that's sendable.
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
-    const sandbox = sinon.createSandbox();
+    const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => true);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(gSync, "sendTabTargets").get(() => mockTargets);
     sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockTargets.find(c => c.clientRecord && c.clientRecord.id == id).clientRecord.type);
 
     let cleanUp = () => {
@@ -578,17 +582,17 @@ add_task(async function sendToDevice_dev
   });
 });
 
 add_task(async function sendToDevice_title() {
   // Open two tabs that are sendable.
   await BrowserTestUtils.withNewTab("http://example.com/a", async otherBrowser => {
     await BrowserTestUtils.withNewTab("http://example.com/b", async () => {
       await promiseSyncReady();
-      const sandbox = sinon.createSandbox();
+      const sandbox = sinon.sandbox.create();
       sandbox.stub(gSync, "syncReady").get(() => true);
       sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => true);
       sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
       sandbox.stub(gSync, "isSendableURI").returns(true);
       sandbox.stub(gSync, "sendTabTargets").get(() => []);
       sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockTargets.find(c => c.clientRecord && c.clientRecord.id == id).clientRecord.type);
 
       let cleanUp = () => {
@@ -635,17 +639,17 @@ add_task(async function sendToDevice_tit
     });
   });
 });
 
 add_task(async function sendToDevice_inUrlbar() {
   // Open a tab that's sendable.
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
-    const sandbox = sinon.createSandbox();
+    const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => true);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(gSync, "sendTabTargets").get(() => mockTargets);
     sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockTargets.find(c => c.clientRecord && c.clientRecord.id == id).clientRecord.type);
 
     let cleanUp = () => {
--- a/browser/base/content/test/pageActions/browser_page_action_menu_share_mac.js
+++ b/browser/base/content/test/pageActions/browser_page_action_menu_share_mac.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
 const URL = "http://example.org/";
 
 // Keep track of title of service we chose to share with
 let serviceName, sharedUrl, sharedTitle;
 let sharingPreferencesCalled = false;
 
 let mockShareData = [{
@@ -32,16 +33,17 @@ let stub = sinon.stub(BrowserPageActions
     openSharingPreferences() {
       sharingPreferencesCalled = true;
     },
   };
 });
 
 registerCleanupFunction(async function() {
   stub.restore();
+  delete window.sinon;
   await EventUtils.synthesizeNativeMouseMove(document.documentElement, 0, 0);
   await PlacesUtils.history.clear();
 });
 
 add_task(async function shareURL() {
   await BrowserTestUtils.withNewTab(URL, async () => {
     // Open the panel.
     await promisePageActionPanelOpen();
--- a/browser/base/content/test/pageActions/browser_page_action_menu_share_win.js
+++ b/browser/base/content/test/pageActions/browser_page_action_menu_share_win.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
 const TEST_URL = getRootDirectory(gTestPath) + "browser_page_action_menu_share_win.html";
 
 // Keep track of site details we are sharing
 let sharedUrl, sharedTitle;
 
 let stub = sinon.stub(BrowserPageActions.shareURL, "_windowsUIUtils").get(() => {
   return {
@@ -16,16 +17,17 @@ let stub = sinon.stub(BrowserPageActions
       sharedUrl = url;
       sharedTitle = title;
     },
   };
 });
 
 registerCleanupFunction(async function() {
   stub.restore();
+  delete window.sinon;
 });
 
 add_task(async function shareURL() {
   if (!AppConstants.isPlatformAndVersionAtLeast("win", "6.4")) {
     Assert.ok(true, "We only expose share on windows 10 and above");
     return;
   }
 
--- a/browser/base/content/test/sync/head.js
+++ b/browser/base/content/test/sync/head.js
@@ -1,21 +1,27 @@
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
 ChromeUtils.import("resource://services-sync/UIState.jsm", this);
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+registerCleanupFunction(function() {
+  delete window.sinon;
+});
 
 function promiseSyncReady() {
   let service = Cc["@mozilla.org/weave/service;1"]
                   .getService(Ci.nsISupports)
                   .wrappedJSObject;
   return service.whenLoaded();
 }
 
 function setupSendTabMocks({ syncReady = true, fxaDevices = null,
                              state = UIState.STATUS_SIGNED_IN, isSendableURI = true }) {
-  const sandbox = sinon.createSandbox();
+  const sandbox = sinon.sandbox.create();
   sandbox.stub(gSync, "syncReady").get(() => syncReady);
   if (fxaDevices) {
     // Clone fxaDevices because it gets sorted in-place.
     sandbox.stub(Weave.Service.clientsEngine, "fxaDevices").get(() => [...fxaDevices]);
   }
   sandbox.stub(Weave.Service.clientsEngine, "hasSyncedThisSession").get(() => !!fxaDevices);
   sandbox.stub(UIState, "get").returns({ status: state });
   sandbox.stub(gSync, "isSendableURI").returns(isSendableURI);
--- a/browser/components/extensions/test/browser/browser_ExtensionControlledPopup.js
+++ b/browser/components/extensions/test/browser/browser_ExtensionControlledPopup.js
@@ -1,13 +1,18 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
+/* global sinon */
 
 "use strict";
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+registerCleanupFunction(() => {
+  delete window.sinon;
+});
 
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 
 function createMarkup(doc) {
   let panel = doc.getElementById("extension-notification-panel");
@@ -117,30 +122,30 @@ add_task(async function testExtensionCon
   ok(!beforeDisableAddon.called, "Settings have not been restored");
 
   // Add the setting and observer.
   await ExtensionSettingsStore.addSetting(id, settingType, settingKey, "controlled", () => "init");
   await popup.addObserver(id);
 
   // Ensure the panel isn't open.
   ok(onObserverAdded.called, "Observing the event");
-  onObserverAdded.resetHistory();
+  onObserverAdded.reset();
   ok(!onObserverRemoved.called, "Observing the event");
   ok(!beforeDisableAddon.called, "Settings have not been restored");
   ok(panel.getAttribute("panelopen") != "true", "The panel is closed");
   is(popupnotification.hidden, true, "The popup is hidden");
   is(addon.userDisabled, false, "The extension is enabled");
   is(await popup.userHasConfirmed(id), false, "The user is not initially confirmed");
 
   // The popup should opened based on the observer event.
   await openPopupWithEvent();
 
   ok(!onObserverAdded.called, "Only one observer has been registered");
   ok(onObserverRemoved.called, "The observer was removed");
-  onObserverRemoved.resetHistory();
+  onObserverRemoved.reset();
   ok(!beforeDisableAddon.called, "Settings have not been restored");
   is(panel.getAttribute("panelopen"), "true", "The panel is open");
   is(popupnotification.hidden, false, "The popup content is visible");
   is(await popup.userHasConfirmed(id), false, "The user has not confirmed yet");
 
   // Verify the description is populated.
   let description = doc.getElementById("extension-controlled-description");
   is(description.textContent,
@@ -157,30 +162,30 @@ add_task(async function testExtensionCon
   ok(!onObserverAdded.called, "The observer hasn't changed");
   ok(!onObserverRemoved.called, "The observer hasn't changed");
   is(await popup.userHasConfirmed(id), false, "The user has not confirmed");
   is(addon.userDisabled, false, "The extension is still enabled");
 
   // Force add the observer again to keep changes.
   await popup.addObserver(id);
   ok(onObserverAdded.called, "The observer was added again");
-  onObserverAdded.resetHistory();
+  onObserverAdded.reset();
   ok(!onObserverRemoved.called, "The observer is still registered");
   is(await popup.userHasConfirmed(id), false, "The user has not confirmed");
 
   // Wait for popup.
   await openPopupWithEvent();
 
   // Keep the changes.
   await closePopupWithAction("button");
 
   // The observer is removed, but the notification is saved.
   ok(!onObserverAdded.called, "The observer wasn't added");
   ok(onObserverRemoved.called, "The observer was removed");
-  onObserverRemoved.resetHistory();
+  onObserverRemoved.reset();
   is(await popup.userHasConfirmed(id), true, "The user has confirmed");
   is(addon.userDisabled, false, "The extension is still enabled");
 
   // Adding the observer again for this add-on won't work, since it is
   // confirmed.
   await popup.addObserver(id);
   ok(!onObserverAdded.called, "The observer isn't added");
   ok(!onObserverRemoved.called, "The observer isn't removed");
@@ -188,17 +193,17 @@ add_task(async function testExtensionCon
 
   // Clear that the user was notified.
   await popup.clearConfirmation(id);
   is(await popup.userHasConfirmed(id), false, "The user confirmation has been cleared");
 
   // Force add the observer again to restore changes.
   await popup.addObserver(id);
   ok(onObserverAdded.called, "The observer was added a third time");
-  onObserverAdded.resetHistory();
+  onObserverAdded.reset();
   ok(!onObserverRemoved.called, "The observer is still active");
   ok(!beforeDisableAddon.called, "We haven't disabled the add-on yet");
   is(await popup.userHasConfirmed(id), false, "The user has not confirmed");
 
   // Wait for popup.
   await openPopupWithEvent();
 
   // Restore the settings.
--- a/browser/components/extensions/test/xpcshell/test_ext_distribution_popup.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_distribution_popup.js
@@ -1,10 +1,11 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
+/* global sinon */
 
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 
--- a/browser/components/payments/test/mochitest/mochitest.ini
+++ b/browser/components/payments/test/mochitest/mochitest.ini
@@ -1,15 +1,15 @@
 [DEFAULT]
 support-files =
    !/browser/extensions/formautofill/content/editAddress.xhtml
    !/browser/extensions/formautofill/content/editCreditCard.xhtml
    ../../../../../browser/extensions/formautofill/content/autofillEditForms.js
    ../../../../../browser/extensions/formautofill/skin/shared/editDialog-shared.css
-   ../../../../../testing/modules/sinon-7.2.7.js
+   ../../../../../testing/modules/sinon-2.3.2.js
    # paymentRequest.xhtml is needed for `importDialogDependencies` so that the relative paths of
    # formautofill/edit*.xhtml work from the *-form elements in paymentRequest.xhtml.
    ../../res/paymentRequest.xhtml
    ../../res/**
    payments_common.js
 skip-if = true || !e10s # Bug 1515048 - Disable for now. Bug 1365964 - Payment Request isn't implemented for non-e10s.
 
 [test_accepted_cards.html]
--- a/browser/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html
+++ b/browser/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html
@@ -3,17 +3,17 @@
 <!--
 Test the PaymentStateSubscriberMixin
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the PaymentStateSubscriberMixin</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <test-element id="el1"></test-element>
   </p>
--- a/browser/components/payments/test/mochitest/test_PaymentsStore.html
+++ b/browser/components/payments/test/mochitest/test_PaymentsStore.html
@@ -5,17 +5,17 @@ Test the PaymentsStore
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the PaymentsStore</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
 
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
   </p>
 <div id="content" style="display: none">
--- a/browser/components/payments/test/mochitest/test_accepted_cards.html
+++ b/browser/components/payments/test/mochitest/test_accepted_cards.html
@@ -4,17 +4,17 @@
 Test the accepted-cards element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the accepted-cards element</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/accepted-cards.css"/>
 </head>
 <body>
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -4,17 +4,17 @@
 Test the address-form element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the address-form element</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="editDialog-shared.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/address-form.css"/>
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -4,17 +4,17 @@
 Test the basic-card-form element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the basic-card-form element</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/accepted-cards.css"/>
 </head>
--- a/browser/components/payments/test/mochitest/test_payment_dialog.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog.html
@@ -4,17 +4,17 @@
 Test the payment-dialog custom element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the payment-dialog element</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="sinon-7.2.7.js"></script>
+  <script src="sinon-2.3.2.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
 </head>
--- a/browser/components/payments/test/unit/head.js
+++ b/browser/components/payments/test/unit/head.js
@@ -1,2 +1,16 @@
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+var {
+  clearInterval,
+  clearTimeout,
+  setInterval,
+  setIntervalWithTarget,
+  setTimeout,
+  setTimeoutWithTarget,
+} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
--- a/browser/components/places/tests/browser/browser_bookmarkProperties_cancel.js
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_cancel.js
@@ -1,16 +1,18 @@
 "use strict";
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
-const sandbox = sinon.createSandbox();
+const sandbox = sinon.sandbox.create();
 
 registerCleanupFunction(async function() {
   sandbox.restore();
+  delete window.sinon;
   await PlacesUtils.bookmarks.eraseEverything();
   await PlacesUtils.history.clear();
 });
 
 let bookmarks; // Bookmarks added via insertTree.
 
 add_task(async function setup() {
   bookmarks = await PlacesUtils.bookmarks.insertTree({
--- a/browser/components/places/tests/browser/browser_controller_onDrop_tagFolder.js
+++ b/browser/components/places/tests/browser/browser_controller_onDrop_tagFolder.js
@@ -1,25 +1,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
-const sandbox = sinon.createSandbox();
+const sandbox = sinon.sandbox.create();
 const TAG_NAME = "testTag";
 
 var bookmarks;
 var bookmarkId;
 
 add_task(async function setup() {
   registerCleanupFunction(async function() {
     sandbox.restore();
+    delete window.sinon;
     await PlacesUtils.bookmarks.eraseEverything();
     await PlacesUtils.history.clear();
   });
 
   sandbox.stub(PlacesTransactions, "batch");
   sandbox.stub(PlacesTransactions, "Tag");
 
   bookmarks = await PlacesUtils.bookmarks.insertTree({
--- a/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
+++ b/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
@@ -1,9 +1,15 @@
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+const {setTimeout, clearTimeout, setInterval, clearInterval} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
 
 /* eslint-disable mozilla/use-chromeutils-generateqi */
 
 add_task(async function test_no_result_node() {
   let functionSpy = sinon.stub().returns(Promise.resolve());
 
   await PlacesUIUtils.batchUpdatesForNode(null, 1, functionSpy);
 
--- a/browser/components/preferences/in-content/tests/browser_sync_pairing.js
+++ b/browser/components/preferences/in-content/tests/browser_sync_pairing.js
@@ -1,18 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* global sinon */
 
 "use strict";
 
 const {UIState} = ChromeUtils.import("resource://services-sync/UIState.jsm", {});
 const {FxAccountsPairingFlow} = ChromeUtils.import("resource://gre/modules/FxAccountsPairing.jsm", {});
 
 // Use sinon for mocking.
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+registerCleanupFunction(() => {
+  delete window.sinon; // test fails with this reference left behind.
+});
 
 let flowCounter = 0;
 
 add_task(async function setup() {
   Services.prefs.setBoolPref("identity.fxaccounts.pairing.enabled", true);
   // Sync start-up might interfere with our tests, don't let UIState send UI updates.
   const origNotifyStateUpdated = UIState._internal.notifyStateUpdated;
   UIState._internal.notifyStateUpdated = () => {};
@@ -28,17 +32,17 @@ add_task(async function setup() {
   registerCleanupFunction(() => {
     UIState._internal.notifyStateUpdated = origNotifyStateUpdated;
     UIState.get = origGet;
     FxAccountsPairingFlow.start = origStart;
   });
 });
 
 add_task(async function testShowsQRCode() {
-  await runWithPairingDialog(async (win) => {
+  await runWithPairingDialog(async (win, sinon) => {
     let doc = win.document;
     let qrContainer = doc.getElementById("qrContainer");
     let qrWrapper = doc.getElementById("qrWrapper");
 
     await TestUtils.waitForCondition(() => qrWrapper.getAttribute("pairing-status") == "ready");
 
     // Verify that a QRcode is being shown.
     Assert.ok(qrContainer.style.backgroundImage.startsWith(`url("data:image/gif;base64,R0lGODdhOgA6AIAAAAAAAP///ywAAAAAOgA6AAAC/4yPqcvtD6OctNqLs968+w+G4gKU5nkiJYO2JuW6KsDGKEw3a7AbPZ+r4Ry7nzFIQkKKN6Avlzowo78`));
@@ -50,49 +54,49 @@ add_task(async function testShowsQRCode(
     info("waiting for dialog to unload");
     await promiseUnloaded;
   });
 });
 
 add_task(async function testCantShowQrCode() {
   const origStart = FxAccountsPairingFlow.start;
   FxAccountsPairingFlow.start = async () => { throw new Error("boom"); };
-  await runWithPairingDialog(async (win) => {
+  await runWithPairingDialog(async (win, sinon) => {
     let doc = win.document;
     let qrWrapper = doc.getElementById("qrWrapper");
 
     await TestUtils.waitForCondition(() => qrWrapper.getAttribute("pairing-status") == "error");
 
     // Close the dialog.
     let promiseUnloaded = BrowserTestUtils.waitForEvent(win, "unload");
     gBrowser.contentDocument.querySelector(".dialogClose").click();
 
     info("waiting for dialog to unload");
     await promiseUnloaded;
   });
   FxAccountsPairingFlow.start = origStart;
 });
 
 add_task(async function testSwitchToWebContent() {
-  await runWithPairingDialog(async (win) => {
+  await runWithPairingDialog(async (win, sinon) => {
     let doc = win.document;
     let qrWrapper = doc.getElementById("qrWrapper");
 
     await TestUtils.waitForCondition(() => qrWrapper.getAttribute("pairing-status") == "ready");
 
     const spySwitchURL = sinon.spy(win.gFxaPairDeviceDialog, "_switchToUrl");
     const emitter = win.gFxaPairDeviceDialog._emitter;
     emitter.emit("view:SwitchToWebContent", "about:robots");
 
     Assert.equal(spySwitchURL.callCount, 1);
   });
 });
 
 add_task(async function testError() {
-  await runWithPairingDialog(async (win) => {
+  await runWithPairingDialog(async (win, sinon) => {
     let doc = win.document;
     let qrWrapper = doc.getElementById("qrWrapper");
 
     await TestUtils.waitForCondition(() => qrWrapper.getAttribute("pairing-status") == "ready");
 
     const emitter = win.gFxaPairDeviceDialog._emitter;
     emitter.emit("view:Error");
 
@@ -111,14 +115,16 @@ async function runWithPairingDialog(test
   await openPreferencesViaOpenPreferencesAPI("paneSync", {leaveOpen: true});
 
   let promiseSubDialogLoaded =
       promiseLoadSubDialog("chrome://browser/content/preferences/in-content/fxaPairDevice.xul");
   gBrowser.contentWindow.gSyncPane.pairAnotherDevice();
 
   let win = await promiseSubDialogLoaded;
 
-  await test(win);
+  let ss = sinon.sandbox.create();
 
-  sinon.restore();
+  await test(win, ss);
+
+  ss.restore();
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 }
--- a/browser/components/preferences/in-content/tests/browser_sync_sanitize.js
+++ b/browser/components/preferences/in-content/tests/browser_sync_sanitize.js
@@ -1,24 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* global sinon */
 
 "use strict";
 
 const {UIState} = ChromeUtils.import("resource://services-sync/UIState.jsm");
 const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
 const {AsyncShutdown} = ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");
 
 const {SyncDisconnect, SyncDisconnectInternal} = ChromeUtils.import("resource://services-sync/SyncDisconnect.jsm", null);
 
 var fxAccountsCommon = {};
 ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommon);
 
 // Use sinon for mocking.
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+registerCleanupFunction(() => {
+  delete window.sinon; // test fails with this reference left behind.
+});
+
 
 add_task(async function setup() {
   // Sync start-up will interfere with our tests, don't let UIState send UI updates.
   const origNotifyStateUpdated = UIState._internal.notifyStateUpdated;
   UIState._internal.notifyStateUpdated = () => {};
 
   const origGet = UIState.get;
   UIState.get = () => { return { status: UIState.STATUS_SIGNED_IN, email: "foo@bar.com" }; };
@@ -32,17 +37,17 @@ add_task(async function setup() {
 
   registerCleanupFunction(() => {
     UIState._internal.notifyStateUpdated = origNotifyStateUpdated;
     UIState.get = origGet;
   });
 });
 
 add_task(async function testDisconnectUI() {
-  await runTestWithSanitizeDialog(async (win) => {
+  await runTestWithSanitizeDialog(async (win, sinon) => {
     let doc = win.document;
     let butDisconnect = doc.getElementById("butDisconnect");
     let butDeleteSync = doc.getElementById("deleteRemoteSyncData");
     let butDeleteOther = doc.getElementById("deleteRemoteOtherData");
 
     // mock both sanitize functions and the fxa signout.
     let spyBrowser = sinon.spy(SyncDisconnectInternal, "doSanitizeBrowserData");
     let spySync = sinon.spy(SyncDisconnectInternal, "doSanitizeSyncData");
@@ -82,17 +87,17 @@ add_task(async function testDisconnectUI
 
     Assert.equal(spyBrowser.callCount, 0, "should not have sanitized the browser");
     Assert.equal(spySync.callCount, 0, "should not have sanitized Sync");
     Assert.equal(spySignout.callCount, 0, "should not have signed out of FxA");
   });
 });
 
 add_task(async function testDisconnectNoSanitize() {
-  await runTestWithSanitizeDialog(async (win) => {
+  await runTestWithSanitizeDialog(async (win, sinon) => {
     let doc = win.document;
     let butDisconnect = doc.getElementById("butDisconnect");
 
     let spySignout = sinon.spy(SyncDisconnectInternal, "doSyncAndAccountDisconnect");
     let spySync = sinon.spy(SyncDisconnectInternal, "doSanitizeSyncData");
     let spyBrowser = sinon.spy(SyncDisconnectInternal, "doSanitizeBrowserData");
 
     let Weave = {
@@ -120,17 +125,17 @@ add_task(async function testDisconnectNo
     Assert.equal(Weave.Service.startOver.callCount, 1, "should have reset sync");
 
     Assert.ok(Services.prefs.prefHasUserValue(fxAccountsCommon.PREF_LAST_FXA_USER),
               "should still have the last-fxa-user pref as we didn't sanitize");
   });
 });
 
 add_task(async function testSanitizeSync() {
-  await runTestWithSanitizeDialog(async (win) => {
+  await runTestWithSanitizeDialog(async (win, sinon) => {
     let doc = win.document;
     let butDisconnect = doc.getElementById("butDisconnect");
     let butDeleteSync = doc.getElementById("deleteRemoteSyncData");
 
     SyncDisconnectInternal.lockRetryInterval = 100;
 
     let spySignout = sinon.spy(SyncDisconnectInternal, "doSyncAndAccountDisconnect");
 
@@ -186,17 +191,17 @@ add_task(async function testSanitizeSync
     Assert.equal(spySignout.callCount, 1, "should have signed out of FxA");
     Assert.equal(Weave.Service.startOver.callCount, 1, "should have reset sync");
     Assert.ok(!Services.prefs.prefHasUserValue(fxAccountsCommon.PREF_LAST_FXA_USER),
               "should have cleared the last-fxa-user pref as we sanitized");
   });
 });
 
 add_task(async function testSanitizeBrowser() {
-  await runTestWithSanitizeDialog(async (win) => {
+  await runTestWithSanitizeDialog(async (win, sinon) => {
     let doc = win.document;
 
     // The dialog should have the main UI visible.
     Assert.equal(doc.getElementById("deleteOptionsContent").hidden, false);
     Assert.equal(doc.getElementById("deletingContent").hidden, true);
 
     let butDisconnect = doc.getElementById("butDisconnect");
     let butDeleteOther = doc.getElementById("deleteRemoteOtherData");
@@ -222,17 +227,17 @@ add_task(async function testSanitizeBrow
 });
 
 add_task(async function testDisconnectAlreadyRunning() {
   // Mock the sanitize process to indicate one is already in progress.
   let resolveExisting;
   SyncDisconnectInternal.promiseDisconnectFinished =
     new Promise(resolve => resolveExisting = resolve);
 
-  await runTestWithSanitizeDialog(async (win) => {
+  await runTestWithSanitizeDialog(async (win, sinon) => {
     let doc = win.document;
     // The dialog should have "waiting" visible.
     Assert.equal(doc.getElementById("deleteOptionsContent").hidden, true);
     Assert.equal(doc.getElementById("deletingContent").hidden, false);
 
     let promiseUnloaded = BrowserTestUtils.waitForEvent(win, "unload");
     resolveExisting();
 
@@ -251,15 +256,17 @@ async function runTestWithSanitizeDialog
   let doc = gBrowser.contentDocument;
 
   let promiseSubDialogLoaded =
       promiseLoadSubDialog("chrome://browser/content/preferences/in-content/syncDisconnect.xul");
   doc.getElementById("fxaUnlinkButton").doCommand();
 
   let win = await promiseSubDialogLoaded;
 
-  await test(win);
+  let ss = sinon.sandbox.create();
 
-  sinon.restore();
+  await test(win, ss);
+
+  ss.restore();
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 }
 
--- a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
+++ b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
@@ -92,18 +92,18 @@ add_task(async function testSyncedTabsSi
   originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     isConfiguredToSyncTabs: true,
     hasSyncedThisSession: true,
     getTabClients() { return Promise.resolve([]); },
     syncTabs() { return Promise.resolve(); },
   };
 
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: true});
-  sinon.stub(SyncedTabs._internal, "getTabClients").resolves(Cu.cloneInto(FIXTURE, {}));
+  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.resolve({verified: true}));
+  sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
 
   await syncedTabsDeckComponent.updatePanel();
   // This is a hacky way of waiting for the view to render. The view renders
   // after the following promise (a different instance of which is triggered
   // in updatePanel) resolves, so we wait for it here as well
   await syncedTabsDeckComponent.tabListComponent._store.getData();
 
   Assert.ok(SyncedTabs._internal.getTabClients.called, "get clients called");
@@ -143,28 +143,29 @@ add_task(async function testSyncedTabsSi
   originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     isConfiguredToSyncTabs: true,
     hasSyncedThisSession: true,
     getTabClients() { return Promise.resolve([]); },
     syncTabs() { return Promise.resolve(); },
   };
 
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: true});
-  sinon.stub(SyncedTabs._internal, "getTabClients").resolves(Cu.cloneInto(FIXTURE, {}));
+  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.resolve({verified: true}));
+  sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
 
   await syncedTabsDeckComponent.updatePanel();
+  // This is a hacky way of waiting for the view to render. The view renders
+  // after the following promise (a different instance of which is triggered
+  // in updatePanel) resolves, so we wait for it here as well
+  await syncedTabsDeckComponent.tabListComponent._store.getData();
 
   let filterInput = syncedTabsDeckComponent._window.document.querySelector(".tabsFilter");
   filterInput.value = "filter text";
   filterInput.blur();
 
-  // This is a hacky way of waiting for the view to render. The view renders
-  // after the following promise (a different instance of which is triggered
-  // in updatePanel) resolves, so we wait for it here as well
   await syncedTabsDeckComponent.tabListComponent._store.getData("filter text");
 
   let selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("tabs-container"),
     "tabs panel is selected");
 
   Assert.equal(selectedPanel.querySelectorAll(".tab").length, 4,
     "four tabs listed");
@@ -187,16 +188,18 @@ add_task(async function testSyncedTabsSi
   Array.prototype.forEach.call(selectedPanel.querySelectorAll(".tab"), (tabNode, i) => {
     checkItem(tabNode, FIXTURE_TABS[i]);
   });
 });
 
 add_task(testClean);
 
 add_task(async function testSyncedTabsSidebarStatus() {
+  let account = null;
+
   await SidebarUI.show("viewTabsSidebar");
   let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
 
   originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     loginFailed: false,
     isConfiguredToSyncTabs: false,
     hasSyncedThisSession: false,
@@ -204,68 +207,65 @@ add_task(async function testSyncedTabsSi
     syncTabs() { return Promise.resolve(); },
   };
 
   Assert.ok(syncedTabsDeckComponent, "component exists");
 
   sinon.spy(syncedTabsDeckComponent, "updatePanel");
   sinon.spy(syncedTabsDeckComponent, "observe");
 
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").rejects("Test error");
+  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.reject("Test error"));
   await syncedTabsDeckComponent.updatePanel();
 
   let selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("notAuthedInfo"),
     "not-authed panel is selected on auth error");
 
   syncedTabsDeckComponent._getSignedInUser.restore();
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves(null);
+  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.resolve(account));
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("notAuthedInfo"),
     "not-authed panel is selected");
 
-  syncedTabsDeckComponent._getSignedInUser.restore();
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: false});
+  account = {verified: false};
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("unverified"),
     "unverified panel is selected");
 
   SyncedTabs._internal.loginFailed = true;
-  syncedTabsDeckComponent._getSignedInUser.restore();
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: true});
+  account = {verified: true};
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("reauth"),
     "reauth panel is selected");
   SyncedTabs._internal.loginFailed = false;
 
-  syncedTabsDeckComponent._getSignedInUser.restore();
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: true});
+  account = {verified: true};
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("tabs-disabled"),
     "tabs disabled panel is selected");
 
   SyncedTabs._internal.isConfiguredToSyncTabs = true;
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("tabs-fetching"),
     "tabs fetch panel is selected");
 
   SyncedTabs._internal.hasSyncedThisSession = true;
-  sinon.stub(SyncedTabs._internal, "getTabClients").resolves([]);
+  sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve([]));
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("singleDeviceInfo"),
     "tabs fetch panel is selected");
 
   SyncedTabs._internal.getTabClients.restore();
-  sinon.stub(SyncedTabs._internal, "getTabClients").resolves([{id: "mock"}]);
+  sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve([{id: "mock"}]));
   await syncedTabsDeckComponent.updatePanel();
   selectedPanel = syncedTabsDeckComponent.container.querySelector(".sync-state.selected");
   Assert.ok(selectedPanel.classList.contains("tabs-container"),
     "tabs panel is selected");
 });
 
 add_task(testClean);
 
@@ -278,18 +278,18 @@ add_task(async function testSyncedTabsSi
   originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     isConfiguredToSyncTabs: true,
     hasSyncedThisSession: true,
     getTabClients() { return Promise.resolve([]); },
     syncTabs() { return Promise.resolve(); },
   };
 
-  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser").resolves({verified: true});
-  sinon.stub(SyncedTabs._internal, "getTabClients").resolves(Cu.cloneInto(FIXTURE, {}));
+  sinon.stub(syncedTabsDeckComponent, "_getSignedInUser", () => Promise.resolve({verified: true}));
+  sinon.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto(FIXTURE, {})));
 
   await syncedTabsDeckComponent.updatePanel();
   // This is a hacky way of waiting for the view to render. The view renders
   // after the following promise (a different instance of which is triggered
   // in updatePanel) resolves, so we wait for it here as well
   await syncedTabsDeckComponent.tabListComponent._store.getData();
 
   info("Right-clicking the search box should show text-related actions");
--- a/browser/components/syncedtabs/test/browser/head.js
+++ b/browser/components/syncedtabs/test/browser/head.js
@@ -1,3 +1,13 @@
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/docs/
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+registerCleanupFunction(async function() {
+  // Cleanup window or the test runner will throw an error
+  delete window.sinon;
+});
--- a/browser/components/syncedtabs/test/xpcshell/head.js
+++ b/browser/components/syncedtabs/test/xpcshell/head.js
@@ -1,9 +1,16 @@
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function() {
   return ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", {});
 });
 
 do_get_profile(); // fxa needs a profile directory for storage.
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+var {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
--- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
+++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsDeckComponent.js
@@ -12,17 +12,17 @@ add_task(async function testInitUninit()
   let deckStore = new SyncedTabsDeckStore();
   let listComponent = {};
   let mockWindow = {};
 
   let ViewMock = sinon.stub();
   let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
   ViewMock.returns(view);
 
-  sinon.stub(SyncedTabs, "syncTabs").callsFake(() => Promise.resolve());
+  sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve());
 
   sinon.spy(deckStore, "on");
   sinon.stub(deckStore, "setPanels");
 
   let component = new SyncedTabsDeckComponent({
     window: mockWindow,
     deckStore,
     listComponent,
@@ -80,17 +80,17 @@ add_task(async function testObserver() {
   let listStore = new SyncedTabsListStore(SyncedTabs);
   let listComponent = {};
   let mockWindow = {};
 
   let ViewMock = sinon.stub();
   let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}};
   ViewMock.returns(view);
 
-  sinon.stub(SyncedTabs, "syncTabs").callsFake(() => Promise.resolve());
+  sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve());
 
   sinon.spy(deckStore, "on");
   sinon.stub(deckStore, "setPanels");
 
   sinon.stub(listStore, "getData");
 
   let component = new SyncedTabsDeckComponent({
     window: mockWindow,
@@ -157,17 +157,17 @@ add_task(async function testPanelStatus(
   let component = new SyncedTabsDeckComponent({
     fxAccounts,
     deckStore,
     listComponent,
     SyncedTabs: SyncedTabsMock,
   });
 
   let account = null;
-  sinon.stub(fxAccounts, "getSignedInUser").callsFake(() => Promise.resolve(account));
+  sinon.stub(fxAccounts, "getSignedInUser", () => Promise.resolve(account));
   let result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
 
   account = {verified: false};
 
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.UNVERIFIED);
 
@@ -186,30 +186,30 @@ add_task(async function testPanelStatus(
 
   SyncedTabsMock.hasSyncedThisSession = false;
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.TABS_FETCHING);
 
   SyncedTabsMock.hasSyncedThisSession = true;
 
   let clients = [];
-  sinon.stub(SyncedTabsMock, "getTabClients").callsFake(() => Promise.resolve(clients));
+  sinon.stub(SyncedTabsMock, "getTabClients", () => Promise.resolve(clients));
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.SINGLE_DEVICE_INFO);
 
   clients = ["mock-client"];
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.TABS_CONTAINER);
 
   fxAccounts.getSignedInUser.restore();
-  sinon.stub(fxAccounts, "getSignedInUser").callsFake(() => Promise.reject("err"));
+  sinon.stub(fxAccounts, "getSignedInUser", () => Promise.reject("err"));
   result = await component.getPanelStatus();
   Assert.equal(result, component.PANELS.NOT_AUTHED_INFO);
 
-  sinon.stub(component, "getPanelStatus").callsFake(() => Promise.resolve("mock-panelId"));
+  sinon.stub(component, "getPanelStatus", () => Promise.resolve("mock-panelId"));
   sinon.spy(deckStore, "selectPanel");
   await component.updatePanel();
   Assert.ok(deckStore.selectPanel.calledWith("mock-panelId"));
 });
 
 add_task(async function testActions() {
   let windowMock = {};
   let chromeWindowMock = {
--- a/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsListStore.js
+++ b/browser/components/syncedtabs/test/xpcshell/test_SyncedTabsListStore.js
@@ -38,17 +38,17 @@ const FIXTURE = [
     "tabs": [],
   },
 ];
 
 add_task(async function testGetDataEmpty() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve([]);
   });
   store.on("change", spy);
 
   await store.getData();
 
   Assert.ok(SyncedTabs.getTabClients.calledWith(""));
   Assert.ok(spy.calledWith({
@@ -72,17 +72,17 @@ add_task(async function testGetDataEmpty
 
   SyncedTabs.getTabClients.restore();
 });
 
 add_task(async function testRowSelectionWithoutFilter() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve(FIXTURE);
   });
 
   await store.getData();
   SyncedTabs.getTabClients.restore();
 
   store.on("change", spy);
 
@@ -123,17 +123,17 @@ add_task(async function testRowSelection
     "move selection up from tab selects previous tab of client");
 });
 
 
 add_task(async function testToggleBranches() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve(FIXTURE);
   });
 
   await store.getData();
   SyncedTabs.getTabClients.restore();
 
   store.selectRow(0);
   store.on("change", spy);
@@ -157,17 +157,17 @@ add_task(async function testToggleBranch
     "selection skips tabs if client is closed");
 });
 
 
 add_task(async function testRowSelectionWithFilter() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve(FIXTURE);
   });
 
   await store.getData("filter");
   SyncedTabs.getTabClients.restore();
 
   store.on("change", spy);
 
@@ -191,17 +191,17 @@ add_task(async function testRowSelection
     "doesn't trigger change if same row selected");
 });
 
 
 add_task(async function testFilterAndClearFilter() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve(FIXTURE);
   });
   store.on("change", spy);
 
   await store.getData("filter");
 
   Assert.ok(SyncedTabs.getTabClients.calledWith("filter"));
   Assert.ok(!spy.args[0][0].canUpdateAll, "can't update all");
@@ -225,17 +225,17 @@ add_task(async function testFilterAndCle
 
   SyncedTabs.getTabClients.restore();
 });
 
 add_task(async function testFocusBlurInput() {
   let store = new SyncedTabsListStore(SyncedTabs);
   let spy = sinon.spy();
 
-  sinon.stub(SyncedTabs, "getTabClients").callsFake(() => {
+  sinon.stub(SyncedTabs, "getTabClients", () => {
     return Promise.resolve(FIXTURE);
   });
   store.on("change", spy);
 
   await store.getData();
   SyncedTabs.getTabClients.restore();
 
   Assert.ok(!spy.args[0][0].canUpdateAll, "must rerender all");
--- a/browser/components/tests/unit/head.js
+++ b/browser/components/tests/unit/head.js
@@ -1,7 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+/* exported sinon */
+var {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
+
 var gProfD = do_get_profile().QueryInterface(Ci.nsIFile);
--- a/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarInput_unit.js
@@ -99,17 +99,17 @@ async function withNewWindow(callback) {
   input.view = null;
 
   Assert.ok(fakeController.view);
   fakeController.removeQueryListener(fakeController.view);
   fakeController.view = null;
 }
 
 add_task(async function setup() {
-  sandbox = sinon.createSandbox();
+  sandbox = sinon.sandbox.create();
 
   fakeController = new UrlbarController({
     browserWindow: window,
   });
 
   sandbox.stub(fakeController, "startQuery");
   sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
 
--- a/browser/components/urlbar/tests/browser/browser_UrlbarLoadRace.js
+++ b/browser/components/urlbar/tests/browser/browser_UrlbarLoadRace.js
@@ -3,17 +3,17 @@
  */
 
 // This test is for testing races of loading the Urlbar when loading shortcuts.
 // For example, ensuring that if a search query is entered, but something causes
 // a page load whilst we're getting the search url, then we don't handle the
 // original search query.
 
 add_task(async function setup() {
-  sandbox = sinon.createSandbox();
+  sandbox = sinon.sandbox.create();
 
   registerCleanupFunction(async () => {
     sandbox.restore();
   });
 });
 
 async function checkShortcutLoading(modifierKeys) {
   let deferred = PromiseUtils.defer();
--- a/browser/components/urlbar/tests/browser/browser_remotetab.js
+++ b/browser/components/urlbar/tests/browser/browser_remotetab.js
@@ -26,17 +26,17 @@ const REMOTE_TAB = {
       "icon": UrlbarUtils.ICON.DEFAULT,
       "client": "7cqCr77ptzX3",
       "lastUsed": 1452124677,
     },
   ],
 };
 
 add_task(async function setup() {
-  sandbox = sinon.createSandbox();
+  sandbox = sinon.sandbox.create();
 
   let originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     isConfiguredToSyncTabs: true,
     hasSyncedThisSession: true,
     getTabClients() { return Promise.resolve([]); },
     syncTabs() { return Promise.resolve(); },
   };
@@ -49,17 +49,17 @@ add_task(async function setup() {
   weaveXPCService.ready = true;
 
   await SpecialPowers.pushPrefEnv({set: [
     ["browser.urlbar.autoFill", false],
     ["services.sync.username", "fake"],
     ["services.sync.syncedTabs.showRemoteTabs", true],
   ]});
 
-  sandbox.stub(SyncedTabs._internal, "getTabClients").callsFake(() => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {})));
+  sandbox.stub(SyncedTabs._internal, "getTabClients", () => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {})));
 
   registerCleanupFunction(async () => {
     sandbox.restore();
     weaveXPCService.ready = oldWeaveServiceReady;
     SyncedTabs._internal = originalSyncedTabsInternal;
   });
 });
 
--- a/browser/components/urlbar/tests/browser/head.js
+++ b/browser/components/urlbar/tests/browser/head.js
@@ -20,9 +20,14 @@ XPCOMUtils.defineLazyModuleGetters(this,
   UrlbarUtils: "resource:///modules/UrlbarUtils.jsm",
 });
 
 /* import-globals-from head-common.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-common.js",
   this);
 
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+/* global sinon */
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
+registerCleanupFunction(function() {
+  delete window.sinon;
+});
--- a/browser/components/urlbar/tests/unit/head.js
+++ b/browser/components/urlbar/tests/unit/head.js
@@ -20,17 +20,25 @@ XPCOMUtils.defineLazyModuleGetters(this,
   UrlbarController: "resource:///modules/UrlbarController.jsm",
   UrlbarInput: "resource:///modules/UrlbarInput.jsm",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
   UrlbarProviderOpenTabs: "resource:///modules/UrlbarProviderOpenTabs.jsm",
   UrlbarProvidersManager: "resource:///modules/UrlbarProvidersManager.jsm",
   UrlbarResult: "resource:///modules/UrlbarResult.jsm",
   UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.jsm",
 });
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+// Sinon needs Timer.jsm for setTimeout etc.
+var {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
 
 /**
  * @param {string} searchString The search string to insert into the context.
  * @param {object} properties Overrides for the default values.
  * @returns {UrlbarQueryContext} Creates a dummy query context with pre-filled
  *          required options.
  */
 function createContext(searchString = "foo", properties = {}) {
--- a/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarController_unit.js
@@ -26,17 +26,17 @@ function assertContextMatches(context, e
 
   for (let [key, value] of Object.entries(expectedValues)) {
     Assert.equal(context[key], value,
       `Should have the expected value for ${key} in the UrlbarQueryContext`);
   }
 }
 
 add_task(function setup() {
-  sandbox = sinon.createSandbox();
+  sandbox = sinon.sandbox.create();
 
   fPM = {
     startQuery: sandbox.stub(),
     cancelQuery: sandbox.stub(),
   };
 
   generalListener = {
     onQueryStarted: sandbox.stub(),
--- a/browser/components/urlbar/tests/unit/test_UrlbarUtils_addToUrlbarHistory.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarUtils_addToUrlbarHistory.js
@@ -9,17 +9,17 @@
 "use strict";
 
 const {PrivateBrowsingUtils} = ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 const {PlacesUIUtils} = ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
 
 let sandbox;
 
 add_task(function setup() {
-  sandbox = sinon.createSandbox();
+  sandbox = sinon.sandbox.create();
 });
 
 add_task(function test_addToUrlbarHistory() {
   sandbox.stub(PlacesUIUtils, "markPageAsTyped");
   sandbox.stub(PrivateBrowsingUtils, "isWindowPrivate").returns(false);
 
   UrlbarUtils.addToUrlbarHistory("http://example.com");
   Assert.ok(PlacesUIUtils.markPageAsTyped.calledOnce,
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -7,17 +7,16 @@
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 var {ObjectUtils} = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
 var {FormLikeFactory} = ChromeUtils.import("resource://gre/modules/FormLikeFactory.jsm");
 var {AddonTestUtils, MockAsyncShutdown} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 var {ExtensionTestUtils} = ChromeUtils.import("resource://testing-common/ExtensionXPCShellUtils.jsm");
 var {FileTestUtils} = ChromeUtils.import("resource://testing-common/FileTestUtils.jsm");
 var {MockDocument} = ChromeUtils.import("resource://testing-common/MockDocument.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
 var {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "DownloadPaths",
                                "resource://gre/modules/DownloadPaths.jsm");
@@ -54,16 +53,31 @@ region-name-tw = Taiwan
   let locales = Services.locale.packagedLocales;
   const mockSource = new FileSource("mock", locales, "");
   L10nRegistry.registerSource(mockSource);
 }
 
 
 do_get_profile();
 
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+var {
+  clearInterval,
+  clearTimeout,
+  setInterval,
+  setIntervalWithTarget,
+  setTimeout,
+  setTimeoutWithTarget,
+} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
+
 const EXTENSION_ID = "formautofill@mozilla.org";
 
 AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 
 async function loadExtension() {
   AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
   await AddonTestUtils.promiseStartupManager();
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -42,32 +42,32 @@ add_task(async function test_activeStatu
   formAutofillParent._active = true;
   formAutofillParent._computeStatus.returns(true);
   formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.addresses.enabled");
   formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.creditCards.enabled");
   Assert.equal(formAutofillParent._onStatusChanged.called, false);
 
   // _active != _computeStatus() => Need to trigger _onStatusChanged
   formAutofillParent._computeStatus.returns(false);
-  formAutofillParent._onStatusChanged.resetHistory();
+  formAutofillParent._onStatusChanged.reset();
   formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.addresses.enabled");
   formAutofillParent.observe(null, "nsPref:changed", "extensions.formautofill.creditCards.enabled");
   Assert.equal(formAutofillParent._onStatusChanged.called, true);
 
   // profile changed => Need to trigger _onStatusChanged
   await Promise.all(["add", "update", "remove", "reconcile"].map(async event => {
     formAutofillParent._computeStatus.returns(!formAutofillParent._active);
-    formAutofillParent._onStatusChanged.resetHistory();
+    formAutofillParent._onStatusChanged.reset();
     await formAutofillParent.observe(null, "formautofill-storage-changed", event);
     Assert.equal(formAutofillParent._onStatusChanged.called, true);
   }));
 
   // profile metadata updated => No need to trigger _onStatusChanged
   formAutofillParent._computeStatus.returns(!formAutofillParent._active);
-  formAutofillParent._onStatusChanged.resetHistory();
+  formAutofillParent._onStatusChanged.reset();
   await formAutofillParent.observe(null, "formautofill-storage-changed", "notifyUsed");
   Assert.equal(formAutofillParent._onStatusChanged.called, false);
 });
 
 add_task(async function test_activeStatus_computeStatus() {
   let formAutofillParent = new FormAutofillParent();
   registerCleanupFunction(function cleanup() {
     Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
--- a/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
+++ b/browser/extensions/formautofill/test/unit/test_addressDataLoader.js
@@ -36,33 +36,33 @@ add_task(async function test_loadDataSta
   Assert.equal(AddressDataLoader._dataLoaded.country, true);
   Assert.equal(AddressDataLoader._dataLoaded.level1.size, 0);
   // _loadScripts should be called
   sinon.assert.called(AddressDataLoader._loadScripts);
   // Verify metadata
   Assert.equal(metadata.id, "data/US");
   Assert.ok(metadata.alternative_names,
             "US alternative names should be loaded from extension");
-  AddressDataLoader._loadScripts.resetHistory();
+  AddressDataLoader._loadScripts.reset();
 
   // Load data without country
   let newMetadata = FormAutofillUtils.getCountryAddressData();
   // _loadScripts should not be called
   sinon.assert.notCalled(AddressDataLoader._loadScripts);
   Assert.deepEqual(metadata, newMetadata, "metadata should be US if country is not specified");
-  AddressDataLoader._loadScripts.resetHistory();
+  AddressDataLoader._loadScripts.reset();
 
   // Load level 1 data that does not exist
   let undefinedMetadata = FormAutofillUtils.getCountryAddressData("US", "CA");
   // _loadScripts should be called
   sinon.assert.called(AddressDataLoader._loadScripts);
   Assert.equal(undefinedMetadata, undefined, "metadata should be undefined");
   Assert.ok(AddressDataLoader._dataLoaded.level1.has("US"),
                "level 1 state array should be set even there's no valid metadata");
-  AddressDataLoader._loadScripts.resetHistory();
+  AddressDataLoader._loadScripts.reset();
 
   // Load level 1 data again
   undefinedMetadata = FormAutofillUtils.getCountryAddressData("US", "AS");
   Assert.equal(undefinedMetadata, undefined, "metadata should be undefined");
   // _loadScripts should not be called
   sinon.assert.notCalled(AddressDataLoader._loadScripts);
 });
 
--- a/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_autofillFormFields.js
@@ -1,17 +1,15 @@
 /*
  * Test for form auto fill content helper fill all inputs function.
  */
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 
 "use strict";
 
-const {setTimeout, clearTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
-
 var FormAutofillHandler, OSKeyStore;
 add_task(async function setup() {
   ({FormAutofillHandler} = ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm"));
   ({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm"));
 });
 
 const TESTCASES = [
   {
--- a/browser/extensions/formautofill/test/unit/test_getRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_getRecords.js
@@ -175,17 +175,17 @@ add_task(async function test_getRecords_
   await formAutofillParent.formAutofillStorage.initialize();
   let collection = formAutofillParent.formAutofillStorage.creditCards;
   let encryptedCCRecords = await Promise.all([TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2].map(async record => {
     let clonedRecord = Object.assign({}, record);
     clonedRecord["cc-number"] = CreditCard.getLongMaskedNumber(record["cc-number"]);
     clonedRecord["cc-number-encrypted"] = await OSKeyStore.encrypt(record["cc-number"]);
     return clonedRecord;
   }));
-  sinon.stub(collection, "getAll").callsFake(() =>
+  sinon.stub(collection, "getAll", () =>
     Promise.resolve([Object.assign({}, encryptedCCRecords[0]), Object.assign({}, encryptedCCRecords[1])]));
 
   let testCases = [
     {
       description: "If the search string could match multiple creditCards",
       filter: {
         collectionName: "creditCards",
         info: {fieldName: "cc-name"},
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -522,43 +522,43 @@ add_task(async function autofill_disable
   sinon.stub(FormAutofillContent, "_onFormSubmit");
 
   // "_onFormSubmit" shouldn't be called if both "addresses" and "creditCards"
   // are disabled.
   Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
   Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
   FormAutofillContent.formSubmitted(form, null);
   Assert.equal(FormAutofillContent._onFormSubmit.called, false);
-  FormAutofillContent._onFormSubmit.resetHistory();
+  FormAutofillContent._onFormSubmit.reset();
 
   // "_onFormSubmit" should be called as usual.
   Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
   Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
   FormAutofillContent.formSubmitted(form, null);
   Assert.equal(FormAutofillContent._onFormSubmit.called, true);
   Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
   Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
-  FormAutofillContent._onFormSubmit.resetHistory();
+  FormAutofillContent._onFormSubmit.reset();
 
   // "address" should be empty if "addresses" pref is disabled.
   Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
   FormAutofillContent.formSubmitted(form, null);
   Assert.equal(FormAutofillContent._onFormSubmit.called, true);
   Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
   Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
-  FormAutofillContent._onFormSubmit.resetHistory();
+  FormAutofillContent._onFormSubmit.reset();
   Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
 
   // "creditCard" should be empty if "creditCards" pref is disabled.
   Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
   FormAutofillContent.formSubmitted(form, null);
   Assert.deepEqual(FormAutofillContent._onFormSubmit.called, true);
   Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
   Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
-  FormAutofillContent._onFormSubmit.resetHistory();
+  FormAutofillContent._onFormSubmit.reset();
   Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
 
   FormAutofillContent._onFormSubmit.restore();
 });
 
 TESTCASES.forEach(testcase => {
   add_task(async function check_records_saving_is_called_correctly() {
     info("Starting testcase: " + testcase.description);
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -29,17 +29,17 @@ add_task(async function test_profileSave
 
   // profile changed => Need to trigger updateValidFields
   ["add", "update", "remove", "reconcile", "removeAll"].forEach(event => {
     formAutofillParent.observe(null, "formautofill-storage-changed", event);
     Assert.equal(formAutofillParent._updateSavedFieldNames.called, true);
   });
 
   // profile metadata updated => no need to trigger updateValidFields
-  formAutofillParent._updateSavedFieldNames.resetHistory();
+  formAutofillParent._updateSavedFieldNames.reset();
   formAutofillParent.observe(null, "formautofill-storage-changed", "notifyUsed");
   Assert.equal(formAutofillParent._updateSavedFieldNames.called, false);
 });
 
 add_task(async function test_profileSavedFieldNames_update() {
   let formAutofillParent = new FormAutofillParent();
   await formAutofillParent.init();
   registerCleanupFunction(function cleanup() {
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar_remotetab.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar_remotetab.js
@@ -11,17 +11,16 @@
 const SCALAR_URLBAR = "browser.engagement.navigation.urlbar";
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
   UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.jsm",
   URLBAR_SELECTED_RESULT_TYPES: "resource:///modules/BrowserUsageTelemetry.jsm",
   URLBAR_SELECTED_RESULT_METHODS: "resource:///modules/BrowserUsageTelemetry.jsm",
 });
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
 
 function assertSearchTelemetryEmpty(search_hist) {
   const scalars = TelemetryTestUtils.getProcessScalars("parent", true, false);
   Assert.ok(!(SCALAR_URLBAR in scalars), `Should not have recorded ${SCALAR_URLBAR}`);
 
   // Make sure SEARCH_COUNTS contains identical values.
   TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.urlbar", undefined);
   TelemetryTestUtils.assertKeyedHistogramSum(search_hist, "other-MozSearch.alias", undefined);
@@ -54,16 +53,19 @@ function assertHistogramResults(histogra
     type, index, 1);
 
   TelemetryTestUtils.assertHistogram(histograms.resultMethodHist,
     method, 1);
 }
 
 
 add_task(async function setup() {
+  /* global sinon */
+  Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
+
   await SpecialPowers.pushPrefEnv({
     set: [
       // Disable search suggestions in the urlbar.
       ["browser.urlbar.suggest.searches", false],
       // Clear historical search suggestions to avoid interference from previous
       // tests.
       ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
       // Use the default matching bucket configuration.
@@ -101,17 +103,17 @@ add_task(async function setup() {
         "url": "http://example.com",
         "icon": UrlbarUtils.ICON.DEFAULT,
         "client": "7cqCr77ptzX3",
         "lastUsed": 1452124677,
       },
     ],
   };
 
-  const sandbox = sinon.createSandbox();
+  const sandbox = sinon.sandbox.create();
 
   let originalSyncedTabsInternal = SyncedTabs._internal;
   SyncedTabs._internal = {
     isConfiguredToSyncTabs: true,
     hasSyncedThisSession: true,
     getTabClients() { return Promise.resolve([]); },
     syncTabs() { return Promise.resolve(); },
   };
@@ -130,16 +132,17 @@ add_task(async function setup() {
   registerCleanupFunction(async function() {
     sandbox.restore();
     weaveXPCService.ready = oldWeaveServiceReady;
     SyncedTabs._internal = originalSyncedTabsInternal;
     Services.telemetry.canRecordExtended = oldCanRecord;
     await PlacesUtils.history.clear();
     await PlacesUtils.bookmarks.eraseEverything();
     Services.telemetry.setEventRecordingEnabled("navigation", false);
+    delete window.sinon;
   });
 });
 
 add_task(async function test_remotetab() {
   const histograms = snapshotHistograms();
 
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
 
--- a/devtools/client/shared/components/test/mochitest/test_accordion.html
+++ b/devtools/client/shared/components/test/mochitest/test_accordion.html
@@ -5,31 +5,31 @@
 <html>
 <!--
 Test that Accordion renders correctly.
 -->
 <head>
   <meta charset="utf-8">
   <title>Accordion component test</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="resource://testing-common/sinon-7.2.7.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
 </head>
 <body>
 <pre id="test">
 <script src="head.js" type="application/javascript"></script>
 <script src="accordion.snapshots.js" type="application/javascript"></script>
 <script type="application/javascript">
 
 "use strict";
 
 window.onload = async function() {
   try {
     const { button, div } = require("devtools/client/shared/vendor/react-dom-factories");
+    const sinon = require("resource://testing-common/sinon-2.3.2.js");
     const React = browserRequire("devtools/client/shared/vendor/react");
     const {
       Simulate,
       renderIntoDocument,
       findAllInRenderedTree,
     } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
     const Accordion =
       browserRequire("devtools/client/shared/components/Accordion");
--- a/devtools/shared/adb/test/xpcshell-head.js
+++ b/devtools/shared/adb/test/xpcshell-head.js
@@ -1,8 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 
 const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
+const Services = require("Services");
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+const {setTimeout, clearTimeout, setInterval, clearInterval} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2690,17 +2690,17 @@ pref("security.notification_enable_delay
 
 #if defined(DEBUG) && !defined(ANDROID)
 pref("csp.about_uris_without_csp", "blank,printpreview,srcdoc,about,addons,cache-entry,config,crashes,debugging,devtools,downloads,home,memory,networking,newtab,performance,plugins,policies,profiles,restartrequired,searchreset,serviceworkers,sessionrestore,support,sync-log,telemetry,url-classifier,webrtc,welcomeback");
 // the following prefs are for testing purposes only.
 pref("csp.overrule_about_uris_without_csp_whitelist", false);
 pref("csp.skip_about_page_has_csp_assert", false);
 // assertion flag will be set to false after fixing Bug 1473549
 pref("security.allow_eval_with_system_principal", false);
-pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,content-task.js,tree.xml,dialog.xml,preferencesbindings.js,wizard.xml,lodash.js,jszip.js,sinon-7.2.7.js,ajv-4.1.1.js,updates.js,setup,jsol.js,parent_utils.js,chrometask_chromescript");
+pref("security.uris_using_eval_with_system_principal", "autocomplete.xml,redux.js,react-redux.js,content-task.js,content-task.js,tree.xml,dialog.xml,preferencesbindings.js,wizard.xml,lodash.js,jszip.js,ajv-4.1.1.js,updates.js,setup,jsol.js,parent_utils.js,chrometask_chromescript");
 #endif
 
 // Default Content Security Policy to apply to signed contents.
 pref("security.signed_content.CSP.default", "script-src 'self'; style-src 'self'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
--- a/services/common/tests/unit/test_async_iterator.js
+++ b/services/common/tests/unit/test_async_iterator.js
@@ -1,14 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {Async} = ChromeUtils.import("resource://services-common/async.js");
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
+// Sinon seems to require setTimeout.
+const {setTimeout, clearTimeout, setInterval, clearInterval} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
 
 function makeArray(length) {
   // Start at 1 so that we can just divide by yieldEvery to get the expected
   // call count. (we exp)
   return Array.from({ length }, (v, i) => i + 1);
 }
 
 // Adjust if we ever change the default
--- a/services/fxaccounts/tests/xpcshell/head.js
+++ b/services/fxaccounts/tests/xpcshell/head.js
@@ -3,18 +3,25 @@
 
 /* import-globals-from ../../../common/tests/unit/head_helpers.js */
 /* import-globals-from ../../../common/tests/unit/head_http.js */
 
 "use strict";
 
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
 
 (function initFxAccountsTestingInfrastructure() {
   do_get_profile();
 
   let ns = {};
   ChromeUtils.import("resource://testing-common/services/common/logging.js", ns);
 
   ns.initTestLogging("Trace");
 }).call(this);
+
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+var {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
--- a/services/fxaccounts/tests/xpcshell/test_profile.js
+++ b/services/fxaccounts/tests/xpcshell/test_profile.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {ON_PROFILE_CHANGE_NOTIFICATION, log} = ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
 const {FxAccountsProfileClient} = ChromeUtils.import("resource://gre/modules/FxAccountsProfileClient.jsm");
 const {FxAccountsProfile} = ChromeUtils.import("resource://gre/modules/FxAccountsProfile.jsm");
 const {PromiseUtils} = ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
-const {setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
 let mockClient = function(fxa) {
   let options = {
     serverURL: "http://127.0.0.1:1111/v1",
     fxa,
   };
   return new FxAccountsProfileClient(options);
 };
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -9,17 +9,16 @@
 // This file expects Service to be defined in the global scope when EHTestsCommon
 // is used (from service.js).
 /* global Service */
 
 var {AddonTestUtils, MockAsyncShutdown} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 var {Async} = ChromeUtils.import("resource://services-common/async.js");
 var {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
 var {PlacesTestUtils} = ChromeUtils.import("resource://testing-common/PlacesTestUtils.jsm");
-var {sinon} = ChromeUtils.import("resource://testing-common/Sinon.jsm");
 var {SerializableSet, Svc, Utils, getChromeWindow} = ChromeUtils.import("resource://services-sync/util.js");
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 var {PlacesUtils} = ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 var {PlacesSyncUtils} = ChromeUtils.import("resource://gre/modules/PlacesSyncUtils.jsm");
 var {ObjectUtils} = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
 var {AccountState, MockFxaStorageManager, SyncTestingInfrastructure, configureFxAccountIdentity, configureIdentity, encryptPayload, getLoginTelemetryScalar, makeFxAccountsInternalMock, makeIdentityConfig, promiseNamedTimer, promiseZeroTimer, sumHistogram, syncTestLogging, waitForZeroTimer} = ChromeUtils.import("resource://testing-common/services/sync/utils.js");
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
@@ -29,16 +28,24 @@ add_task(async function head_setup() {
   // so it's also called as part of SyncTestingInfrastructure().
   syncTestLogging();
   // If a test imports Service, make sure it is initialized first.
   if (typeof Service !== "undefined") {
     await Service.promiseInitialized;
   }
 });
 
+// ================================================
+// Load mocking/stubbing library, sinon
+// docs: http://sinonjs.org/releases/v2.3.2/
+var {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
+Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
+/* globals sinon */
+// ================================================
+
 XPCOMUtils.defineLazyGetter(this, "SyncPingSchema", function() {
   let ns = {};
   ChromeUtils.import("resource://gre/modules/FileUtils.jsm", ns);
   ChromeUtils.import("resource://gre/modules/NetUtil.jsm", ns);
   let stream = Cc["@mozilla.org/network/file-input-stream;1"]
                .createInstance(Ci.nsIFileInputStream);
   let schema;
   try {
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -1579,17 +1579,17 @@ add_task(async function test_command_syn
 
   try {
     equal(collection.count(), 2, "2 remote records written");
     await syncClientsEngine(server);
     equal(collection.count(), 3, "3 remote records written (+1 for the synced local record)");
 
     await engine.sendCommand("wipeAll", []);
     await engine._tracker.addChangedID(engine.localID);
-    const getClientFxaDeviceId = sinon.stub(engine, "getClientFxaDeviceId").callsFake((id) => "fxa-" + id);
+    const getClientFxaDeviceId = sinon.stub(engine, "getClientFxaDeviceId", (id) => "fxa-" + id);
     const engineMock = sinon.mock(engine);
     let _notifyCollectionChanged = engineMock.expects("_notifyCollectionChanged")
                                              .withArgs(["fxa-" + remoteId, "fxa-" + remoteId2]);
     _("Syncing.");
     await syncClientsEngine(server);
     _notifyCollectionChanged.verify();
 
     engineMock.restore();
@@ -1791,32 +1791,32 @@ add_task(async function test_other_clien
 });
 
 add_task(async function device_disconnected_notification_updates_known_stale_clients() {
   const spyUpdate = sinon.spy(engine, "updateKnownStaleClients");
 
   Services.obs.notifyObservers(null, "fxaccounts:device_disconnected",
                                JSON.stringify({ isLocalDevice: false }));
   ok(spyUpdate.calledOnce, "updateKnownStaleClients should be called");
-  spyUpdate.resetHistory();
+  spyUpdate.reset();
 
   Services.obs.notifyObservers(null, "fxaccounts:device_disconnected",
                                JSON.stringify({ isLocalDevice: true }));
   ok(spyUpdate.notCalled, "updateKnownStaleClients should not be called");
 
   spyUpdate.restore();
 });
 
 add_task(async function update_known_stale_clients() {
   const makeFakeClient = (id) => ({ id, fxaDeviceId: `fxa-${id}` });
   const clients = [makeFakeClient("one"), makeFakeClient("two"), makeFakeClient("three")];
   const stubRemoteClients = sinon.stub(engine._store, "_remoteClients").get(() => {
     return clients;
   });
-  const stubFetchFxADevices = sinon.stub(engine, "_fetchFxADevices").callsFake(() => {
+  const stubFetchFxADevices = sinon.stub(engine, "_fetchFxADevices", () => {
     engine._knownStaleFxADeviceIds = ["fxa-one", "fxa-two"];
   });
 
   engine._knownStaleFxADeviceIds = null;
   await engine.updateKnownStaleClients();
   ok(clients[0].stale);
   ok(clients[1].stale);
   ok(!clients[2].stale);
@@ -1830,17 +1830,17 @@ add_task(async function test_create_reco
   await generateNewKeys(Service.collectionKeys);
 
   let server = await serverForFoo(engine);
   await SyncTestingInfrastructure(server);
 
   const fakeLimit = 4 * 1024;
 
   let maxSizeStub = sinon.stub(Service,
-    "getMemcacheMaxRecordPayloadSize").callsFake(() => fakeLimit);
+    "getMemcacheMaxRecordPayloadSize", () => fakeLimit);
 
   let user = server.user("foo");
   let remoteId = Utils.makeGUID();
 
   _("Create remote client record");
   user.collection("clients").insertRecord({
     id: remoteId,
     name: "Remote client",
--- a/services/sync/tests/unit/test_disconnect_shutdown.js
+++ b/services/sync/tests/unit/test_disconnect_shutdown.js
@@ -1,10 +1,11 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+/* global sinon */
 
 "use strict";
 
 const {SyncDisconnect, SyncDisconnectInternal} = ChromeUtils.import("resource://services-sync/SyncDisconnect.jsm", null);
 const {AsyncShutdown} = ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");
 
 add_task(async function test_shutdown_blocker() {
   let spySignout = sinon.stub(SyncDisconnectInternal, "doSyncAndAccountDisconnect");
--- a/services/sync/tests/unit/test_tab_store.js
+++ b/services/sync/tests/unit/test_tab_store.js
@@ -107,17 +107,17 @@ add_task(async function test_createRecor
   store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, numtabs);
   record = await store.createRecord("fake-guid");
   ok(record instanceof TabSetRecord);
   // This number is sensitive to our hard-coded default max record payload size
   // in service.js (256 * 1024). Given our mock session-store etc, it is the
   // actual max we can fit.
   equal(record.tabs.length, 2672);
 
-  let maxSizeStub = sinon.stub(Service, "getMemcacheMaxRecordPayloadSize").callsFake(() => 512 * 1024);
+  let maxSizeStub = sinon.stub(Service, "getMemcacheMaxRecordPayloadSize", () => 512 * 1024);
   try {
     numtabs = 5400;
     _("Modify the max record payload size and create a big record");
     store.getWindowEnumerator = mockGetWindowEnumerator.bind(this, "http://foo.com", 1, numtabs);
     record = await store.createRecord("fake-guid");
     ok(record instanceof TabSetRecord);
     equal(record.tabs.length, 5365);
   } finally {
--- a/services/sync/tests/unit/test_uistate.js
+++ b/services/sync/tests/unit/test_uistate.js
@@ -11,17 +11,17 @@ add_task(async function test_isReady_unc
   UIState.reset();
 
   let refreshState = sinon.spy(UIStateInternal, "refreshState");
 
   // On the first call, returns false
   // Does not trigger a refresh of the state since services.sync.username is undefined
   ok(!UIState.isReady());
   ok(!refreshState.called);
-  refreshState.resetHistory();
+  refreshState.reset();
 
   // On subsequent calls, only return true
   ok(UIState.isReady());
   ok(!refreshState.called);
 
   refreshState.restore();
 });
 
@@ -29,17 +29,17 @@ add_task(async function test_isReady_sig
   UIState.reset();
   Services.prefs.setCharPref("services.sync.username", "foo");
 
   let refreshState = sinon.spy(UIStateInternal, "refreshState");
 
   // On the first call, returns false and triggers a refresh of the state
   ok(!UIState.isReady());
   ok(refreshState.calledOnce);
-  refreshState.resetHistory();
+  refreshState.reset();
 
   // On subsequent calls, only return true
   ok(UIState.isReady());
   ok(!refreshState.called);
 
   refreshState.restore();
 });
 
@@ -235,17 +235,17 @@ add_task(async function test_observer_re
                        "fxaccounts:onlogin", "fxaccounts:onlogout",
                        "fxaccounts:profilechange"];
 
   for (let topic of shouldRefresh) {
     let uiUpdateObserved = observeUIUpdate();
     Services.obs.notifyObservers(null, topic);
     await uiUpdateObserved;
     ok(refreshState.calledOnce);
-    refreshState.resetHistory();
+    refreshState.reset();
   }
 
   refreshState.restore();
 });
 
 // Drive the UIState in a configured state.
 async function configureUIState(syncing, lastSync = new Date()) {
   UIState.reset();
deleted file mode 100644
--- a/testing/modules/Sinon.jsm
+++ /dev/null
@@ -1,22 +0,0 @@
-"use strict";
-
-var EXPORTED_SYMBOLS = ["sinon"];
-
-var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// ================================================
-// Load mocking/stubbing library sinon
-// docs: http://sinonjs.org/releases/v7.2.7/
-const {clearInterval, clearTimeout, setInterval, setIntervalWithTarget, setTimeout, setTimeoutWithTarget} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
-// eslint-disable-next-line no-unused-vars
-const global = {
-    clearInterval,
-    clearTimeout,
-    setInterval,
-    setIntervalWithTarget,
-    setTimeout,
-    setTimeoutWithTarget,
-};
-Services.scriptloader.loadSubScript("resource://testing-common/sinon-7.2.7.js", this);
-const sinon = global.sinon;
-// ================================================
--- a/testing/modules/moz.build
+++ b/testing/modules/moz.build
@@ -10,18 +10,17 @@ BROWSER_CHROME_MANIFESTS += ['tests/brow
 TESTING_JS_MODULES += [
     'ajv-4.1.1.js',
     'AppData.jsm',
     'AppInfo.jsm',
     'Assert.jsm',
     'CoverageUtils.jsm',
     'FileTestUtils.jsm',
     'MockRegistrar.jsm',
-    'sinon-7.2.7.js',
-    'Sinon.jsm',
+    'sinon-2.3.2.js',
     'StructuredLog.jsm',
     'TestUtils.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     TESTING_JS_MODULES += [
         'MockRegistry.jsm',
     ]
new file mode 100644
--- /dev/null
+++ b/testing/modules/sinon-2.3.2.js
@@ -0,0 +1,11778 @@
+/* Sinon.JS 2.3.2, 2017-05-26, @license BSD-3 */(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.sinon = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+"use strict";
+
+var match = require("./sinon/match");
+var deepEqual = require("./sinon/util/core/deep-equal");
+var deprecated = require("./sinon/util/core/deprecated");
+
+function exposeCoreUtils(target, utils) {
+    var keys = Object.keys(utils);
+
+    keys.forEach(function (key) {
+        var value = utils[key];
+
+        // allow deepEqual to check equality of matchers through dependency injection. Otherwise we get a circular
+        // dependency
+        if (key === "deepEqual") {
+            value = deepEqual.use(match);
+        }
+        if (typeof value === "function") {
+            value = deprecated.wrap(value, deprecated.defaultMsg(key));
+        }
+        target[key] = value;
+    });
+}
+
+function exposeEventTarget(target, eventTarget) {
+    var keys = Object.keys(eventTarget);
+
+    keys.forEach(function (key) {
+        target[key] = deprecated.wrap(eventTarget[key], deprecated.defaultMsg("EventTarget"));
+    });
+}
+
+// Expose internal utilities on `sinon` global for backwards compatibility.
+exposeCoreUtils(exports, require("./sinon/util/core/index"));
+
+exports.assert = require("./sinon/assert");
+exports.collection = require("./sinon/collection");
+exports.match = match;
+exports.spy = require("./sinon/spy");
+exports.spyCall = require("./sinon/call");
+exports.stub = require("./sinon/stub");
+exports.mock = require("./sinon/mock");
+exports.sandbox = require("./sinon/sandbox");
+exports.expectation = require("./sinon/mock-expectation");
+exports.createStubInstance = require("./sinon/stub").createStubInstance;
+
+var fakeTimers = require("./sinon/util/fake_timers");
+exports.useFakeTimers = fakeTimers.useFakeTimers;
+exports.clock = fakeTimers.clock;
+exports.timers = fakeTimers.timers;
+
+var event = require("./sinon/util/event");
+exports.Event = deprecated.wrap(event.Event, deprecated.defaultMsg("Event"));
+exports.CustomEvent = deprecated.wrap(event.CustomEvent, deprecated.defaultMsg("CustomEvent"));
+exports.ProgressEvent = deprecated.wrap(event.ProgressEvent, deprecated.defaultMsg("ProgressEvent"));
+exports.EventTarget = {};
+exposeEventTarget(exports.EventTarget, event.EventTarget);
+
+var fakeXhr = require("./sinon/util/fake_xml_http_request");
+exports.xhr = fakeXhr.xhr;
+exports.FakeXMLHttpRequest = fakeXhr.FakeXMLHttpRequest;
+exports.useFakeXMLHttpRequest = fakeXhr.useFakeXMLHttpRequest;
+
+exports.fakeServer = require("./sinon/util/fake_server");
+exports.fakeServerWithClock = require("./sinon/util/fake_server_with_clock");
+
+var behavior = require("./sinon/behavior");
+
+exports.addBehavior = function (name, fn) {
+    behavior.addBehavior(exports.stub, name, fn);
+};
+
+},{"./sinon/assert":2,"./sinon/behavior":3,"./sinon/call":5,"./sinon/collection":7,"./sinon/match":10,"./sinon/mock":12,"./sinon/mock-expectation":11,"./sinon/sandbox":14,"./sinon/spy":16,"./sinon/stub":20,"./sinon/util/core/deep-equal":23,"./sinon/util/core/deprecated":25,"./sinon/util/core/index":33,"./sinon/util/event":43,"./sinon/util/fake_server":44,"./sinon/util/fake_server_with_clock":45,"./sinon/util/fake_timers":46,"./sinon/util/fake_xml_http_request":47}],2:[function(require,module,exports){
+(function (global){
+"use strict";
+
+var calledInOrder = require("./util/core/called-in-order");
+var orderByFirstCall = require("./util/core/order-by-first-call");
+var timesInWords = require("./util/core/times-in-words");
+var format = require("./util/core/format");
+var sinonMatch = require("./match");
+
+var slice = Array.prototype.slice;
+
+var assert;
+
+function verifyIsStub() {
+    var args = Array.prototype.slice.call(arguments);
+
+    args.forEach(function (method) {
+        if (!method) {
+            assert.fail("fake is not a spy");
+        }
+
+        if (method.proxy && method.proxy.isSinonProxy) {
+            verifyIsStub(method.proxy);
+        } else {
+            if (typeof method !== "function") {
+                assert.fail(method + " is not a function");
+            }
+
+            if (typeof method.getCall !== "function") {
+                assert.fail(method + " is not stubbed");
+            }
+        }
+    });
+}
+
+function verifyIsValidAssertion(assertionMethod, assertionArgs) {
+    switch (assertionMethod) {
+        case "notCalled":
+        case "called":
+        case "calledOnce":
+        case "calledTwice":
+        case "calledThrice":
+            if (assertionArgs.length !== 0) {
+                assert.fail(assertionMethod +
+                            " takes 1 argument but was called with " + (assertionArgs.length + 1) + " arguments");
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+function failAssertion(object, msg) {
+    object = object || global;
+    var failMethod = object.fail || assert.fail;
+    failMethod.call(object, msg);
+}
+
+function mirrorPropAsAssertion(name, method, message) {
+    if (arguments.length === 2) {
+        message = method;
+        method = name;
+    }
+
+    assert[name] = function (fake) {
+        verifyIsStub(fake);
+
+        var args = slice.call(arguments, 1);
+        var failed = false;
+
+        verifyIsValidAssertion(name, args);
+
+        if (typeof method === "function") {
+            failed = !method(fake);
+        } else {
+            failed = typeof fake[method] === "function" ?
+                !fake[method].apply(fake, args) : !fake[method];
+        }
+
+        if (failed) {
+            failAssertion(this, (fake.printf || fake.proxy.printf).apply(fake, [message].concat(args)));
+        } else {
+            assert.pass(name);
+        }
+    };
+}
+
+function exposedName(prefix, prop) {
+    return !prefix || /^fail/.test(prop) ? prop :
+        prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
+}
+
+assert = {
+    failException: "AssertError",
+
+    fail: function fail(message) {
+        var error = new Error(message);
+        error.name = this.failException || assert.failException;
+
+        throw error;
+    },
+
+    pass: function pass() {},
+
+    callOrder: function assertCallOrder() {
+        verifyIsStub.apply(null, arguments);
+        var expected = "";
+        var actual = "";
+
+        if (!calledInOrder(arguments)) {
+            try {
+                expected = [].join.call(arguments, ", ");
+                var calls = slice.call(arguments);
+                var i = calls.length;
+                while (i) {
+                    if (!calls[--i].called) {
+                        calls.splice(i, 1);
+                    }
+                }
+                actual = orderByFirstCall(calls).join(", ");
+            } catch (e) {
+                // If this fails, we'll just fall back to the blank string
+            }
+
+            failAssertion(this, "expected " + expected + " to be " +
+                        "called in order but were called as " + actual);
+        } else {
+            assert.pass("callOrder");
+        }
+    },
+
+    callCount: function assertCallCount(method, count) {
+        verifyIsStub(method);
+
+        if (method.callCount !== count) {
+            var msg = "expected %n to be called " + timesInWords(count) +
+                " but was called %c%C";
+            failAssertion(this, method.printf(msg));
+        } else {
+            assert.pass("callCount");
+        }
+    },
+
+    expose: function expose(target, options) {
+        if (!target) {
+            throw new TypeError("target is null or undefined");
+        }
+
+        var o = options || {};
+        var prefix = typeof o.prefix === "undefined" && "assert" || o.prefix;
+        var includeFail = typeof o.includeFail === "undefined" || !!o.includeFail;
+        var instance = this;
+
+        Object.keys(instance).forEach(function (method) {
+            if (method !== "expose" && (includeFail || !/^(fail)/.test(method))) {
+                target[exposedName(prefix, method)] = instance[method];
+            }
+        });
+
+        return target;
+    },
+
+    match: function match(actual, expectation) {
+        var matcher = sinonMatch(expectation);
+        if (matcher.test(actual)) {
+            assert.pass("match");
+        } else {
+            var formatted = [
+                "expected value to match",
+                "    expected = " + format(expectation),
+                "    actual = " + format(actual)
+            ];
+
+            failAssertion(this, formatted.join("\n"));
+        }
+    }
+};
+
+mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
+mirrorPropAsAssertion("notCalled", function (spy) {
+    return !spy.called;
+}, "expected %n to not have been called but was called %c%C");
+mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
+mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
+mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
+mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
+mirrorPropAsAssertion(
+    "alwaysCalledOn",
+    "expected %n to always be called with %1 as this but was called with %t"
+);
+mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
+mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
+mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %D");
+mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %D");
+mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %D");
+mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %D");
+mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %D");
+mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %D");
+mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
+mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
+mirrorPropAsAssertion("threw", "%n did not throw exception%C");
+mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
+
+module.exports = assert;
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"./match":10,"./util/core/called-in-order":22,"./util/core/format":28,"./util/core/order-by-first-call":36,"./util/core/times-in-words":38}],3:[function(require,module,exports){
+(function (process){
+"use strict";
+
+var extend = require("./util/core/extend");
+var functionName = require("./util/core/function-name");
+var valueToString = require("./util/core/value-to-string");
+
+var slice = Array.prototype.slice;
+var join = Array.prototype.join;
+var useLeftMostCallback = -1;
+var useRightMostCallback = -2;
+
+var nextTick = (function () {
+    if (typeof process === "object" && typeof process.nextTick === "function") {
+        return process.nextTick;
+    }
+
+    if (typeof setImmediate === "function") {
+        return setImmediate;
+    }
+
+    return function (callback) {
+        setTimeout(callback, 0);
+    };
+})();
+
+function getCallback(behavior, args) {
+    var callArgAt = behavior.callArgAt;
+
+    if (callArgAt >= 0) {
+        return args[callArgAt];
+    }
+
+    var argumentList;
+
+    if (callArgAt === useLeftMostCallback) {
+        argumentList = args;
+    }
+
+    if (callArgAt === useRightMostCallback) {
+        argumentList = slice.call(args).reverse();
+    }
+
+    var callArgProp = behavior.callArgProp;
+
+    for (var i = 0, l = argumentList.length; i < l; ++i) {
+        if (!callArgProp && typeof argumentList[i] === "function") {
+            return argumentList[i];
+        }
+
+        if (callArgProp && argumentList[i] &&
+            typeof argumentList[i][callArgProp] === "function") {
+            return argumentList[i][callArgProp];
+        }
+    }
+
+    return null;
+}
+
+function getCallbackError(behavior, func, args) {
+    if (behavior.callArgAt < 0) {
+        var msg;
+
+        if (behavior.callArgProp) {
+            msg = functionName(behavior.stub) +
+                " expected to yield to '" + valueToString(behavior.callArgProp) +
+                "', but no object with such a property was passed.";
+        } else {
+            msg = functionName(behavior.stub) +
+                " expected to yield, but no callback was passed.";
+        }
+
+        if (args.length > 0) {
+            msg += " Received [" + join.call(args, ", ") + "]";
+        }
+
+        return msg;
+    }
+
+    return "argument at index " + behavior.callArgAt + " is not a function: " + func;
+}
+
+function callCallback(behavior, args) {
+    if (typeof behavior.callArgAt === "number") {
+        var func = getCallback(behavior, args);
+
+        if (typeof func !== "function") {
+            throw new TypeError(getCallbackError(behavior, func, args));
+        }
+
+        if (behavior.callbackAsync) {
+            nextTick(function () {
+                func.apply(behavior.callbackContext, behavior.callbackArguments);
+            });
+        } else {
+            func.apply(behavior.callbackContext, behavior.callbackArguments);
+        }
+    }
+}
+
+var proto = {
+    create: function create(stub) {
+        var behavior = extend({}, proto);
+        delete behavior.create;
+        delete behavior.addBehavior;
+        delete behavior.createBehavior;
+        behavior.stub = stub;
+
+        return behavior;
+    },
+
+    isPresent: function isPresent() {
+        return (typeof this.callArgAt === "number" ||
+                this.exception ||
+                typeof this.returnArgAt === "number" ||
+                this.returnThis ||
+                typeof this.throwArgAt === "number" ||
+                this.fakeFn ||
+                this.returnValueDefined);
+    },
+
+    invoke: function invoke(context, args) {
+        callCallback(this, args);
+
+        if (this.exception) {
+            throw this.exception;
+        } else if (typeof this.returnArgAt === "number") {
+            return args[this.returnArgAt];
+        } else if (this.returnThis) {
+            return context;
+        } else if (typeof this.throwArgAt === "number") {
+            if (args.length < this.throwArgAt) {
+                throw new TypeError(
+                    "throwArgs failed: " + this.throwArgAt
+                    + " arguments required but only " + args.length
+                    + " present"
+                );
+            }
+            throw args[this.throwArgAt];
+        } else if (this.fakeFn) {
+            return this.fakeFn.apply(context, args);
+        } else if (this.resolve) {
+            return (this.promiseLibrary || Promise).resolve(this.returnValue);
+        } else if (this.reject) {
+            return (this.promiseLibrary || Promise).reject(this.returnValue);
+        } else if (this.callsThrough) {
+            return this.stub.wrappedMethod.apply(context, args);
+        }
+        return this.returnValue;
+    },
+
+    onCall: function onCall(index) {
+        return this.stub.onCall(index);
+    },
+
+    onFirstCall: function onFirstCall() {
+        return this.stub.onFirstCall();
+    },
+
+    onSecondCall: function onSecondCall() {
+        return this.stub.onSecondCall();
+    },
+
+    onThirdCall: function onThirdCall() {
+        return this.stub.onThirdCall();
+    },
+
+    withArgs: function withArgs(/* arguments */) {
+        throw new Error(
+            "Defining a stub by invoking \"stub.onCall(...).withArgs(...)\" " +
+            "is not supported. Use \"stub.withArgs(...).onCall(...)\" " +
+            "to define sequential behavior for calls with certain arguments."
+        );
+    }
+};
+
+function createAsyncVersion(syncFnName) {
+    return function () {
+        var result = this[syncFnName].apply(this, arguments);
+        this.callbackAsync = true;
+        return result;
+    };
+}
+
+// create asynchronous versions of callsArg* and yields* methods
+Object.keys(proto).forEach(function (method) {
+    // need to avoid creating anotherasync versions of the newly added async methods
+    if (method.match(/^(callsArg|yields)/) && !method.match(/Async/)) {
+        proto[method + "Async"] = createAsyncVersion(method);
+    }
+});
+
+function createBehavior(behaviorMethod) {
+    return function () {
+        this.defaultBehavior = this.defaultBehavior || proto.create(this);
+        this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
+        return this;
+    };
+}
+
+function addBehavior(stub, name, fn) {
+    proto[name] = function () {
+        fn.apply(this, [this].concat([].slice.call(arguments)));
+        return this.stub || this;
+    };
+
+    stub[name] = createBehavior(name);
+}
+
+proto.addBehavior = addBehavior;
+proto.createBehavior = createBehavior;
+module.exports = proto;
+
+}).call(this,require('_process'))
+
+},{"./util/core/extend":27,"./util/core/function-name":29,"./util/core/value-to-string":40,"_process":68}],4:[function(require,module,exports){
+/*global Blob */
+"use strict";
+
+exports.isSupported = (function () {
+    try {
+        return !!new Blob();
+    } catch (e) {
+        return false;
+    }
+}());
+
+},{}],5:[function(require,module,exports){
+"use strict";
+
+var sinonMatch = require("./match");
+var deepEqual = require("./util/core/deep-equal").use(sinonMatch);
+var functionName = require("./util/core/function-name");
+var sinonFormat = require("./util/core/format");
+var valueToString = require("./util/core/value-to-string");
+var slice = Array.prototype.slice;
+
+function throwYieldError(proxy, text, args) {
+    var msg = functionName(proxy) + text;
+    if (args.length) {
+        msg += " Received [" + slice.call(args).join(", ") + "]";
+    }
+    throw new Error(msg);
+}
+
+var callProto = {
+    calledOn: function calledOn(thisValue) {
+        if (sinonMatch && sinonMatch.isMatcher(thisValue)) {
+            return thisValue.test(this.thisValue);
+        }
+        return this.thisValue === thisValue;
+    },
+
+    calledWith: function calledWith() {
+        var self = this;
+        var calledWithArgs = slice.call(arguments);
+
+        if (calledWithArgs.length > self.args.length) {
+            return false;
+        }
+
+        return calledWithArgs.reduce(function (prev, arg, i) {
+            return prev && deepEqual(arg, self.args[i]);
+        }, true);
+    },
+
+    calledWithMatch: function calledWithMatch() {
+        var self = this;
+        var calledWithMatchArgs = slice.call(arguments);
+
+        if (calledWithMatchArgs.length > self.args.length) {
+            return false;
+        }
+
+        return calledWithMatchArgs.reduce(function (prev, expectation, i) {
+            var actual = self.args[i];
+
+            return prev && (sinonMatch && sinonMatch(expectation).test(actual));
+        }, true);
+    },
+
+    calledWithExactly: function calledWithExactly() {
+        return arguments.length === this.args.length &&
+            this.calledWith.apply(this, arguments);
+    },
+
+    notCalledWith: function notCalledWith() {
+        return !this.calledWith.apply(this, arguments);
+    },
+
+    notCalledWithMatch: function notCalledWithMatch() {
+        return !this.calledWithMatch.apply(this, arguments);
+    },
+
+    returned: function returned(value) {
+        return deepEqual(value, this.returnValue);
+    },
+
+    threw: function threw(error) {
+        if (typeof error === "undefined" || !this.exception) {
+            return !!this.exception;
+        }
+
+        return this.exception === error || this.exception.name === error;
+    },
+
+    calledWithNew: function calledWithNew() {
+        return this.proxy.prototype && this.thisValue instanceof this.proxy;
+    },
+
+    calledBefore: function (other) {
+        return this.callId < other.callId;
+    },
+
+    calledAfter: function (other) {
+        return this.callId > other.callId;
+    },
+
+    calledImmediatelyBefore: function (other) {
+        return this.callId === other.callId - 1;
+    },
+
+    calledImmediatelyAfter: function (other) {
+        return this.callId === other.callId + 1;
+    },
+
+    callArg: function (pos) {
+        this.args[pos]();
+    },
+
+    callArgOn: function (pos, thisValue) {
+        this.args[pos].apply(thisValue);
+    },
+
+    callArgWith: function (pos) {
+        this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
+    },
+
+    callArgOnWith: function (pos, thisValue) {
+        var args = slice.call(arguments, 2);
+        this.args[pos].apply(thisValue, args);
+    },
+
+    throwArg: function (pos) {
+        if (pos > this.args.length) {
+            throw new TypeError(
+                "Not enough arguments: " + pos
+                + " required but only " + this.args.length
+                + " present"
+            );
+        }
+
+        throw this.args[pos];
+    },
+
+    "yield": function () {
+        this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
+    },
+
+    yieldOn: function (thisValue) {
+        var args = slice.call(this.args);
+        var yieldFn = args.filter(function (arg) {
+            return typeof arg === "function";
+        })[0];
+
+        if (!yieldFn) {
+            throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
+        }
+
+        yieldFn.apply(thisValue, slice.call(arguments, 1));
+    },
+
+    yieldTo: function (prop) {
+        this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
+    },
+
+    yieldToOn: function (prop, thisValue) {
+        var args = slice.call(this.args);
+        var yieldArg = args.filter(function (arg) {
+            return arg && typeof arg[prop] === "function";
+        })[0];
+        var yieldFn = yieldArg && yieldArg[prop];
+
+        if (!yieldFn) {
+            throwYieldError(this.proxy, " cannot yield to '" + valueToString(prop) +
+                "' since no callback was passed.", args);
+        }
+
+        yieldFn.apply(thisValue, slice.call(arguments, 2));
+    },
+
+    toString: function () {
+        var callStr = this.proxy ? this.proxy.toString() + "(" : "";
+        var formattedArgs;
+
+        if (!this.args) {
+            return ":(";
+        }
+
+        formattedArgs = slice.call(this.args).map(function (arg) {
+            return sinonFormat(arg);
+        });
+
+        callStr = callStr + formattedArgs.join(", ") + ")";
+
+        if (typeof this.returnValue !== "undefined") {
+            callStr += " => " + sinonFormat(this.returnValue);
+        }
+
+        if (this.exception) {
+            callStr += " !" + this.exception.name;
+
+            if (this.exception.message) {
+                callStr += "(" + this.exception.message + ")";
+            }
+        }
+        if (this.stack) {
+            // Omit the error message and the two top stack frames in sinon itself:
+            callStr += ( this.stack.split("\n")[3] || "unknown" ).replace(/^\s*(?:at\s+|@)?/, " at ");
+        }
+
+        return callStr;
+    }
+};
+Object.defineProperty(callProto, "stack", {
+    enumerable: true,
+    configurable: true,
+    get: function () {
+        return this.errorWithCallStack && this.errorWithCallStack.stack || "";
+    }
+});
+
+callProto.invokeCallback = callProto.yield;
+
+function createSpyCall(spy, thisValue, args, returnValue, exception, id, errorWithCallStack) {
+    if (typeof id !== "number") {
+        throw new TypeError("Call id is not a number");
+    }
+    var proxyCall = Object.create(callProto);
+    proxyCall.proxy = spy;
+    proxyCall.thisValue = thisValue;
+    proxyCall.args = args;
+    proxyCall.returnValue = returnValue;
+    proxyCall.exception = exception;
+    proxyCall.callId = id;
+    proxyCall.errorWithCallStack = errorWithCallStack;
+
+    return proxyCall;
+}
+createSpyCall.toString = callProto.toString; // used by mocks
+
+module.exports = createSpyCall;
+
+},{"./match":10,"./util/core/deep-equal":23,"./util/core/format":28,"./util/core/function-name":29,"./util/core/value-to-string":40}],6:[function(require,module,exports){
+"use strict";
+
+var walk = require("./util/core/walk");
+var getPropertyDescriptor = require("./util/core/get-property-descriptor");
+
+function collectMethod(methods, object, prop, propOwner) {
+    if (
+        typeof getPropertyDescriptor(propOwner, prop).value === "function" &&
+        object.hasOwnProperty(prop)
+    ) {
+        methods.push(object[prop]);
+    }
+}
+
+// This function returns an array of all the own methods on the passed object
+function collectOwnMethods(object) {
+    var methods = [];
+
+    walk(object, collectMethod.bind(null, methods, object));
+
+    return methods;
+}
+
+module.exports = collectOwnMethods;
+
+},{"./util/core/get-property-descriptor":32,"./util/core/walk":41}],7:[function(require,module,exports){
+"use strict";
+
+var sinonSpy = require("./spy");
+var sinonStub = require("./stub");
+var sinonMock = require("./mock");
+var sandboxStub = require("./sandbox-stub");
+var collectOwnMethods = require("./collect-own-methods");
+
+var push = [].push;
+
+function getFakes(fakeCollection) {
+    if (!fakeCollection.fakes) {
+        fakeCollection.fakes = [];
+    }
+
+    return fakeCollection.fakes;
+}
+
+function each(fakeCollection, method) {
+    var fakes = getFakes(fakeCollection);
+    var matchingFakes = fakes.filter(function (fake) {
+        return typeof fake[method] === "function";
+    });
+
+    matchingFakes.forEach(function (fake) {
+        fake[method]();
+    });
+}
+
+var collection = {
+    verify: function verify() {
+        each(this, "verify");
+    },
+
+    restore: function restore() {
+        each(this, "restore");
+        this.fakes = [];
+    },
+
+    reset: function reset() {
+        each(this, "reset");
+    },
+
+    resetBehavior: function resetBehavior() {
+        each(this, "resetBehavior");
+    },
+
+    resetHistory: function resetHistory() {
+        each(this, "resetHistory");
+    },
+
+    verifyAndRestore: function verifyAndRestore() {
+        var exception;
+
+        try {
+            this.verify();
+        } catch (e) {
+            exception = e;
+        }
+
+        this.restore();
+
+        if (exception) {
+            throw exception;
+        }
+    },
+
+    add: function add(fake) {
+        push.call(getFakes(this), fake);
+        return fake;
+    },
+
+    addUsingPromise: function (fake) {
+        fake.usingPromise(this.promiseLibrary);
+        return fake;
+    },
+
+    spy: function spy() {
+        return this.add(sinonSpy.apply(sinonSpy, arguments));
+    },
+
+    stub: function stub(object, property/*, value*/) {
+        if (arguments.length > 2) {
+            return sandboxStub.apply(this, arguments);
+        }
+
+        var stubbed = sinonStub.apply(null, arguments);
+        var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
+
+        if (isStubbingEntireObject) {
+            var ownMethods = collectOwnMethods(stubbed);
+            ownMethods.forEach(this.add.bind(this));
+            if (this.promiseLibrary) {
+                ownMethods.forEach(this.addUsingPromise.bind(this));
+            }
+        } else {
+            this.add(stubbed);
+            if (this.promiseLibrary) {
+                stubbed.usingPromise(this.promiseLibrary);
+            }
+        }
+
+        return stubbed;
+    },
+
+    mock: function mock() {
+        return this.add(sinonMock.apply(null, arguments));
+    },
+
+    inject: function inject(obj) {
+        var col = this;
+
+        obj.spy = function () {
+            return col.spy.apply(col, arguments);
+        };
+
+        obj.stub = function () {
+            return col.stub.apply(col, arguments);
+        };
+
+        obj.mock = function () {
+            return col.mock.apply(col, arguments);
+        };
+
+        return obj;
+    }
+};
+
+module.exports = collection;
+
+},{"./collect-own-methods":6,"./mock":12,"./sandbox-stub":13,"./spy":16,"./stub":20}],8:[function(require,module,exports){
+(function (process){
+"use strict";
+
+var canColor = typeof process !== "undefined";
+
+function colorize(str, color) {
+    if (!canColor) {
+        return str;
+    }
+
+    return "\x1b[" + color + "m" + str + "\x1b[0m";
+}
+
+exports.red = function (str) {
+    return colorize(str, 31);
+};
+
+exports.green = function (str) {
+    return colorize(str, 32);
+};
+
+}).call(this,require('_process'))
+
+},{"_process":68}],9:[function(require,module,exports){
+"use strict";
+var slice = [].slice;
+var useLeftMostCallback = -1;
+var useRightMostCallback = -2;
+
+function throwsException(fake, error, message) {
+    if (typeof error === "string") {
+        fake.exception = new Error(message || "");
+        fake.exception.name = error;
+    } else if (!error) {
+        fake.exception = new Error("Error");
+    } else {
+        fake.exception = error;
+    }
+}
+
+module.exports = {
+    callsFake: function callsFake(fake, fn) {
+        fake.fakeFn = fn;
+    },
+
+    callsArg: function callsArg(fake, pos) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.callArgAt = pos;
+        fake.callbackArguments = [];
+        fake.callbackContext = undefined;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    callsArgOn: function callsArgOn(fake, pos, context) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.callArgAt = pos;
+        fake.callbackArguments = [];
+        fake.callbackContext = context;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    callsArgWith: function callsArgWith(fake, pos) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.callArgAt = pos;
+        fake.callbackArguments = slice.call(arguments, 2);
+        fake.callbackContext = undefined;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    callsArgOnWith: function callsArgWith(fake, pos, context) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.callArgAt = pos;
+        fake.callbackArguments = slice.call(arguments, 3);
+        fake.callbackContext = context;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    usingPromise: function usingPromise(fake, promiseLibrary) {
+        fake.promiseLibrary = promiseLibrary;
+    },
+
+    yields: function (fake) {
+        fake.callArgAt = useLeftMostCallback;
+        fake.callbackArguments = slice.call(arguments, 1);
+        fake.callbackContext = undefined;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    yieldsRight: function (fake) {
+        fake.callArgAt = useRightMostCallback;
+        fake.callbackArguments = slice.call(arguments, 1);
+        fake.callbackContext = undefined;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    yieldsOn: function (fake, context) {
+        fake.callArgAt = useLeftMostCallback;
+        fake.callbackArguments = slice.call(arguments, 2);
+        fake.callbackContext = context;
+        fake.callArgProp = undefined;
+        fake.callbackAsync = false;
+    },
+
+    yieldsTo: function (fake, prop) {
+        fake.callArgAt = useLeftMostCallback;
+        fake.callbackArguments = slice.call(arguments, 2);
+        fake.callbackContext = undefined;
+        fake.callArgProp = prop;
+        fake.callbackAsync = false;
+    },
+
+    yieldsToOn: function (fake, prop, context) {
+        fake.callArgAt = useLeftMostCallback;
+        fake.callbackArguments = slice.call(arguments, 3);
+        fake.callbackContext = context;
+        fake.callArgProp = prop;
+        fake.callbackAsync = false;
+    },
+
+    throws: throwsException,
+    throwsException: throwsException,
+
+    returns: function returns(fake, value) {
+        fake.returnValue = value;
+        fake.resolve = false;
+        fake.reject = false;
+        fake.returnValueDefined = true;
+        fake.exception = undefined;
+        fake.fakeFn = undefined;
+    },
+
+    returnsArg: function returnsArg(fake, pos) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.returnArgAt = pos;
+    },
+
+    throwsArg: function throwsArg(fake, pos) {
+        if (typeof pos !== "number") {
+            throw new TypeError("argument index is not number");
+        }
+
+        fake.throwArgAt = pos;
+    },
+
+    returnsThis: function returnsThis(fake) {
+        fake.returnThis = true;
+    },
+
+    resolves: function resolves(fake, value) {
+        fake.returnValue = value;
+        fake.resolve = true;
+        fake.reject = false;
+        fake.returnValueDefined = true;
+        fake.exception = undefined;
+        fake.fakeFn = undefined;
+    },
+
+    rejects: function rejects(fake, error, message) {
+        var reason;
+        if (typeof error === "string") {
+            reason = new Error(message || "");
+            reason.name = error;
+        } else if (!error) {
+            reason = new Error("Error");
+        } else {
+            reason = error;
+        }
+        fake.returnValue = reason;
+        fake.resolve = false;
+        fake.reject = true;
+        fake.returnValueDefined = true;
+        fake.exception = undefined;
+        fake.fakeFn = undefined;
+
+        return fake;
+    },
+
+    callThrough: function callThrough(fake) {
+        fake.callsThrough = true;
+    },
+
+    get: function get(fake, getterFunction) {
+        var rootStub = fake.stub || fake;
+
+        Object.defineProperty(rootStub.rootObj, rootStub.propName, {
+            get: getterFunction,
+            configurable: true
+        });
+
+        return fake;
+    },
+
+    set: function set(fake, setterFunction) {
+        var rootStub = fake.stub || fake;
+
+        Object.defineProperty(rootStub.rootObj, rootStub.propName, { // eslint-disable-line accessor-pairs
+            set: setterFunction,
+            configurable: true
+        });
+
+        return fake;
+    },
+
+    value: function value(fake, newVal) {
+        var rootStub = fake.stub || fake;
+
+        Object.defineProperty(rootStub.rootObj, rootStub.propName, {
+            value: newVal,
+            enumerable: true,
+            configurable: true
+        });
+
+        return fake;
+    }
+};
+
+function createAsyncVersion(syncFnName) {
+    return function () {
+        var result = module.exports[syncFnName].apply(this, arguments);
+        this.callbackAsync = true;
+        return result;
+    };
+}
+
+// create asynchronous versions of callsArg* and yields* methods
+Object.keys(module.exports).forEach(function (method) {
+    // need to avoid creating anotherasync versions of the newly added async methods
+    if (method.match(/^(callsArg|yields)/) && !method.match(/Async/)) {
+        module.exports[method + "Async"] = createAsyncVersion(method);
+    }
+});
+
+},{}],10:[function(require,module,exports){
+"use strict";
+
+var deepEqual = require("./util/core/deep-equal").use(match); // eslint-disable-line no-use-before-define
+var every = require("./util/core/every");
+var functionName = require("./util/core/function-name");
+var iterableToString = require("./util/core/iterable-to-string");
+var typeOf = require("./util/core/typeOf");
+var valueToString = require("./util/core/value-to-string");
+
+var indexOf = Array.prototype.indexOf;
+
+function assertType(value, type, name) {
+    var actual = typeOf(value);
+    if (actual !== type) {
+        throw new TypeError("Expected type of " + name + " to be " +
+            type + ", but was " + actual);
+    }
+}
+
+var matcher = {
+    toString: function () {
+        return this.message;
+    }
+};
+
+function isMatcher(object) {
+    return matcher.isPrototypeOf(object);
+}
+
+function matchObject(expectation, actual) {
+    if (actual === null || actual === undefined) {
+        return false;
+    }
+
+    return Object.keys(expectation).every(function (key) {
+        var exp = expectation[key];
+        var act = actual[key];
+
+        if (isMatcher(exp)) {
+            if (!exp.test(act)) {
+                return false;
+            }
+        } else if (typeOf(exp) === "object") {
+            if (!matchObject(exp, act)) {
+                return false;
+            }
+        } else if (!deepEqual(exp, act)) {
+            return false;
+        }
+
+        return true;
+    });
+}
+
+var TYPE_MAP = {
+    "function": function (m, expectation, message) {
+        m.test = expectation;
+        m.message = message || "match(" + functionName(expectation) + ")";
+    },
+    number: function (m, expectation) {
+        m.test = function (actual) {
+            // we need type coercion here
+            return expectation == actual; // eslint-disable-line eqeqeq
+        };
+    },
+    object: function (m, expectation) {
+        var array = [];
+
+        if (typeof expectation.test === "function") {
+            m.test = function (actual) {
+                return expectation.test(actual) === true;
+            };
+            m.message = "match(" + functionName(expectation.test) + ")";
+            return m;
+        }
+
+        array = Object.keys(expectation).map(function (key) {
+            return key + ": " + valueToString(expectation[key]);
+        });
+
+        m.test = function (actual) {
+            return matchObject(expectation, actual);
+        };
+        m.message = "match(" + array.join(", ") + ")";
+
+        return m;
+    },
+    regexp: function (m, expectation) {
+        m.test = function (actual) {
+            return typeof actual === "string" && expectation.test(actual);
+        };
+    },
+    string: function (m, expectation) {
+        m.test = function (actual) {
+            return typeof actual === "string" && actual.indexOf(expectation) !== -1;
+        };
+        m.message = "match(\"" + expectation + "\")";
+    }
+};
+
+function match(expectation, message) {
+    var m = Object.create(matcher);
+    var type = typeOf(expectation);
+
+    if (type in TYPE_MAP) {
+        TYPE_MAP[type](m, expectation, message);
+    } else {
+        m.test = function (actual) {
+            return deepEqual(expectation, actual);
+        };
+    }
+
+    if (!m.message) {
+        m.message = "match(" + valueToString(expectation) + ")";
+    }
+
+    return m;
+}
+
+matcher.or = function (m2) {
+    if (!arguments.length) {
+        throw new TypeError("Matcher expected");
+    } else if (!isMatcher(m2)) {
+        m2 = match(m2);
+    }
+    var m1 = this;
+    var or = Object.create(matcher);
+    or.test = function (actual) {
+        return m1.test(actual) || m2.test(actual);
+    };
+    or.message = m1.message + ".or(" + m2.message + ")";
+    return or;
+};
+
+matcher.and = function (m2) {
+    if (!arguments.length) {
+        throw new TypeError("Matcher expected");
+    } else if (!isMatcher(m2)) {
+        m2 = match(m2);
+    }
+    var m1 = this;
+    var and = Object.create(matcher);
+    and.test = function (actual) {
+        return m1.test(actual) && m2.test(actual);
+    };
+    and.message = m1.message + ".and(" + m2.message + ")";
+    return and;
+};
+
+match.isMatcher = isMatcher;
+
+match.any = match(function () {
+    return true;
+}, "any");
+
+match.defined = match(function (actual) {
+    return actual !== null && actual !== undefined;
+}, "defined");
+
+match.truthy = match(function (actual) {
+    return !!actual;
+}, "truthy");
+
+match.falsy = match(function (actual) {
+    return !actual;
+}, "falsy");
+
+match.same = function (expectation) {
+    return match(function (actual) {
+        return expectation === actual;
+    }, "same(" + valueToString(expectation) + ")");
+};
+
+match.typeOf = function (type) {
+    assertType(type, "string", "type");
+    return match(function (actual) {
+        return typeOf(actual) === type;
+    }, "typeOf(\"" + type + "\")");
+};
+
+match.instanceOf = function (type) {
+    assertType(type, "function", "type");
+    return match(function (actual) {
+        return actual instanceof type;
+    }, "instanceOf(" + functionName(type) + ")");
+};
+
+function createPropertyMatcher(propertyTest, messagePrefix) {
+    return function (property, value) {
+        assertType(property, "string", "property");
+        var onlyProperty = arguments.length === 1;
+        var message = messagePrefix + "(\"" + property + "\"";
+        if (!onlyProperty) {
+            message += ", " + valueToString(value);
+        }
+        message += ")";
+        return match(function (actual) {
+            if (actual === undefined || actual === null ||
+                    !propertyTest(actual, property)) {
+                return false;
+            }
+            return onlyProperty || deepEqual(value, actual[property]);
+        }, message);
+    };
+}
+
+match.has = createPropertyMatcher(function (actual, property) {
+    if (typeof actual === "object") {
+        return property in actual;
+    }
+    return actual[property] !== undefined;
+}, "has");
+
+match.hasOwn = createPropertyMatcher(function (actual, property) {
+    return actual.hasOwnProperty(property);
+}, "hasOwn");
+
+match.array = match.typeOf("array");
+
+match.array.deepEquals = function (expectation) {
+    return match(function (actual) {
+        // Comparing lengths is the fastest way to spot a difference before iterating through every item
+        var sameLength = actual.length === expectation.length;
+        return typeOf(actual) === "array" && sameLength && every(actual, function (element, index) {
+            return expectation[index] === element;
+        });
+    }, "deepEquals([" + iterableToString(expectation) + "])");
+};
+
+match.array.startsWith = function (expectation) {
+    return match(function (actual) {
+        return typeOf(actual) === "array" && every(expectation, function (expectedElement, index) {
+            return actual[index] === expectedElement;
+        });
+    }, "startsWith([" + iterableToString(expectation) + "])");
+};
+
+match.array.endsWith = function (expectation) {
+    return match(function (actual) {
+        // This indicates the index in which we should start matching
+        var offset = actual.length - expectation.length;
+
+        return typeOf(actual) === "array" && every(expectation, function (expectedElement, index) {
+            return actual[offset + index] === expectedElement;
+        });
+    }, "endsWith([" + iterableToString(expectation) + "])");
+};
+
+match.array.contains = function (expectation) {
+    return match(function (actual) {
+        return typeOf(actual) === "array" && every(expectation, function (expectedElement) {
+            return indexOf.call(actual, expectedElement) !== -1;
+        });
+    }, "contains([" + iterableToString(expectation) + "])");
+};
+
+match.map = match.typeOf("map");
+
+match.map.deepEquals = function mapDeepEquals(expectation) {
+    return match(function (actual) {
+        // Comparing lengths is the fastest way to spot a difference before iterating through every item
+        var sameLength = actual.size === expectation.size;
+        return typeOf(actual) === "map" && sameLength && every(actual, function (element, key) {
+            return expectation.has(key) && expectation.get(key) === element;
+        });
+    }, "deepEquals(Map[" + iterableToString(expectation) + "])");
+};
+
+match.map.contains = function mapContains(expectation) {
+    return match(function (actual) {
+        return typeOf(actual) === "map" && every(expectation, function (element, key) {
+            return actual.has(key) && actual.get(key) === element;
+        });
+    }, "contains(Map[" + iterableToString(expectation) + "])");
+};
+
+match.set = match.typeOf("set");
+
+match.set.deepEquals = function setDeepEquals(expectation) {
+    return match(function (actual) {
+        // Comparing lengths is the fastest way to spot a difference before iterating through every item
+        var sameLength = actual.size === expectation.size;
+        return typeOf(actual) === "set" && sameLength && every(actual, function (element) {
+            return expectation.has(element);
+        });
+    }, "deepEquals(Set[" + iterableToString(expectation) + "])");
+};
+
+match.set.contains = function setContains(expectation) {
+    return match(function (actual) {
+        return typeOf(actual) === "set" && every(expectation, function (element) {
+            return actual.has(element);
+        });
+    }, "contains(Set[" + iterableToString(expectation) + "])");
+};
+
+match.bool = match.typeOf("boolean");
+match.number = match.typeOf("number");
+match.string = match.typeOf("string");
+match.object = match.typeOf("object");
+match.func = match.typeOf("function");
+match.regexp = match.typeOf("regexp");
+match.date = match.typeOf("date");
+match.symbol = match.typeOf("symbol");
+
+module.exports = match;
+
+},{"./util/core/deep-equal":23,"./util/core/every":26,"./util/core/function-name":29,"./util/core/iterable-to-string":34,"./util/core/typeOf":39,"./util/core/value-to-string":40}],11:[function(require,module,exports){
+"use strict";
+
+var spyInvoke = require("./spy").invoke;
+var spyCallToString = require("./call").toString;
+var timesInWords = require("./util/core/times-in-words");
+var extend = require("./util/core/extend");
+var match = require("./match");
+var stub = require("./stub");
+var assert = require("./assert");
+var deepEqual = require("./util/core/deep-equal").use(match);
+var format = require("./util/core/format");
+var valueToString = require("./util/core/value-to-string");
+
+var slice = Array.prototype.slice;
+var push = Array.prototype.push;
+
+function callCountInWords(callCount) {
+    if (callCount === 0) {
+        return "never called";
+    }
+
+    return "called " + timesInWords(callCount);
+}
+
+function expectedCallCountInWords(expectation) {
+    var min = expectation.minCalls;
+    var max = expectation.maxCalls;
+
+    if (typeof min === "number" && typeof max === "number") {
+        var str = timesInWords(min);
+
+        if (min !== max) {
+            str = "at least " + str + " and at most " + timesInWords(max);
+        }
+
+        return str;
+    }
+
+    if (typeof min === "number") {
+        return "at least " + timesInWords(min);
+    }
+
+    return "at most " + timesInWords(max);
+}
+
+function receivedMinCalls(expectation) {
+    var hasMinLimit = typeof expectation.minCalls === "number";
+    return !hasMinLimit || expectation.callCount >= expectation.minCalls;
+}
+
+function receivedMaxCalls(expectation) {
+    if (typeof expectation.maxCalls !== "number") {
+        return false;
+    }
+
+    return expectation.callCount === expectation.maxCalls;
+}
+
+function verifyMatcher(possibleMatcher, arg) {
+    var isMatcher = match && match.isMatcher(possibleMatcher);
+
+    return isMatcher && possibleMatcher.test(arg) || true;
+}
+
+var mockExpectation = {
+    minCalls: 1,
+    maxCalls: 1,
+
+    create: function create(methodName) {
+        var expectation = extend(stub.create(), mockExpectation);
+        delete expectation.create;
+        expectation.method = methodName;
+
+        return expectation;
+    },
+
+    invoke: function invoke(func, thisValue, args) {
+        this.verifyCallAllowed(thisValue, args);
+
+        return spyInvoke.apply(this, arguments);
+    },
+
+    atLeast: function atLeast(num) {
+        if (typeof num !== "number") {
+            throw new TypeError("'" + valueToString(num) + "' is not number");
+        }
+
+        if (!this.limitsSet) {
+            this.maxCalls = null;
+            this.limitsSet = true;
+        }
+
+        this.minCalls = num;
+
+        return this;
+    },
+
+    atMost: function atMost(num) {
+        if (typeof num !== "number") {
+            throw new TypeError("'" + valueToString(num) + "' is not number");
+        }
+
+        if (!this.limitsSet) {
+            this.minCalls = null;
+            this.limitsSet = true;
+        }
+
+        this.maxCalls = num;
+
+        return this;
+    },
+
+    never: function never() {
+        return this.exactly(0);
+    },
+
+    once: function once() {
+        return this.exactly(1);
+    },
+
+    twice: function twice() {
+        return this.exactly(2);
+    },
+
+    thrice: function thrice() {
+        return this.exactly(3);
+    },
+
+    exactly: function exactly(num) {
+        if (typeof num !== "number") {
+            throw new TypeError("'" + valueToString(num) + "' is not a number");
+        }
+
+        this.atLeast(num);
+        return this.atMost(num);
+    },
+
+    met: function met() {
+        return !this.failed && receivedMinCalls(this);
+    },
+
+    verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
+        var expectedArguments = this.expectedArguments;
+
+        if (receivedMaxCalls(this)) {
+            this.failed = true;
+            mockExpectation.fail(this.method + " already called " + timesInWords(this.maxCalls));
+        }
+
+        if ("expectedThis" in this && this.expectedThis !== thisValue) {
+            mockExpectation.fail(this.method + " called with " + valueToString(thisValue) +
+                " as thisValue, expected " + valueToString(this.expectedThis));
+        }
+
+        if (!("expectedArguments" in this)) {
+            return;
+        }
+
+        if (!args) {
+            mockExpectation.fail(this.method + " received no arguments, expected " +
+                format(expectedArguments));
+        }
+
+        if (args.length < expectedArguments.length) {
+            mockExpectation.fail(this.method + " received too few arguments (" + format(args) +
+                "), expected " + format(expectedArguments));
+        }
+
+        if (this.expectsExactArgCount &&
+            args.length !== expectedArguments.length) {
+            mockExpectation.fail(this.method + " received too many arguments (" + format(args) +
+                "), expected " + format(expectedArguments));
+        }
+
+        expectedArguments.forEach(function (expectedArgument, i) {
+            if (!verifyMatcher(expectedArgument, args[i])) {
+                mockExpectation.fail(this.method + " received wrong arguments " + format(args) +
+                    ", didn't match " + expectedArguments.toString());
+            }
+
+            if (!deepEqual(expectedArgument, args[i])) {
+                mockExpectation.fail(this.method + " received wrong arguments " + format(args) +
+                    ", expected " + format(expectedArguments));
+            }
+        }, this);
+    },
+
+    allowsCall: function allowsCall(thisValue, args) {
+        var expectedArguments = this.expectedArguments;
+
+        if (this.met() && receivedMaxCalls(this)) {
+            return false;
+        }
+
+        if ("expectedThis" in this && this.expectedThis !== thisValue) {
+            return false;
+        }
+
+        if (!("expectedArguments" in this)) {
+            return true;
+        }
+
+        args = args || [];
+
+        if (args.length < expectedArguments.length) {
+            return false;
+        }
+
+        if (this.expectsExactArgCount &&
+            args.length !== expectedArguments.length) {
+            return false;
+        }
+
+        return expectedArguments.every(function (expectedArgument, i) {
+            if (!verifyMatcher(expectedArgument, args[i])) {
+                return false;
+            }
+
+            if (!deepEqual(expectedArgument, args[i])) {
+                return false;
+            }
+
+            return true;
+        });
+    },
+
+    withArgs: function withArgs() {
+        this.expectedArguments = slice.call(arguments);
+        return this;
+    },
+
+    withExactArgs: function withExactArgs() {
+        this.withArgs.apply(this, arguments);
+        this.expectsExactArgCount = true;
+        return this;
+    },
+
+    on: function on(thisValue) {
+        this.expectedThis = thisValue;
+        return this;
+    },
+
+    toString: function () {
+        var args = (this.expectedArguments || []).slice();
+
+        if (!this.expectsExactArgCount) {
+            push.call(args, "[...]");
+        }
+
+        var callStr = spyCallToString.call({
+            proxy: this.method || "anonymous mock expectation",
+            args: args
+        });
+
+        var message = callStr.replace(", [...", "[, ...") + " " +
+            expectedCallCountInWords(this);
+
+        if (this.met()) {
+            return "Expectation met: " + message;
+        }
+
+        return "Expected " + message + " (" +
+            callCountInWords(this.callCount) + ")";
+    },
+
+    verify: function verify() {
+        if (!this.met()) {
+            mockExpectation.fail(this.toString());
+        } else {
+            mockExpectation.pass(this.toString());
+        }
+
+        return true;
+    },
+
+    pass: function pass(message) {
+        assert.pass(message);
+    },
+
+    fail: function fail(message) {
+        var exception = new Error(message);
+        exception.name = "ExpectationError";
+
+        throw exception;
+    }
+};
+
+module.exports = mockExpectation;
+
+},{"./assert":2,"./call":5,"./match":10,"./spy":16,"./stub":20,"./util/core/deep-equal":23,"./util/core/extend":27,"./util/core/format":28,"./util/core/times-in-words":38,"./util/core/value-to-string":40}],12:[function(require,module,exports){
+"use strict";
+
+var mockExpectation = require("./mock-expectation");
+var spyCallToString = require("./call").toString;
+var extend = require("./util/core/extend");
+var match = require("./match");
+var deepEqual = require("./util/core/deep-equal").use(match);
+var wrapMethod = require("./util/core/wrap-method");
+
+var push = Array.prototype.push;
+
+function mock(object) {
+    if (!object) {
+        return mockExpectation.create("Anonymous mock");
+    }
+
+    return mock.create(object);
+}
+
+function each(collection, callback) {
+    var col = collection || [];
+
+    col.forEach(callback);
+}
+
+function arrayEquals(arr1, arr2, compareLength) {
+    if (compareLength && (arr1.length !== arr2.length)) {
+        return false;
+    }
+
+    return arr1.every(function (element, i) {
+        return deepEqual(element, arr2[i]);
+
+    });
+}
+
+extend(mock, {
+    create: function create(object) {
+        if (!object) {
+            throw new TypeError("object is null");
+        }
+
+        var mockObject = extend({}, mock);
+        mockObject.object = object;
+        delete mockObject.create;
+
+        return mockObject;
+    },
+
+    expects: function expects(method) {
+        if (!method) {
+            throw new TypeError("method is falsy");
+        }
+
+        if (!this.expectations) {
+            this.expectations = {};
+            this.proxies = [];
+            this.failures = [];
+        }
+
+        if (!this.expectations[method]) {
+            this.expectations[method] = [];
+            var mockObject = this;
+
+            wrapMethod(this.object, method, function () {
+                return mockObject.invokeMethod(method, this, arguments);
+            });
+
+            push.call(this.proxies, method);
+        }
+
+        var expectation = mockExpectation.create(method);
+        push.call(this.expectations[method], expectation);
+
+        return expectation;
+    },
+
+    restore: function restore() {
+        var object = this.object;
+
+        each(this.proxies, function (proxy) {
+            if (typeof object[proxy].restore === "function") {
+                object[proxy].restore();
+            }
+        });
+    },
+
+    verify: function verify() {
+        var expectations = this.expectations || {};
+        var messages = this.failures ? this.failures.slice() : [];
+        var met = [];
+
+        each(this.proxies, function (proxy) {
+            each(expectations[proxy], function (expectation) {
+                if (!expectation.met()) {
+                    push.call(messages, expectation.toString());
+                } else {
+                    push.call(met, expectation.toString());
+                }
+            });
+        });
+
+        this.restore();
+
+        if (messages.length > 0) {
+            mockExpectation.fail(messages.concat(met).join("\n"));
+        } else if (met.length > 0) {
+            mockExpectation.pass(messages.concat(met).join("\n"));
+        }
+
+        return true;
+    },
+
+    invokeMethod: function invokeMethod(method, thisValue, args) {
+        /* if we cannot find any matching files we will explicitly call mockExpection#fail with error messages */
+        /* eslint consistent-return: "off" */
+        var expectations = this.expectations && this.expectations[method] ? this.expectations[method] : [];
+        var currentArgs = args || [];
+        var available;
+
+        var expectationsWithMatchingArgs = expectations.filter(function (expectation) {
+            var expectedArgs = expectation.expectedArguments || [];
+
+            return arrayEquals(expectedArgs, currentArgs, expectation.expectsExactArgCount);
+        });
+
+        var expectationsToApply = expectationsWithMatchingArgs.filter(function (expectation) {
+            return !expectation.met() && expectation.allowsCall(thisValue, args);
+        });
+
+        if (expectationsToApply.length > 0) {
+            return expectationsToApply[0].apply(thisValue, args);
+        }
+
+        var messages = [];
+        var exhausted = 0;
+
+        expectationsWithMatchingArgs.forEach(function (expectation) {
+            if (expectation.allowsCall(thisValue, args)) {
+                available = available || expectation;
+            } else {
+                exhausted += 1;
+            }
+        });
+
+        if (available && exhausted === 0) {
+            return available.apply(thisValue, args);
+        }
+
+        expectations.forEach(function (expectation) {
+            push.call(messages, "    " + expectation.toString());
+        });
+
+        messages.unshift("Unexpected call: " + spyCallToString.call({
+            proxy: method,
+            args: args
+        }));
+
+        var err = new Error();
+        if (!err.stack) {
+            // PhantomJS does not serialize the stack trace until the error has been thrown
+            try {
+                throw err;
+            } catch (e) {/* empty */}
+        }
+        this.failures.push("Unexpected call: " + spyCallToString.call({
+            proxy: method,
+            args: args,
+            stack: err.stack
+        }));
+
+        mockExpectation.fail(messages.join("\n"));
+    }
+});
+
+module.exports = mock;
+
+},{"./call":5,"./match":10,"./mock-expectation":11,"./util/core/deep-equal":23,"./util/core/extend":27,"./util/core/wrap-method":42}],13:[function(require,module,exports){
+"use strict";
+
+var collectOwnMethods = require("./collect-own-methods");
+var deprecated = require("./util/core/deprecated");
+var getPropertyDescriptor = require("./util/core/get-property-descriptor");
+var stubNonFunctionProperty = require("./stub-non-function-property");
+var sinonStub = require("./stub");
+var throwOnFalsyObject = require("./throw-on-falsy-object");
+
+// This is deprecated and will be removed in a future version of sinon.
+// We will only consider pull requests that fix serious bugs in the implementation
+function sandboxStub(object, property/*, value*/) {
+    deprecated.printWarning(
+      "sandbox.stub(obj, 'meth', val) is deprecated and will be removed from " +
+      "the public API in a future version of sinon." +
+      "\n Use sandbox(obj, 'meth').callsFake(fn) instead in order to stub a function." +
+      "\n Use sandbox(obj, 'meth').value(fn) instead in order to stub a non-function value."
+    );
+
+    throwOnFalsyObject.apply(null, arguments);
+
+    var actualDescriptor = getPropertyDescriptor(object, property);
+    var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
+    var isStubbingNonFuncProperty = typeof object === "object"
+                                    && typeof property !== "undefined"
+                                    && (typeof actualDescriptor === "undefined"
+                                    || typeof actualDescriptor.value !== "function");
+
+
+    // When passing a value as third argument it will be applied to stubNonFunctionProperty
+    var stubbed = isStubbingNonFuncProperty ?
+                    stubNonFunctionProperty.apply(null, arguments) :
+                    sinonStub.apply(null, arguments);
+
+    if (isStubbingEntireObject) {
+        var ownMethods = collectOwnMethods(stubbed);
+        ownMethods.forEach(this.add.bind(this));
+        if (this.promiseLibrary) {
+            ownMethods.forEach(this.addUsingPromise.bind(this));
+        }
+    } else {
+        this.add(stubbed);
+        if (this.promiseLibrary) {
+            stubbed.usingPromise(this.promiseLibrary);
+        }
+    }
+
+    return stubbed;
+}
+
+module.exports = sandboxStub;
+
+},{"./collect-own-methods":6,"./stub":20,"./stub-non-function-property":19,"./throw-on-falsy-object":21,"./util/core/deprecated":25,"./util/core/get-property-descriptor":32}],14:[function(require,module,exports){
+"use strict";
+
+var extend = require("./util/core/extend");
+var sinonCollection = require("./collection");
+var sinonMatch = require("./match");
+var sinonAssert = require("./assert");
+var sinonClock = require("./util/fake_timers");
+var fakeServer = require("./util/fake_server");
+var fakeXhr = require("./util/fake_xml_http_request");
+var fakeServerWithClock = require("./util/fake_server_with_clock");
+
+var push = [].push;
+
+var sinonSandbox = Object.create(sinonCollection);
+
+function exposeValue(sandbox, config, key, value) {
+    if (!value) {
+        return;
+    }
+
+    if (config.injectInto && !(key in config.injectInto)) {
+        config.injectInto[key] = value;
+        sandbox.injectedKeys.push(key);
+    } else {
+        push.call(sandbox.args, value);
+    }
+}
+
+function prepareSandboxFromConfig(config) {
+    var sandbox = Object.create(sinonSandbox);
+
+    if (config.useFakeServer) {
+        if (typeof config.useFakeServer === "object") {
+            sandbox.serverPrototype = config.useFakeServer;
+        }
+
+        sandbox.useFakeServer();
+    }
+
+    if (config.useFakeTimers) {
+        if (typeof config.useFakeTimers === "object") {
+            sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
+        } else {
+            sandbox.useFakeTimers();
+        }
+    }
+
+    return sandbox;
+}
+
+extend(sinonSandbox, {
+    useFakeTimers: function useFakeTimers() {
+        this.clock = sinonClock.useFakeTimers.apply(null, arguments);
+
+        return this.add(this.clock);
+    },
+
+    serverPrototype: fakeServerWithClock,
+
+    useFakeServer: function useFakeServer() {
+        var proto = this.serverPrototype || fakeServer;
+
+        if (!proto || !proto.create) {
+            return null;
+        }
+
+        this.server = proto.create();
+        return this.add(this.server);
+    },
+
+    useFakeXMLHttpRequest: function useFakeXMLHttpRequest() {
+        var xhr = fakeXhr.useFakeXMLHttpRequest();
+        return this.add(xhr);
+    },
+
+    inject: function (obj) {
+        sinonCollection.inject.call(this, obj);
+
+        if (this.clock) {
+            obj.clock = this.clock;
+        }
+
+        if (this.server) {
+            obj.server = this.server;
+            obj.requests = this.server.requests;
+        }
+
+        obj.match = sinonMatch;
+
+        return obj;
+    },
+
+    usingPromise: function (promiseLibrary) {
+
+        this.promiseLibrary = promiseLibrary;
+
+        return this;
+    },
+
+    restore: function () {
+        if (arguments.length) {
+            throw new Error("sandbox.restore() does not take any parameters. Perhaps you meant stub.restore()");
+        }
+
+        sinonCollection.restore.apply(this, arguments);
+        this.restoreContext();
+    },
+
+    restoreContext: function () {
+        var injectedKeys = this.injectedKeys;
+        var injectInto = this.injectInto;
+
+        if (!injectedKeys) {
+            return;
+        }
+
+        injectedKeys.forEach(function (injectedKey) {
+            delete injectInto[injectedKey];
+        });
+
+        injectedKeys = [];
+    },
+
+    create: function (config) {
+        if (!config) {
+            return Object.create(sinonSandbox);
+        }
+
+        var sandbox = prepareSandboxFromConfig(config);
+        sandbox.args = sandbox.args || [];
+        sandbox.injectedKeys = [];
+        sandbox.injectInto = config.injectInto;
+        var exposed = sandbox.inject({});
+
+        if (config.properties) {
+            config.properties.forEach(function (prop) {
+                var value = exposed[prop] || prop === "sandbox" && sandbox;
+                exposeValue(sandbox, config, prop, value);
+            });
+        } else {
+            exposeValue(sandbox, config, "sandbox");
+        }
+
+        return sandbox;
+    },
+
+    match: sinonMatch,
+
+    assert: sinonAssert
+});
+
+module.exports = sinonSandbox;
+
+},{"./assert":2,"./collection":7,"./match":10,"./util/core/extend":27,"./util/fake_server":44,"./util/fake_server_with_clock":45,"./util/fake_timers":46,"./util/fake_xml_http_request":47}],15:[function(require,module,exports){
+"use strict";
+
+var color = require("./color");
+var timesInWords = require("./util/core/times-in-words");
+var sinonFormat = require("./util/core/format");
+var sinonMatch = require("./match");
+var jsDiff = require("diff");
+var push = Array.prototype.push;
+
+function colorSinonMatchText(matcher, calledArg, calledArgMessage) {
+    if (!matcher.test(calledArg)) {
+        matcher.message = color.red(matcher.message);
+        if (calledArgMessage) {
+            calledArgMessage = color.green(calledArgMessage);
+        }
+    }
+    return calledArgMessage + " " + matcher.message;
+}
+
+function colorDiffText(diff) {
+    var objects = diff.map(function (part) {
+        var text = part.value;
+        if (part.added) {
+            text = color.green(text);
+        } else if (part.removed) {
+            text = color.red(text);
+        }
+        if (diff.length === 2) {
+            text += " "; // format simple diffs
+        }
+        return text;
+    });
+    return objects.join("");
+}
+
+module.exports = {
+    c: function (spyInstance) {
+        return timesInWords(spyInstance.callCount);
+    },
+
+    n: function (spyInstance) {
+        return spyInstance.toString();
+    },
+
+    D: function (spyInstance, args) {
+        var message = "";
+
+        for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
+            // describe multiple calls
+            if (l > 1) {
+                if (i > 0) {
+                    message += "\n";
+                }
+                message += "Call " + (i + 1) + ":";
+            }
+            var calledArgs = spyInstance.getCall(i).args;
+            for (var j = 0; j < calledArgs.length || j < args.length; ++j) {
+                message += "\n";
+                var calledArgMessage = j < calledArgs.length ? sinonFormat(calledArgs[j]) : "";
+                if (sinonMatch.isMatcher(args[j])) {
+                    message += colorSinonMatchText(args[j], calledArgs[j], calledArgMessage);
+                } else {
+                    var expectedArgMessage = j < args.length ? sinonFormat(args[j]) : "";
+                    var diff = jsDiff.diffJson(calledArgMessage, expectedArgMessage);
+                    message += colorDiffText(diff);
+                }
+            }
+        }
+
+        return message;
+    },
+
+    C: function (spyInstance) {
+        var calls = [];
+
+        for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
+            var stringifiedCall = "    " + spyInstance.getCall(i).toString();
+            if (/\n/.test(calls[i - 1])) {
+                stringifiedCall = "\n" + stringifiedCall;
+            }
+            push.call(calls, stringifiedCall);
+        }
+
+        return calls.length > 0 ? "\n" + calls.join("\n") : "";
+    },
+
+    t: function (spyInstance) {
+        var objects = [];
+
+        for (var i = 0, l = spyInstance.callCount; i < l; ++i) {
+            push.call(objects, sinonFormat(spyInstance.thisValues[i]));
+        }
+
+        return objects.join(", ");
+    },
+
+    "*": function (spyInstance, args) {
+        return args.map(function (arg) { return sinonFormat(arg); }).join(", ");
+    }
+};
+
+},{"./color":8,"./match":10,"./util/core/format":28,"./util/core/times-in-words":38,"diff":58}],16:[function(require,module,exports){
+"use strict";
+
+var extend = require("./util/core/extend");
+var functionName = require("./util/core/function-name");
+var functionToString = require("./util/core/function-to-string");
+var getPropertyDescriptor = require("./util/core/get-property-descriptor");
+var sinonMatch = require("./match");
+var deepEqual = require("./util/core/deep-equal").use(sinonMatch);
+var spyCall = require("./call");
+var wrapMethod = require("./util/core/wrap-method");
+var sinonFormat = require("./util/core/format");
+var valueToString = require("./util/core/value-to-string");
+
+var push = Array.prototype.push;
+var slice = Array.prototype.slice;
+var callId = 0;
+var ErrorConstructor = Error.prototype.constructor;
+
+function spy(object, property, types) {
+    var descriptor, methodDesc;
+
+    if (!property && typeof object === "function") {
+        return spy.create(object);
+    }
+
+    if (!object && !property) {
+        return spy.create(function () { });
+    }
+
+    if (!types) {
+        return wrapMethod(object, property, spy.create(object[property]));
+    }
+
+    descriptor = {};
+    methodDesc = getPropertyDescriptor(object, property);
+
+    types.forEach(function (type) {
+        descriptor[type] = spy.create(methodDesc[type]);
+    });
+
+    return wrapMethod(object, property, descriptor);
+}
+
+function matchingFake(fakes, args, strict) {
+    if (!fakes) {
+        return undefined;
+    }
+
+    var matchingFakes = fakes.filter(function (fake) {
+        return fake.matches(args, strict);
+    });
+
+    return matchingFakes.pop();
+}
+
+function incrementCallCount() {
+    this.called = true;
+    this.callCount += 1;
+    this.notCalled = false;
+    this.calledOnce = this.callCount === 1;
+    this.calledTwice = this.callCount === 2;
+    this.calledThrice = this.callCount === 3;
+}
+
+function createCallProperties() {
+    this.firstCall = this.getCall(0);
+    this.secondCall = this.getCall(1);
+    this.thirdCall = this.getCall(2);
+    this.lastCall = this.getCall(this.callCount - 1);
+}
+
+function createProxy(func, proxyLength) {
+    // Retain the function length:
+    var p;
+    if (proxyLength) {
+        // Do not change this to use an eval. Projects that depend on sinon block the use of eval.
+        // ref: https://github.com/sinonjs/sinon/issues/710
+        switch (proxyLength) {
+            /*eslint-disable no-unused-vars, max-len*/
+            case 1: p = function proxy(a) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 2: p = function proxy(a, b) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 3: p = function proxy(a, b, c) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 4: p = function proxy(a, b, c, d) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 5: p = function proxy(a, b, c, d, e) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 6: p = function proxy(a, b, c, d, e, f) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 7: p = function proxy(a, b, c, d, e, f, g) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 8: p = function proxy(a, b, c, d, e, f, g, h) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 9: p = function proxy(a, b, c, d, e, f, g, h, i) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 10: p = function proxy(a, b, c, d, e, f, g, h, i, j) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 11: p = function proxy(a, b, c, d, e, f, g, h, i, j, k) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            case 12: p = function proxy(a, b, c, d, e, f, g, h, i, j, k, l) { return p.invoke(func, this, slice.call(arguments)); }; break;
+            default: p = function proxy() { return p.invoke(func, this, slice.call(arguments)); }; break;
+            /*eslint-enable*/
+        }
+    } else {
+        p = function proxy() {
+            return p.invoke(func, this, slice.call(arguments));
+        };
+    }
+    p.isSinonProxy = true;
+    return p;
+}
+
+var uuid = 0;
+
+// Public API
+var spyApi = {
+    formatters: require("./spy-formatters"),
+
+    reset: function () {
+        if (this.invoking) {
+            var err = new Error("Cannot reset Sinon function while invoking it. " +
+                                "Move the call to .reset outside of the callback.");
+            err.name = "InvalidResetException";
+            throw err;
+        }
+
+        this.called = false;
+        this.notCalled = true;
+        this.calledOnce = false;
+        this.calledTwice = false;
+        this.calledThrice = false;
+        this.callCount = 0;
+        this.firstCall = null;
+        this.secondCall = null;
+        this.thirdCall = null;
+        this.lastCall = null;
+        this.args = [];
+        this.returnValues = [];
+        this.thisValues = [];
+        this.exceptions = [];
+        this.callIds = [];
+        this.errorsWithCallStack = [];
+        if (this.fakes) {
+            this.fakes.forEach(function (fake) {
+                if (fake.resetHistory) {
+                    fake.resetHistory();
+                } else {
+                    fake.reset();
+                }
+            });
+        }
+
+        return this;
+    },
+
+    create: function create(func, spyLength) {
+        var name;
+
+        if (typeof func !== "function") {
+            func = function () { };
+        } else {
+            name = functionName(func);
+        }
+
+        if (!spyLength) {
+            spyLength = func.length;
+        }
+
+        var proxy = createProxy(func, spyLength);
+
+        extend(proxy, spy);
+        delete proxy.create;
+        extend(proxy, func);
+
+        proxy.reset();
+        proxy.prototype = func.prototype;
+        proxy.displayName = name || "spy";
+        proxy.toString = functionToString;
+        proxy.instantiateFake = spy.create;
+        proxy.id = "spy#" + uuid++;
+
+        return proxy;
+    },
+
+    invoke: function invoke(func, thisValue, args) {
+        var matching = matchingFake(this.fakes, args);
+        var exception, returnValue;
+
+        incrementCallCount.call(this);
+        push.call(this.thisValues, thisValue);
+        push.call(this.args, args);
+        push.call(this.callIds, callId++);
+
+        // Make call properties available from within the spied function:
+        createCallProperties.call(this);
+
+        try {
+            this.invoking = true;
+
+            if (matching) {
+                returnValue = matching.invoke(func, thisValue, args);
+            } else {
+                returnValue = (this.func || func).apply(thisValue, args);
+            }
+
+            var thisCall = this.getCall(this.callCount - 1);
+            if (thisCall.calledWithNew() && typeof returnValue !== "object") {
+                returnValue = thisValue;
+            }
+        } catch (e) {
+            exception = e;
+        } finally {
+            delete this.invoking;
+        }
+
+        push.call(this.exceptions, exception);
+        push.call(this.returnValues, returnValue);
+        var err = new ErrorConstructor();
+        // 1. Please do not get stack at this point. It's may be so very slow, and not actually used
+        // 2. PhantomJS does not serialize the stack trace until the error has been thrown:
+        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/Stack
+        try {
+            throw err;
+        } catch (e) {/* empty */}
+        push.call(this.errorsWithCallStack, err);
+
+        // Make return value and exception available in the calls:
+        createCallProperties.call(this);
+
+        if (exception !== undefined) {
+            throw exception;
+        }
+
+        return returnValue;
+    },
+
+    named: function named(name) {
+        this.displayName = name;
+        return this;
+    },
+
+    getCall: function getCall(i) {
+        if (i < 0 || i >= this.callCount) {
+            return null;
+        }
+
+        return spyCall(this, this.thisValues[i], this.args[i],
+                                this.returnValues[i], this.exceptions[i],
+                                this.callIds[i], this.errorsWithCallStack[i]);
+    },
+
+    getCalls: function () {
+        var calls = [];
+        var i;
+
+        for (i = 0; i < this.callCount; i++) {
+            calls.push(this.getCall(i));
+        }
+
+        return calls;
+    },
+
+    calledBefore: function calledBefore(spyFn) {
+        if (!this.called) {
+            return false;
+        }
+
+        if (!spyFn.called) {
+            return true;
+        }
+
+        return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
+    },
+
+    calledAfter: function calledAfter(spyFn) {
+        if (!this.called || !spyFn.called) {
+            return false;
+        }
+
+        return this.callIds[this.callCount - 1] > spyFn.callIds[0];
+    },
+
+    calledImmediatelyBefore: function calledImmediatelyBefore(spyFn) {
+        if (!this.called || !spyFn.called) {
+            return false;
+        }
+
+        return this.callIds[this.callCount - 1] === spyFn.callIds[spyFn.callCount - 1] - 1;
+    },
+
+    calledImmediatelyAfter: function calledImmediatelyAfter(spyFn) {
+        if (!this.called || !spyFn.called) {
+            return false;
+        }
+
+        return this.callIds[this.callCount - 1] === spyFn.callIds[spyFn.callCount - 1] + 1;
+    },
+
+    withArgs: function () {
+        var args = slice.call(arguments);
+
+        if (this.fakes) {
+            var match = matchingFake(this.fakes, args, true);
+
+            if (match) {
+                return match;
+            }
+        } else {
+            this.fakes = [];
+        }
+
+        var original = this;
+        var fake = this.instantiateFake();
+        fake.matchingArguments = args;
+        fake.parent = this;
+        push.call(this.fakes, fake);
+
+        fake.withArgs = function () {
+            return original.withArgs.apply(original, arguments);
+        };
+
+        original.args.forEach(function (arg, i) {
+            if (!fake.matches(arg)) {
+                return;
+            }
+
+            incrementCallCount.call(fake);
+            push.call(fake.thisValues, original.thisValues[i]);
+            push.call(fake.args, arg);
+            push.call(fake.returnValues, original.returnValues[i]);
+            push.call(fake.exceptions, original.exceptions[i]);
+            push.call(fake.callIds, original.callIds[i]);
+        });
+
+        createCallProperties.call(fake);
+
+        return fake;
+    },
+
+    matches: function (args, strict) {
+        var margs = this.matchingArguments;
+
+        if (margs.length <= args.length &&
+            deepEqual(margs, args.slice(0, margs.length))) {
+            return !strict || margs.length === args.length;
+        }
+
+        return undefined;
+    },
+
+    printf: function (format) {
+        var spyInstance = this;
+        var args = slice.call(arguments, 1);
+        var formatter;
+
+        return (format || "").replace(/%(.)/g, function (match, specifyer) {
+            formatter = spyApi.formatters[specifyer];
+
+            if (typeof formatter === "function") {
+                return formatter.call(null, spyInstance, args);
+            } else if (!isNaN(parseInt(specifyer, 10))) {
+                return sinonFormat(args[specifyer - 1]);
+            }
+
+            return "%" + specifyer;
+        });
+    }
+};
+
+function delegateToCalls(method, matchAny, actual, notCalled) {
+    spyApi[method] = function () {
+        if (!this.called) {
+            if (notCalled) {
+                return notCalled.apply(this, arguments);
+            }
+            return false;
+        }
+
+        var currentCall;
+        var matches = 0;
+
+        for (var i = 0, l = this.callCount; i < l; i += 1) {
+            currentCall = this.getCall(i);
+
+            if (currentCall[actual || method].apply(currentCall, arguments)) {
+                matches += 1;
+
+                if (matchAny) {
+                    return true;
+                }
+            }
+        }
+
+        return matches === this.callCount;
+    };
+}
+
+delegateToCalls("calledOn", true);
+delegateToCalls("alwaysCalledOn", false, "calledOn");
+delegateToCalls("calledWith", true);
+delegateToCalls("calledWithMatch", true);
+delegateToCalls("alwaysCalledWith", false, "calledWith");
+delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
+delegateToCalls("calledWithExactly", true);
+delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
+delegateToCalls("neverCalledWith", false, "notCalledWith", function () {
+    return true;
+});
+delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch", function () {
+    return true;
+});
+delegateToCalls("threw", true);
+delegateToCalls("alwaysThrew", false, "threw");
+delegateToCalls("returned", true);
+delegateToCalls("alwaysReturned", false, "returned");
+delegateToCalls("calledWithNew", true);
+delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
+delegateToCalls("callArg", false, "callArgWith", function () {
+    throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+});
+spyApi.callArgWith = spyApi.callArg;
+delegateToCalls("callArgOn", false, "callArgOnWith", function () {
+    throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+});
+spyApi.callArgOnWith = spyApi.callArgOn;
+delegateToCalls("throwArg", false, "throwArg", function () {
+    throw new Error(this.toString() + " cannot throw arg since it was not yet invoked.");
+});
+delegateToCalls("yield", false, "yield", function () {
+    throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+});
+// "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
+spyApi.invokeCallback = spyApi.yield;
+delegateToCalls("yieldOn", false, "yieldOn", function () {
+    throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+});
+delegateToCalls("yieldTo", false, "yieldTo", function (property) {
+    throw new Error(this.toString() + " cannot yield to '" + valueToString(property) +
+        "' since it was not yet invoked.");
+});
+delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
+    throw new Error(this.toString() + " cannot yield to '" + valueToString(property) +
+        "' since it was not yet invoked.");
+});
+
+extend(spy, spyApi);
+spy.spyCall = spyCall;
+module.exports = spy;
+
+},{"./call":5,"./match":10,"./spy-formatters":15,"./util/core/deep-equal":23,"./util/core/extend":27,"./util/core/format":28,"./util/core/function-name":29,"./util/core/function-to-string":30,"./util/core/get-property-descriptor":32,"./util/core/value-to-string":40,"./util/core/wrap-method":42}],17:[function(require,module,exports){
+"use strict";
+
+var deprecated = require("./util/core/deprecated");
+var spy = require("./spy");
+var wrapMethod = require("./util/core/wrap-method");
+
+// This is deprecated and will be removed in a future version of sinon.
+// We will only consider pull requests that fix serious bugs in the implementation
+function stubDescriptor(object, property, descriptor) {
+    var wrapper;
+
+    deprecated.printWarning(
+      "sinon.stub(obj, 'meth', fn) is deprecated and will be removed from " +
+      "the public API in a future version of sinon." +
+      "\n Use stub(obj, 'meth').callsFake(fn)." +
+      "\n Codemod available at https://github.com/hurrymaplelad/sinon-codemod"
+    );
+
+    if (!!descriptor && typeof descriptor !== "function" && typeof descriptor !== "object") {
+        throw new TypeError("Custom stub should be a property descriptor");
+    }
+
+    if (typeof descriptor === "object" && Object.keys(descriptor).length === 0) {
+        throw new TypeError("Expected property descriptor to have at least one key");
+    }
+
+    if (typeof descriptor === "function") {
+        wrapper = spy && spy.create ? spy.create(descriptor) : descriptor;
+    } else {
+        wrapper = descriptor;
+        if (spy && spy.create) {
+            Object.keys(wrapper).forEach(function (type) {
+                wrapper[type] = spy.create(wrapper[type]);
+            });
+        }
+    }
+
+    return wrapMethod(object, property, wrapper);
+}
+
+module.exports = stubDescriptor;
+
+},{"./spy":16,"./util/core/deprecated":25,"./util/core/wrap-method":42}],18:[function(require,module,exports){
+"use strict";
+
+var getPropertyDescriptor = require("./util/core/get-property-descriptor");
+var walk = require("./util/core/walk");
+
+function stubEntireObject(stub, object) {
+    walk(object || {}, function (prop, propOwner) {
+        // we don't want to stub things like toString(), valueOf(), etc. so we only stub if the object
+        // is not Object.prototype
+        if (
+            propOwner !== Object.prototype &&
+            prop !== "constructor" &&
+            typeof getPropertyDescriptor(propOwner, prop).value === "function"
+        ) {
+            stub(object, prop);
+        }
+    });
+
+    return object;
+}
+
+module.exports = stubEntireObject;
+
+},{"./util/core/get-property-descriptor":32,"./util/core/walk":41}],19:[function(require,module,exports){
+"use strict";
+
+var valueToString = require("./util/core/value-to-string");
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+function stubNonFunctionProperty(object, property, value) {
+    var original = object[property];
+
+    if (!hasOwnProperty.call(object, property)) {
+        throw new TypeError("Cannot stub non-existent own property " + valueToString(property));
+    }
+
+    object[property] = value;
+
+    return {
+        restore: function restore() {
+            object[property] = original;
+        }
+    };
+}
+
+module.exports = stubNonFunctionProperty;
+
+},{"./util/core/value-to-string":40}],20:[function(require,module,exports){
+"use strict";
+
+var behavior = require("./behavior");
+var behaviors = require("./default-behaviors");
+var spy = require("./spy");
+var extend = require("./util/core/extend");
+var functionToString = require("./util/core/function-to-string");
+var getPropertyDescriptor = require("./util/core/get-property-descriptor");
+var wrapMethod = require("./util/core/wrap-method");
+var stubEntireObject = require("./stub-entire-object");
+var stubDescriptor = require("./stub-descriptor");
+var throwOnFalsyObject = require("./throw-on-falsy-object");
+
+function stub(object, property, descriptor) {
+    throwOnFalsyObject.apply(null, arguments);
+
+    var actualDescriptor = getPropertyDescriptor(object, property);
+    var isStubbingEntireObject = typeof property === "undefined" && typeof object === "object";
+    var isCreatingNewStub = !object && typeof property === "undefined";
+    var isStubbingDescriptor = object && property && Boolean(descriptor);
+    var isStubbingNonFuncProperty = typeof object === "object"
+                                    && typeof property !== "undefined"
+                                    && (typeof actualDescriptor === "undefined"
+                                    || typeof actualDescriptor.value !== "function")
+                                    && typeof descriptor === "undefined";
+    var isStubbingExistingMethod = !isStubbingDescriptor
+                                    && typeof object === "object"
+                                    && typeof actualDescriptor !== "undefined"
+                                    && typeof actualDescriptor.value === "function";
+    var arity = isStubbingExistingMethod ? object[property].length : 0;
+
+    if (isStubbingEntireObject) {
+        return stubEntireObject(stub, object);
+    }
+
+    if (isStubbingDescriptor) {
+        return stubDescriptor.apply(null, arguments);
+    }
+
+    if (isCreatingNewStub) {
+        return stub.create();
+    }
+
+    var s = stub.create(arity);
+    s.rootObj = object;
+    s.propName = property;
+    s.restore = function restore() {
+        if (actualDescriptor !== undefined) {
+            Object.defineProperty(object, property, actualDescriptor);
+            return;
+        }
+
+        delete object[property];
+    };
+
+    return isStubbingNonFuncProperty ? s : wrapMethod(object, property, s);
+}
+
+stub.createStubInstance = function (constructor) {
+    if (typeof constructor !== "function") {
+        throw new TypeError("The constructor should be a function.");
+    }
+    return stub(Object.create(constructor.prototype));
+};
+
+/*eslint-disable no-use-before-define*/
+function getParentBehaviour(stubInstance) {
+    return (stubInstance.parent && getCurrentBehavior(stubInstance.parent));
+}
+
+function getDefaultBehavior(stubInstance) {
+    return stubInstance.defaultBehavior ||
+            getParentBehaviour(stubInstance) ||
+            behavior.create(stubInstance);
+}
+
+function getCurrentBehavior(stubInstance) {
+    var currentBehavior = stubInstance.behaviors[stubInstance.callCount - 1];
+    return currentBehavior && currentBehavior.isPresent() ? currentBehavior : getDefaultBehavior(stubInstance);
+}
+/*eslint-enable no-use-before-define*/
+
+var uuid = 0;
+
+var proto = {
+    create: function create(stubLength) {
+        var functionStub = function () {
+            return getCurrentBehavior(functionStub).invoke(this, arguments);
+        };
+
+        functionStub.id = "stub#" + uuid++;
+        var orig = functionStub;
+        functionStub = spy.create(functionStub, stubLength);
+        functionStub.func = orig;
+
+        extend(functionStub, stub);
+        functionStub.instantiateFake = stub.create;
+        functionStub.displayName = "stub";
+        functionStub.toString = functionToString;
+
+        functionStub.defaultBehavior = null;
+        functionStub.behaviors = [];
+
+        return functionStub;
+    },
+
+    resetBehavior: function () {
+        var fakes = this.fakes || [];
+
+        this.defaultBehavior = null;
+        this.behaviors = [];
+
+        delete this.returnValue;
+        delete this.returnArgAt;
+        delete this.throwArgAt;
+        delete this.fakeFn;
+        this.returnThis = false;
+
+        fakes.forEach(function (fake) {
+            fake.resetBehavior();
+        });
+    },
+
+    resetHistory: spy.reset,
+
+    reset: function () {
+        this.resetHistory();
+        this.resetBehavior();
+    },
+
+    onCall: function onCall(index) {
+        if (!this.behaviors[index]) {
+            this.behaviors[index] = behavior.create(this);
+        }
+
+        return this.behaviors[index];
+    },
+
+    onFirstCall: function onFirstCall() {
+        return this.onCall(0);
+    },
+
+    onSecondCall: function onSecondCall() {
+        return this.onCall(1);
+    },
+
+    onThirdCall: function onThirdCall() {
+        return this.onCall(2);
+    }
+};
+
+Object.keys(behavior).forEach(function (method) {
+    if (behavior.hasOwnProperty(method) &&
+        !proto.hasOwnProperty(method) &&
+        method !== "create" &&
+        method !== "withArgs" &&
+        method !== "invoke") {
+        proto[method] = behavior.createBehavior(method);
+    }
+});
+
+Object.keys(behaviors).forEach(function (method) {
+    if (behaviors.hasOwnProperty(method) && !proto.hasOwnProperty(method)) {
+        behavior.addBehavior(stub, method, behaviors[method]);
+    }
+});
+
+extend(stub, proto);
+module.exports = stub;
+
+},{"./behavior":3,"./default-behaviors":9,"./spy":16,"./stub-descriptor":17,"./stub-entire-object":18,"./throw-on-falsy-object":21,"./util/core/extend":27,"./util/core/function-to-string":30,"./util/core/get-property-descriptor":32,"./util/core/wrap-method":42}],21:[function(require,module,exports){
+"use strict";
+var valueToString = require("./util/core/value-to-string");
+
+function throwOnFalsyObject(object, property) {
+    if (property && !object) {
+        var type = object === null ? "null" : "undefined";
+        throw new Error("Trying to stub property '" + valueToString(property) + "' of " + type);
+    }
+}
+
+module.exports = throwOnFalsyObject;
+
+},{"./util/core/value-to-string":40}],22:[function(require,module,exports){
+"use strict";
+
+var every = Array.prototype.every;
+
+module.exports = function calledInOrder(spies) {
+    var callMap = {};
+
+    function hasCallsLeft(spy) {
+        if (callMap[spy.id] === undefined) {
+            callMap[spy.id] = 0;
+        }
+
+        return callMap[spy.id] < spy.callCount;
+    }
+
+    if (arguments.length > 1) {
+        spies = arguments;
+    }
+
+    return every.call(spies, function checkAdjacentCalls(spy, i) {
+        var calledBeforeNext = true;
+
+        if (i !== spies.length - 1) {
+            calledBeforeNext = spy.calledBefore(spies[i + 1]);
+        }
+
+        if (hasCallsLeft(spy) && calledBeforeNext) {
+            callMap[spy.id] += 1;
+            return true;
+        }
+
+        return false;
+    });
+};
+
+},{}],23:[function(require,module,exports){
+"use strict";
+
+var div = typeof document !== "undefined" && document.createElement("div");
+
+function isReallyNaN(val) {
+    return val !== val;
+}
+
+function isDOMNode(obj) {
+    var success = false;
+
+    try {
+        obj.appendChild(div);
+        success = div.parentNode === obj;
+    } catch (e) {
+        return false;
+    } finally {
+        try {
+            obj.removeChild(div);
+        } catch (e) {
+            // Remove failed, not much we can do about that
+        }
+    }
+
+    return success;
+}
+
+function isElement(obj) {
+    return div && obj && obj.nodeType === 1 && isDOMNode(obj);
+}
+
+var deepEqual = module.exports = function deepEqual(a, b) {
+    if (typeof a !== "object" || typeof b !== "object") {
+        return isReallyNaN(a) && isReallyNaN(b) || a === b;
+    }
+
+    if (isElement(a) || isElement(b)) {
+        return a === b;
+    }
+
+    if (a === b) {
+        return true;
+    }
+
+    if ((a === null && b !== null) || (a !== null && b === null)) {
+        return false;
+    }
+
+    if (a instanceof RegExp && b instanceof RegExp) {
+        return (a.source === b.source) && (a.global === b.global) &&
+            (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline);
+    }
+
+    if (a instanceof Error && b instanceof Error) {
+        return a === b;
+    }
+
+    var aString = Object.prototype.toString.call(a);
+    if (aString !== Object.prototype.toString.call(b)) {
+        return false;
+    }
+
+    if (aString === "[object Date]") {
+        return a.valueOf() === b.valueOf();
+    }
+
+    var prop;
+    var aLength = 0;
+    var bLength = 0;
+
+    if (aString === "[object Array]" && a.length !== b.length) {
+        return false;
+    }
+
+    for (prop in a) {
+        if (Object.prototype.hasOwnProperty.call(a, prop)) {
+            aLength += 1;
+
+            if (!(prop in b)) {
+                return false;
+            }
+
+            // allow alternative function for recursion
+            if (!(arguments[2] || deepEqual)(a[prop], b[prop])) {
+                return false;
+            }
+        }
+    }
+
+    for (prop in b) {
+        if (Object.prototype.hasOwnProperty.call(b, prop)) {
+            bLength += 1;
+        }
+    }
+
+    return aLength === bLength;
+};
+
+deepEqual.use = function (match) {
+    return function deepEqual$matcher(a, b) {
+        // If both are matchers they must be the same instance in order to be considered equal
+        // If we didn't do that we would end up running one matcher against the other
+        if (match.isMatcher(a)) {
+            if (match.isMatcher(b)) {
+                return a === b;
+            }
+
+            return a.test(b);
+        }
+
+        return deepEqual(a, b, deepEqual$matcher);
+    };
+};
+
+},{}],24:[function(require,module,exports){
+"use strict";
+
+module.exports = {
+    injectIntoThis: true,
+    injectInto: null,
+    properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+    useFakeTimers: true,
+    useFakeServer: true
+};
+
+},{}],25:[function(require,module,exports){
+/*eslint no-console: 0 */
+"use strict";
+
+// wrap returns a function that will invoke the supplied function and print a deprecation warning to the console each
+// time it is called.
+exports.wrap = function (func, msg) {
+    var wrapped = function () {
+        exports.printWarning(msg);
+        return func.apply(this, arguments);
+    };
+    if (func.prototype) {
+        wrapped.prototype = func.prototype;
+    }
+    return wrapped;
+};
+
+// defaultMsg returns a string which can be supplied to `wrap()` to notify the user that a particular part of the
+// sinon API has been deprecated.
+exports.defaultMsg = function (funcName) {
+    return "sinon." + funcName + " is deprecated and will be removed from the public API in a future version of sinon.";
+};
+
+exports.printWarning = function (msg) {
+    // Watch out for IE7 and below! :(
+    if (typeof console !== "undefined") {
+        if (console.info) {
+            console.info(msg);
+        } else {
+            console.log(msg);
+        }
+    }
+};
+
+},{}],26:[function(require,module,exports){
+"use strict";
+
+// This is an `every` implementation that works for all iterables
+module.exports = function every(obj, fn) {
+    var pass = true;
+
+    try {
+        obj.forEach(function () {
+            if (!fn.apply(this, arguments)) {
+                // Throwing an error is the only way to break `forEach`
+                throw new Error();
+            }
+        });
+    } catch (e) {
+        pass = false;
+    }
+
+    return pass;
+};
+
+},{}],27:[function(require,module,exports){
+"use strict";
+
+// Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+var hasDontEnumBug = (function () {
+    var obj = {
+        constructor: function () {
+            return "0";
+        },
+        toString: function () {
+            return "1";
+        },
+        valueOf: function () {
+            return "2";
+        },
+        toLocaleString: function () {
+            return "3";
+        },
+        prototype: function () {
+            return "4";
+        },
+        isPrototypeOf: function () {
+            return "5";
+        },
+        propertyIsEnumerable: function () {
+            return "6";
+        },
+        hasOwnProperty: function () {
+            return "7";
+        },
+        length: function () {
+            return "8";
+        },
+        unique: function () {
+            return "9";
+        }
+    };
+
+    var result = [];
+    for (var prop in obj) {
+        if (obj.hasOwnProperty(prop)) {
+            result.push(obj[prop]());
+        }
+    }
+    return result.join("") !== "0123456789";
+})();
+
+/* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will
+ *         override properties in previous sources.
+ *
+ * target - The Object to extend
+ * sources - Objects to copy properties from.
+ *
+ * Returns the extended target
+ */
+module.exports = function extend(target /*, sources */) {
+    var sources = Array.prototype.slice.call(arguments, 1);
+    var source, i, prop;
+
+    for (i = 0; i < sources.length; i++) {
+        source = sources[i];
+
+        for (prop in source) {
+            if (source.hasOwnProperty(prop)) {
+                target[prop] = source[prop];
+            }
+        }
+
+        // Make sure we copy (own) toString method even when in JScript with DontEnum bug
+        // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+        if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) {
+            target.toString = source.toString;
+        }
+    }
+
+    return target;
+};
+
+},{}],28:[function(require,module,exports){
+"use strict";
+
+var formatio = require("formatio");
+
+var formatter = formatio.configure({
+    quoteStrings: false,
+    limitChildrenCount: 250
+});
+
+module.exports = function format() {
+    return formatter.ascii.apply(formatter, arguments);
+};
+
+},{"formatio":64}],29:[function(require,module,exports){
+"use strict";
+
+module.exports = function functionName(func) {
+    var name = func.displayName || func.name;
+    var matches;
+
+    // Use function decomposition as a last resort to get function
+    // name. Does not rely on function decomposition to work - if it
+    // doesn't debugging will be slightly less informative
+    // (i.e. toString will say 'spy' rather than 'myFunc').
+    if (!name && (matches = func.toString().match(/function ([^\s\(]+)/))) {
+        name = matches[1];
+    }
+
+    return name;
+};
+
+
+},{}],30:[function(require,module,exports){
+"use strict";
+
+module.exports = function toString() {
+    var i, prop, thisValue;
+    if (this.getCall && this.callCount) {
+        i = this.callCount;
+
+        while (i--) {
+            thisValue = this.getCall(i).thisValue;
+
+            for (prop in thisValue) {
+                if (thisValue[prop] === this) {
+                    return prop;
+                }
+            }
+        }
+    }
+
+    return this.displayName || "sinon fake";
+};
+
+},{}],31:[function(require,module,exports){
+"use strict";
+
+var defaultConfig = require("./default-config");
+
+module.exports = function getConfig(custom) {
+    var config = {};
+    var prop;
+
+    custom = custom || {};
+
+    for (prop in defaultConfig) {
+        if (defaultConfig.hasOwnProperty(prop)) {
+            config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaultConfig[prop];
+        }
+    }
+
+    return config;
+};
+
+},{"./default-config":24}],32:[function(require,module,exports){
+"use strict";
+
+module.exports = function getPropertyDescriptor(object, property) {
+    var proto = object;
+    var descriptor;
+
+    while (proto && !(descriptor = Object.getOwnPropertyDescriptor(proto, property))) {
+        proto = Object.getPrototypeOf(proto);
+    }
+    return descriptor;
+};
+
+},{}],33:[function(require,module,exports){
+"use strict";
+
+module.exports = {
+    calledInOrder: require("./called-in-order"),
+    configureLogError: require("./log_error"),
+    defaultConfig: require("./default-config"),
+    deepEqual: require("./deep-equal"),
+    every: require("./every"),
+    extend: require("./extend"),
+    format: require("./format"),
+    functionName: require("./function-name"),
+    functionToString: require("./function-to-string"),
+    getConfig: require("./get-config"),
+    getPropertyDescriptor: require("./get-property-descriptor"),
+    iterableToString: require("./iterable-to-string"),
+    orderByFirstCall: require("./order-by-first-call"),
+    restore: require("./restore"),
+    timesInWords: require("./times-in-words"),
+    typeOf: require("./typeOf"),
+    walk: require("./walk"),
+    wrapMethod: require("./wrap-method")
+};
+
+},{"./called-in-order":22,"./deep-equal":23,"./default-config":24,"./every":26,"./extend":27,"./format":28,"./function-name":29,"./function-to-string":30,"./get-config":31,"./get-property-descriptor":32,"./iterable-to-string":34,"./log_error":35,"./order-by-first-call":36,"./restore":37,"./times-in-words":38,"./typeOf":39,"./walk":41,"./wrap-method":42}],34:[function(require,module,exports){
+"use strict";
+var typeOf = require("./typeOf");
+
+module.exports = function iterableToString(obj) {
+    var representation = "";
+
+    function stringify(item) {
+        return typeof item === "string" ? "'" + item + "'" : String(item);
+    }
+
+    function mapToString(map) {
+        map.forEach(function (value, key) {
+            representation += "[" + stringify(key) + "," + stringify(value) + "],";
+        });
+
+        representation = representation.slice(0, -1);
+        return representation;
+    }
+
+    function genericIterableToString(iterable) {
+        iterable.forEach(function (value) {
+            representation += stringify(value) + ",";
+        });
+
+        representation = representation.slice(0, -1);
+        return representation;
+    }
+
+    if (typeOf(obj) === "map") {
+        return mapToString(obj);
+    }
+
+    return genericIterableToString(obj);
+};
+
+},{"./typeOf":39}],35:[function(require,module,exports){
+"use strict";
+
+// cache a reference to setTimeout, so that our reference won't be stubbed out
+// when using fake timers and errors will still get logged
+// https://github.com/cjohansen/Sinon.JS/issues/381
+var realSetTimeout = setTimeout;
+
+function configure(config) {
+    config = config || {};
+    // Function which prints errors.
+    if (!config.hasOwnProperty("logger")) {
+        config.logger = function () { };
+    }
+    // When set to true, any errors logged will be thrown immediately;
+    // If set to false, the errors will be thrown in separate execution frame.
+    if (!config.hasOwnProperty("useImmediateExceptions")) {
+        config.useImmediateExceptions = true;
+    }
+    // wrap realSetTimeout with something we can stub in tests
+    if (!config.hasOwnProperty("setTimeout")) {
+        config.setTimeout = realSetTimeout;
+    }
+
+    return function logError(label, e) {
+        var msg = label + " threw exception: ";
+        var err = { name: e.name || label, message: e.message || e.toString(), stack: e.stack };
+
+        function throwLoggedError() {
+            err.message = msg + err.message;
+            throw err;
+        }
+
+        config.logger(msg + "[" + err.name + "] " + err.message);
+
+        if (err.stack) {
+            config.logger(err.stack);
+        }
+
+        if (config.useImmediateExceptions) {
+            throwLoggedError();
+        } else {
+            config.setTimeout(throwLoggedError, 0);
+        }
+    };
+}
+
+module.exports = configure;
+
+},{}],36:[function(require,module,exports){
+"use strict";
+
+module.exports = function orderByFirstCall(spies) {
+    return spies.sort(function (a, b) {
+        // uuid, won't ever be equal
+        var aCall = a.getCall(0);
+        var bCall = b.getCall(0);
+        var aId = aCall && aCall.callId || -1;
+        var bId = bCall && bCall.callId || -1;
+
+        return aId < bId ? -1 : 1;
+    });
+};
+
+},{}],37:[function(require,module,exports){
+"use strict";
+
+var walk = require("./walk");
+
+function isRestorable(obj) {
+    return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
+}
+
+module.exports = function restore(object) {
+    if (object !== null && typeof object === "object") {
+        walk(object, function (prop) {
+            if (isRestorable(object[prop])) {
+                object[prop].restore();
+            }
+        });
+    } else if (isRestorable(object)) {
+        object.restore();
+    }
+};
+
+},{"./walk":41}],38:[function(require,module,exports){
+"use strict";
+
+var array = [null, "once", "twice", "thrice"];
+
+module.exports = function timesInWords(count) {
+    return array[count] || (count || 0) + " times";
+};
+
+},{}],39:[function(require,module,exports){
+"use strict";
+
+var type = require("type-detect");
+
+module.exports = function typeOf(value) {
+    return type(value).toLowerCase();
+};
+
+},{"type-detect":73}],40:[function(require,module,exports){
+"use strict";
+
+module.exports = function (value) {
+    if (value && value.toString) {
+        return value.toString();
+    }
+    return String(value);
+};
+
+},{}],41:[function(require,module,exports){
+"use strict";
+
+function walkInternal(obj, iterator, context, originalObj, seen) {
+    var proto, prop;
+
+    if (typeof Object.getOwnPropertyNames !== "function") {
+        // We explicitly want to enumerate through all of the prototype's properties
+        // in this case, therefore we deliberately leave out an own property check.
+        /* eslint-disable guard-for-in */
+        for (prop in obj) {
+            iterator.call(context, obj[prop], prop, obj);
+        }
+        /* eslint-enable guard-for-in */
+
+        return;
+    }
+
+    Object.getOwnPropertyNames(obj).forEach(function (k) {
+        if (seen[k] !== true) {
+            seen[k] = true;
+            var target = typeof Object.getOwnPropertyDescriptor(obj, k).get === "function" ?
+                originalObj : obj;
+            iterator.call(context, k, target);
+        }
+    });
+
+    proto = Object.getPrototypeOf(obj);
+    if (proto) {
+        walkInternal(proto, iterator, context, originalObj, seen);
+    }
+}
+
+/* Walks the prototype chain of an object and iterates over every own property
+ * name encountered. The iterator is called in the same fashion that Array.prototype.forEach
+ * works, where it is passed the value, key, and own object as the 1st, 2nd, and 3rd positional
+ * argument, respectively. In cases where Object.getOwnPropertyNames is not available, walk will
+ * default to using a simple for..in loop.
+ *
+ * obj - The object to walk the prototype chain for.
+ * iterator - The function to be called on each pass of the walk.
+ * context - (Optional) When given, the iterator will be called with this object as the receiver.
+ */
+module.exports = function walk(obj, iterator, context) {
+    return walkInternal(obj, iterator, context, obj, {});
+};
+
+},{}],42:[function(require,module,exports){
+"use strict";
+
+var getPropertyDescriptor = require("./get-property-descriptor");
+var valueToString = require("./value-to-string");
+
+var hasOwn = Object.prototype.hasOwnProperty;
+
+function isFunction(obj) {
+    return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
+}
+
+function mirrorProperties(target, source) {
+    for (var prop in source) {
+        if (!hasOwn.call(target, prop)) {
+            target[prop] = source[prop];
+        }
+    }
+}
+
+// Cheap way to detect if we have ES5 support.
+var hasES5Support = "keys" in Object;
+
+module.exports = function wrapMethod(object, property, method) {
+    if (!object) {
+        throw new TypeError("Should wrap property of object");
+    }
+
+    if (typeof method !== "function" && typeof method !== "object") {
+        throw new TypeError("Method wrapper should be a function or a property descriptor");
+    }
+
+    function checkWrappedMethod(wrappedMethod) {
+        var error;
+
+        if (!isFunction(wrappedMethod)) {
+            error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+                                valueToString(property) + " as function");
+        } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
+            error = new TypeError("Attempted to wrap " + valueToString(property) + " which is already wrapped");
+        } else if (wrappedMethod.calledBefore) {
+            var verb = wrappedMethod.returns ? "stubbed" : "spied on";
+            error = new TypeError("Attempted to wrap " + valueToString(property) + " which is already " + verb);
+        }
+
+        if (error) {
+            if (wrappedMethod && wrappedMethod.stackTrace) {
+                error.stack += "\n--------------\n" + wrappedMethod.stackTrace;
+            }
+            throw error;
+        }
+    }
+
+    var error, wrappedMethod, i;
+
+    function simplePropertyAssignment() {
+        wrappedMethod = object[property];
+        checkWrappedMethod(wrappedMethod);
+        object[property] = method;
+        method.displayName = property;
+    }
+
+    // Firefox has a problem when using hasOwn.call on objects from other frames.
+    var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property);
+
+    if (hasES5Support) {
+        var methodDesc = (typeof method === "function") ? {value: method} : method;
+        var wrappedMethodDesc = getPropertyDescriptor(object, property);
+
+        if (!wrappedMethodDesc) {
+            error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+                                property + " as function");
+        } else if (wrappedMethodDesc.restore && wrappedMethodDesc.restore.sinon) {
+            error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
+        }
+        if (error) {
+            if (wrappedMethodDesc && wrappedMethodDesc.stackTrace) {
+                error.stack += "\n--------------\n" + wrappedMethodDesc.stackTrace;
+            }
+            throw error;
+        }
+
+        var types = Object.keys(methodDesc);
+        for (i = 0; i < types.length; i++) {
+            wrappedMethod = wrappedMethodDesc[types[i]];
+            checkWrappedMethod(wrappedMethod);
+        }
+
+        mirrorProperties(methodDesc, wrappedMethodDesc);
+        for (i = 0; i < types.length; i++) {
+            mirrorProperties(methodDesc[types[i]], wrappedMethodDesc[types[i]]);
+        }
+        Object.defineProperty(object, property, methodDesc);
+
+        // catch failing assignment
+        // this is the converse of the check in `.restore` below
+        if ( typeof method === "function" && object[property] !== method ) {
+            // correct any wrongdoings caused by the defineProperty call above,
+            // such as adding new items (if object was a Storage object)
+            delete object[property];
+            simplePropertyAssignment();
+        }
+    } else {
+        simplePropertyAssignment();
+    }
+
+    method.displayName = property;
+
+    // Set up a stack trace which can be used later to find what line of
+    // code the original method was created on.
+    method.stackTrace = (new Error("Stack Trace for original")).stack;
+
+    method.restore = function () {
+        // For prototype properties try to reset by delete first.
+        // If this fails (ex: localStorage on mobile safari) then force a reset
+        // via direct assignment.
+        if (!owned) {
+            // In some cases `delete` may throw an error
+            try {
+                delete object[property];
+            } catch (e) {} // eslint-disable-line no-empty
+            // For native code functions `delete` fails without throwing an error
+            // on Chrome < 43, PhantomJS, etc.
+        } else if (hasES5Support) {
+            Object.defineProperty(object, property, wrappedMethodDesc);
+        }
+
+        if (hasES5Support) {
+            var descriptor = getPropertyDescriptor(object, property);
+            if (descriptor && descriptor.value === method) {
+                object[property] = wrappedMethod;
+            }
+        }
+        else {
+        // Use strict equality comparison to check failures then force a reset
+        // via direct assignment.
+            if (object[property] === method) {
+                object[property] = wrappedMethod;
+            }
+        }
+    };
+
+    method.wrappedMethod = wrappedMethod;
+
+    method.restore.sinon = true;
+
+    if (!hasES5Support) {
+        mirrorProperties(method, wrappedMethod);
+    }
+
+    return method;
+};
+
+},{"./get-property-descriptor":32,"./value-to-string":40}],43:[function(require,module,exports){
+"use strict";
+
+var push = [].push;
+
+function Event(type, bubbles, cancelable, target) {
+    this.initEvent(type, bubbles, cancelable, target);
+}
+
+Event.prototype = {
+    initEvent: function (type, bubbles, cancelable, target) {
+        this.type = type;
+        this.bubbles = bubbles;
+        this.cancelable = cancelable;
+        this.target = target;
+    },
+
+    stopPropagation: function () {},
+
+    preventDefault: function () {
+        this.defaultPrevented = true;
+    }
+};
+
+function ProgressEvent(type, progressEventRaw, target) {
+    this.initEvent(type, false, false, target);
+    this.loaded = typeof progressEventRaw.loaded === "number" ? progressEventRaw.loaded : null;
+    this.total = typeof progressEventRaw.total === "number" ? progressEventRaw.total : null;
+    this.lengthComputable = !!progressEventRaw.total;
+}
+
+ProgressEvent.prototype = new Event();
+
+ProgressEvent.prototype.constructor = ProgressEvent;
+
+function CustomEvent(type, customData, target) {
+    this.initEvent(type, false, false, target);
+    this.detail = customData.detail || null;
+}
+
+CustomEvent.prototype = new Event();
+
+CustomEvent.prototype.constructor = CustomEvent;
+
+var EventTarget = {
+    addEventListener: function addEventListener(event, listener) {
+        this.eventListeners = this.eventListeners || {};
+        this.eventListeners[event] = this.eventListeners[event] || [];
+        push.call(this.eventListeners[event], listener);
+    },
+
+    removeEventListener: function removeEventListener(event, listener) {
+        var listeners = this.eventListeners && this.eventListeners[event] || [];
+        var index = listeners.indexOf(listener);
+
+        if (index === -1) {
+            return;
+        }
+
+        listeners.splice(index, 1);
+    },
+
+    dispatchEvent: function dispatchEvent(event) {
+        var self = this;
+        var type = event.type;
+        var listeners = self.eventListeners && self.eventListeners[type] || [];
+
+        listeners.forEach(function (listener) {
+            if (typeof listener === "function") {
+                listener.call(self, event);
+            } else {
+                listener.handleEvent(event);
+            }
+        });
+
+        return !!event.defaultPrevented;
+    }
+};
+
+module.exports = {
+    Event: Event,
+    ProgressEvent: ProgressEvent,
+    CustomEvent: CustomEvent,
+    EventTarget: EventTarget
+};
+
+},{}],44:[function(require,module,exports){
+"use strict";
+
+var fakeXhr = require("./fake_xml_http_request");
+var push = [].push;
+var format = require("./core/format");
+var configureLogError = require("./core/log_error");
+var pathToRegexp = require("path-to-regexp");
+
+function responseArray(handler) {
+    var response = handler;
+
+    if (Object.prototype.toString.call(handler) !== "[object Array]") {
+        response = [200, {}, handler];
+    }
+
+    if (typeof response[2] !== "string") {
+        throw new TypeError("Fake server response body should be string, but was " +
+                            typeof response[2]);
+    }
+
+    return response;
+}
+
+function getDefaultWindowLocation() {
+    return { "host": "localhost", "protocol": "http" };
+}
+
+function getWindowLocation() {
+    if (typeof window === "undefined") {
+        // Fallback
+        return getDefaultWindowLocation();
+    }
+
+    if (typeof window.location !== "undefined") {
+        // Browsers place location on window
+        return window.location;
+    }
+
+    if ((typeof window.window !== "undefined") && (typeof window.window.location !== "undefined")) {
+        // React Native on Android places location on window.window
+        return window.window.location;
+    }
+
+    return getDefaultWindowLocation();
+}
+
+var wloc = getWindowLocation();
+
+var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
+
+function matchOne(response, reqMethod, reqUrl) {
+    var rmeth = response.method;
+    var matchMethod = !rmeth || rmeth.toLowerCase() === reqMethod.toLowerCase();
+    var url = response.url;
+    var matchUrl = !url || url === reqUrl || (typeof url.test === "function" && url.test(reqUrl));
+
+    return matchMethod && matchUrl;
+}
+
+function match(response, request) {
+    var requestUrl = request.url;
+
+    if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
+        requestUrl = requestUrl.replace(rCurrLoc, "");
+    }
+
+    if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
+        if (typeof response.response === "function") {
+            var ru = response.url;
+            var args = [request].concat(ru && typeof ru.exec === "function" ? ru.exec(requestUrl).slice(1) : []);
+            return response.response.apply(response, args);
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+function incrementRequestCount() {
+    var count = ++this.requestCount;
+
+    this.requested = true;
+
+    this.requestedOnce = count === 1;
+    this.requestedTwice = count === 2;
+    this.requestedThrice = count === 3;
+
+    this.firstRequest = this.getRequest(0);
+    this.secondRequest = this.getRequest(1);
+    this.thirdRequest = this.getRequest(2);
+
+    this.lastRequest = this.getRequest(count - 1);
+}
+
+var fakeServer = {
+    create: function (config) {
+        var server = Object.create(this);
+        server.configure(config);
+        this.xhr = fakeXhr.useFakeXMLHttpRequest();
+        server.requests = [];
+        server.requestCount = 0;
+        server.queue = [];
+        server.responses = [];
+
+
+        this.xhr.onCreate = function (xhrObj) {
+            xhrObj.unsafeHeadersEnabled = function () {
+                return !(server.unsafeHeadersEnabled === false);
+            };
+            server.addRequest(xhrObj);
+        };
+
+        return server;
+    },
+
+    configure: function (config) {
+        var self = this;
+        var whitelist = {
+            "autoRespond": true,
+            "autoRespondAfter": true,
+            "respondImmediately": true,
+            "fakeHTTPMethods": true,
+            "logger": true,
+            "unsafeHeadersEnabled": true
+        };
+
+        config = config || {};
+
+        Object.keys(config).forEach(function (setting) {
+            if (setting in whitelist) {
+                self[setting] = config[setting];
+            }
+        });
+
+        self.logError = configureLogError(config);
+    },
+
+    addRequest: function addRequest(xhrObj) {
+        var server = this;
+        push.call(this.requests, xhrObj);
+
+        incrementRequestCount.call(this);
+
+        xhrObj.onSend = function () {
+            server.handleRequest(this);
+
+            if (server.respondImmediately) {
+                server.respond();
+            } else if (server.autoRespond && !server.responding) {
+                setTimeout(function () {
+                    server.responding = false;
+                    server.respond();
+                }, server.autoRespondAfter || 10);
+
+                server.responding = true;
+            }
+        };
+    },
+
+    getHTTPMethod: function getHTTPMethod(request) {
+        if (this.fakeHTTPMethods && /post/i.test(request.method)) {
+            var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
+            return matches ? matches[1] : request.method;
+        }
+
+        return request.method;
+    },
+
+    handleRequest: function handleRequest(xhr) {
+        if (xhr.async) {
+            push.call(this.queue, xhr);
+        } else {
+            this.processRequest(xhr);
+        }
+    },
+
+    logger: function () {
+        // no-op; override via configure()
+    },
+
+    logError: configureLogError({}),
+
+    log: function log(response, request) {
+        var str;
+
+        str = "Request:\n" + format(request) + "\n\n";
+        str += "Response:\n" + format(response) + "\n\n";
+
+        if (typeof this.logger === "function") {
+            this.logger(str);
+        }
+    },
+
+    respondWith: function respondWith(method, url, body) {
+        if (arguments.length === 1 && typeof method !== "function") {
+            this.response = responseArray(method);
+            return;
+        }
+
+        if (arguments.length === 1) {
+            body = method;
+            url = method = null;
+        }
+
+        if (arguments.length === 2) {
+            body = url;
+            url = method;
+            method = null;
+        }
+
+        push.call(this.responses, {
+            method: method,
+            url: typeof url === "string" && url !== "" ? pathToRegexp(url) : url,
+            response: typeof body === "function" ? body : responseArray(body)
+        });
+    },
+
+    respond: function respond() {
+        if (arguments.length > 0) {
+            this.respondWith.apply(this, arguments);
+        }
+
+        var queue = this.queue || [];
+        var requests = queue.splice(0, queue.length);
+        var self = this;
+
+        requests.forEach(function (request) {
+            self.processRequest(request);
+        });
+    },
+
+    processRequest: function processRequest(request) {
+        try {
+            if (request.aborted) {
+                return;
+            }
+
+            var response = this.response || [404, {}, ""];
+
+            if (this.responses) {
+                for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
+                    if (match.call(this, this.responses[i], request)) {
+                        response = this.responses[i].response;
+                        break;
+                    }
+                }
+            }
+
+            if (request.readyState !== 4) {
+                this.log(response, request);
+
+                request.respond(response[0], response[1], response[2]);
+            }
+        } catch (e) {
+            this.logError("Fake server request processing", e);
+        }
+    },
+
+    restore: function restore() {
+        return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
+    },
+
+    getRequest: function getRequest(index) {
+        return this.requests[index] || null;
+    },
+
+    reset: function reset() {
+        this.resetBehavior();
+        this.resetHistory();
+    },
+
+    resetBehavior: function resetBehavior() {
+        this.responses.length = this.queue.length = 0;
+    },
+
+    resetHistory: function resetHistory() {
+        this.requests.length = this.requestCount = 0;
+
+        this.requestedOnce = this.requestedTwice = this.requestedThrice = this.requested = false;
+
+        this.firstRequest = this.secondRequest = this.thirdRequest = this.lastRequest = null;
+    }
+};
+
+module.exports = fakeServer;
+
+},{"./core/format":28,"./core/log_error":35,"./fake_xml_http_request":47,"path-to-regexp":67}],45:[function(require,module,exports){
+"use strict";
+
+var fakeServer = require("./fake_server");
+var fakeTimers = require("./fake_timers");
+
+function Server() {}
+Server.prototype = fakeServer;
+
+var fakeServerWithClock = new Server();
+
+fakeServerWithClock.addRequest = function addRequest(xhr) {
+    if (xhr.async) {
+        if (typeof setTimeout.clock === "object") {
+            this.clock = setTimeout.clock;
+        } else {
+            this.clock = fakeTimers.useFakeTimers();
+            this.resetClock = true;
+        }
+
+        if (!this.longestTimeout) {
+            var clockSetTimeout = this.clock.setTimeout;
+            var clockSetInterval = this.clock.setInterval;
+            var server = this;
+
+            this.clock.setTimeout = function (fn, timeout) {
+                server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+                return clockSetTimeout.apply(this, arguments);
+            };
+
+            this.clock.setInterval = function (fn, timeout) {
+                server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
+
+                return clockSetInterval.apply(this, arguments);
+            };
+        }
+    }
+
+    return fakeServer.addRequest.call(this, xhr);
+};
+
+fakeServerWithClock.respond = function respond() {
+    var returnVal = fakeServer.respond.apply(this, arguments);
+
+    if (this.clock) {
+        this.clock.tick(this.longestTimeout || 0);
+        this.longestTimeout = 0;
+
+        if (this.resetClock) {
+            this.clock.restore();
+            this.resetClock = false;
+        }
+    }
+
+    return returnVal;
+};
+
+fakeServerWithClock.restore = function restore() {
+    if (this.clock) {
+        this.clock.restore();
+    }
+
+    return fakeServer.restore.apply(this, arguments);
+};
+
+module.exports = fakeServerWithClock;
+
+},{"./fake_server":44,"./fake_timers":46}],46:[function(require,module,exports){
+"use strict";
+
+var llx = require("lolex");
+
+exports.useFakeTimers = function () {
+    var now;
+    var methods = Array.prototype.slice.call(arguments);
+
+    if (typeof methods[0] === "string") {
+        now = 0;
+    } else {
+        now = methods.shift();
+    }
+
+    var clock = llx.install(now || 0, methods);
+    clock.restore = clock.uninstall;
+    return clock;
+};
+
+exports.clock = {
+    create: function (now) {
+        return llx.createClock(now);
+    }
+};
+
+exports.timers = {
+    setTimeout: setTimeout,
+    clearTimeout: clearTimeout,
+    setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
+    clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate : undefined),
+    setInterval: setInterval,
+    clearInterval: clearInterval,
+    Date: Date
+};
+
+},{"lolex":66}],47:[function(require,module,exports){
+(function (global){
+"use strict";
+
+var TextEncoder = require("text-encoding").TextEncoder;
+
+var configureLogError = require("./core/log_error");
+var sinonEvent = require("./event");
+var extend = require("./core/extend");
+
+function getWorkingXHR(globalScope) {
+    var supportsXHR = typeof globalScope.XMLHttpRequest !== "undefined";
+    if (supportsXHR) {
+        return globalScope.XMLHttpRequest;
+    }
+
+    var supportsActiveX = typeof globalScope.ActiveXObject !== "undefined";
+    if (supportsActiveX) {
+        return function () {
+            return new globalScope.ActiveXObject("MSXML2.XMLHTTP.3.0");
+        };
+    }
+
+    return false;
+}
+
+var supportsProgress = typeof ProgressEvent !== "undefined";
+var supportsCustomEvent = typeof CustomEvent !== "undefined";
+var supportsFormData = typeof FormData !== "undefined";
+var supportsArrayBuffer = typeof ArrayBuffer !== "undefined";
+var supportsBlob = require("../blob").isSupported;
+var isReactNative = global.navigator && global.navigator.product === "ReactNative";
+var sinonXhr = { XMLHttpRequest: global.XMLHttpRequest };
+sinonXhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
+sinonXhr.GlobalActiveXObject = global.ActiveXObject;
+sinonXhr.supportsActiveX = typeof sinonXhr.GlobalActiveXObject !== "undefined";
+sinonXhr.supportsXHR = typeof sinonXhr.GlobalXMLHttpRequest !== "undefined";
+sinonXhr.workingXHR = getWorkingXHR(global);
+sinonXhr.supportsCORS = isReactNative ||
+    (sinonXhr.supportsXHR && "withCredentials" in (new sinonXhr.GlobalXMLHttpRequest()));
+
+var unsafeHeaders = {
+    "Accept-Charset": true,
+    "Accept-Encoding": true,
+    "Connection": true,
+    "Content-Length": true,
+    "Cookie": true,
+    "Cookie2": true,
+    "Content-Transfer-Encoding": true,
+    "Date": true,
+    "Expect": true,
+    "Host": true,
+    "Keep-Alive": true,
+    "Referer": true,
+    "TE": true,
+    "Trailer": true,
+    "Transfer-Encoding": true,
+    "Upgrade": true,
+    "User-Agent": true,
+    "Via": true
+};
+
+
+function EventTargetHandler() {
+    var self = this;
+    var events = ["loadstart", "progress", "abort", "error", "load", "timeout", "loadend"];
+
+    function addEventListener(eventName) {
+        self.addEventListener(eventName, function (event) {
+            var listener = self["on" + eventName];
+
+            if (listener && typeof listener === "function") {
+                listener.call(this, event);
+            }
+        });
+    }
+
+    events.forEach(addEventListener);
+}
+
+EventTargetHandler.prototype = sinonEvent.EventTarget;
+
+// Note that for FakeXMLHttpRequest to work pre ES5
+// we lose some of the alignment with the spec.
+// To ensure as close a match as possible,
+// set responseType before calling open, send or respond;
+function FakeXMLHttpRequest(config) {
+    EventTargetHandler.call(this);
+    this.readyState = FakeXMLHttpRequest.UNSENT;
+    this.requestHeaders = {};
+    this.requestBody = null;
+    this.status = 0;
+    this.statusText = "";
+    this.upload = new EventTargetHandler();
+    this.responseType = "";
+    this.response = "";
+    this.logError = configureLogError(config);
+    if (sinonXhr.supportsCORS) {
+        this.withCredentials = false;
+    }
+
+    if (typeof FakeXMLHttpRequest.onCreate === "function") {
+        FakeXMLHttpRequest.onCreate(this);
+    }
+}
+
+function verifyState(xhr) {
+    if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
+        throw new Error("INVALID_STATE_ERR");
+    }
+
+    if (xhr.sendFlag) {
+        throw new Error("INVALID_STATE_ERR");
+    }
+}
+
+function getHeader(headers, header) {
+    var foundHeader = Object.keys(headers).filter(function (h) {
+        return h.toLowerCase() === header.toLowerCase();
+    });
+
+    return foundHeader[0] || null;
+}
+
+function excludeSetCookie2Header(header) {
+    return !/^Set-Cookie2?$/i.test(header);
+}
+
+// largest arity in XHR is 5 - XHR#open
+var apply = function (obj, method, args) {
+    switch (args.length) {
+        case 0: return obj[method]();
+        case 1: return obj[method](args[0]);
+        case 2: return obj[method](args[0], args[1]);
+        case 3: return obj[method](args[0], args[1], args[2]);
+        case 4: return obj[method](args[0], args[1], args[2], args[3]);
+        case 5: return obj[method](args[0], args[1], args[2], args[3], args[4]);
+        default: throw new Error("Unhandled case");
+    }
+};
+
+FakeXMLHttpRequest.filters = [];
+FakeXMLHttpRequest.addFilter = function addFilter(fn) {
+    this.filters.push(fn);
+};
+FakeXMLHttpRequest.defake = function defake(fakeXhr, xhrArgs) {
+    var xhr = new sinonXhr.workingXHR(); // eslint-disable-line new-cap
+
+    [
+        "open",
+        "setRequestHeader",
+        "send",
+        "abort",
+        "getResponseHeader",
+        "getAllResponseHeaders",
+        "addEventListener",
+        "overrideMimeType",
+        "removeEventListener"
+    ].forEach(function (method) {
+        fakeXhr[method] = function () {
+            return apply(xhr, method, arguments);
+        };
+    });
+
+    var copyAttrs = function (args) {
+        args.forEach(function (attr) {
+            fakeXhr[attr] = xhr[attr];
+        });
+    };
+
+    var stateChange = function stateChange() {
+        fakeXhr.readyState = xhr.readyState;
+        if (xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
+            copyAttrs(["status", "statusText"]);
+        }
+        if (xhr.readyState >= FakeXMLHttpRequest.LOADING) {
+            copyAttrs(["responseText", "response"]);
+        }
+        if (xhr.readyState === FakeXMLHttpRequest.DONE) {
+            copyAttrs(["responseXML"]);
+        }
+        if (fakeXhr.onreadystatechange) {
+            fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr });
+        }
+    };
+
+    if (xhr.addEventListener) {
+        Object.keys(fakeXhr.eventListeners).forEach(function (event) {
+            /*eslint-disable no-loop-func*/
+            fakeXhr.eventListeners[event].forEach(function (handler) {
+                xhr.addEventListener(event, handler);
+            });
+            /*eslint-enable no-loop-func*/
+        });
+
+        xhr.addEventListener("readystatechange", stateChange);
+    } else {
+        xhr.onreadystatechange = stateChange;
+    }
+    apply(xhr, "open", xhrArgs);
+};
+FakeXMLHttpRequest.useFilters = false;
+
+function verifyRequestOpened(xhr) {
+    if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
+        throw new Error("INVALID_STATE_ERR - " + xhr.readyState);
+    }
+}
+
+function verifyRequestSent(xhr) {
+    if (xhr.readyState === FakeXMLHttpRequest.DONE) {
+        throw new Error("Request done");
+    }
+}
+
+function verifyHeadersReceived(xhr) {
+    if (xhr.async && xhr.readyState !== FakeXMLHttpRequest.HEADERS_RECEIVED) {
+        throw new Error("No headers received");
+    }
+}
+
+function verifyResponseBodyType(body) {
+    if (typeof body !== "string") {
+        var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
+                             body + ", which is not a string.");
+        error.name = "InvalidBodyException";
+        throw error;
+    }
+}
+
+function convertToArrayBuffer(body, encoding) {
+    return new TextEncoder(encoding || "utf-8").encode(body).buffer;
+}
+
+function isXmlContentType(contentType) {
+    return !contentType || /(text\/xml)|(application\/xml)|(\+xml)/.test(contentType);
+}
+
+function convertResponseBody(responseType, contentType, body) {
+    if (responseType === "" || responseType === "text") {
+        return body;
+    } else if (supportsArrayBuffer && responseType === "arraybuffer") {
+        return convertToArrayBuffer(body);
+    } else if (responseType === "json") {
+        try {
+            return JSON.parse(body);
+        } catch (e) {
+            // Return parsing failure as null
+            return null;
+        }
+    } else if (supportsBlob && responseType === "blob") {
+        var blobOptions = {};
+        if (contentType) {
+            blobOptions.type = contentType;
+        }
+        return new Blob([convertToArrayBuffer(body)], blobOptions);
+    } else if (responseType === "document") {
+        if (isXmlContentType(contentType)) {
+            return FakeXMLHttpRequest.parseXML(body);
+        }
+        return null;
+    }
+    throw new Error("Invalid responseType " + responseType);
+}
+
+function clearResponse(xhr) {
+    if (xhr.responseType === "" || xhr.responseType === "text") {
+        xhr.response = xhr.responseText = "";
+    } else {
+        xhr.response = xhr.responseText = null;
+    }
+    xhr.responseXML = null;
+}
+
+FakeXMLHttpRequest.parseXML = function parseXML(text) {
+    // Treat empty string as parsing failure
+    if (text !== "") {
+        try {
+            if (typeof DOMParser !== "undefined") {
+                var parser = new DOMParser();
+                return parser.parseFromString(text, "text/xml");
+            }
+            var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
+            xmlDoc.async = "false";
+            xmlDoc.loadXML(text);
+            return xmlDoc;
+        } catch (e) {
+            // Unable to parse XML - no biggie
+        }
+    }
+
+    return null;
+};
+
+FakeXMLHttpRequest.statusCodes = {
+    100: "Continue",
+    101: "Switching Protocols",
+    200: "OK",
+    201: "Created",
+    202: "Accepted",
+    203: "Non-Authoritative Information",
+    204: "No Content",
+    205: "Reset Content",
+    206: "Partial Content",
+    207: "Multi-Status",
+    300: "Multiple Choice",
+    301: "Moved Permanently",
+    302: "Found",
+    303: "See Other",
+    304: "Not Modified",
+    305: "Use Proxy",
+    307: "Temporary Redirect",
+    400: "Bad Request",
+    401: "Unauthorized",
+    402: "Payment Required",
+    403: "Forbidden",
+    404: "Not Found",
+    405: "Method Not Allowed",
+    406: "Not Acceptable",
+    407: "Proxy Authentication Required",
+    408: "Request Timeout",
+    409: "Conflict",
+    410: "Gone",
+    411: "Length Required",
+    412: "Precondition Failed",
+    413: "Request Entity Too Large",
+    414: "Request-URI Too Long",
+    415: "Unsupported Media Type",
+    416: "Requested Range Not Satisfiable",
+    417: "Expectation Failed",
+    422: "Unprocessable Entity",
+    500: "Internal Server Error",
+    501: "Not Implemented",
+    502: "Bad Gateway",
+    503: "Service Unavailable",
+    504: "Gateway Timeout",
+    505: "HTTP Version Not Supported"
+};
+
+extend(FakeXMLHttpRequest.prototype, sinonEvent.EventTarget, {
+    async: true,
+
+    open: function open(method, url, async, username, password) {
+        this.method = method;
+        this.url = url;
+        this.async = typeof async === "boolean" ? async : true;
+        this.username = username;
+        this.password = password;
+        clearResponse(this);
+        this.requestHeaders = {};
+        this.sendFlag = false;
+
+        if (FakeXMLHttpRequest.useFilters === true) {
+            var xhrArgs = arguments;
+            var defake = FakeXMLHttpRequest.filters.some(function (filter) {
+                return filter.apply(this, xhrArgs);
+            });
+            if (defake) {
+                FakeXMLHttpRequest.defake(this, arguments);
+                return;
+            }
+        }
+        this.readyStateChange(FakeXMLHttpRequest.OPENED);
+    },
+
+    readyStateChange: function readyStateChange(state) {
+        this.readyState = state;
+
+        var readyStateChangeEvent = new sinonEvent.Event("readystatechange", false, false, this);
+        var event, progress;
+
+        if (typeof this.onreadystatechange === "function") {
+            try {
+                this.onreadystatechange(readyStateChangeEvent);
+            } catch (e) {
+                this.logError("Fake XHR onreadystatechange handler", e);
+            }
+        }
+
+        if (this.readyState === FakeXMLHttpRequest.DONE) {
+            if (this.aborted || this.status === 0) {
+                progress = {loaded: 0, total: 0};
+                event = this.aborted ? "abort" : "error";
+            } else {
+                progress = {loaded: 100, total: 100};
+                event = "load";
+            }
+
+            if (supportsProgress) {
+                this.upload.dispatchEvent(new sinonEvent.ProgressEvent("progress", progress, this));
+                this.upload.dispatchEvent(new sinonEvent.ProgressEvent(event, progress, this));
+                this.upload.dispatchEvent(new sinonEvent.ProgressEvent("loadend", progress, this));
+            }
+
+            this.dispatchEvent(new sinonEvent.ProgressEvent("progress", progress, this));
+            this.dispatchEvent(new sinonEvent.ProgressEvent(event, progress, this));
+            this.dispatchEvent(new sinonEvent.ProgressEvent("loadend", progress, this));
+        }
+
+        this.dispatchEvent(readyStateChangeEvent);
+    },
+
+    setRequestHeader: function setRequestHeader(header, value) {
+        verifyState(this);
+
+        var checkUnsafeHeaders = true;
+        if (typeof this.unsafeHeadersEnabled === "function") {
+            checkUnsafeHeaders = this.unsafeHeadersEnabled();
+        }
+
+        if (checkUnsafeHeaders && (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header))) {
+            throw new Error("Refused to set unsafe header \"" + header + "\"");
+        }
+
+        if (this.requestHeaders[header]) {
+            this.requestHeaders[header] += "," + value;
+        } else {
+            this.requestHeaders[header] = value;
+        }
+    },
+
+    setStatus: function setStatus(status) {
+        var sanitizedStatus = typeof status === "number" ? status : 200;
+
+        verifyRequestOpened(this);
+        this.status = sanitizedStatus;
+        this.statusText = FakeXMLHttpRequest.statusCodes[sanitizedStatus];
+    },
+
+    // Helps testing
+    setResponseHeaders: function setResponseHeaders(headers) {
+        verifyRequestOpened(this);
+
+        var responseHeaders = this.responseHeaders = {};
+
+        Object.keys(headers).forEach(function (header) {
+            responseHeaders[header] = headers[header];
+        });
+
+        if (this.async) {
+            this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
+        } else {
+            this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
+        }
+    },
+
+    // Currently treats ALL data as a DOMString (i.e. no Document)
+    send: function send(data) {
+        verifyState(this);
+
+        if (!/^(head)$/i.test(this.method)) {
+            var contentType = getHeader(this.requestHeaders, "Content-Type");
+            if (this.requestHeaders[contentType]) {
+                var value = this.requestHeaders[contentType].split(";");
+                this.requestHeaders[contentType] = value[0] + ";charset=utf-8";
+            } else if (supportsFormData && !(data instanceof FormData)) {
+                this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
+            }
+
+            this.requestBody = data;
+        }
+
+        this.errorFlag = false;
+        this.sendFlag = this.async;
+        clearResponse(this);
+        this.readyStateChange(FakeXMLHttpRequest.OPENED);
+
+        if (typeof this.onSend === "function") {
+            this.onSend(this);
+        }
+
+        this.dispatchEvent(new sinonEvent.Event("loadstart", false, false, this));
+    },
+
+    abort: function abort() {
+        this.aborted = true;
+        clearResponse(this);
+        this.errorFlag = true;
+        this.requestHeaders = {};
+        this.responseHeaders = {};
+
+        if (this.readyState !== FakeXMLHttpRequest.UNSENT && this.sendFlag
+            && this.readyState !== FakeXMLHttpRequest.DONE) {
+            this.readyStateChange(FakeXMLHttpRequest.DONE);
+            this.sendFlag = false;
+        }
+
+        this.readyState = FakeXMLHttpRequest.UNSENT;
+    },
+
+    error: function () {
+        clearResponse(this);
+        this.errorFlag = true;
+        this.requestHeaders = {};
+        this.responseHeaders = {};
+
+        this.readyStateChange(FakeXMLHttpRequest.DONE);
+    },
+
+    getResponseHeader: function getResponseHeader(header) {
+        if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+            return null;
+        }
+
+        if (/^Set-Cookie2?$/i.test(header)) {
+            return null;
+        }
+
+        header = getHeader(this.responseHeaders, header);
+
+        return this.responseHeaders[header] || null;
+    },
+
+    getAllResponseHeaders: function getAllResponseHeaders() {
+        if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
+            return "";
+        }
+
+        var responseHeaders = this.responseHeaders;
+        var headers = Object.keys(responseHeaders)
+            .filter(excludeSetCookie2Header)
+            .reduce(function (prev, header) {
+                var value = responseHeaders[header];
+
+                return prev + (header + ": " + value + "\r\n");
+            }, "");
+
+        return headers;
+    },
+
+    setResponseBody: function setResponseBody(body) {
+        verifyRequestSent(this);
+        verifyHeadersReceived(this);
+        verifyResponseBodyType(body);
+        var contentType = this.overriddenMimeType || this.getResponseHeader("Content-Type");
+
+        var isTextResponse = this.responseType === "" || this.responseType === "text";
+        clearResponse(this);
+        if (this.async) {
+            var chunkSize = this.chunkSize || 10;
+            var index = 0;
+
+            do {
+                this.readyStateChange(FakeXMLHttpRequest.LOADING);
+
+                if (isTextResponse) {
+                    this.responseText = this.response += body.substring(index, index + chunkSize);
+                }
+                index += chunkSize;
+            } while (index < body.length);
+        }
+
+        this.response = convertResponseBody(this.responseType, contentType, body);
+        if (isTextResponse) {
+            this.responseText = this.response;
+        }
+
+        if (this.responseType === "document") {
+            this.responseXML = this.response;
+        } else if (this.responseType === "" && isXmlContentType(contentType)) {
+            this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
+        }
+        this.readyStateChange(FakeXMLHttpRequest.DONE);
+    },
+
+    respond: function respond(status, headers, body) {
+        this.setStatus(status);
+        this.setResponseHeaders(headers || {});
+        this.setResponseBody(body || "");
+    },
+
+    uploadProgress: function uploadProgress(progressEventRaw) {
+        if (supportsProgress) {
+            this.upload.dispatchEvent(new sinonEvent.ProgressEvent("progress", progressEventRaw));
+        }
+    },
+
+    downloadProgress: function downloadProgress(progressEventRaw) {
+        if (supportsProgress) {
+            this.dispatchEvent(new sinonEvent.ProgressEvent("progress", progressEventRaw));
+        }
+    },
+
+    uploadError: function uploadError(error) {
+        if (supportsCustomEvent) {
+            this.upload.dispatchEvent(new sinonEvent.CustomEvent("error", {detail: error}));
+        }
+    },
+
+    overrideMimeType: function overrideMimeType(type) {
+        if (this.readyState >= FakeXMLHttpRequest.LOADING) {
+            throw new Error("INVALID_STATE_ERR");
+        }
+        this.overriddenMimeType = type;
+    }
+});
+
+var states = {
+    UNSENT: 0,
+    OPENED: 1,
+    HEADERS_RECEIVED: 2,
+    LOADING: 3,
+    DONE: 4
+};
+
+extend(FakeXMLHttpRequest, states);
+extend(FakeXMLHttpRequest.prototype, states);
+
+function useFakeXMLHttpRequest() {
+    FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
+        if (sinonXhr.supportsXHR) {
+            global.XMLHttpRequest = sinonXhr.GlobalXMLHttpRequest;
+        }
+
+        if (sinonXhr.supportsActiveX) {
+            global.ActiveXObject = sinonXhr.GlobalActiveXObject;
+        }
+
+        delete FakeXMLHttpRequest.restore;
+
+        if (keepOnCreate !== true) {
+            delete FakeXMLHttpRequest.onCreate;
+        }
+    };
+    if (sinonXhr.supportsXHR) {
+        global.XMLHttpRequest = FakeXMLHttpRequest;
+    }
+
+    if (sinonXhr.supportsActiveX) {
+        global.ActiveXObject = function ActiveXObject(objId) {
+            if (objId === "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
+
+                return new FakeXMLHttpRequest();
+            }
+
+            return new sinonXhr.GlobalActiveXObject(objId);
+        };
+    }
+
+    return FakeXMLHttpRequest;
+}
+
+module.exports = {
+    xhr: sinonXhr,
+    FakeXMLHttpRequest: FakeXMLHttpRequest,
+    useFakeXMLHttpRequest: useFakeXMLHttpRequest
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"../blob":4,"./core/extend":27,"./core/log_error":35,"./event":43,"text-encoding":70}],48:[function(require,module,exports){
+/*istanbul ignore start*/"use strict";
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/convertChangesToDMP = convertChangesToDMP;
+// See: http://code.google.com/p/google-diff-match-patch/wiki/API
+function convertChangesToDMP(changes) {
+  var ret = [],
+      change = /*istanbul ignore start*/void 0 /*istanbul ignore end*/,
+      operation = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+  for (var i = 0; i < changes.length; i++) {
+    change = changes[i];
+    if (change.added) {
+      operation = 1;
+    } else if (change.removed) {
+      operation = -1;
+    } else {
+      operation = 0;
+    }
+
+    ret.push([operation, change.value]);
+  }
+  return ret;
+}
+
+
+},{}],49:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/convertChangesToXML = convertChangesToXML;
+function convertChangesToXML(changes) {
+  var ret = [];
+  for (var i = 0; i < changes.length; i++) {
+    var change = changes[i];
+    if (change.added) {
+      ret.push('<ins>');
+    } else if (change.removed) {
+      ret.push('<del>');
+    }
+
+    ret.push(escapeHTML(change.value));
+
+    if (change.added) {
+      ret.push('</ins>');
+    } else if (change.removed) {
+      ret.push('</del>');
+    }
+  }
+  return ret.join('');
+}
+
+function escapeHTML(s) {
+  var n = s;
+  n = n.replace(/&/g, '&amp;');
+  n = n.replace(/</g, '&lt;');
+  n = n.replace(/>/g, '&gt;');
+  n = n.replace(/"/g, '&quot;');
+
+  return n;
+}
+
+
+},{}],50:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.arrayDiff = undefined;
+exports. /*istanbul ignore end*/diffArrays = diffArrays;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/var arrayDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/arrayDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+arrayDiff.tokenize = arrayDiff.join = function (value) {
+  return value.slice();
+};
+
+function diffArrays(oldArr, newArr, callback) {
+  return arrayDiff.diff(oldArr, newArr, callback);
+}
+
+
+},{"./base":51}],51:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports['default'] = /*istanbul ignore end*/Diff;
+function Diff() {}
+
+Diff.prototype = { /*istanbul ignore start*/
+  /*istanbul ignore end*/diff: function diff(oldString, newString) {
+    /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
+
+    var callback = options.callback;
+    if (typeof options === 'function') {
+      callback = options;
+      options = {};
+    }
+    this.options = options;
+
+    var self = this;
+
+    function done(value) {
+      if (callback) {
+        setTimeout(function () {
+          callback(undefined, value);
+        }, 0);
+        return true;
+      } else {
+        return value;
+      }
+    }
+
+    // Allow subclasses to massage the input prior to running
+    oldString = this.castInput(oldString);
+    newString = this.castInput(newString);
+
+    oldString = this.removeEmpty(this.tokenize(oldString));
+    newString = this.removeEmpty(this.tokenize(newString));
+
+    var newLen = newString.length,
+        oldLen = oldString.length;
+    var editLength = 1;
+    var maxEditLength = newLen + oldLen;
+    var bestPath = [{ newPos: -1, components: [] }];
+
+    // Seed editLength = 0, i.e. the content starts with the same values
+    var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0);
+    if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
+      // Identity per the equality and tokenizer
+      return done([{ value: this.join(newString), count: newString.length }]);
+    }
+
+    // Main worker method. checks all permutations of a given edit length for acceptance.
+    function execEditLength() {
+      for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
+        var basePath = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+        var addPath = bestPath[diagonalPath - 1],
+            removePath = bestPath[diagonalPath + 1],
+            _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
+        if (addPath) {
+          // No one else is going to attempt to use this value, clear it
+          bestPath[diagonalPath - 1] = undefined;
+        }
+
+        var canAdd = addPath && addPath.newPos + 1 < newLen,
+            canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen;
+        if (!canAdd && !canRemove) {
+          // If this path is a terminal then prune
+          bestPath[diagonalPath] = undefined;
+          continue;
+        }
+
+        // Select the diagonal that we want to branch from. We select the prior
+        // path whose position in the new string is the farthest from the origin
+        // and does not pass the bounds of the diff graph
+        if (!canAdd || canRemove && addPath.newPos < removePath.newPos) {
+          basePath = clonePath(removePath);
+          self.pushComponent(basePath.components, undefined, true);
+        } else {
+          basePath = addPath; // No need to clone, we've pulled it from the list
+          basePath.newPos++;
+          self.pushComponent(basePath.components, true, undefined);
+        }
+
+        _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath);
+
+        // If we have hit the end of both strings, then we are done
+        if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) {
+          return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken));
+        } else {
+          // Otherwise track this path as a potential candidate and continue.
+          bestPath[diagonalPath] = basePath;
+        }
+      }
+
+      editLength++;
+    }
+
+    // Performs the length of edit iteration. Is a bit fugly as this has to support the
+    // sync and async mode which is never fun. Loops over execEditLength until a value
+    // is produced.
+    if (callback) {
+      (function exec() {
+        setTimeout(function () {
+          // This should not happen, but we want to be safe.
+          /* istanbul ignore next */
+          if (editLength > maxEditLength) {
+            return callback();
+          }
+
+          if (!execEditLength()) {
+            exec();
+          }
+        }, 0);
+      })();
+    } else {
+      while (editLength <= maxEditLength) {
+        var ret = execEditLength();
+        if (ret) {
+          return ret;
+        }
+      }
+    }
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/pushComponent: function pushComponent(components, added, removed) {
+    var last = components[components.length - 1];
+    if (last && last.added === added && last.removed === removed) {
+      // We need to clone here as the component clone operation is just
+      // as shallow array clone
+      components[components.length - 1] = { count: last.count + 1, added: added, removed: removed };
+    } else {
+      components.push({ count: 1, added: added, removed: removed });
+    }
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) {
+    var newLen = newString.length,
+        oldLen = oldString.length,
+        newPos = basePath.newPos,
+        oldPos = newPos - diagonalPath,
+        commonCount = 0;
+    while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) {
+      newPos++;
+      oldPos++;
+      commonCount++;
+    }
+
+    if (commonCount) {
+      basePath.components.push({ count: commonCount });
+    }
+
+    basePath.newPos = newPos;
+    return oldPos;
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/equals: function equals(left, right) {
+    return left === right;
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/removeEmpty: function removeEmpty(array) {
+    var ret = [];
+    for (var i = 0; i < array.length; i++) {
+      if (array[i]) {
+        ret.push(array[i]);
+      }
+    }
+    return ret;
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/castInput: function castInput(value) {
+    return value;
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/tokenize: function tokenize(value) {
+    return value.split('');
+  },
+  /*istanbul ignore start*/ /*istanbul ignore end*/join: function join(chars) {
+    return chars.join('');
+  }
+};
+
+function buildValues(diff, components, newString, oldString, useLongestToken) {
+  var componentPos = 0,
+      componentLen = components.length,
+      newPos = 0,
+      oldPos = 0;
+
+  for (; componentPos < componentLen; componentPos++) {
+    var component = components[componentPos];
+    if (!component.removed) {
+      if (!component.added && useLongestToken) {
+        var value = newString.slice(newPos, newPos + component.count);
+        value = value.map(function (value, i) {
+          var oldValue = oldString[oldPos + i];
+          return oldValue.length > value.length ? oldValue : value;
+        });
+
+        component.value = diff.join(value);
+      } else {
+        component.value = diff.join(newString.slice(newPos, newPos + component.count));
+      }
+      newPos += component.count;
+
+      // Common case
+      if (!component.added) {
+        oldPos += component.count;
+      }
+    } else {
+      component.value = diff.join(oldString.slice(oldPos, oldPos + component.count));
+      oldPos += component.count;
+
+      // Reverse add and remove so removes are output first to match common convention
+      // The diffing algorithm is tied to add then remove output and this is the simplest
+      // route to get the desired output with minimal overhead.
+      if (componentPos && components[componentPos - 1].added) {
+        var tmp = components[componentPos - 1];
+        components[componentPos - 1] = components[componentPos];
+        components[componentPos] = tmp;
+      }
+    }
+  }
+
+  // Special case handle for when one terminal is ignored. For this case we merge the
+  // terminal into the prior string and drop the change.
+  var lastComponent = components[componentLen - 1];
+  if (componentLen > 1 && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) {
+    components[componentLen - 2].value += lastComponent.value;
+    components.pop();
+  }
+
+  return components;
+}
+
+function clonePath(path) {
+  return { newPos: path.newPos, components: path.components.slice(0) };
+}
+
+
+},{}],52:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.characterDiff = undefined;
+exports. /*istanbul ignore end*/diffChars = diffChars;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/var characterDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/characterDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+function diffChars(oldStr, newStr, callback) {
+  return characterDiff.diff(oldStr, newStr, callback);
+}
+
+
+},{"./base":51}],53:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.cssDiff = undefined;
+exports. /*istanbul ignore end*/diffCss = diffCss;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/var cssDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/cssDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+cssDiff.tokenize = function (value) {
+  return value.split(/([{}:;,]|\s+)/);
+};
+
+function diffCss(oldStr, newStr, callback) {
+  return cssDiff.diff(oldStr, newStr, callback);
+}
+
+
+},{"./base":51}],54:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.jsonDiff = undefined;
+
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
+exports. /*istanbul ignore end*/diffJson = diffJson;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = canonicalize;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+/*istanbul ignore end*/
+var /*istanbul ignore start*/_line = require('./line') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/
+
+var objectPrototypeToString = Object.prototype.toString;
+
+var jsonDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/jsonDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+// Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
+// dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
+jsonDiff.useLongestToken = true;
+
+jsonDiff.tokenize = /*istanbul ignore start*/_line.lineDiff. /*istanbul ignore end*/tokenize;
+jsonDiff.castInput = function (value) {
+  /*istanbul ignore start*/var /*istanbul ignore end*/undefinedReplacement = this.options.undefinedReplacement;
+
+
+  return typeof value === 'string' ? value : JSON.stringify(canonicalize(value), function (k, v) {
+    if (typeof v === 'undefined') {
+      return undefinedReplacement;
+    }
+
+    return v;
+  }, '  ');
+};
+jsonDiff.equals = function (left, right) {
+  return (/*istanbul ignore start*/_base2['default']. /*istanbul ignore end*/prototype.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'))
+  );
+};
+
+function diffJson(oldObj, newObj, options) {
+  return jsonDiff.diff(oldObj, newObj, options);
+}
+
+// This function handles the presence of circular references by bailing out when encountering an
+// object that is already on the "stack" of items being processed.
+function canonicalize(obj, stack, replacementStack) {
+  stack = stack || [];
+  replacementStack = replacementStack || [];
+
+  var i = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+
+  for (i = 0; i < stack.length; i += 1) {
+    if (stack[i] === obj) {
+      return replacementStack[i];
+    }
+  }
+
+  var canonicalizedObj = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+
+  if ('[object Array]' === objectPrototypeToString.call(obj)) {
+    stack.push(obj);
+    canonicalizedObj = new Array(obj.length);
+    replacementStack.push(canonicalizedObj);
+    for (i = 0; i < obj.length; i += 1) {
+      canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack);
+    }
+    stack.pop();
+    replacementStack.pop();
+    return canonicalizedObj;
+  }
+
+  if (obj && obj.toJSON) {
+    obj = obj.toJSON();
+  }
+
+  if ( /*istanbul ignore start*/(typeof /*istanbul ignore end*/obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object' && obj !== null) {
+    stack.push(obj);
+    canonicalizedObj = {};
+    replacementStack.push(canonicalizedObj);
+    var sortedKeys = [],
+        key = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+    for (key in obj) {
+      /* istanbul ignore else */
+      if (obj.hasOwnProperty(key)) {
+        sortedKeys.push(key);
+      }
+    }
+    sortedKeys.sort();
+    for (i = 0; i < sortedKeys.length; i += 1) {
+      key = sortedKeys[i];
+      canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack);
+    }
+    stack.pop();
+    replacementStack.pop();
+  } else {
+    canonicalizedObj = obj;
+  }
+  return canonicalizedObj;
+}
+
+
+},{"./base":51,"./line":55}],55:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.lineDiff = undefined;
+exports. /*istanbul ignore end*/diffLines = diffLines;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = diffTrimmedLines;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+/*istanbul ignore end*/
+var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/var lineDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/lineDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+lineDiff.tokenize = function (value) {
+  var retLines = [],
+      linesAndNewlines = value.split(/(\n|\r\n)/);
+
+  // Ignore the final empty token that occurs if the string ends with a new line
+  if (!linesAndNewlines[linesAndNewlines.length - 1]) {
+    linesAndNewlines.pop();
+  }
+
+  // Merge the content and line separators into single tokens
+  for (var i = 0; i < linesAndNewlines.length; i++) {
+    var line = linesAndNewlines[i];
+
+    if (i % 2 && !this.options.newlineIsToken) {
+      retLines[retLines.length - 1] += line;
+    } else {
+      if (this.options.ignoreWhitespace) {
+        line = line.trim();
+      }
+      retLines.push(line);
+    }
+  }
+
+  return retLines;
+};
+
+function diffLines(oldStr, newStr, callback) {
+  return lineDiff.diff(oldStr, newStr, callback);
+}
+function diffTrimmedLines(oldStr, newStr, callback) {
+  var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true });
+  return lineDiff.diff(oldStr, newStr, options);
+}
+
+
+},{"../util/params":63,"./base":51}],56:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.sentenceDiff = undefined;
+exports. /*istanbul ignore end*/diffSentences = diffSentences;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/var sentenceDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/sentenceDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+sentenceDiff.tokenize = function (value) {
+  return value.split(/(\S.+?[.!?])(?=\s+|$)/);
+};
+
+function diffSentences(oldStr, newStr, callback) {
+  return sentenceDiff.diff(oldStr, newStr, callback);
+}
+
+
+},{"./base":51}],57:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.wordDiff = undefined;
+exports. /*istanbul ignore end*/diffWords = diffWords;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = diffWordsWithSpace;
+
+var /*istanbul ignore start*/_base = require('./base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+/*istanbul ignore end*/
+var /*istanbul ignore start*/_params = require('../util/params') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/
+
+// Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode
+//
+// Ranges and exceptions:
+// Latin-1 Supplement, 0080–00FF
+//  - U+00D7  × Multiplication sign
+//  - U+00F7  ÷ Division sign
+// Latin Extended-A, 0100–017F
+// Latin Extended-B, 0180–024F
+// IPA Extensions, 0250–02AF
+// Spacing Modifier Letters, 02B0–02FF
+//  - U+02C7  ˇ &#711;  Caron
+//  - U+02D8  ˘ &#728;  Breve
+//  - U+02D9  ˙ &#729;  Dot Above
+//  - U+02DA  ˚ &#730;  Ring Above
+//  - U+02DB  ˛ &#731;  Ogonek
+//  - U+02DC  ˜ &#732;  Small Tilde
+//  - U+02DD  ˝ &#733;  Double Acute Accent
+// Latin Extended Additional, 1E00–1EFF
+var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/;
+
+var reWhitespace = /\S/;
+
+var wordDiff = /*istanbul ignore start*/exports. /*istanbul ignore end*/wordDiff = new /*istanbul ignore start*/_base2['default']() /*istanbul ignore end*/;
+wordDiff.equals = function (left, right) {
+  return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
+};
+wordDiff.tokenize = function (value) {
+  var tokens = value.split(/(\s+|\b)/);
+
+  // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set.
+  for (var i = 0; i < tokens.length - 1; i++) {
+    // If we have an empty string in the next field and we have only word chars before and after, merge
+    if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) {
+      tokens[i] += tokens[i + 2];
+      tokens.splice(i + 1, 2);
+      i--;
+    }
+  }
+
+  return tokens;
+};
+
+function diffWords(oldStr, newStr, callback) {
+  var options = /*istanbul ignore start*/(0, _params.generateOptions) /*istanbul ignore end*/(callback, { ignoreWhitespace: true });
+  return wordDiff.diff(oldStr, newStr, options);
+}
+function diffWordsWithSpace(oldStr, newStr, callback) {
+  return wordDiff.diff(oldStr, newStr, callback);
+}
+
+
+},{"../util/params":63,"./base":51}],58:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports.canonicalize = exports.convertChangesToXML = exports.convertChangesToDMP = exports.parsePatch = exports.applyPatches = exports.applyPatch = exports.createPatch = exports.createTwoFilesPatch = exports.structuredPatch = exports.diffArrays = exports.diffJson = exports.diffCss = exports.diffSentences = exports.diffTrimmedLines = exports.diffLines = exports.diffWordsWithSpace = exports.diffWords = exports.diffChars = exports.Diff = undefined;
+/*istanbul ignore end*/
+var /*istanbul ignore start*/_base = require('./diff/base') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _base2 = _interopRequireDefault(_base);
+
+/*istanbul ignore end*/
+var /*istanbul ignore start*/_character = require('./diff/character') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_word = require('./diff/word') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_line = require('./diff/line') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_sentence = require('./diff/sentence') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_css = require('./diff/css') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_json = require('./diff/json') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_array = require('./diff/array') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_apply = require('./patch/apply') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_parse = require('./patch/parse') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_create = require('./patch/create') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_dmp = require('./convert/dmp') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_xml = require('./convert/xml') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+exports. /*istanbul ignore end*/Diff = _base2['default'];
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffChars = _character.diffChars;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWords = _word.diffWords;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffWordsWithSpace = _word.diffWordsWithSpace;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffLines = _line.diffLines;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffTrimmedLines = _line.diffTrimmedLines;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffSentences = _sentence.diffSentences;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffCss = _css.diffCss;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffJson = _json.diffJson;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/diffArrays = _array.diffArrays;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/structuredPatch = _create.structuredPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = _create.createTwoFilesPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = _create.createPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatch = _apply.applyPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = _apply.applyPatches;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/parsePatch = _parse.parsePatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToDMP = _dmp.convertChangesToDMP;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/convertChangesToXML = _xml.convertChangesToXML;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/canonicalize = _json.canonicalize; /* See LICENSE file for terms of use */
+
+/*
+ * Text diff implementation.
+ *
+ * This library supports the following APIS:
+ * JsDiff.diffChars: Character by character diff
+ * JsDiff.diffWords: Word (as defined by \b regex) diff which ignores whitespace
+ * JsDiff.diffLines: Line based diff
+ *
+ * JsDiff.diffCss: Diff targeted at CSS content
+ *
+ * These methods are based on the implementation proposed in
+ * "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).
+ * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927
+ */
+
+
+},{"./convert/dmp":48,"./convert/xml":49,"./diff/array":50,"./diff/base":51,"./diff/character":52,"./diff/css":53,"./diff/json":54,"./diff/line":55,"./diff/sentence":56,"./diff/word":57,"./patch/apply":59,"./patch/create":60,"./patch/parse":61}],59:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/applyPatch = applyPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/applyPatches = applyPatches;
+
+var /*istanbul ignore start*/_parse = require('./parse') /*istanbul ignore end*/;
+
+var /*istanbul ignore start*/_distanceIterator = require('../util/distance-iterator') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+var _distanceIterator2 = _interopRequireDefault(_distanceIterator);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
+
+/*istanbul ignore end*/function applyPatch(source, uniDiff) {
+  /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
+
+  if (typeof uniDiff === 'string') {
+    uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff);
+  }
+
+  if (Array.isArray(uniDiff)) {
+    if (uniDiff.length > 1) {
+      throw new Error('applyPatch only works with a single input.');
+    }
+
+    uniDiff = uniDiff[0];
+  }
+
+  // Apply the diff to the input
+  var lines = source.split(/\r\n|[\n\v\f\r\x85]/),
+      delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [],
+      hunks = uniDiff.hunks,
+      compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) /*istanbul ignore start*/{
+    return (/*istanbul ignore end*/line === patchContent
+    );
+  },
+      errorCount = 0,
+      fuzzFactor = options.fuzzFactor || 0,
+      minLine = 0,
+      offset = 0,
+      removeEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/,
+      addEOFNL = /*istanbul ignore start*/void 0 /*istanbul ignore end*/;
+
+  /**
+   * Checks if the hunk exactly fits on the provided location
+   */
+  function hunkFits(hunk, toPos) {
+    for (var j = 0; j < hunk.lines.length; j++) {
+      var line = hunk.lines[j],
+          operation = line[0],
+          content = line.substr(1);
+
+      if (operation === ' ' || operation === '-') {
+        // Context sanity check
+        if (!compareLine(toPos + 1, lines[toPos], operation, content)) {
+          errorCount++;
+
+          if (errorCount > fuzzFactor) {
+            return false;
+          }
+        }
+        toPos++;
+      }
+    }
+
+    return true;
+  }
+
+  // Search best fit offsets for each hunk based on the previous ones
+  for (var i = 0; i < hunks.length; i++) {
+    var hunk = hunks[i],
+        maxLine = lines.length - hunk.oldLines,
+        localOffset = 0,
+        toPos = offset + hunk.oldStart - 1;
+
+    var iterator = /*istanbul ignore start*/(0, _distanceIterator2['default']) /*istanbul ignore end*/(toPos, minLine, maxLine);
+
+    for (; localOffset !== undefined; localOffset = iterator()) {
+      if (hunkFits(hunk, toPos + localOffset)) {
+        hunk.offset = offset += localOffset;
+        break;
+      }
+    }
+
+    if (localOffset === undefined) {
+      return false;
+    }
+
+    // Set lower text limit to end of the current hunk, so next ones don't try
+    // to fit over already patched text
+    minLine = hunk.offset + hunk.oldStart + hunk.oldLines;
+  }
+
+  // Apply patch hunks
+  for (var _i = 0; _i < hunks.length; _i++) {
+    var _hunk = hunks[_i],
+        _toPos = _hunk.offset + _hunk.newStart - 1;
+    if (_hunk.newLines == 0) {
+      _toPos++;
+    }
+
+    for (var j = 0; j < _hunk.lines.length; j++) {
+      var line = _hunk.lines[j],
+          operation = line[0],
+          content = line.substr(1),
+          delimiter = _hunk.linedelimiters[j];
+
+      if (operation === ' ') {
+        _toPos++;
+      } else if (operation === '-') {
+        lines.splice(_toPos, 1);
+        delimiters.splice(_toPos, 1);
+        /* istanbul ignore else */
+      } else if (operation === '+') {
+          lines.splice(_toPos, 0, content);
+          delimiters.splice(_toPos, 0, delimiter);
+          _toPos++;
+        } else if (operation === '\\') {
+          var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null;
+          if (previousOperation === '+') {
+            removeEOFNL = true;
+          } else if (previousOperation === '-') {
+            addEOFNL = true;
+          }
+        }
+    }
+  }
+
+  // Handle EOFNL insertion/removal
+  if (removeEOFNL) {
+    while (!lines[lines.length - 1]) {
+      lines.pop();
+      delimiters.pop();
+    }
+  } else if (addEOFNL) {
+    lines.push('');
+    delimiters.push('\n');
+  }
+  for (var _k = 0; _k < lines.length - 1; _k++) {
+    lines[_k] = lines[_k] + delimiters[_k];
+  }
+  return lines.join('');
+}
+
+// Wrapper that supports multiple file patches via callbacks.
+function applyPatches(uniDiff, options) {
+  if (typeof uniDiff === 'string') {
+    uniDiff = /*istanbul ignore start*/(0, _parse.parsePatch) /*istanbul ignore end*/(uniDiff);
+  }
+
+  var currentIndex = 0;
+  function processIndex() {
+    var index = uniDiff[currentIndex++];
+    if (!index) {
+      return options.complete();
+    }
+
+    options.loadFile(index, function (err, data) {
+      if (err) {
+        return options.complete(err);
+      }
+
+      var updatedContent = applyPatch(data, index, options);
+      options.patched(index, updatedContent, function (err) {
+        if (err) {
+          return options.complete(err);
+        }
+
+        processIndex();
+      });
+    });
+  }
+  processIndex();
+}
+
+
+},{"../util/distance-iterator":62,"./parse":61}],60:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/structuredPatch = structuredPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/createTwoFilesPatch = createTwoFilesPatch;
+/*istanbul ignore start*/exports. /*istanbul ignore end*/createPatch = createPatch;
+
+var /*istanbul ignore start*/_line = require('../diff/line') /*istanbul ignore end*/;
+
+/*istanbul ignore start*/
+function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
+
+/*istanbul ignore end*/function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
+  if (!options) {
+    options = {};
+  }
+  if (typeof options.context === 'undefined') {
+    options.context = 4;
+  }
+
+  var diff = /*istanbul ignore start*/(0, _line.diffLines) /*istanbul ignore end*/(oldStr, newStr, options);
+  diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier
+
+  function contextLines(lines) {
+    return lines.map(function (entry) {
+      return ' ' + entry;
+    });
+  }
+
+  var hunks = [];
+  var oldRangeStart = 0,
+      newRangeStart = 0,
+      curRange = [],
+      oldLine = 1,
+      newLine = 1;
+  /*istanbul ignore start*/
+  var _loop = function _loop( /*istanbul ignore end*/i) {
+    var current = diff[i],
+        lines = current.lines || current.value.replace(/\n$/, '').split('\n');
+    current.lines = lines;
+
+    if (current.added || current.removed) {
+      /*istanbul ignore start*/
+      var _curRange;
+
+      /*istanbul ignore end*/
+      // If we have previous context, start with that
+      if (!oldRangeStart) {
+        var prev = diff[i - 1];
+        oldRangeStart = oldLine;
+        newRangeStart = newLine;
+
+        if (prev) {
+          curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
+          oldRangeStart -= curRange.length;
+          newRangeStart -= curRange.length;
+        }
+      }
+
+      // Output our changes
+      /*istanbul ignore start*/(_curRange = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/lines.map(function (entry) {
+        return (current.added ? '+' : '-') + entry;
+      })));
+
+      // Track the updated file position
+      if (current.added) {
+        newLine += lines.length;
+      } else {
+        oldLine += lines.length;
+      }
+    } else {
+      // Identical context lines. Track line changes
+      if (oldRangeStart) {
+        // Close out any changes that have been output (or join overlapping)
+        if (lines.length <= options.context * 2 && i < diff.length - 2) {
+          /*istanbul ignore start*/
+          var _curRange2;
+
+          /*istanbul ignore end*/
+          // Overlapping
+          /*istanbul ignore start*/(_curRange2 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange2 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines)));
+        } else {
+          /*istanbul ignore start*/
+          var _curRange3;
+
+          /*istanbul ignore end*/
+          // end the range and output
+          var contextSize = Math.min(lines.length, options.context);
+          /*istanbul ignore start*/(_curRange3 = /*istanbul ignore end*/curRange).push. /*istanbul ignore start*/apply /*istanbul ignore end*/( /*istanbul ignore start*/_curRange3 /*istanbul ignore end*/, /*istanbul ignore start*/_toConsumableArray( /*istanbul ignore end*/contextLines(lines.slice(0, contextSize))));
+
+          var hunk = {
+            oldStart: oldRangeStart,
+            oldLines: oldLine - oldRangeStart + contextSize,
+            newStart: newRangeStart,
+            newLines: newLine - newRangeStart + contextSize,
+            lines: curRange
+          };
+          if (i >= diff.length - 2 && lines.length <= options.context) {
+            // EOF is inside this hunk
+            var oldEOFNewline = /\n$/.test(oldStr);
+            var newEOFNewline = /\n$/.test(newStr);
+            if (lines.length == 0 && !oldEOFNewline) {
+              // special case: old has no eol and no trailing context; no-nl can end up before adds
+              curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file');
+            } else if (!oldEOFNewline || !newEOFNewline) {
+              curRange.push('\\ No newline at end of file');
+            }
+          }
+          hunks.push(hunk);
+
+          oldRangeStart = 0;
+          newRangeStart = 0;
+          curRange = [];
+        }
+      }
+      oldLine += lines.length;
+      newLine += lines.length;
+    }
+  };
+
+  for (var i = 0; i < diff.length; i++) {
+    /*istanbul ignore start*/
+    _loop( /*istanbul ignore end*/i);
+  }
+
+  return {
+    oldFileName: oldFileName, newFileName: newFileName,
+    oldHeader: oldHeader, newHeader: newHeader,
+    hunks: hunks
+  };
+}
+
+function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
+  var diff = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
+
+  var ret = [];
+  if (oldFileName == newFileName) {
+    ret.push('Index: ' + oldFileName);
+  }
+  ret.push('===================================================================');
+  ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader));
+  ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader));
+
+  for (var i = 0; i < diff.hunks.length; i++) {
+    var hunk = diff.hunks[i];
+    ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@');
+    ret.push.apply(ret, hunk.lines);
+  }
+
+  return ret.join('\n') + '\n';
+}
+
+function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
+  return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
+}
+
+
+},{"../diff/line":55}],61:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/parsePatch = parsePatch;
+function parsePatch(uniDiff) {
+  /*istanbul ignore start*/var /*istanbul ignore end*/options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
+
+  var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/),
+      delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [],
+      list = [],
+      i = 0;
+
+  function parseIndex() {
+    var index = {};
+    list.push(index);
+
+    // Parse diff metadata
+    while (i < diffstr.length) {
+      var line = diffstr[i];
+
+      // File header found, end parsing diff metadata
+      if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) {
+        break;
+      }
+
+      // Diff index
+      var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line);
+      if (header) {
+        index.index = header[1];
+      }
+
+      i++;
+    }
+
+    // Parse file headers if they are defined. Unified diff requires them, but
+    // there's no technical issues to have an isolated hunk without file header
+    parseFileHeader(index);
+    parseFileHeader(index);
+
+    // Parse hunks
+    index.hunks = [];
+
+    while (i < diffstr.length) {
+      var _line = diffstr[i];
+
+      if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) {
+        break;
+      } else if (/^@@/.test(_line)) {
+        index.hunks.push(parseHunk());
+      } else if (_line && options.strict) {
+        // Ignore unexpected content unless in strict mode
+        throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line));
+      } else {
+        i++;
+      }
+    }
+  }
+
+  // Parses the --- and +++ headers, if none are found, no lines
+  // are consumed.
+  function parseFileHeader(index) {
+    var headerPattern = /^(---|\+\+\+)\s+([\S ]*)(?:\t(.*?)\s*)?$/;
+    var fileHeader = headerPattern.exec(diffstr[i]);
+    if (fileHeader) {
+      var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
+      index[keyPrefix + 'FileName'] = fileHeader[2];
+      index[keyPrefix + 'Header'] = fileHeader[3];
+
+      i++;
+    }
+  }
+
+  // Parses a hunk
+  // This assumes that we are at the start of a hunk.
+  function parseHunk() {
+    var chunkHeaderIndex = i,
+        chunkHeaderLine = diffstr[i++],
+        chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
+
+    var hunk = {
+      oldStart: +chunkHeader[1],
+      oldLines: +chunkHeader[2] || 1,
+      newStart: +chunkHeader[3],
+      newLines: +chunkHeader[4] || 1,
+      lines: [],
+      linedelimiters: []
+    };
+
+    var addCount = 0,
+        removeCount = 0;
+    for (; i < diffstr.length; i++) {
+      // Lines starting with '---' could be mistaken for the "remove line" operation
+      // But they could be the header for the next file. Therefore prune such cases out.
+      if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) {
+        break;
+      }
+      var operation = diffstr[i][0];
+
+      if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
+        hunk.lines.push(diffstr[i]);
+        hunk.linedelimiters.push(delimiters[i] || '\n');
+
+        if (operation === '+') {
+          addCount++;
+        } else if (operation === '-') {
+          removeCount++;
+        } else if (operation === ' ') {
+          addCount++;
+          removeCount++;
+        }
+      } else {
+        break;
+      }
+    }
+
+    // Handle the empty block count case
+    if (!addCount && hunk.newLines === 1) {
+      hunk.newLines = 0;
+    }
+    if (!removeCount && hunk.oldLines === 1) {
+      hunk.oldLines = 0;
+    }
+
+    // Perform optional sanity checking
+    if (options.strict) {
+      if (addCount !== hunk.newLines) {
+        throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
+      }
+      if (removeCount !== hunk.oldLines) {
+        throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
+      }
+    }
+
+    return hunk;
+  }
+
+  while (i < diffstr.length) {
+    parseIndex();
+  }
+
+  return list;
+}
+
+
+},{}],62:[function(require,module,exports){
+/*istanbul ignore start*/"use strict";
+
+exports.__esModule = true;
+
+exports["default"] = /*istanbul ignore end*/function (start, minLine, maxLine) {
+  var wantForward = true,
+      backwardExhausted = false,
+      forwardExhausted = false,
+      localOffset = 1;
+
+  return function iterator() {
+    if (wantForward && !forwardExhausted) {
+      if (backwardExhausted) {
+        localOffset++;
+      } else {
+        wantForward = false;
+      }
+
+      // Check if trying to fit beyond text length, and if not, check it fits
+      // after offset location (or desired location on first iteration)
+      if (start + localOffset <= maxLine) {
+        return localOffset;
+      }
+
+      forwardExhausted = true;
+    }
+
+    if (!backwardExhausted) {
+      if (!forwardExhausted) {
+        wantForward = true;
+      }
+
+      // Check if trying to fit before text beginning, and if not, check it fits
+      // before offset location
+      if (minLine <= start - localOffset) {
+        return -localOffset++;
+      }
+
+      backwardExhausted = true;
+      return iterator();
+    }
+
+    // We tried to fit hunk before text beginning and beyond text lenght, then
+    // hunk can't fit on the text. Return undefined
+  };
+};
+
+
+},{}],63:[function(require,module,exports){
+/*istanbul ignore start*/'use strict';
+
+exports.__esModule = true;
+exports. /*istanbul ignore end*/generateOptions = generateOptions;
+function generateOptions(options, defaults) {
+  if (typeof options === 'function') {
+    defaults.callback = options;
+  } else if (options) {
+    for (var name in options) {
+      /* istanbul ignore else */
+      if (options.hasOwnProperty(name)) {
+        defaults[name] = options[name];
+      }
+    }
+  }
+  return defaults;
+}
+
+
+},{}],64:[function(require,module,exports){
+(function (global){
+((typeof define === "function" && define.amd && function (m) {
+    define("formatio", ["samsam"], m);
+}) || (typeof module === "object" && function (m) {
+    module.exports = m(require("samsam"));
+}) || function (m) { this.formatio = m(this.samsam); }
+)(function (samsam) {
+    "use strict";
+
+    var formatio = {
+        excludeConstructors: ["Object", /^.$/],
+        quoteStrings: true,
+        limitChildrenCount: 0
+    };
+
+    var hasOwn = Object.prototype.hasOwnProperty;
+
+    var specialObjects = [];
+    if (typeof global !== "undefined") {
+        specialObjects.push({ object: global, value: "[object global]" });
+    }
+    if (typeof document !== "undefined") {
+        specialObjects.push({
+            object: document,
+            value: "[object HTMLDocument]"
+        });
+    }
+    if (typeof window !== "undefined") {
+        specialObjects.push({ object: window, value: "[object Window]" });
+    }
+
+    function functionName(func) {
+        if (!func) { return ""; }
+        if (func.displayName) { return func.displayName; }
+        if (func.name) { return func.name; }
+        var matches = func.toString().match(/function\s+([^\(]+)/m);
+        return (matches && matches[1]) || "";
+    }
+
+    function constructorName(f, object) {
+        var name = functionName(object && object.constructor);
+        var excludes = f.excludeConstructors ||
+                formatio.excludeConstructors || [];
+
+        var i, l;
+        for (i = 0, l = excludes.length; i < l; ++i) {
+            if (typeof excludes[i] === "string" && excludes[i] === name) {
+                return "";
+            } else if (excludes[i].test && excludes[i].test(name)) {
+                return "";
+            }
+        }
+
+        return name;
+    }
+
+    function isCircular(object, objects) {
+        if (typeof object !== "object") { return false; }
+        var i, l;
+        for (i = 0, l = objects.length; i < l; ++i) {
+            if (objects[i] === object) { return true; }
+        }
+        return false;
+    }
+
+    function ascii(f, object, processed, indent) {
+        if (typeof object === "string") {
+            if (object.length === 0) { return "(empty string)"; }
+            var qs = f.quoteStrings;
+            var quote = typeof qs !== "boolean" || qs;
+            return processed || quote ? '"' + object + '"' : object;
+        }
+
+        if (typeof object === "function" && !(object instanceof RegExp)) {
+            return ascii.func(object);
+        }
+
+        processed = processed || [];
+
+        if (isCircular(object, processed)) { return "[Circular]"; }
+
+        if (Object.prototype.toString.call(object) === "[object Array]") {
+            return ascii.array.call(f, object, processed);
+        }
+
+        if (!object) { return String((1/object) === -Infinity ? "-0" : object); }
+        if (samsam.isElement(object)) { return ascii.element(object); }
+
+        if (typeof object.toString === "function" &&
+                object.toString !== Object.prototype.toString) {
+            return object.toString();
+        }
+
+        var i, l;
+        for (i = 0, l = specialObjects.length; i < l; i++) {
+            if (object === specialObjects[i].object) {
+                return specialObjects[i].value;
+            }
+        }
+
+        if (typeof Set !== 'undefined' && object instanceof Set) {
+            return ascii.set.call(f, object, processed);
+        }
+
+        return ascii.object.call(f, object, processed, indent);
+    }
+
+    ascii.func = function (func) {
+        return "function " + functionName(func) + "() {}";
+    };
+
+    function delimit(str, delimiters) {
+        delimiters = delimiters || ["[", "]"];
+        return delimiters[0] + str + delimiters[1];
+    }
+
+    ascii.array = function (array, processed, delimiters) {
+        processed = processed || [];
+        processed.push(array);
+        var pieces = [];
+        var i, l;
+        l = (this.limitChildrenCount > 0) ? 
+            Math.min(this.limitChildrenCount, array.length) : array.length;
+
+        for (i = 0; i < l; ++i) {
+            pieces.push(ascii(this, array[i], processed));
+        }
+
+        if (l < array.length) {
+            pieces.push("[... " + (array.length - l) + " more elements]");
+        }
+
+        return delimit(pieces.join(", "), delimiters);
+    };
+
+    ascii.set = function (set, processed) {
+        return ascii.array.call(this, Array.from(set), processed, ['Set {', '}']);
+    };
+
+    ascii.object = function (object, processed, indent) {
+        processed = processed || [];
+        processed.push(object);
+        indent = indent || 0;
+        var pieces = [], properties = samsam.keys(object).sort();
+        var length = 3;
+        var prop, str, obj, i, k, l;
+        l = (this.limitChildrenCount > 0) ? 
+            Math.min(this.limitChildrenCount, properties.length) : properties.length;
+
+        for (i = 0; i < l; ++i) {
+            prop = properties[i];
+            obj = object[prop];
+
+            if (isCircular(obj, processed)) {
+                str = "[Circular]";
+            } else {
+                str = ascii(this, obj, processed, indent + 2);
+            }
+
+            str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
+            length += str.length;
+            pieces.push(str);
+        }
+
+        var cons = constructorName(this, object);
+        var prefix = cons ? "[" + cons + "] " : "";
+        var is = "";
+        for (i = 0, k = indent; i < k; ++i) { is += " "; }
+
+        if(l < properties.length)
+            pieces.push("[... " + (properties.length - l) + " more elements]");
+
+        if (length + indent > 80) {
+            return prefix + "{\n  " + is + pieces.join(",\n  " + is) + "\n" +
+                is + "}";
+        }
+        return prefix + "{ " + pieces.join(", ") + " }";
+    };
+
+    ascii.element = function (element) {
+        var tagName = element.tagName.toLowerCase();
+        var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;
+
+        for (i = 0, l = attrs.length; i < l; ++i) {
+            attr = attrs.item(i);
+            attrName = attr.nodeName.toLowerCase().replace("html:", "");
+            val = attr.nodeValue;
+            if (attrName !== "contenteditable" || val !== "inherit") {
+                if (!!val) { pairs.push(attrName + "=\"" + val + "\""); }
+            }
+        }
+
+        var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
+        // SVG elements have undefined innerHTML
+        var content = element.innerHTML || '';
+
+        if (content.length > 20) {
+            content = content.substr(0, 20) + "[...]";
+        }
+
+        var res = formatted + pairs.join(" ") + ">" + content +
+                "</" + tagName + ">";
+
+        return res.replace(/ contentEditable="inherit"/, "");
+    };
+
+    function Formatio(options) {
+        for (var opt in options) {
+            this[opt] = options[opt];
+        }
+    }
+
+    Formatio.prototype = {
+        functionName: functionName,
+
+        configure: function (options) {
+            return new Formatio(options);
+        },
+
+        constructorName: function (object) {
+            return constructorName(this, object);
+        },
+
+        ascii: function (object, processed, indent) {
+            return ascii(this, object, processed, indent);
+        }
+    };
+
+    return Formatio.prototype;
+});
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{"samsam":69}],65:[function(require,module,exports){
+module.exports = Array.isArray || function (arr) {
+  return Object.prototype.toString.call(arr) == '[object Array]';
+};
+
+},{}],66:[function(require,module,exports){
+(function (global){
+"use strict";
+
+var userAgent = global.navigator && global.navigator.userAgent;
+var isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
+
+// Make properties writable in IE, as per
+// http://www.adequatelygood.com/Replacing-setTimeout-Globally.html
+if (isRunningInIE) {
+    global.setTimeout = global.setTimeout;
+    global.clearTimeout = global.clearTimeout;
+    global.setInterval = global.setInterval;
+    global.clearInterval = global.clearInterval;
+    global.Date = global.Date;
+}
+
+// setImmediate is not a standard function
+// avoid adding the prop to the window object if not present
+if (global.setImmediate !== undefined) {
+    global.setImmediate = global.setImmediate;
+    global.clearImmediate = global.clearImmediate;
+}
+
+// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
+// browsers, a number.
+// see https://github.com/cjohansen/Sinon.JS/pull/436
+
+var NOOP = function () { return undefined; };
+var timeoutResult = setTimeout(NOOP, 0);
+var addTimerReturnsObject = typeof timeoutResult === "object";
+var hrtimePresent = (global.process && typeof global.process.hrtime === "function");
+clearTimeout(timeoutResult);
+
+var NativeDate = Date;
+var uniqueTimerId = 1;
+
+/**
+ * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
+ * number of milliseconds. This is used to support human-readable strings passed
+ * to clock.tick()
+ */
+function parseTime(str) {
+    if (!str) {
+        return 0;
+    }
+
+    var strings = str.split(":");
+    var l = strings.length;
+    var i = l;
+    var ms = 0;
+    var parsed;
+
+    if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+        throw new Error("tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits");
+    }
+
+    while (i--) {
+        parsed = parseInt(strings[i], 10);
+
+        if (parsed >= 60) {
+            throw new Error("Invalid time " + str);
+        }
+
+        ms += parsed * Math.pow(60, (l - i - 1));
+    }
+
+    return ms * 1000;
+}
+
+/**
+ * Floor function that also works for negative numbers
+ */
+function fixedFloor(n) {
+    return (n >= 0 ? Math.floor(n) : Math.ceil(n));
+}
+
+/**
+ * % operator that also works for negative numbers
+ */
+function fixedModulo(n, m) {
+    return ((n % m) + m) % m;
+}
+
+/**
+ * Used to grok the `now` parameter to createClock.
+ */
+function getEpoch(epoch) {
+    if (!epoch) { return 0; }
+    if (typeof epoch.getTime === "function") { return epoch.getTime(); }
+    if (typeof epoch === "number") { return epoch; }
+    throw new TypeError("now should be milliseconds since UNIX epoch");
+}
+
+function inRange(from, to, timer) {
+    return timer && timer.callAt >= from && timer.callAt <= to;
+}
+
+function mirrorDateProperties(target, source) {
+    var prop;
+    for (prop in source) {
+        if (source.hasOwnProperty(prop)) {
+            target[prop] = source[prop];
+        }
+    }
+
+    // set special now implementation
+    if (source.now) {
+        target.now = function now() {
+            return target.clock.now;
+        };
+    } else {
+        delete target.now;
+    }
+
+    // set special toSource implementation
+    if (source.toSource) {
+        target.toSource = function toSource() {
+            return source.toSource();
+        };
+    } else {
+        delete target.toSource;
+    }
+
+    // set special toString implementation
+    target.toString = function toString() {
+        return source.toString();
+    };
+
+    target.prototype = source.prototype;
+    target.parse = source.parse;
+    target.UTC = source.UTC;
+    target.prototype.toUTCString = source.prototype.toUTCString;
+
+    return target;
+}
+
+function createDate() {
+    function ClockDate(year, month, date, hour, minute, second, ms) {
+        // Defensive and verbose to avoid potential harm in passing
+        // explicit undefined when user does not pass argument
+        switch (arguments.length) {
+            case 0:
+                return new NativeDate(ClockDate.clock.now);
+            case 1:
+                return new NativeDate(year);
+            case 2:
+                return new NativeDate(year, month);
+            case 3:
+                return new NativeDate(year, month, date);
+            case 4:
+                return new NativeDate(year, month, date, hour);
+            case 5:
+                return new NativeDate(year, month, date, hour, minute);
+            case 6:
+                return new NativeDate(year, month, date, hour, minute, second);
+            default:
+                return new NativeDate(year, month, date, hour, minute, second, ms);
+        }
+    }
+
+    return mirrorDateProperties(ClockDate, NativeDate);
+}
+
+function addTimer(clock, timer) {
+    if (timer.func === undefined) {
+        throw new Error("Callback must be provided to timer calls");
+    }
+
+    if (!clock.timers) {
+        clock.timers = {};
+    }
+
+    timer.id = uniqueTimerId++;
+    timer.createdAt = clock.now;
+    timer.callAt = clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
+
+    clock.timers[timer.id] = timer;
+
+    if (addTimerReturnsObject) {
+        return {
+            id: timer.id,
+            ref: NOOP,
+            unref: NOOP
+        };
+    }
+
+    return timer.id;
+}
+
+
+/* eslint consistent-return: "off" */
+function compareTimers(a, b) {
+    // Sort first by absolute timing
+    if (a.callAt < b.callAt) {
+        return -1;
+    }
+    if (a.callAt > b.callAt) {
+        return 1;
+    }
+
+    // Sort next by immediate, immediate timers take precedence
+    if (a.immediate && !b.immediate) {
+        return -1;
+    }
+    if (!a.immediate && b.immediate) {
+        return 1;
+    }
+
+    // Sort next by creation time, earlier-created timers take precedence
+    if (a.createdAt < b.createdAt) {
+        return -1;
+    }
+    if (a.createdAt > b.createdAt) {
+        return 1;
+    }
+
+    // Sort next by id, lower-id timers take precedence
+    if (a.id < b.id) {
+        return -1;
+    }
+    if (a.id > b.id) {
+        return 1;
+    }
+
+    // As timer ids are unique, no fallback `0` is necessary
+}
+
+function firstTimerInRange(clock, from, to) {
+    var timers = clock.timers;
+    var timer = null;
+    var id, isInRange;
+
+    for (id in timers) {
+        if (timers.hasOwnProperty(id)) {
+            isInRange = inRange(from, to, timers[id]);
+
+            if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) {
+                timer = timers[id];
+            }
+        }
+    }
+
+    return timer;
+}
+
+function firstTimer(clock) {
+    var timers = clock.timers;
+    var timer = null;
+    var id;
+
+    for (id in timers) {
+        if (timers.hasOwnProperty(id)) {
+            if (!timer || compareTimers(timer, timers[id]) === 1) {
+                timer = timers[id];
+            }
+        }
+    }
+
+    return timer;
+}
+
+function lastTimer(clock) {
+    var timers = clock.timers;
+    var timer = null;
+    var id;
+
+    for (id in timers) {
+        if (timers.hasOwnProperty(id)) {
+            if (!timer || compareTimers(timer, timers[id]) === -1) {
+                timer = timers[id];
+            }
+        }
+    }
+
+    return timer;
+}
+
+function callTimer(clock, timer) {
+    var exception;
+
+    if (typeof timer.interval === "number") {
+        clock.timers[timer.id].callAt += timer.interval;
+    } else {
+        delete clock.timers[timer.id];
+    }
+
+    try {
+        if (typeof timer.func === "function") {
+            timer.func.apply(null, timer.args);
+        } else {
+            /* eslint no-eval: "off" */
+            eval(timer.func);
+        }
+    } catch (e) {
+        exception = e;
+    }
+
+    if (!clock.timers[timer.id]) {
+        if (exception) {
+            throw exception;
+        }
+        return;
+    }
+
+    if (exception) {
+        throw exception;
+    }
+}
+
+function timerType(timer) {
+    if (timer.immediate) {
+        return "Immediate";
+    }
+    if (timer.interval !== undefined) {
+        return "Interval";
+    }
+    return "Timeout";
+}
+
+function clearTimer(clock, timerId, ttype) {
+    if (!timerId) {
+        // null appears to be allowed in most browsers, and appears to be
+        // relied upon by some libraries, like Bootstrap carousel
+        return;
+    }
+
+    if (!clock.timers) {
+        clock.timers = [];
+    }
+
+    // in Node, timerId is an object with .ref()/.unref(), and
+    // its .id field is the actual timer id.
+    if (typeof timerId === "object") {
+        timerId = timerId.id;
+    }
+
+    if (clock.timers.hasOwnProperty(timerId)) {
+        // check that the ID matches a timer of the correct type
+        var timer = clock.timers[timerId];
+        if (timerType(timer) === ttype) {
+            delete clock.timers[timerId];
+        } else {
+            throw new Error("Cannot clear timer: timer created with set" + timerType(timer)
+                            + "() but cleared with clear" + ttype + "()");
+        }
+    }
+}
+
+function uninstall(clock, target) {
+    var method,
+        i,
+        l;
+    var installedHrTime = "_hrtime";
+
+    for (i = 0, l = clock.methods.length; i < l; i++) {
+        method = clock.methods[i];
+        if (method === "hrtime" && target.process) {
+            target.process.hrtime = clock[installedHrTime];
+        } else {
+            if (target[method] && target[method].hadOwnProperty) {
+                target[method] = clock["_" + method];
+            } else {
+                try {
+                    delete target[method];
+                } catch (ignore) { /* eslint empty-block: "off" */ }
+            }
+        }
+    }
+
+    // Prevent multiple executions which will completely remove these props
+    clock.methods = [];
+}
+
+function hijackMethod(target, method, clock) {
+    var prop;
+
+    clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
+    clock["_" + method] = target[method];
+
+    if (method === "Date") {
+        var date = mirrorDateProperties(clock[method], target[method]);
+        target[method] = date;
+    } else {
+        target[method] = function () {
+            return clock[method].apply(clock, arguments);
+        };
+
+        for (prop in clock[method]) {
+            if (clock[method].hasOwnProperty(prop)) {
+                target[method][prop] = clock[method][prop];
+            }
+        }
+    }
+
+    target[method].clock = clock;
+}
+
+var timers = {
+    setTimeout: setTimeout,
+    clearTimeout: clearTimeout,
+    setImmediate: global.setImmediate,
+    clearImmediate: global.clearImmediate,
+    setInterval: setInterval,
+    clearInterval: clearInterval,
+    Date: Date
+};
+
+if (hrtimePresent) {
+    timers.hrtime = global.process.hrtime;
+}
+
+var keys = Object.keys || function (obj) {
+    var ks = [];
+    var key;
+
+    for (key in obj) {
+        if (obj.hasOwnProperty(key)) {
+            ks.push(key);
+        }
+    }
+
+    return ks;
+};
+
+exports.timers = timers;
+
+function createClock(now, loopLimit) {
+    loopLimit = loopLimit || 1000;
+
+    var clock = {
+        now: getEpoch(now),
+        hrNow: 0,
+        timeouts: {},
+        Date: createDate(),
+        loopLimit: loopLimit
+    };
+
+    clock.Date.clock = clock;
+
+    clock.setTimeout = function setTimeout(func, timeout) {
+        return addTimer(clock, {
+            func: func,
+            args: Array.prototype.slice.call(arguments, 2),
+            delay: timeout
+        });
+    };
+
+    clock.clearTimeout = function clearTimeout(timerId) {
+        return clearTimer(clock, timerId, "Timeout");
+    };
+
+    clock.setInterval = function setInterval(func, timeout) {
+        return addTimer(clock, {
+            func: func,
+            args: Array.prototype.slice.call(arguments, 2),
+            delay: timeout,
+            interval: timeout
+        });
+    };
+
+    clock.clearInterval = function clearInterval(timerId) {
+        return clearTimer(clock, timerId, "Interval");
+    };
+
+    clock.setImmediate = function setImmediate(func) {
+        return addTimer(clock, {
+            func: func,
+            args: Array.prototype.slice.call(arguments, 1),
+            immediate: true
+        });
+    };
+
+    clock.clearImmediate = function clearImmediate(timerId) {
+        return clearTimer(clock, timerId, "Immediate");
+    };
+
+    clock.tick = function tick(ms) {
+        ms = typeof ms === "number" ? ms : parseTime(ms);
+        var tickFrom = clock.now;
+        var tickTo = clock.now + ms;
+        var previous = clock.now;
+        var timer = firstTimerInRange(clock, tickFrom, tickTo);
+        var oldNow, firstException;
+
+        clock.duringTick = true;
+
+        function updateHrTime(newNow) {
+            clock.hrNow += (newNow - clock.now);
+        }
+
+        while (timer && tickFrom <= tickTo) {
+            if (clock.timers[timer.id]) {
+                updateHrTime(timer.callAt);
+                tickFrom = timer.callAt;
+                clock.now = timer.callAt;
+                try {
+                    oldNow = clock.now;
+                    callTimer(clock, timer);
+                    // compensate for any setSystemTime() call during timer callback
+                    if (oldNow !== clock.now) {
+                        tickFrom += clock.now - oldNow;
+                        tickTo += clock.now - oldNow;
+                        previous += clock.now - oldNow;
+                    }
+                } catch (e) {
+                    firstException = firstException || e;
+                }
+            }
+
+            timer = firstTimerInRange(clock, previous, tickTo);
+            previous = tickFrom;
+        }
+
+        clock.duringTick = false;
+        updateHrTime(tickTo);
+        clock.now = tickTo;
+
+        if (firstException) {
+            throw firstException;
+        }
+
+        return clock.now;
+    };
+
+    clock.next = function next() {
+        var timer = firstTimer(clock);
+        if (!timer) {
+            return clock.now;
+        }
+
+        clock.duringTick = true;
+        try {
+            clock.now = timer.callAt;
+            callTimer(clock, timer);
+            return clock.now;
+        } finally {
+            clock.duringTick = false;
+        }
+    };
+
+    clock.runAll = function runAll() {
+        var numTimers, i;
+        for (i = 0; i < clock.loopLimit; i++) {
+            if (!clock.timers) {
+                return clock.now;
+            }
+
+            numTimers = Object.keys(clock.timers).length;
+            if (numTimers === 0) {
+                return clock.now;
+            }
+
+            clock.next();
+        }
+
+        throw new Error("Aborting after running " + clock.loopLimit + " timers, assuming an infinite loop!");
+    };
+
+    clock.runToLast = function runToLast() {
+        var timer = lastTimer(clock);
+        if (!timer) {
+            return clock.now;
+        }
+
+        return clock.tick(timer.callAt);
+    };
+
+    clock.reset = function reset() {
+        clock.timers = {};
+    };
+
+    clock.setSystemTime = function setSystemTime(systemTime) {
+        // determine time difference
+        var newNow = getEpoch(systemTime);
+        var difference = newNow - clock.now;
+        var id, timer;
+
+        // update 'system clock'
+        clock.now = newNow;
+
+        // update timers and intervals to keep them stable
+        for (id in clock.timers) {
+            if (clock.timers.hasOwnProperty(id)) {
+                timer = clock.timers[id];
+                timer.createdAt += difference;
+                timer.callAt += difference;
+            }
+        }
+    };
+
+    if (hrtimePresent) {
+        clock.hrtime = function (prev) {
+            if (Array.isArray(prev)) {
+                var oldSecs = (prev[0] + prev[1] / 1e9);
+                var newSecs = (clock.hrNow / 1000);
+                var difference = (newSecs - oldSecs);
+                var secs = fixedFloor(difference);
+                var nanosecs = fixedModulo(difference * 1e9, 1e9);
+                return [
+                    secs,
+                    nanosecs
+                ];
+            }
+            return [
+                fixedFloor(clock.hrNow / 1000),
+                fixedModulo(clock.hrNow * 1e6, 1e9)
+            ];
+        };
+    }
+
+    return clock;
+}
+exports.createClock = createClock;
+
+exports.install = function install(target, now, toFake, loopLimit) {
+    var i, l;
+
+    if (target instanceof Date) {
+        toFake = now;
+        now = target.getTime();
+        target = null;
+    }
+
+    if (typeof target === "number") {
+        toFake = now;
+        now = target;
+        target = null;
+    }
+
+    if (!target) {
+        target = global;
+    }
+
+    var clock = createClock(now, loopLimit);
+
+    clock.uninstall = function () {
+        uninstall(clock, target);
+    };
+
+    clock.methods = toFake || [];
+
+    if (clock.methods.length === 0) {
+        clock.methods = keys(timers);
+    }
+
+    for (i = 0, l = clock.methods.length; i < l; i++) {
+        if (clock.methods[i] === "hrtime") {
+            if (target.process && typeof target.process.hrtime === "function") {
+                hijackMethod(target.process, clock.methods[i], clock);
+            }
+        } else {
+            hijackMethod(target, clock.methods[i], clock);
+        }
+    }
+
+    return clock;
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+
+},{}],67:[function(require,module,exports){
+var isarray = require('isarray')
+
+/**
+ * Expose `pathToRegexp`.
+ */
+module.exports = pathToRegexp
+module.exports.parse = parse
+module.exports.compile = compile
+module.exports.tokensToFunction = tokensToFunction
+module.exports.tokensToRegExp = tokensToRegExp
+