Merge mozilla inbound to mozilla-central a=merge
authorarthur.iakab <aiakab@mozilla.com>
Fri, 13 Apr 2018 12:43:21 +0300
changeset 466675 cf543bdd424f24f5320d6284c19cb5c51eda054a
parent 466654 260e4c83c8a91313595b26a830963b927d2ad29d (current diff)
parent 466674 73fa6225a03ff9566bb77a140cd9356b861d5b3d (diff)
child 466676 8a94faa5cc60495da5d80d4b3c07bf5877d2e6d8
child 466689 43855a242386b8b0ab11cede40a6f41302c0dec4
child 466707 037aa924d8f3c0cd4bf073e4f9c6133db5923ac7
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
cf543bdd424f / 61.0a1 / 20180413100443 / files
nightly linux64
cf543bdd424f / 61.0a1 / 20180413100443 / files
nightly mac
cf543bdd424f / 61.0a1 / 20180413100443 / files
nightly win32
cf543bdd424f / 61.0a1 / 20180413100443 / files
nightly win64
cf543bdd424f / 61.0a1 / 20180413100443 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla inbound to mozilla-central a=merge
browser/themes/shared/icons/device-mobile.svg
browser/themes/shared/jar.inc.mn
browser/themes/shared/sync-desktopIcon.svg
browser/themes/shared/sync-mobileIcon.svg
mobile/android/base/java/org/mozilla/gecko/GeckoAccessibility.java
mobile/android/thirdparty/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
mobile/android/thirdparty/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
mobile/android/thirdparty/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -8,56 +8,53 @@ var EXPORTED_SYMBOLS = ["AccessFu"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/accessibility/Utils.jsm");
 
 if (Utils.MozBuildApp === "mobile/android") {
   ChromeUtils.import("resource://gre/modules/Messaging.jsm");
 }
 
-// const ACCESSFU_DISABLE = 0;
-const ACCESSFU_ENABLE = 1;
-const ACCESSFU_AUTO = 2;
-
-const SCREENREADER_SETTING = "accessibility.screenreader";
 const QUICKNAV_MODES_PREF = "accessibility.accessfu.quicknav_modes";
 const QUICKNAV_INDEX_PREF = "accessibility.accessfu.quicknav_index";
 
+const GECKOVIEW_MESSAGE = {
+  ACTIVATE: "GeckoView:AccessibilityActivate",
+  VIEW_FOCUSED: "GeckoView:AccessibilityViewFocused",
+  LONG_PRESS: "GeckoView:AccessibilityLongPress",
+  BY_GRANULARITY: "GeckoView:AccessibilityByGranularity",
+  NEXT: "GeckoView:AccessibilityNext",
+  PREVIOUS: "GeckoView:AccessibilityPrevious",
+  SCROLL_BACKWARD: "GeckoView:AccessibilityScrollBackward",
+  SCROLL_FORWARD: "GeckoView:AccessibilityScrollForward",
+};
+
 var AccessFu = {
   /**
    * Initialize chrome-layer accessibility functionality.
    * If accessibility is enabled on the platform, then a special accessibility
    * mode is started.
    */
-  attach: function attach(aWindow) {
+  attach: function attach(aWindow, aInTest = false) {
     Utils.init(aWindow);
 
-    if (Utils.MozBuildApp === "mobile/android") {
-      EventDispatcher.instance.dispatch("Accessibility:Ready");
-      EventDispatcher.instance.registerListener(this, "Accessibility:Settings");
+    if (!aInTest) {
+      this._enable();
     }
-
-    this._activatePref = new PrefCache(
-      "accessibility.accessfu.activate", this._enableOrDisable.bind(this));
-
-    this._enableOrDisable();
   },
 
   /**
    * Shut down chrome-layer accessibility functionality from the outside.
    */
   detach: function detach() {
     // Avoid disabling twice.
     if (this._enabled) {
       this._disable();
     }
-    if (Utils.MozBuildApp === "mobile/android") {
-      EventDispatcher.instance.unregisterListener(this, "Accessibility:Settings");
-    }
-    delete this._activatePref;
+
     Utils.uninit();
   },
 
   /**
    * A lazy getter for event handler that binds the scope to AccessFu object.
    */
   get handleEvent() {
     delete this.handleEvent;
@@ -112,26 +109,18 @@ var AccessFu = {
       new PrefCache("accessibility.accessfu.notify_output");
 
 
     this.Input.start();
     Output.start();
     PointerAdapter.start();
 
     if (Utils.MozBuildApp === "mobile/android") {
-      EventDispatcher.instance.registerListener(this, [
-        "Accessibility:ActivateObject",
-        "Accessibility:Focus",
-        "Accessibility:LongPress",
-        "Accessibility:MoveByGranularity",
-        "Accessibility:NextObject",
-        "Accessibility:PreviousObject",
-        "Accessibility:ScrollBackward",
-        "Accessibility:ScrollForward",
-      ]);
+      Utils.win.WindowEventDispatcher.registerListener(this,
+        Object.values(GECKOVIEW_MESSAGE));
     }
 
     Services.obs.addObserver(this, "remote-browser-shown");
     Services.obs.addObserver(this, "inprocess-browser-shown");
     Utils.win.addEventListener("TabOpen", this);
     Utils.win.addEventListener("TabClose", this);
     Utils.win.addEventListener("TabSelect", this);
 
@@ -167,56 +156,31 @@ var AccessFu = {
     Utils.win.removeEventListener("TabOpen", this);
     Utils.win.removeEventListener("TabClose", this);
     Utils.win.removeEventListener("TabSelect", this);
 
     Services.obs.removeObserver(this, "remote-browser-shown");
     Services.obs.removeObserver(this, "inprocess-browser-shown");
 
     if (Utils.MozBuildApp === "mobile/android") {
-      EventDispatcher.instance.unregisterListener(this, [
-        "Accessibility:ActivateObject",
-        "Accessibility:Focus",
-        "Accessibility:LongPress",
-        "Accessibility:MoveByGranularity",
-        "Accessibility:NextObject",
-        "Accessibility:PreviousObject",
-        "Accessibility:ScrollBackward",
-        "Accessibility:ScrollForward",
-      ]);
+      Utils.win.WindowEventDispatcher.unregisterListener(this,
+        Object.values(GECKOVIEW_MESSAGE));
     }
 
     delete this._quicknavModesPref;
     delete this._notifyOutputPref;
 
     if (this.doneCallback) {
       this.doneCallback();
       delete this.doneCallback;
     }
 
     Logger.info("AccessFu:Disabled");
   },
 
-  _enableOrDisable: function _enableOrDisable() {
-    try {
-      if (!this._activatePref) {
-        return;
-      }
-      let activatePref = this._activatePref.value;
-      if (activatePref == ACCESSFU_ENABLE ||
-          this._systemPref && activatePref == ACCESSFU_AUTO) {
-        this._enable();
-      } else {
-        this._disable();
-      }
-    } catch (x) {
-      dump("Error " + x.message + " " + x.fileName + ":" + x.lineNumber);
-    }
-  },
-
   receiveMessage: function receiveMessage(aMessage) {
     Logger.debug(() => {
       return ["Recieved", aMessage.name, JSON.stringify(aMessage.json)];
     });
 
     switch (aMessage.name) {
       case "AccessFu:Ready":
         let mm = Utils.getMessageManager(aMessage.target);
@@ -291,50 +255,53 @@ var AccessFu = {
     if (this._enabled) {
       this._addMessageListeners(aMessageManager);
     }
     this._loadFrameScript(aMessageManager);
   },
 
   onEvent(event, data, callback) {
     switch (event) {
-      case "Accessibility:Settings":
-        this._systemPref = data.enabled;
-        this._enableOrDisable();
+      case GECKOVIEW_MESSAGE.SETTINGS:
+        if (data.enabled) {
+          this._enable();
+        } else {
+          this._disable();
+        }
         break;
-      case "Accessibility:NextObject":
-      case "Accessibility:PreviousObject": {
+      case GECKOVIEW_MESSAGE.NEXT:
+      case GECKOVIEW_MESSAGE.PREVIOUS: {
         let rule = "Simple";
         if (data && data.rule && data.rule.length) {
           rule = data.rule.substr(0, 1).toUpperCase() +
             data.rule.substr(1).toLowerCase();
         }
-        let method = event.replace(/Accessibility:(\w+)Object/, "move$1");
+        let method = event.replace(/GeckoView:Accessibility(\w+)/, "move$1");
         this.Input.moveCursor(method, rule, "gesture");
         break;
       }
-      case "Accessibility:ActivateObject":
+      case GECKOVIEW_MESSAGE.ACTIVATE:
         this.Input.activateCurrent(data);
         break;
-      case "Accessibility:LongPress":
+      case GECKOVIEW_MESSAGE.LONG_PRESS:
         this.Input.sendContextMenuMessage();
         break;
-      case "Accessibility:ScrollForward":
+      case GECKOVIEW_MESSAGE.SCROLL_FORWARD:
         this.Input.androidScroll("forward");
         break;
-      case "Accessibility:ScrollBackward":
+      case GECKOVIEW_MESSAGE.SCROLL_BACKWARD:
         this.Input.androidScroll("backward");
         break;
-      case "Accessibility:Focus":
+      case GECKOVIEW_MESSAGE.VIEW_FOCUSED:
         this._focused = data.gainFocus;
         if (this._focused) {
           this.autoMove({ forcePresent: true, noOpIfOnScreen: true });
         }
         break;
-      case "Accessibility:MoveByGranularity":
+      case GECKOVIEW_MESSAGE.BY_GRANULARITY:
         this.Input.moveByGranularity(data);
         break;
     }
   },
 
   observe: function observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "remote-browser-shown":
@@ -379,24 +346,17 @@ var AccessFu = {
             delay: 500,
             forcePresent: true,
             noOpIfOnScreen: true,
             moveMethod: "moveFirst" });
         }
         break;
       }
       default:
-      {
-        // A settings change, it does not have an event type
-        if (aEvent.settingName == SCREENREADER_SETTING) {
-          this._systemPref = aEvent.settingValue;
-          this._enableOrDisable();
-        }
         break;
-      }
     }
   },
 
   autoMove: function autoMove(aOptions) {
     let mm = Utils.getMessageManager(Utils.CurrentBrowser);
     mm.sendAsyncMessage("AccessFu:AutoMove", aOptions);
   },
 
@@ -580,17 +540,17 @@ var Output = {
     }
   },
 
   Android: function Android(aDetails, aBrowser) {
     const ANDROID_VIEW_TEXT_CHANGED = 0x10;
     const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
 
     for (let androidEvent of aDetails) {
-      androidEvent.type = "Accessibility:Event";
+      androidEvent.type = "GeckoView:AccessibilityEvent";
       if (androidEvent.bounds) {
         androidEvent.bounds = AccessFu.adjustContentBounds(
           androidEvent.bounds, aBrowser);
       }
 
       switch (androidEvent.eventType) {
         case ANDROID_VIEW_TEXT_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustText(
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -136,17 +136,18 @@ var Utils = { // jshint ignore:line
         return this.win.shell;
       default:
         return null;
     }
   },
 
   get CurrentBrowser() {
     if (!this.BrowserApp) {
-      return null;
+      // Get the first content browser element when no 'BrowserApp' exists.
+      return this.win.document.querySelector("browser[type=content]");
     }
     if (this.MozBuildApp == "b2g") {
       return this.BrowserApp.contentBrowser;
     }
     return this.BrowserApp.selectedBrowser;
   },
 
   get CurrentContentDoc() {
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -136,24 +136,30 @@ var AccessFuTest = {
       for (var testFunc of gTestFuncs) {
         yield testFunc;
       }
     })();
 
     // Start AccessFu and put it in stand-by.
     ChromeUtils.import("resource://gre/modules/accessibility/AccessFu.jsm");
 
-    AccessFu.attach(getMainChromeWindow(window));
+    let chromeWin = getMainChromeWindow(window);
+    chromeWin.WindowEventDispatcher = {
+      dispatch: () => {},
+      sendRequest: () => {}
+    };
 
     AccessFu.readyCallback = function readyCallback() {
       // Enable logging to the console service.
       Logger.test = true;
       Logger.logLevel = Logger.DEBUG;
     };
 
+    AccessFu.attach(chromeWin, true);
+
     var prefs = [["accessibility.accessfu.notify_output", 1]];
     prefs.push.apply(prefs, aAdditionalPrefs);
 
     this.originalDwellThreshold = GestureSettings.dwellThreshold;
     this.originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
     this.originalMaxGestureResolveTimeout =
       GestureSettings.maxGestureResolveTimeout;
     // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
--- a/accessible/tests/mochitest/jsat/test_alive.html
+++ b/accessible/tests/mochitest/jsat/test_alive.html
@@ -8,24 +8,16 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="./jsatcommon.js"></script>
   <script type="application/javascript">
 
-    function prefStart() {
-      AccessFuTest.once_log("AccessFu:Enabled", () =>
-        ok(AccessFu._enabled, "AccessFu was enabled again."));
-      AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
-      // Start AccessFu via pref.
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 1]]});
-    }
-
     // Listen for 'EventManager.stop' and enable AccessFu again.
     function settingsStart() {
       isnot(AccessFu._enabled, true, "AccessFu was disabled.");
       // XXX: Bug 978076 - test start with SettingsManager.
       // navigator.mozSettings.createLock().set(
       //  {'accessibility.screenreader': false});
       AccessFuTest.once_log("EventManager.start", () => {
         ok(AccessFu._enabled, "AccessFu was enabled again.");
@@ -42,29 +34,17 @@
       //  {'accessibility.screenreader': false});
       AccessFuTest.once_log("EventManager.stop", () => {
         isnot(AccessFu._enabled, "AccessFu was disabled.");
         AccessFuTest.finish();
       });
       AccessFu._disable();
     }
 
-    // Listen for initial 'EventManager.start' and disable AccessFu.
-    function prefStop() {
-      ok(AccessFu._enabled, "AccessFu was started via preference.");
-      AccessFuTest.once_log("AccessFu:Disabled", () =>
-        isnot(AccessFu._enabled, true, "AccessFu was disabled."));
-      AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest);
-
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 0]]});
-    }
-
     function doTest() {
-      AccessFuTest.addFunc(prefStart);
-      AccessFuTest.addFunc(prefStop);
       AccessFuTest.addFunc(settingsStart);
       AccessFuTest.addFunc(settingsStop);
       AccessFuTest.waitForExplicitFinish();
       AccessFuTest.runTests(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
--- a/accessible/tests/mochitest/jsat/test_live_regions.html
+++ b/accessible/tests/mochitest/jsat/test_live_regions.html
@@ -9,23 +9,23 @@
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="./jsatcommon.js"></script>
   <script type="application/javascript">
 
     function startAccessFu() {
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 1]]});
       AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+      AccessFu._enable();
     }
 
     function stopAccessFu() {
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 0]]});
       AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
+      AccessFu._disable();
     }
 
     function hide(id) {
       var element = document.getElementById(id);
       element.style.display = "none";
     }
 
     function show(id) {
--- a/accessible/tests/mochitest/jsat/test_quicknav_modes.html
+++ b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
@@ -8,20 +8,19 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="./jsatcommon.js"></script>
   <script type="application/javascript">
 
-    function prefStart() {
-      // Start AccessFu via pref.
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 1]]});
+    function startAccessFu() {
       AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+      AccessFu._enable();
     }
 
     function nextMode(aCurrentMode, aNextMode) {
       return function() {
         is(AccessFu.Input.quickNavMode.current, aCurrentMode,
           "initial current mode is correct");
         AccessFu.Input.quickNavMode.next();
         _expectMode(aNextMode, AccessFuTest.nextTest);
@@ -64,34 +63,33 @@
       } else {
         AccessFuTest.once_log("Quicknav mode: " + aExpectedMode, function() {
           ok(true, "correct mode");
           aCallback();
         });
       }
     }
 
-    // Listen for initial 'EventManager.start' and disable AccessFu.
-    function prefStop() {
-      ok(AccessFu._enabled, "AccessFu was started via preference.");
+    function stopAccessFu() {
+      ok(AccessFu._enabled, "AccessFu is enabled.");
       AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
-      SpecialPowers.pushPrefEnv({"set": [["accessibility.accessfu.activate", 0]]});
+      AccessFu._disable();
     }
 
     function doTest() {
-      AccessFuTest.addFunc(prefStart);
+      AccessFuTest.addFunc(startAccessFu);
       AccessFuTest.addFunc(nextMode("Link", "Heading"));
       AccessFuTest.addFunc(nextMode("Heading", "FormElement"));
       AccessFuTest.addFunc(nextMode("FormElement", "Link"));
       AccessFuTest.addFunc(nextMode("Link", "Heading"));
       AccessFuTest.addFunc(prevMode("Heading", "Link"));
       AccessFuTest.addFunc(prevMode("Link", "FormElement"));
       AccessFuTest.addFunc(setMode(1, "Heading"));
       AccessFuTest.addFunc(reconfigureModes);
-      AccessFuTest.addFunc(prefStop);
+      AccessFuTest.addFunc(stopAccessFu);
       AccessFuTest.waitForExplicitFinish();
       AccessFuTest.runTests([   // Will call SimpleTest.finish();
         ["accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement"]]);
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -379,18 +379,17 @@ var gSync = {
       targetDevice.setAttribute("clientId", clientId);
       targetDevice.setAttribute("clientType", clientType);
       targetDevice.setAttribute("label", name);
       fragment.appendChild(targetDevice);
     }
 
     const clients = this.remoteClients;
     for (let client of clients) {
-      const type = client.formfactor && client.formfactor.includes("tablet") ?
-                   "tablet" : client.type;
+      const type = Weave.Service.clientsEngine.getClientType(client.id);
       addTargetDevice(client.id, client.name, type, new Date(client.serverLastModified * 1000));
     }
 
     // "Send to All Devices" menu item
     if (clients.length > 1) {
       const separator = createDeviceNodeFn();
       separator.classList.add("sync-menuitem");
       fragment.appendChild(separator);
--- a/browser/base/content/test/sync/browser_contextmenu_sendpage.js
+++ b/browser/base/content/test/sync/browser_contextmenu_sendpage.js
@@ -4,16 +4,17 @@
 "use strict";
 
 const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
 
 add_task(async function setup() {
   await promiseSyncReady();
   // gSync.init() is called in a requestIdleCallback. Force its initialization.
   gSync.init();
+  sinon.stub(Weave.Service.clientsEngine, "getClientType").returns("desktop");
   await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
 });
 
 add_task(async function test_page_contextmenu() {
   const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: remoteClientsFixture,
                                       state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
 
   await openContentContextMenu("#moztext", "context-sendpagetodevice");
@@ -188,16 +189,17 @@ add_task(async function test_page_contex
   [...document.querySelectorAll(".sync-ui-item")].forEach(e => e.hidden = false);
 });
 
 // We are not going to bother testing the visibility of context-sendlinktodevice
 // since it uses the exact same code.
 // However, browser_contextmenu.js contains tests that verify its presence.
 
 add_task(async function teardown() {
+  Weave.Service.clientsEngine.getClientType.restore();
   gBrowser.removeCurrentTab();
 });
 
 function checkPopup(expectedItems = null) {
   const popup = document.getElementById("context-sendpagetodevice-popup");
   if (!expectedItems) {
     is(popup.state, "closed", "Popup should be hidden.");
     return;
--- a/browser/base/content/test/urlbar/browser_page_action_menu.js
+++ b/browser/base/content/test/urlbar/browser_page_action_menu.js
@@ -264,16 +264,17 @@ add_task(async function sendToDevice_syn
     const lastSync = sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => true);
     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);
       lastSync.get(() => Date.now());
       sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
+      sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockRemoteClients.find(c => c.id == id).type);
     });
 
     let onShowingSubview = BrowserPageActions.sendToDevice.onShowingSubview;
     sandbox.stub(BrowserPageActions.sendToDevice, "onShowingSubview").callsFake((...args) => {
       this.numCall++ || (this.numCall = 1);
       onShowingSubview.call(BrowserPageActions.sendToDevice, ...args);
       testSendTabToDeviceMenu(this.numCall);
     });
@@ -400,16 +401,17 @@ add_task(async function sendToDevice_noD
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
     const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(gSync, "remoteClients").get(() => []);
+    sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockRemoteClients.find(c => c.id == id).type);
 
     let cleanUp = () => {
       sandbox.restore();
     };
     registerCleanupFunction(cleanUp);
 
     // Open the panel.
     await promisePageActionPanelOpen();
@@ -465,16 +467,17 @@ add_task(async function sendToDevice_dev
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
     const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
+    sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockRemoteClients.find(c => c.id == id).type);
 
     let cleanUp = () => {
       sandbox.restore();
     };
     registerCleanupFunction(cleanUp);
 
     // Open the panel.
     await promisePageActionPanelOpen();
@@ -529,16 +532,17 @@ add_task(async function sendToDevice_inU
   await BrowserTestUtils.withNewTab("http://example.com/", async () => {
     await promiseSyncReady();
     const sandbox = sinon.sandbox.create();
     sandbox.stub(gSync, "syncReady").get(() => true);
     sandbox.stub(Weave.Service.clientsEngine, "isFirstSync").get(() => false);
     sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN });
     sandbox.stub(gSync, "isSendableURI").returns(true);
     sandbox.stub(gSync, "remoteClients").get(() => mockRemoteClients);
+    sandbox.stub(Weave.Service.clientsEngine, "getClientType").callsFake(id => mockRemoteClients.find(c => c.id == id).type);
 
     let cleanUp = () => {
       sandbox.restore();
     };
     registerCleanupFunction(cleanUp);
 
     // Add Send to Device to the urlbar.
     let action = PageActions.actionForID("sendToDevice");
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -62,16 +62,20 @@ ChromeUtils.import("resource://gre/modul
     // Setting the size of the window in the features string instead of here
     // causes the window to grow by the size of the titlebar.
     win.resizeTo(width, height);
   }
 
   // The window becomes visible after OnStopRequest, so make this happen now.
   win.stop();
 
+  let { TelemetryTimestamps } =
+    ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", {});
+  TelemetryTimestamps.add("blankWindowShown");
+
   // Used in nsBrowserContentHandler.js to close unwanted blank windows.
   docElt.setAttribute("windowtype", "navigator:blank");
 })();
 
 Cu.importGlobalProperties(["fetch"]);
 
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils", "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
 XPCOMUtils.defineLazyGetter(this, "WeaveService", () =>
--- a/browser/components/syncedtabs/TabListView.js
+++ b/browser/components/syncedtabs/TabListView.js
@@ -226,24 +226,20 @@ TabListView.prototype = {
     } else {
       itemNode.classList.remove("closed");
     }
     if (item.selected) {
       itemNode.classList.add("selected");
     } else {
       itemNode.classList.remove("selected");
     }
-    if (item.isMobile) {
-      itemNode.classList.add("device-image-mobile");
-    } else {
-      itemNode.classList.add("device-image-desktop");
-    }
     if (item.focused) {
       itemNode.focus();
     }
+    itemNode.setAttribute("clientType", item.clientType);
     itemNode.dataset.id = item.id;
     itemNode.querySelector(".item-title").textContent = item.name;
   },
 
   /**
    * Update the element representing a tab, ensuring it's in sync with the
    * underlying data.
    * @param {tab} item - Item to use as a source.
--- a/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
+++ b/browser/components/syncedtabs/test/browser/browser_sidebar_syncedtabslist.js
@@ -1,34 +1,34 @@
 "use strict";
 
 const FIXTURE = [
   {
     "id": "7cqCr77ptzX3",
     "type": "client",
     "lastModified": 1492201200,
     "name": "zcarter's Nightly on MacBook-Pro-25",
-    "isMobile": false,
+    "clientType": "desktop",
     "tabs": [
       {
         "type": "tab",
         "title": "Firefox for Android — Mobile Web browser — More ways to customize and protect your privacy — Mozilla",
         "url": "https://www.mozilla.org/en-US/firefox/android/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar",
         "icon": "chrome://mozapps/skin/places/defaultFavicon.svg",
         "client": "7cqCr77ptzX3",
         "lastUsed": 1452124677
       }
     ]
   },
   {
     "id": "2xU5h-4bkWqA",
     "type": "client",
     "lastModified": 1492201200,
     "name": "laptop",
-    "isMobile": false,
+    "clientType": "desktop",
     "tabs": [
       {
         "type": "tab",
         "title": "Firefox for iOS — Mobile Web browser for your iPhone, iPad and iPod touch — Mozilla",
         "url": "https://www.mozilla.org/en-US/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar",
         "icon": "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon.dc6635050bf5.ico",
         "client": "2xU5h-4bkWqA",
         "lastUsed": 1451519425
@@ -52,17 +52,17 @@ const FIXTURE = [
       }
     ]
   },
   {
     "id": "OL3EJCsdb2JD",
     "type": "client",
     "lastModified": 1492201200,
     "name": "desktop",
-    "isMobile": false,
+    "clientType": "desktop",
     "tabs": []
   }
 ];
 
 let originalSyncedTabsInternal = null;
 
 async function testClean() {
   let syncedTabsDeckComponent = window.SidebarUI.browser.contentWindow.syncedTabsDeckComponent;
--- a/browser/themes/linux/customizableui/panelUI.css
+++ b/browser/themes/linux/customizableui/panelUI.css
@@ -1,18 +1,14 @@
 /* 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/. */
 
 %include ../../shared/customizableui/panelUI.inc.css
 
-#BMB_bookmarksPopup > menuitem[type="checkbox"] {
-  -moz-appearance: none !important; /* important, to override toolkit rule */
-}
-
 #BMB_bookmarksPopup menupopup {
   -moz-appearance: none;
   background: var(--arrowpanel-background);
   color: var(--arrowpanel-color);
   border: 1px solid var(--arrowpanel-border-color);
   margin-top: -6px;
   padding-top: 1px;
 }
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -44,17 +44,15 @@ browser.jar:
   skin/classic/browser/places/downloads.png           (places/downloads.png)
   skin/classic/browser/preferences/alwaysAsk.png      (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/preferences.css    (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/applications.css   (preferences/applications.css)
   skin/classic/browser/tabbrowser/tabDragIndicator.png      (tabbrowser/tabDragIndicator.png)
 
-  skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
-  skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/feeds/audioFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png              chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/videoFeedIcon16.png            chrome://browser/skin/feeds/feedIcon16.png
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -53,18 +53,16 @@ browser.jar:
   skin/classic/browser/preferences/application.png          (preferences/application.png)
   skin/classic/browser/preferences/saveFile.png             (preferences/saveFile.png)
 * skin/classic/browser/preferences/preferences.css          (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/applications.css         (preferences/applications.css)
   skin/classic/browser/tabbrowser/tabDragIndicator.png                   (tabbrowser/tabDragIndicator.png)
   skin/classic/browser/tabbrowser/tabDragIndicator@2x.png                (tabbrowser/tabDragIndicator@2x.png)
-  skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
-  skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/e10s-64@2x.png                                  (../shared/e10s-64@2x.png)
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/feeds/audioFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png                 chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/videoFeedIcon16.png                 chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/notification-icons/geo-detailed.svg       chrome://browser/skin/notification-icons/geo.svg
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1010,17 +1010,17 @@ toolbarbutton.subviewbutton@buttonStateH
 menu.subviewbutton@menuStateHover@,
 menuitem.subviewbutton@menuStateHover@,
 .widget-overflow-list .toolbarbutton-1@buttonStateHover@,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
   color: inherit;
   background-color: var(--arrowpanel-dimmed);
 }
 
-panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
+panelview .toolbarbutton-1@buttonStateActive@,
 toolbarbutton.subviewbutton@buttonStateActive@,
 menu.subviewbutton@menuStateActive@,
 menuitem.subviewbutton@menuStateActive@,
 .widget-overflow-list .toolbarbutton-1@buttonStateActive@,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
   color: inherit;
   background-color: var(--arrowpanel-dimmed-further);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
@@ -1290,20 +1290,16 @@ toolbarpaletteitem[place="menu-panel"] >
   padding-inline-start: .5em;
 }
 
 .subviewbutton > .menu-iconic-left {
   -moz-appearance: none;
   margin-inline-end: 0;
 }
 
-menuitem[checked="true"].subviewbutton > .menu-iconic-left {
-  visibility: hidden;
-}
-
 #PanelUI-panicView > .panel-subview-body,
 #PanelUI-panicView {
   overflow: visible;
 }
 
 #PanelUI-panicView.cui-widget-panelview {
   min-width: 280px;
 }
rename from browser/themes/shared/icons/device-mobile.svg
rename to browser/themes/shared/icons/device-phone.svg
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -123,17 +123,17 @@
   skin/classic/browser/bookmark-star-on-tray.svg      (../shared/icons/bookmark-star-on-tray.svg)
   skin/classic/browser/characterEncoding.svg          (../shared/icons/characterEncoding.svg)
   skin/classic/browser/chevron.svg                    (../shared/icons/chevron.svg)
   skin/classic/browser/chevron-animation.svg          (../shared/icons/chevron-animation.svg)
   skin/classic/browser/check.svg                      (../shared/icons/check.svg)
   skin/classic/browser/check-animation.svg            (../shared/icons/check-animation.svg)
   skin/classic/browser/customize.svg                  (../shared/icons/customize.svg)
   skin/classic/browser/developer.svg                  (../shared/icons/developer.svg)
-  skin/classic/browser/device-mobile.svg              (../shared/icons/device-mobile.svg)
+  skin/classic/browser/device-phone.svg               (../shared/icons/device-phone.svg)
   skin/classic/browser/device-tablet.svg              (../shared/icons/device-tablet.svg)
   skin/classic/browser/device-desktop.svg             (../shared/icons/device-desktop.svg)
   skin/classic/browser/edit-copy.svg                  (../shared/icons/edit-copy.svg)
   skin/classic/browser/edit-cut.svg                   (../shared/icons/edit-cut.svg)
   skin/classic/browser/edit-paste.svg                 (../shared/icons/edit-paste.svg)
   skin/classic/browser/feed.svg                       (../shared/icons/feed.svg)
   skin/classic/browser/folder.svg                     (../shared/icons/folder.svg)
   skin/classic/browser/forget.svg                     (../shared/icons/forget.svg)
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -78,17 +78,17 @@
   list-style-image: url("chrome://browser/skin/tab.svg");
 }
 
 #PanelUI-remotetabs-syncnow {
   list-style-image: url("chrome://browser/skin/sync.svg");
 }
 
 #PanelUI-remotetabs-view-managedevices {
-  list-style-image: url("chrome://browser/skin/device-mobile.svg");
+  list-style-image: url("chrome://browser/skin/device-phone.svg");
 }
 
 #appMenuViewHistorySidebar,
 #PanelUI-remotetabs-view-sidebar,
 #panelMenu_viewBookmarksSidebar {
   list-style-image: url("chrome://browser/skin/sidebars.svg");
 }
 
deleted file mode 100644
--- a/browser/themes/shared/sync-desktopIcon.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg"
-     width="16" height="16" viewBox="0 0 16 16">
-  <path fill="context-fill" d="M15,14H1a1,1,0,0,1-1-1V12.526H16V13A1,1,0,0,1,15,14ZM1,4A1,1,0,0,1,2,3H14a1,1,0,0,1,1,1v8H1V4Zm1,7H14V4H2v7Z"/>
-  <rect fill="context-fill" fill-opacity="0.15" x="2" y="4" width="12" height="7"/>
-</svg>
deleted file mode 100644
--- a/browser/themes/shared/sync-mobileIcon.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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/. -->
-<svg xmlns="http://www.w3.org/2000/svg"
-     width="16" height="16" viewBox="0 0 16 16">
-  <path fill="context-fill" d="M12,16H4a1,1,0,0,1-1-1V1A1,1,0,0,1,4,0h8a1,1,0,0,1,1,1V15A1,1,0,0,1,12,16Zm-4-.684a0.785,0.785,0,1,0-.785-0.785A0.785,0.785,0,0,0,8,15.316ZM12,2H4V13h8V2Z"/>
-  <rect fill="context-fill" fill-opacity="0.15" x="4" y="2" width="8" height="11"/>
-</svg>
--- a/browser/themes/shared/syncedtabs/sidebar.inc.css
+++ b/browser/themes/shared/syncedtabs/sidebar.inc.css
@@ -83,34 +83,35 @@ body {
 .client .item.tab > .item-title-container {
   padding-inline-start: 35px;
 }
 
 .item.tab > .item-title-container {
   padding-inline-start: 20px;
 }
 
-.item.client.device-image-desktop > .item-title-container > .item-icon-container {
-  background-image: url("chrome://browser/skin/sync-desktopIcon.svg");
+.item.client[clientType] > .item-title-container > .item-icon-container {
   -moz-context-properties: fill;
   fill: #4d4d4d;
 }
 
-.item.client.device-image-desktop.selected:focus > .item-title-container > .item-icon-container {
+.item.client[clientType].selected:focus > .item-title-container > .item-icon-container {
   fill: white;
 }
 
-.item.client.device-image-mobile > .item-title-container > .item-icon-container {
-  background-image: url("chrome://browser/skin/sync-mobileIcon.svg");
-  -moz-context-properties: fill;
-  fill: #4d4d4d;
+.item.client[clientType=phone] > .item-title-container > .item-icon-container {
+  background-image: url("chrome://browser/skin/device-phone.svg");
 }
 
-.item.client.device-image-mobile.selected:focus > .item-title-container > .item-icon-container {
-  fill: white;
+.item.client[clientType=tablet] > .item-title-container > .item-icon-container {
+  background-image: url("chrome://browser/skin/device-tablet.svg");
+}
+
+.item.client[clientType=desktop] > .item-title-container > .item-icon-container {
+  background-image: url("chrome://browser/skin/device-desktop.svg");
 }
 
 .item.tab > .item-title-container > .item-icon-container {
   background-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -146,18 +146,18 @@
   list-style-image: url("chrome://browser/skin/send-to-device.svg");
 }
 
 #pageAction-panel-sendToDevice:-moz-locale-dir(rtl) > .toolbarbutton-icon,
 #pageAction-urlbar-sendToDevice:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
 
-.pageAction-sendToDevice-device[clientType=mobile] {
-  list-style-image: url("chrome://browser/skin/device-mobile.svg");
+.pageAction-sendToDevice-device[clientType=phone] {
+  list-style-image: url("chrome://browser/skin/device-phone.svg");
 }
 
 .pageAction-sendToDevice-device[clientType=tablet] {
   list-style-image: url("chrome://browser/skin/device-tablet.svg");
 }
 
 .pageAction-sendToDevice-device[clientType=desktop] {
   list-style-image: url("chrome://browser/skin/device-desktop.svg");
--- a/browser/themes/windows/customizableui/panelUI.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -32,19 +32,17 @@
 
 /* bookmark panel separator */
 #BMB_bookmarksPopup menuseparator {
   padding-top: 0;
   padding-bottom: 0;
 }
 
 /* Disabled empty item looks too small otherwise, because it has no icon. */
-menuitem.subviewbutton[disabled]:not(.menuitem-iconic),
-/* Same for checkbox menu items, whose icons lose size due to -moz-appearance: none: */
-menuitem[type="checkbox"].subviewbutton {
+menuitem.subviewbutton[disabled]:not(.menuitem-iconic) {
   /* This is 16px for an icon + 3px for its margins + 1px for its padding +
    * 2px for its border, see above */
   min-height: 22px;
 }
 
 menu.subviewbutton > .menu-right {
   -moz-appearance: none;
   list-style-image: url(chrome://browser/skin/customizableui/menu-arrow.svg);
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -11,18 +11,16 @@ browser.jar:
 * skin/classic/browser/compacttheme.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
 * skin/classic/browser/searchbar.css
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
-  skin/classic/browser/sync-desktopIcon.svg  (../shared/sync-desktopIcon.svg)
-  skin/classic/browser/sync-mobileIcon.svg  (../shared/sync-mobileIcon.svg)
   skin/classic/browser/webRTC-indicator.css  (../shared/webRTC-indicator.css)
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
 * skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsView.css   (downloads/allDownloadsView.css)
 * skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 35.0
+Version 36.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-34...release-35
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-35...release-36
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -573,24 +573,26 @@ menuseparator {
 .shortcut,
 .add-button {
   fill: var(--theme-splitter-color);
 }
 
 .folder,
 .domain,
 .source-icon,
-.file {
+.file,
+.extension {
   background-color: var(--theme-comment);
 }
 
 .worker,
 .file,
 .folder,
-.source-icon {
+.source-icon,
+.extension {
   position: relative;
   top: 2px;
 }
 
 .domain,
 .worker,
 .refresh,
 .add-button {
@@ -607,16 +609,22 @@ menuseparator {
 
 img.domain,
 img.folder,
 img.source-icon {
   width: 15px;
   height: 15px;
 }
 
+img.extension {
+  width: 13px;
+  height: 13px;
+  margin-inline-start: 2px;
+}
+
 img.result-item-icon {
   height: 18px;
   width: 18px;
 }
 
 img.domain {
   mask: url("chrome://devtools/skin/images/debugger/domain.svg") no-repeat;
 }
@@ -640,26 +648,31 @@ img.tab {
 img.react {
   mask: url("chrome://devtools/skin/images/debugger/react.svg") no-repeat;
 }
 
 img.typescript {
   mask: url("chrome://devtools/skin/images/debugger/typescript.svg") no-repeat;
 }
 
+img.extension {
+  mask: url("chrome://devtools/skin/images/debugger/extension.svg") no-repeat;
+}
+
 img.file {
   mask: url("chrome://devtools/skin/images/debugger/file.svg") no-repeat;
   width: 13px;
   height: 13px;
 }
 
 img.domain,
 img.folder,
 img.file,
-img.source-icon {
+img.source-icon,
+img.extension {
   mask-size: 100%;
   margin-inline-end: 5px;
   display: inline-block;
 }
 
 img.result-item-icon {
   mask-size: 100%;
   margin-inline-end: 15px;
@@ -2976,33 +2989,33 @@ html[dir="rtl"] .breakpoints-list .break
 }
 
 .input-expression.error {
   border: 1px solid red;
   animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
 }
 
 .input-expression::placeholder {
-  text-align: center;
   font-style: italic;
   color: var(--theme-comment);
 }
 
 .input-expression:focus {
   outline: none;
   cursor: text;
 }
 
 .expressions-list {
   /* TODO: add normalize */
   margin: 0;
   padding: 0;
 }
 .expression-input-container {
   display: flex;
+  border: 1px solid var(--theme-highlight-blue);
 }
 
 .expression-container {
   border: 1px;
   padding: 0.25em 1em 0.25em 0.5em;
   width: 100%;
   color: var(--theme-body-color);
   background-color: var(--theme-body-background);
@@ -3422,17 +3435,18 @@ img.stepIn,
 img.stepOut,
 img.resume,
 img.rewind,
 img.reverseStepOver,
 img.reverseStepIn,
 img.reverseStepOut,
 img.replay-previous,
 img.replay-next,
-img.resume {
+img.resume,
+img.shortcuts {
   background-color: var(--theme-body-color);
 }
 
 .command-bar img.pause {
   mask: url("chrome://devtools/skin/images/debugger/pause.svg") no-repeat;
 }
 
 .command-bar img.stepOver {
@@ -3499,16 +3513,20 @@ img.resume {
 }
 
 .command-bar img.replay-next {
   mask: url("chrome://devtools/skin/images/debugger/forward.svg") no-repeat;
   mask-size: 75%;
   margin-top: 5px;
 }
 
+.command-bar img.shortcuts {
+  mask: url("chrome://devtools/skin/images/help.svg") no-repeat;
+}
+
 .command-bar .replay-inactive {
   opacity: 0.5;
 }
 
 .command-bar .step-position {
   color: var(--theme-comment-alt);
   padding-top: 8px;
   margin-inline-end: 4px;
@@ -3928,16 +3946,17 @@ html .welcomebox .toggle-button-end.coll
   position: relative;
   transition: all 0.15s ease;
   min-width: 40px;
   overflow: hidden;
   padding: 5px;
   margin-inline-start: 3px;
   margin-top: 3px;
   cursor: default;
+  height: 27px;
 }
 
 .source-tab:first-child {
   margin-inline-start: 0;
 }
 
 .source-tab:hover {
   background-color: var(--theme-toolbar-background-alt);
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -3635,17 +3635,17 @@ function update(state = initialSourcesSt
       return state.merge({ tabs: action.tabs });
 
     case "LOAD_SOURCE_TEXT":
       return setSourceTextProps(state, action);
 
     case "BLACKBOX":
       if (action.status === "done") {
         const url = action.source.url;
-        const isBlackBoxed = action.value.isBlackBoxed;
+        const { isBlackBoxed } = action.value;
         updateBlackBoxList(url, isBlackBoxed);
         return state.setIn(["sources", action.source.id, "isBlackBoxed"], isBlackBoxed);
       }
       break;
 
     case "NAVIGATE":
       const source = getSelectedSource({ sources: state });
       const url = source && source.url;
@@ -6030,23 +6030,21 @@ function removeBreakpoint(location) {
  */
 function enableBreakpoint(location) {
   return async ({ dispatch, getState, client, sourceMaps }) => {
     const breakpoint = (0, _selectors.getBreakpoint)(getState(), location);
     if (!breakpoint || breakpoint.loading) {
       return;
     }
 
-    const action = {
+    return dispatch({
       type: "ENABLE_BREAKPOINT",
       breakpoint,
       [_promise.PROMISE]: (0, _addBreakpoint2.default)(getState, client, sourceMaps, breakpoint)
-    };
-
-    return dispatch(action);
+    });
   };
 }
 
 /**
  * Disable a single breakpoint
  *
  * @memberof actions/breakpoints
  * @static
@@ -6057,22 +6055,20 @@ function disableBreakpoint(location) {
 
     if (!bp || bp.loading) {
       return;
     }
 
     await client.removeBreakpoint(bp.generatedLocation);
     const newBreakpoint = _extends({}, bp, { disabled: true });
 
-    const action = {
+    return dispatch({
       type: "DISABLE_BREAKPOINT",
       breakpoint: newBreakpoint
-    };
-
-    return dispatch(action);
+    });
   };
 }
 
 /**
  * Toggle All Breakpoints
  *
  * @memberof actions/breakpoints
  * @static
@@ -6381,17 +6377,17 @@ function addExpression(input) {
    * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 function clearExpressionError() {
   return { type: "CLEAR_EXPRESSION_ERROR" };
 }
 
 function updateExpression(input, expression) {
   return async ({ dispatch, getState }) => {
-    if (!input || input == expression.input) {
+    if (!input) {
       return;
     }
 
     const expressionError = await parser.hasSyntaxError(input);
     dispatch({
       type: "UPDATE_EXPRESSION",
       expression,
       input: expressionError ? expression.input : input,
@@ -9738,16 +9734,17 @@ Object.defineProperty(exports, "__esModu
 exports.isVisible = isVisible;
 exports.getLineNumberWidth = getLineNumberWidth;
 exports.resizeBreakpointGutter = resizeBreakpointGutter;
 exports.resizeToggleButton = resizeToggleButton;
 /* 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/>. */
 
+
 /* Checks to see if the root element is available and
  * if the element is visible. We check the width of the element
  * because it is more reliable than either checking a focus state or
  * the visibleState or hidden property.
  */
 function isVisible() {
   const el = document.querySelector("#mount");
   return el && el.getBoundingClientRect().width;
@@ -12694,16 +12691,21 @@ async function fetchSources() {
  * listWorkers. On Fennec 60 or older, the call will silently crash and prevent
  * the client from resuming.
  * XXX: Remove when FF60 for Android is no longer used or available.
  *
  * See https://bugzilla.mozilla.org/show_bug.cgi?id=1443550 for more details.
  */
 async function checkServerSupportsListWorkers() {
   const root = await tabTarget.root;
+  // root is not available on all debug targets.
+  if (!root) {
+    return false;
+  }
+
   const deviceFront = await (0, _frontsDevice.getDeviceFront)(debuggerClient, root);
   const description = await deviceFront.getDescription();
 
   const isFennec = description.apptype === "mobile/android";
   if (!isFennec) {
     // Explicitly return true early to avoid calling Services.vs.compare.
     // This would force us to extent the Services shim provided by
     // devtools-modules, used when this code runs in a tab.
@@ -15198,19 +15200,21 @@ const svg = {
   breakpoint: __webpack_require__(350),
   "column-breakpoint": __webpack_require__(998),
   "case-match": __webpack_require__(351),
   choo: __webpack_require__(1290),
   close: __webpack_require__(352),
   coffeescript: __webpack_require__(2250),
   dojo: __webpack_require__(806),
   domain: __webpack_require__(353),
+  extension: __webpack_require__(3632),
   file: __webpack_require__(354),
   folder: __webpack_require__(355),
   globe: __webpack_require__(356),
+  help: __webpack_require__(3633),
   home: __webpack_require__(3604),
   javascript: __webpack_require__(2251),
   jquery: __webpack_require__(999),
   underscore: __webpack_require__(1117),
   lodash: __webpack_require__(1118),
   ember: __webpack_require__(1119),
   vuejs: __webpack_require__(1174),
   "magnifying-glass": __webpack_require__(357),
@@ -16912,17 +16916,18 @@ class SourcesTree extends _react.Compone
         sourceTree
       }));
     }
   }
 
   renderItemName(name) {
     const hosts = {
       "ng://": "Angular",
-      "webpack://": "Webpack"
+      "webpack://": "Webpack",
+      "moz-extension://": L10N.getStr("extensionsText")
     };
 
     return hosts[name] || name;
   }
 
   renderEmptyElement(message) {
     return _react2.default.createElement(
       "div",
@@ -17060,19 +17065,20 @@ var _initialiseProps = function () {
     return `${item.path}/${item.name}/${blackBoxedPart}`;
   };
 
   this.getIcon = (sources, item, depth) => {
     const { debuggeeUrl, projectRoot } = this.props;
 
     if (item.path === "webpack://") {
       return _react2.default.createElement(_Svg2.default, { name: "webpack" });
-    }
-    if (item.path === "/Angular") {
+    } else if (item.path === "ng://") {
       return _react2.default.createElement(_Svg2.default, { name: "angular" });
+    } else if (item.path === "moz-extension://") {
+      return _react2.default.createElement("img", { className: "extension" });
     }
 
     if (depth === 0 && projectRoot === "") {
       return _react2.default.createElement("img", {
         className: (0, _classnames2.default)("domain", {
           debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
         })
       });
@@ -22805,16 +22811,28 @@ function debugBtn(onClick, type, classNa
       key: type,
       title: tooltip
     },
     _react2.default.createElement(_Svg2.default, { name: type, title: tooltip, "aria-label": tooltip })
   );
 }
 
 class SecondaryPanes extends _react.Component {
+  constructor(props) {
+    super(props);
+
+    this.onExpressionAdded = () => {
+      this.setState({ showExpressionsInput: false });
+    };
+
+    this.state = {
+      showExpressionsInput: false
+    };
+  }
+
   renderBreakpointsToggle() {
     const {
       toggleAllBreakpoints,
       breakpoints,
       breakpointsDisabled,
       breakpointsLoading
     } = this.props;
     const isIndeterminate = !breakpointsDisabled && breakpoints.some(x => x.disabled);
@@ -22845,17 +22863,20 @@ class SecondaryPanes extends _react.Comp
 
     return _react2.default.createElement("input", inputProps);
   }
 
   watchExpressionHeaderButtons() {
     return [debugBtn(evt => {
       evt.stopPropagation();
       this.props.evaluateExpressions();
-    }, "refresh", "refresh", L10N.getStr("watchExpressions.refreshButton"))];
+    }, "refresh", "refresh", L10N.getStr("watchExpressions.refreshButton")), debugBtn(evt => {
+      evt.stopPropagation();
+      this.setState({ showExpressionsInput: true });
+    }, "plus", "plus", L10N.getStr("expressions.placeholder"))];
   }
 
   getScopeItem() {
     return {
       header: L10N.getStr("scopes.header"),
       className: "scopes-pane",
       component: _react2.default.createElement(Scopes, null),
       opened: _prefs.prefs.scopesVisible,
@@ -22879,17 +22900,20 @@ class SecondaryPanes extends _react.Comp
     };
   }
 
   getWatchItem() {
     return {
       header: L10N.getStr("watchExpressions.header"),
       className: "watch-expressions-pane",
       buttons: this.watchExpressionHeaderButtons(),
-      component: _react2.default.createElement(_Expressions2.default, null),
+      component: _react2.default.createElement(_Expressions2.default, {
+        showInput: this.state.showExpressionsInput,
+        onExpressionAdded: this.onExpressionAdded
+      }),
       opened: _prefs.prefs.expressionsVisible,
       onToggle: opened => {
         _prefs.prefs.expressionsVisible = opened;
       }
     };
   }
 
   getCallStackItem() {
@@ -23096,19 +23120,19 @@ var _reactRedux = __webpack_require__(35
 var _immutable = __webpack_require__(3594);
 
 var I = _interopRequireWildcard(_immutable);
 
 var _reselect = __webpack_require__(993);
 
 var _lodash = __webpack_require__(2);
 
-var _BreakpointItem = __webpack_require__(3630);
-
-var _BreakpointItem2 = _interopRequireDefault(_BreakpointItem);
+var _Breakpoint = __webpack_require__(3634);
+
+var _Breakpoint2 = _interopRequireDefault(_Breakpoint);
 
 var _actions = __webpack_require__(1354);
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _source = __webpack_require__(1356);
 
 var _selectors = __webpack_require__(3590);
@@ -23164,17 +23188,17 @@ class Breakpoints extends _react.Compone
   }
 
   removeBreakpoint(event, breakpoint) {
     event.stopPropagation();
     this.props.removeBreakpoint(breakpoint.location);
   }
 
   renderBreakpoint(breakpoint) {
-    return _react2.default.createElement(_BreakpointItem2.default, {
+    return _react2.default.createElement(_Breakpoint2.default, {
       key: breakpoint.locationId,
       breakpoint: breakpoint,
       onClick: () => this.selectBreakpoint(breakpoint),
       onContextMenu: e => (0, _BreakpointsContextMenu2.default)(_extends({}, this.props, { breakpoint, contextMenuEvent: e })),
       onChange: () => this.handleCheckbox(breakpoint),
       onCloseClick: ev => this.removeBreakpoint(ev, breakpoint)
     });
   }
@@ -23288,35 +23312,44 @@ class Expressions extends _react.Compone
     };
 
     this.handleKeyDown = e => {
       if (e.key === "Escape") {
         this.clear();
       }
     };
 
+    this.hideInput = () => {
+      this.props.onExpressionAdded();
+    };
+
     this.handleExistingSubmit = async (e, expression) => {
       e.preventDefault();
       e.stopPropagation();
 
       this.props.updateExpression(this.state.inputValue, expression);
+      this.hideInput();
     };
 
     this.handleNewSubmit = async e => {
       const { inputValue } = this.state;
       e.preventDefault();
       e.stopPropagation();
 
       this.props.clearExpressionError();
       await this.props.addExpression(this.state.inputValue);
       this.setState({
         editing: false,
         editIndex: -1,
         inputValue: this.props.expressionError ? inputValue : ""
       });
+
+      if (!this.props.expressionError) {
+        this.hideInput();
+      }
     };
 
     this.renderExpression = (expression, index) => {
       const { expressionError, openLink } = this.props;
       const { editing, editIndex } = this.state;
       const { input, updating } = expression;
       const isEditingExpr = editing && editIndex === index;
       if (isEditingExpr || isEditingExpr && expressionError) {
@@ -23359,17 +23392,21 @@ class Expressions extends _react.Compone
             _react2.default.createElement(_Close2.default, {
               handleClick: e => this.deleteExpression(e, expression)
             })
           )
         )
       );
     };
 
-    this.state = { editing: false, editIndex: -1, inputValue: "" };
+    this.state = {
+      editing: false,
+      editIndex: -1,
+      inputValue: ""
+    };
   }
 
   componentDidMount() {
     const { expressions, evaluateExpressions } = this.props;
     if (expressions.size > 0) {
       evaluateExpressions();
     }
   }
@@ -23377,18 +23414,19 @@ class Expressions extends _react.Compone
   componentWillReceiveProps(nextProps) {
     if (this.state.editing && !nextProps.expressionError) {
       this.clear();
     }
   }
 
   shouldComponentUpdate(nextProps, nextState) {
     const { editing, inputValue } = this.state;
-    const { expressions, expressionError } = this.props;
-    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue;
+    const { expressions, expressionError, showInput } = this.props;
+
+    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue || nextProps.showInput !== showInput;
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (this._input && !prevState.editing) {
       const input = this._input;
       input.setSelectionRange(0, input.value.length);
       input.focus();
     }
@@ -23403,34 +23441,40 @@ class Expressions extends _react.Compone
   }
 
   deleteExpression(e, expression) {
     e.stopPropagation();
     const { deleteExpression } = this.props;
     deleteExpression(expression);
   }
 
+  onBlur() {
+    this.clear();
+    this.hideInput();
+  }
+
   renderNewExpressionInput() {
     const { expressionError } = this.props;
     const { editing, inputValue } = this.state;
     const error = editing === false && expressionError === true;
     const placeholder = error ? L10N.getStr("expressions.errorMsg") : L10N.getStr("expressions.placeholder");
     return _react2.default.createElement(
       "li",
       { className: "expression-input-container" },
       _react2.default.createElement(
         "form",
         { className: "expression-input-form", onSubmit: this.handleNewSubmit },
         _react2.default.createElement("input", {
           className: (0, _classnames2.default)("input-expression", { error }),
           type: "text",
           placeholder: placeholder,
           onChange: this.handleChange,
-          onBlur: this.clear,
+          onBlur: this.hideInput,
           onKeyDown: this.handleKeyDown,
+          autoFocus: "true",
           value: !editing ? inputValue : ""
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
   renderExpressionEditInput(expression) {
@@ -23456,22 +23500,23 @@ class Expressions extends _react.Compone
           ref: c => this._input = c
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
   render() {
-    const { expressions } = this.props;
+    const { expressions, showInput } = this.props;
+
     return _react2.default.createElement(
       "ul",
       { className: "pane expressions-list" },
       expressions.map(this.renderExpression),
-      this.renderNewExpressionInput()
+      showInput && this.renderNewExpressionInput()
     );
   }
 }
 
 exports.default = (0, _reactRedux.connect)(state => ({
   expressions: (0, _selectors.getExpressions)(state),
   expressionError: (0, _selectors.getExpressionError)(state)
 }), _actions2.default)(Expressions);
@@ -23996,17 +24041,19 @@ class EventListeners extends _react.Comp
       this.props.enableBreakpoint(breakpoint.location);
     } else {
       this.props.disableBreakpoint(breakpoint.location);
     }
   }
 
   removeBreakpoint(event, breakpoint) {
     event.stopPropagation();
-    this.props.removeBreakpoint(breakpoint.location);
+    if (breakpoint) {
+      this.props.removeBreakpoint(breakpoint.location);
+    }
   }
 
   render() {
     const { listeners } = this.props;
     return _react2.default.createElement(
       "div",
       { className: "pane event-listeners" },
       listeners.map(this.renderListener)
@@ -24195,16 +24242,17 @@ exports.default = Accordion;
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+exports.debugBtn = debugBtn;
 
 var _propTypes = __webpack_require__(20);
 
 var _propTypes2 = _interopRequireDefault(_propTypes);
 
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
@@ -24521,51 +24569,37 @@ exports.default = (0, _reactRedux.connec
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* 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 _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
+var _CommandBar = __webpack_require__(1608);
+
 __webpack_require__(1295);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-function debugBtn(onClick, type, className, tooltip, disabled = false) {
-  const props = {
-    onClick,
-    key: type,
-    "aria-label": tooltip,
-    title: tooltip,
-    disabled
-  };
-
-  return _react2.default.createElement(
-    "button",
-    _extends({ className: (0, _classnames2.default)(type, className) }, props),
-    "?"
-  );
-}
+/* 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/>. */
 
 class UtilsBar extends _react.Component {
   renderUtilButtons() {
-    return [debugBtn(this.props.toggleShortcutsModal, "shortcut", "active", L10N.getStr("shortcuts.buttonName"), false)];
+    return [(0, _CommandBar.debugBtn)(this.props.toggleShortcutsModal, "shortcuts", "active", L10N.getStr("shortcuts.buttonName"), false)];
   }
 
   render() {
     return _react2.default.createElement(
       "div",
       {
         className: (0, _classnames2.default)("command-bar bottom", {
           vertical: !this.props.horizontal
@@ -25918,16 +25952,18 @@ var _loadSourceText = __webpack_require_
 var _parser = __webpack_require__(1365);
 
 var _promise = __webpack_require__(1653);
 
 var _locColumn = __webpack_require__(2349);
 
 var _findGeneratedBindingFromPosition = __webpack_require__(2358);
 
+var _firefox = __webpack_require__(1500);
+
 var _prefs = __webpack_require__(226);
 
 var _log = __webpack_require__(2359);
 
 var _devtoolsSourceMap = __webpack_require__(1360);
 
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
@@ -26159,21 +26195,23 @@ async function findGeneratedBinding(sour
 }
 
 function buildGeneratedBindingList(scopes, generatedAstScopes, thisBinding) {
   // The server's binding data doesn't include general 'this' binding
   // information, so we manually inject the one 'this' binding we have into
   // the normal binding data we are working with.
   const frameThisOwner = generatedAstScopes.find(generated => "this" in generated.bindings);
 
+  let globalScope = null;
   const clientScopes = [];
   for (let s = scopes; s; s = s.parent) {
     const bindings = s.bindings ? Object.assign({}, ...s.bindings.arguments, s.bindings.variables) : {};
 
     clientScopes.push(bindings);
+    globalScope = s;
   }
 
   const generatedMainScopes = generatedAstScopes.slice(0, -2);
   const generatedGlobalScopes = generatedAstScopes.slice(-2);
 
   const clientMainScopes = clientScopes.slice(0, generatedMainScopes.length);
   const clientGlobalScopes = clientScopes.slice(generatedMainScopes.length);
 
@@ -26189,39 +26227,53 @@ function buildGeneratedBindingList(scope
     }
 
     for (const name of Object.keys(generated.bindings)) {
       const { refs } = generated.bindings[name];
       for (const loc of refs) {
         acc.push({
           name,
           loc,
-          desc: bindings[name] || null
+          desc: () => Promise.resolve(bindings[name] || null)
         });
       }
     }
     return acc;
   }, []);
 
   // Bindings in the global/lexical global of the generated code may or
   // may not be the real global if the generated code is running inside
   // of an evaled context. To handle this, we just look up the client scope
   // hierarchy to find the closest binding with that name.
   for (const generated of generatedGlobalScopes) {
     for (const name of Object.keys(generated.bindings)) {
       const { refs } = generated.bindings[name];
+      const bindings = clientGlobalScopes.find(b => (0, _lodash.has)(b, name));
+
       for (const loc of refs) {
-        const bindings = clientGlobalScopes.find(b => (0, _lodash.has)(b, name));
-
         if (bindings) {
           generatedBindings.push({
             name,
             loc,
-            desc: bindings[name]
+            desc: () => Promise.resolve(bindings[name])
           });
+        } else {
+          const globalGrip = globalScope && globalScope.object;
+          if (globalGrip) {
+            // Should always exist, just checking to keep Flow happy.
+
+            generatedBindings.push({
+              name,
+              loc,
+              desc: async () => {
+                const objectClient = (0, _firefox.createObjectClient)(globalGrip);
+                return (await objectClient.getProperty(name)).descriptor;
+              }
+            });
+          }
         }
       }
     }
   }
 
   // Sort so we can binary-search.
   return generatedBindings.sort((a, b) => {
     const aStart = a.loc.start;
@@ -31362,17 +31414,19 @@ function createPrettySource(sourceId) {
   return async ({ dispatch, getState, sourceMaps }) => {
     const source = (0, _selectors.getSource)(getState(), sourceId);
     const url = (0, _source.getPrettySourceURL)(source.url);
     const id = await sourceMaps.generatedToOriginalId(sourceId, url);
 
     const prettySource = {
       url,
       id,
+      isBlackBoxed: false,
       isPrettyPrinted: true,
+      isWasm: false,
       contentType: "text/javascript",
       loadedState: "loading"
     };
     dispatch({ type: "ADD_SOURCE", source: prettySource });
 
     const { code, mappings } = await (0, _prettyPrint.prettyPrint)({ source, url });
     await sourceMaps.applySourceMap(source.id, url, code, mappings);
 
@@ -31901,18 +31955,17 @@ function selectSourceURL(url, options = 
       // we have a valid { sourceId: string } object. So we are overriding
       // the error
       // $FlowIgnore
       await dispatch(selectLocation(location, options.tabIndex));
     } else {
       dispatch({
         type: "SELECT_SOURCE_URL",
         url: url,
-        tabIndex: options.tabIndex,
-        location: options.location
+        line: options.location ? options.location.line : null
       });
     }
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
@@ -34905,17 +34958,17 @@ async function mapBindingReferenceToDesc
   // Allow the mapping to point anywhere within the generated binding
   // location to allow for less than perfect sourcemaps. Since you also
   // need at least one character between identifiers, we also give one
   // characters of space at the front the generated binding in order
   // to increase the probability of finding the right mapping.
   if (mapped.start.line === binding.loc.start.line && (0, _locColumn.locColumn)(mapped.start) >= (0, _locColumn.locColumn)(binding.loc.start) - 1 && (0, _locColumn.locColumn)(mapped.start) <= (0, _locColumn.locColumn)(binding.loc.end)) {
     return {
       name: binding.name,
-      desc: binding.desc,
+      desc: await binding.desc(),
       expression: binding.name
     };
   }
 
   return null;
 }
 
 /**
@@ -34927,17 +34980,17 @@ async function mapBindingReferenceToDesc
  */
 async function mapImportDeclarationToDescriptor(binding, mapped) {
   // When trying to map an actual import declaration binding, we can try
   // to map it back to the namespace object in the original code.
   if (!mappingContains(mapped, binding.loc)) {
     return null;
   }
 
-  const desc = await readDescriptorProperty(binding.desc, mapped.importName,
+  const desc = await readDescriptorProperty((await binding.desc()), mapped.importName,
   // If the value was optimized out or otherwise unavailable, we skip it
   // entirely because there is a good chance that this means that this
   // isn't the right binding. This allows us to catch cases like
   //
   //   var _mod = require(...);
   //   var _mod2 = _interopRequire(_mod);
   //
   // where "_mod" is optimized out because it is only referenced once, and
@@ -34989,17 +35042,17 @@ async function mapImportReferenceToDescr
   //   ^^^^^^^^^^^^^^^^^
   //   ^                 // wrapped to column 0 of next line
 
   if (!mappingContains(mapped, binding.loc)) {
     return null;
   }
 
   let expression = binding.name;
-  let desc = binding.desc;
+  let desc = await binding.desc();
 
   if (binding.loc.type === "ref") {
     const { meta } = binding.loc;
 
     // Limit to 2 simple property or inherits operartions, since it would
     // just be more work to search more and it is very unlikely that
     // bindings would be mapped to more than a single member + inherits
     // wrapper.
@@ -35057,34 +35110,68 @@ async function readDescriptorProperty(de
     return desc;
   }
 
   const objectClient = (0, _firefox.createObjectClient)(desc.value);
   return (await objectClient.getProperty(property)).descriptor;
 }
 
 function mappingContains(mapped, item) {
-  return (item.start.line > mapped.start.line || item.start.line === mapped.start.line && (0, _locColumn.locColumn)(item.start) >= (0, _locColumn.locColumn)(mapped.start)) && (item.end.line < mapped.end.line || item.end.line === mapped.end.line && (0, _locColumn.locColumn)(item.end) <= (0, _locColumn.locColumn)(mapped.end));
+  return positionCmp(item.start, mapped.start) >= 0 && positionCmp(item.end, mapped.end) <= 0;
+}
+
+/**
+ * * === 0 - Positions are equal.
+ * * < 0 - first position before second position
+ * * > 0 - first position after second position
+ */
+function positionCmp(p1, p2) {
+  if (p1.line === p2.line) {
+    const l1 = (0, _locColumn.locColumn)(p1);
+    const l2 = (0, _locColumn.locColumn)(p2);
+
+    if (l1 === l2) {
+      return 0;
+    }
+    return l1 < l2 ? -1 : 1;
+  }
+
+  return p1.line < p2.line ? -1 : 1;
 }
 
 async function getGeneratedLocationRange(pos, source, sourceMaps) {
-  const start = await sourceMaps.getGeneratedLocation(pos.start, source);
-  const end = await sourceMaps.getGeneratedLocation(pos.end, source);
+  const start = await getGeneratedLocation(sourceMaps, pos.start, source);
+  const end = await getGeneratedLocation(sourceMaps, pos.end, source);
 
   // Since the map takes the closest location, sometimes mapping a
   // binding's location can point at the start of a binding listed after
   // it, so we need to make sure it maps to a location that actually has
   // a size in order to avoid picking up the wrong descriptor.
   if ((0, _lodash.isEqual)(start, end)) {
     return null;
   }
 
   return { start, end };
 }
 
+async function getGeneratedLocation(sourceMaps, pos, source) {
+  const all = await sourceMaps.getAllGeneratedLocations(pos, source);
+  if (all.length > 0) {
+    // Grab the earliest mapping since generally if there are multiple
+    // mappings, the later mappings are for random punctuation marks.
+    return all.reduce((acc, p) => {
+      return !acc || positionCmp(p, acc) < 0 ? p : acc;
+    });
+  }
+
+  // Fall back to the standard logic to take the mapping closest to the
+  // target location.
+  return await sourceMaps.getGeneratedLocation(pos, source);
+}
+
 /***/ }),
 
 /***/ 2359:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -39120,17 +39207,38 @@ function fetchExtra() {
 
 /***/ 363:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\"><path fill=\"black\" id=\"svg_1\" fill-rule=\"evenodd\" d=\"m4.55195,12.97461l7.4,-5l-7.4,-5l0,10zm-0.925,0l0,-10c0,-0.785 0.8,-1.264 1.415,-0.848l7.4,5c0.58,0.392 0.58,1.304 0,1.696l-7.4,5c-0.615,0.416 -1.415,-0.063 -1.415,-0.848z\"></path></svg>"
 
 /***/ }),
 
-/***/ 3630:
+/***/ 3631:
+/***/ (function(module, exports) {
+
+module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 28 28\"><path fill=\"context-fill\" d=\"M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z\"></path></svg>"
+
+/***/ }),
+
+/***/ 3632:
+/***/ (function(module, exports) {
+
+module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"context-fill\" d=\"M14.5 8c-.971 0-1 1-1.75 1a.765.765 0 0 1-.75-.75V5a1 1 0 0 0-1-1H7.75A.765.765 0 0 1 7 3.25c0-.75 1-.779 1-1.75C8 .635 7.1 0 6 0S4 .635 4 1.5c0 .971 1 1 1 1.75a.765.765 0 0 1-.75.75H1a1 1 0 0 0-1 1v2.25A.765.765 0 0 0 .75 8c.75 0 .779-1 1.75-1C3.365 7 4 7.9 4 9s-.635 2-1.5 2c-.971 0-1-1-1.75-1a.765.765 0 0 0-.75.75V15a1 1 0 0 0 1 1h3.25a.765.765 0 0 0 .75-.75c0-.75-1-.779-1-1.75 0-.865.9-1.5 2-1.5s2 .635 2 1.5c0 .971-1 1-1 1.75a.765.765 0 0 0 .75.75H11a1 1 0 0 0 1-1v-3.25a.765.765 0 0 1 .75-.75c.75 0 .779 1 1.75 1 .865 0 1.5-.9 1.5-2s-.635-2-1.5-2z\"></path></svg>"
+
+/***/ }),
+
+/***/ 3633:
+/***/ (function(module, exports) {
+
+module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\"><path fill=\"context-fill\" d=\"M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zM8 3.125A2.7 2.7 0 0 0 5.125 6a.875.875 0 0 0 1.75 0c0-1 .6-1.125 1.125-1.125a1.105 1.105 0 0 1 1.13.744.894.894 0 0 1-.53 1.016A2.738 2.738 0 0 0 7.125 9v.337a.875.875 0 0 0 1.75 0v-.37a1.041 1.041 0 0 1 .609-.824A2.637 2.637 0 0 0 10.82 5.16 2.838 2.838 0 0 0 8 3.125zm0 7.625A1.25 1.25 0 1 0 9.25 12 1.25 1.25 0 0 0 8 10.75z\"></path></svg>"
+
+/***/ }),
+
+/***/ 3634:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -39164,21 +39272,22 @@ function _interopRequireDefault(obj) { r
 function getBreakpointLocation(source, line, column) {
   const isWasm = source && source.isWasm;
   const columnVal = _prefs.features.columnBreakpoints && column ? `:${column}` : "";
   const bpLocation = isWasm ? `0x${line.toString(16).toUpperCase()}` : `${line}${columnVal}`;
 
   return bpLocation;
 }
 
-class BreakpointItem extends _react.Component {
+class Breakpoint extends _react.Component {
 
   componentDidMount() {
     this.setupEditor();
   }
+
   componentDidUpdate() {
     this.setupEditor();
   }
 
   componentWillUnmount() {
     if (this.editor) {
       this.editor.destroy();
     }
@@ -39186,23 +39295,27 @@ class BreakpointItem extends _react.Comp
 
   shouldComponentUpdate(nextProps) {
     const prevBreakpoint = this.props.breakpoint;
     const nextBreakpoint = nextProps.breakpoint;
 
     return !prevBreakpoint || prevBreakpoint.text != nextBreakpoint.text || prevBreakpoint.disabled != nextBreakpoint.disabled || prevBreakpoint.condition != nextBreakpoint.condition || prevBreakpoint.hidden != nextBreakpoint.hidden || prevBreakpoint.isCurrentlyPaused != nextBreakpoint.isCurrentlyPaused;
   }
 
+  getBreakpointText() {
+    const { breakpoint } = this.props;
+    return breakpoint.condition || breakpoint.text;
+  }
+
   setupEditor() {
-    const { breakpoint } = this.props;
     if (this.editor) {
       return;
     }
 
-    this.editor = (0, _breakpoint.createEditor)(breakpoint.text);
+    this.editor = (0, _breakpoint.createEditor)(this.getBreakpointText());
 
     // disables the default search shortcuts
     // $FlowIgnore
     this.editor._initShortcuts = () => {};
 
     const node = _reactDom2.default.findDOMNode(this);
     if (node instanceof HTMLElement) {
       const mountNode = node.querySelector(".breakpoint-label");
@@ -39210,28 +39323,62 @@ class BreakpointItem extends _react.Comp
         // $FlowIgnore
         mountNode.innerHTML = "";
         this.editor.appendToLocalElement(mountNode);
         this.editor.codeMirror.on("mousedown", (_, e) => e.preventDefault());
       }
     }
   }
 
-  render() {
-    const {
-      breakpoint,
-      onClick,
-      onChange,
-      onContextMenu,
-      onCloseClick
-    } = this.props;
+  renderCheckbox() {
+    const { onChange, breakpoint } = this.props;
+    const { disabled } = breakpoint;
+
+    return _react2.default.createElement("input", {
+      type: "checkbox",
+      className: "breakpoint-checkbox",
+      checked: !disabled,
+      onChange: onChange,
+      onClick: ev => ev.stopPropagation()
+    });
+  }
+
+  renderText() {
+    const text = this.getBreakpointText();
+
+    return _react2.default.createElement(
+      "label",
+      { className: "breakpoint-label", title: text },
+      text
+    );
+  }
+
+  renderLineClose() {
+    const { breakpoint, onCloseClick } = this.props;
+    const { line, column } = breakpoint.location;
+
+    return _react2.default.createElement(
+      "div",
+      { className: "breakpoint-line-close" },
+      _react2.default.createElement(
+        "div",
+        { className: "breakpoint-line" },
+        getBreakpointLocation(breakpoint.source, line, column)
+      ),
+      _react2.default.createElement(_Close2.default, {
+        handleClick: onCloseClick,
+        tooltip: L10N.getStr("breakpoints.removeBreakpointTooltip")
+      })
+    );
+  }
+
+  render() {
+    const { breakpoint, onClick, onContextMenu } = this.props;
 
     const locationId = breakpoint.locationId;
-    const line = breakpoint.location.line;
-    const column = breakpoint.location.column;
     const isCurrentlyPaused = breakpoint.isCurrentlyPaused;
     const isDisabled = breakpoint.disabled;
     const isConditional = !!breakpoint.condition;
 
     return _react2.default.createElement(
       "div",
       {
         className: (0, _classnames2.default)({
@@ -39239,53 +39386,24 @@ class BreakpointItem extends _react.Comp
           paused: isCurrentlyPaused,
           disabled: isDisabled,
           "is-conditional": isConditional
         }),
         key: locationId,
         onClick: onClick,
         onContextMenu: onContextMenu
       },
-      _react2.default.createElement("input", {
-        type: "checkbox",
-        className: "breakpoint-checkbox",
-        checked: !isDisabled,
-        onChange: onChange,
-        onClick: ev => ev.stopPropagation()
-      }),
-      _react2.default.createElement(
-        "label",
-        { className: "breakpoint-label", title: breakpoint.text },
-        breakpoint.text
-      ),
-      _react2.default.createElement(
-        "div",
-        { className: "breakpoint-line-close" },
-        _react2.default.createElement(
-          "div",
-          { className: "breakpoint-line" },
-          getBreakpointLocation(breakpoint.source, line, column)
-        ),
-        _react2.default.createElement(_Close2.default, {
-          handleClick: onCloseClick,
-          tooltip: L10N.getStr("breakpoints.removeBreakpointTooltip")
-        })
-      )
-    );
-  }
-}
-
-exports.default = BreakpointItem;
-
-/***/ }),
-
-/***/ 3631:
-/***/ (function(module, exports) {
-
-module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 28 28\"><path fill=\"context-fill\" d=\"M15 11h-1V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v6H1a1 1 0 0 0 0 2h14a1 1 0 1 0 0-2z\"></path></svg>"
+      this.renderCheckbox(),
+      this.renderText(),
+      this.renderLineClose()
+    );
+  }
+}
+
+exports.default = Breakpoint;
 
 /***/ }),
 
 /***/ 364:
 /***/ (function(module, exports) {
 
 module.exports = "<!-- 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/. --><svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" viewBox=\"0 0 33 12\"><path id=\"base-path\" d=\"M27.1,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h26.1 c0.6,0,1.2-0.3,1.5-0.7L33,6l-4.4-5.3C28.2,0.3,27.7,0,27.1,0z\"></path></svg>"
 
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -50,16 +50,23 @@ support-files =
   examples/babel/fixtures/try-catches/output.js
   examples/babel/fixtures/try-catches/output.js.map
   examples/babel/fixtures/webpack-modules/output.js
   examples/babel/fixtures/webpack-modules/output.js.map
   examples/babel/fixtures/webpack-modules-es6/output.js
   examples/babel/fixtures/webpack-modules-es6/output.js.map
   examples/babel/fixtures/webpack-standalone/output.js
   examples/babel/fixtures/webpack-standalone/output.js.map
+  examples/ember/quickstart/dist/index.html
+  examples/ember/quickstart/dist/assets/quickstart.css
+  examples/ember/quickstart/dist/assets/quickstart.js
+  examples/ember/quickstart/dist/assets/quickstart.map
+  examples/ember/quickstart/dist/assets/vendor.css
+  examples/ember/quickstart/dist/assets/vendor.js
+  examples/ember/quickstart/dist/assets/vendor.map
   examples/sourcemaps/bundle.js
   examples/sourcemaps/bundle.js.map
   examples/sourcemaps2/main.min.js
   examples/sourcemaps2/main.js
   examples/sourcemaps2/main.js.map
   examples/sourcemaps3/bundle.js
   examples/sourcemaps3/bundle.js.map
   examples/sourcemaps3/sorted.js
@@ -155,16 +162,17 @@ skip-if = !e10s # This test is only vali
 [browser_dbg-console.js]
 [browser_dbg-console-link.js]
 [browser_dbg-content-script-sources.js]
 skip-if = (os == "win" && ccov) # Bug 1424154
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
+[browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
 [browser_dbg-iframes.js]
 [browser_dbg-inline-cache.js]
 [browser_dbg-keyboard-navigation.js]
 [browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-breakpoint-console.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-breakpoint-console.js
@@ -1,54 +1,16 @@
 
 async function evalInConsoleAtPoint(dbg, fixture, { line, column }, statements) {
-  const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
-
   const filename = `fixtures/${fixture}/input.js`;
-  await waitForSources(dbg, filename);
-
-  ok(true, "Original sources exist");
-  const source = findSource(dbg, filename);
-
-  await selectSource(dbg, source);
-
-  // Test that breakpoint is not off by a line.
-  await addBreakpoint(dbg, source, line);
-
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
-  ok(
-    getBreakpoint(getState(), { sourceId: source.id, line, column }),
-    "Breakpoint has correct line"
-  );
-
   const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
 
-  const invokeResult = invokeInTab(fnName);
-
-  let invokeFailed = await Promise.race([
-    waitForPaused(dbg),
-    invokeResult.then(() => new Promise(() => {}), () => true)
-  ]);
-
-  if (invokeFailed) {
-    return invokeResult;
-  }
-
-  assertPausedLocation(dbg);
-
-  await assertConsoleEval(dbg, statements);
-
-  await removeBreakpoint(dbg, source.id, line, column);
-
-  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
-
-  await resume(dbg);
-
-  // If the invoke errored later somehow, capture here so the error is reported nicely.
-  await invokeResult;
+  await invokeWithBreakpoint(dbg, fnName, filename, { line, column }, async () => {
+    await assertConsoleEval(dbg, statements);
+  });
 
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
 async function assertConsoleEval(dbg, statements) {
   const jsterm = (await dbg.toolbox.selectTool("webconsole")).hud.jsterm;
 
   for (const [index, statement] of statements.entries()) {
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-preview.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-preview.js
@@ -26,60 +26,22 @@ async function assertPreviews(dbg, previ
 
     // Move to column 0 after to make sure that the preview created by this
     // test does not affect later attempts to hover and preview.
     hoverAtPos(dbg, { line: line - 1, ch: 0 });
   }
 }
 
 async function breakpointPreviews(dbg, fixture, { line, column }, previews) {
-  const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
-
   const filename = `fixtures/${fixture}/input.js`;
-  await waitForSources(dbg, filename);
-
-  ok(true, "Original sources exist");
-  const source = findSource(dbg, filename);
-
-  await selectSource(dbg, source);
-
-  // Test that breakpoint is not off by a line.
-  await addBreakpoint(dbg, source, line);
-
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
-  ok(
-    getBreakpoint(getState(), { sourceId: source.id, line, column }),
-    "Breakpoint has correct line"
-  );
-
   const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
 
-  const invokeResult = invokeInTab(fnName);
-
-  let invokeFailed = await Promise.race([
-    waitForPaused(dbg),
-    invokeResult.then(() => new Promise(() => {}), () => true)
-  ]);
-
-  if (invokeFailed) {
-    return invokeResult;
-  }
-
-  assertPausedLocation(dbg);
-
-  await assertPreviews(dbg, previews);
-
-  await removeBreakpoint(dbg, source.id, line, column);
-
-  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
-
-  await resume(dbg);
-
-  // If the invoke errored later somehow, capture here so the error is reported nicely.
-  await invokeResult;
+  await invokeWithBreakpoint(dbg, fnName, filename, { line, column }, async () => {
+    await assertPreviews(dbg, previews);
+  });
 
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
 add_task(async function() {
   await pushPref("devtools.debugger.features.map-scopes", true);
 
   const dbg = await initDebugger("doc-babel.html");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-scopes.js
@@ -2,97 +2,26 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // This test can be really slow on debug platforms and should be split.
 requestLongerTimeout(6);
 
 // Tests loading sourcemapped sources for Babel's compile output.
 
 async function breakpointScopes(dbg, fixture, { line, column }, scopes) {
-  const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
-
   const filename = `fixtures/${fixture}/input.js`;
-  await waitForSources(dbg, filename);
-
-  ok(true, "Original sources exist");
-  const source = findSource(dbg, filename);
-
-  await selectSource(dbg, source);
-
-  // Test that breakpoint is not off by a line.
-  await addBreakpoint(dbg, source, line);
-
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
-  ok(
-    getBreakpoint(getState(), { sourceId: source.id, line, column }),
-    "Breakpoint has correct line"
-  );
-
   const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
 
-  const invokeResult = invokeInTab(fnName);
-
-  let invokeFailed = await Promise.race([
-    waitForPaused(dbg),
-    invokeResult.then(() => new Promise(() => {}), () => true)
-  ]);
-
-  if (invokeFailed) {
-    return invokeResult;
-  }
-
-  assertPausedLocation(dbg);
-
-  await assertScopes(dbg, scopes);
-
-  await removeBreakpoint(dbg, source.id, line, column);
-
-  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
-
-  await resume(dbg);
-
-  // If the invoke errored later somehow, capture here so the error is reported nicely.
-  await invokeResult;
+  await invokeWithBreakpoint(dbg, fnName, filename, { line, column }, async () => {
+    await assertScopes(dbg, scopes);
+  });
 
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
-async function expandAllScopes(dbg) {
-  const scopes = await waitForElement(dbg, "scopes");
-  const scopeElements = scopes.querySelectorAll(
-    `.tree-node[aria-level="1"][data-expandable="true"]:not([aria-expanded="true"])`
-  );
-  const indices = Array.from(scopeElements, el => {
-    return Array.prototype.indexOf.call(el.parentNode.childNodes, el);
-  }).reverse();
-
-  for (const index of indices) {
-    await toggleScopeNode(dbg, index + 1);
-  }
-}
-
-async function assertScopes(dbg, items) {
-  await expandAllScopes(dbg);
-
-  for (const [i, val] of items.entries()) {
-    if (Array.isArray(val)) {
-      is(getScopeLabel(dbg, i + 1), val[0]);
-      is(
-        getScopeValue(dbg, i + 1),
-        val[1],
-        `"${val[0]}" has the expected "${val[1]}" value`
-      );
-    } else {
-      is(getScopeLabel(dbg, i + 1), val);
-    }
-  }
-
-  is(getScopeLabel(dbg, items.length + 1), "Window");
-}
-
 add_task(async function() {
   await pushPref("devtools.debugger.features.map-scopes", true);
 
   const dbg = await initDebugger("doc-babel.html");
 
   await breakpointScopes(dbg, "eval-source-maps", { line: 14, column: 4 }, [
     "Block",
     ["three", "5"],
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-stepping.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-babel-stepping.js
@@ -1,61 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests for stepping through Babel's compile output.
 requestLongerTimeout(4);
 
 async function breakpointSteps(dbg, fixture, { line, column }, steps) {
-  const { selectors: { getBreakpoint, getBreakpoints }, getState } = dbg;
-
   const filename = `fixtures/${fixture}/input.js`;
-  await waitForSources(dbg, filename);
-
-  ok(true, "Original sources exist");
-  const source = findSource(dbg, filename);
-
-  await selectSource(dbg, source);
-
-  // Test that breakpoint is not off by a line.
-  await addBreakpoint(dbg, source, line);
-
-  is(getBreakpoints(getState()).size, 1, "One breakpoint exists");
-
-  ok(
-    getBreakpoint(getState(), { sourceId: source.id, line, column }),
-    "Breakpoint has correct line"
-  );
-
   const fnName = fixture.replace(/-([a-z])/g, (s, c) => c.toUpperCase());
 
-  const invokeResult = invokeInTab(fnName);
-
-  let invokeFailed = await Promise.race([
-    waitForPaused(dbg),
-    invokeResult.then(() => new Promise(() => {}), () => true)
-  ]);
-
-  if (invokeFailed) {
-    return invokeResult;
-  }
-
-  assertPausedLocation(dbg);
-
-  await removeBreakpoint(dbg, source.id, line, column);
-
-  is(getBreakpoints(getState()).size, 0, "Breakpoint reverted");
-
-  await runSteps(dbg, source, steps);
-
-  await resume(dbg);
-
-  // If the invoke errored later somehow, capture here so the error is
-  // reported nicely.
-  await invokeResult;
+  await invokeWithBreakpoint(dbg, fnName, filename, { line, column }, async source => {
+    await runSteps(dbg, source, steps);
+  });
 
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
 async function runSteps(dbg, source, steps) {
   const { selectors: { getVisibleSelectedFrame }, getState } = dbg;
 
   for (const [i, [type, position]] of steps.entries()) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-ember-quickstart.js
@@ -0,0 +1,15 @@
+
+add_task(async function() {
+  await pushPref("devtools.debugger.features.map-scopes", true);
+
+  const dbg = await initDebugger("ember/quickstart/dist/");
+
+  await invokeWithBreakpoint(dbg, "mapTestFunction", "quickstart/router.js", { line: 13, column: 2 }, async () => {
+    await assertScopes(dbg, [
+      "Module",
+      ["config", "{\u2026}"],
+      "EmberRouter:Class()",
+      "Router:Class()",
+    ]);
+  });
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js
@@ -5,28 +5,30 @@
  * test pausing on an errored watch expression
  * assert that you can:
  * 1. resume
  * 2. still evalutate expressions
  * 3. expand properties
  */
 
 const expressionSelectors = {
+  plusIcon: ".watch-expressions-pane button.plus",
   input: "input.input-expression"
 };
 
 function getLabel(dbg, index) {
   return findElement(dbg, "expressionNode", index).innerText;
 }
 
 function getValue(dbg, index) {
   return findElement(dbg, "expressionValue", index).innerText;
 }
 
 async function addExpression(dbg, input) {
+  findElementWithSelector(dbg, expressionSelectors.plusIcon).click();
   const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION");
   findElementWithSelector(dbg, expressionSelectors.input).focus();
   type(dbg, input);
   pressKey(dbg, "Enter");
   await evaluation;
 }
 
 add_task(async function() {
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js
@@ -5,16 +5,17 @@
  * tests the watch expressions component
  * 1. add watch expressions
  * 2. edit watch expressions
  * 3. delete watch expressions
  * 4. expanding properties when not paused
  */
 
 const expressionSelectors = {
+  plusIcon: ".watch-expressions-pane button.plus",
   input: "input.input-expression"
 };
 
 function getLabel(dbg, index) {
   return findElement(dbg, "expressionNode", index).innerText;
 }
 
 function getValue(dbg, index) {
@@ -28,16 +29,17 @@ function assertEmptyValue(dbg, index) {
     return;
   }
 
   is(value, null);
 }
 
 async function addExpression(dbg, input) {
   info("Adding an expression");
+  findElementWithSelector(dbg, expressionSelectors.plusIcon).click();
   findElementWithSelector(dbg, expressionSelectors.input).focus();
   type(dbg, input);
   pressKey(dbg, "Enter");
 
   await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
 }
 
 async function editExpression(dbg, input) {
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.editorconfig
@@ -0,0 +1,20 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+insert_final_newline = false
+
+[*.{diff,md}]
+trim_trailing_whitespace = false
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.ember-cli
@@ -0,0 +1,9 @@
+{
+  /**
+    Ember CLI sends analytics information by default. The data is completely
+    anonymous, but there are times when you might want to disable this behavior.
+
+    Setting `disableAnalytics` to true will prevent any data from being sent.
+  */
+  "disableAnalytics": false
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.eslintrc.js
@@ -0,0 +1,38 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    ecmaVersion: 2017,
+    sourceType: 'module'
+  },
+  plugins: [
+    'ember'
+  ],
+  extends: [
+    'eslint:recommended',
+    'plugin:ember/recommended'
+  ],
+  env: {
+    browser: true
+  },
+  rules: {
+  },
+  overrides: [
+    // node files
+    {
+      files: [
+        'testem.js',
+        'ember-cli-build.js',
+        'config/**/*.js',
+        'lib/*/index.js'
+      ],
+      parserOptions: {
+        sourceType: 'script',
+        ecmaVersion: 2015
+      },
+      env: {
+        browser: false,
+        node: true
+      }
+    }
+  ]
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.gitignore
@@ -0,0 +1,23 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+# /dist # not ignored because we want to load the static files
+/tmp
+
+# dependencies
+/node_modules
+/bower_components
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log*
+yarn-error.log
+testem.log
+
+# ember-try
+.node_modules.ember-try/
+bower.json.ember-try
+package.json.ember-try
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.travis.yml
@@ -0,0 +1,26 @@
+---
+language: node_js
+node_js:
+  - "6"
+
+sudo: false
+dist: trusty
+
+addons:
+  chrome: stable
+
+cache:
+  directories:
+    - $HOME/.npm
+
+env:
+  global:
+    # See https://git.io/vdao3 for details.
+    - JOBS=1
+
+before_install:
+  - npm config set spin false
+
+script:
+  - npm run lint:js
+  - npm test
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/.watchmanconfig
@@ -0,0 +1,3 @@
+{
+  "ignore_dirs": ["tmp", "dist"]
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/README.md
@@ -0,0 +1,4 @@
+
+### Building
+
+* `ember build`
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/app/app.js
@@ -0,0 +1,14 @@
+import Application from '@ember/application';
+import Resolver from './resolver';
+import loadInitializers from 'ember-load-initializers';
+import config from './config/environment';
+
+const App = Application.extend({
+  modulePrefix: config.modulePrefix,
+  podModulePrefix: config.podModulePrefix,
+  Resolver
+});
+
+loadInitializers(App, config.modulePrefix);
+
+export default App;
new file mode 100644
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/app/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title>Quickstart</title>
+    <meta name="description" content="">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    {{content-for "head"}}
+
+    <link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
+    <link integrity="" rel="stylesheet" href="{{rootURL}}assets/quickstart.css">
+
+    {{content-for "head-footer"}}
+  </head>
+  <body>
+    {{content-for "body"}}
+
+    <script src="{{rootURL}}assets/vendor.js"></script>
+    <script src="{{rootURL}}assets/quickstart.js"></script>
+
+    {{content-for "body-footer"}}
+  </body>
+</html>
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/app/resolver.js
@@ -0,0 +1,3 @@
+import Resolver from 'ember-resolver';
+
+export default Resolver;
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/app/router.js
@@ -0,0 +1,16 @@
+import EmberRouter from '@ember/routing/router';
+import config from './config/environment';
+
+const Router = EmberRouter.extend({
+  location: config.locationType,
+  rootURL: config.rootURL
+});
+
+Router.map(function() {
+});
+
+window.mapTestFunction = () => {
+  window.console.log("pause here", config, Router);
+};
+
+export default Router;
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/app/templates/application.hbs
@@ -0,0 +1,5 @@
+{{!-- The following component displays Ember's default welcome message. --}}
+{{welcome-page}}
+{{!-- Feel free to remove this! --}}
+
+{{outlet}}
\ No newline at end of file
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/config/environment.js
@@ -0,0 +1,58 @@
+'use strict';
+
+module.exports = function(environment) {
+  let ENV = {
+    modulePrefix: 'quickstart',
+    environment,
+    locationType: 'auto',
+    EmberENV: {
+      FEATURES: {
+        // Here you can enable experimental features on an ember canary build
+        // e.g. 'with-controller': true
+      },
+      EXTEND_PROTOTYPES: {
+        // Prevent Ember Data from overriding Date.parse.
+        Date: false
+      }
+    },
+
+    APP: {
+      // Here you can pass flags/options to your application instance
+      // when it is created
+    },
+
+    // NOTE(logan): Hard-code the URL for the debugger example to allow it to
+    // function properly. The default "/" root makes assets fail to load.
+    rootURL: "/browser/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/dist/",
+
+    // If using "http://localhost:8000/integration/examples/ember/quickstart/dist/" to
+    // access this test example, uncomment this line and re-run "yarn build".
+    // rootURL: "/integration/examples/ember/quickstart/dist/",
+  };
+
+  if (environment === 'development') {
+    // ENV.APP.LOG_RESOLVER = true;
+    // ENV.APP.LOG_ACTIVE_GENERATION = true;
+    // ENV.APP.LOG_TRANSITIONS = true;
+    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
+    // ENV.APP.LOG_VIEW_LOOKUPS = true;
+  }
+
+  if (environment === 'test') {
+    // Testem prefers this...
+    ENV.locationType = 'none';
+
+    // keep test console output quieter
+    ENV.APP.LOG_ACTIVE_GENERATION = false;
+    ENV.APP.LOG_VIEW_LOOKUPS = false;
+
+    ENV.APP.rootElement = '#ember-testing';
+    ENV.APP.autoboot = false;
+  }
+
+  if (environment === 'production') {
+    // here you can enable a production-specific feature
+  }
+
+  return ENV;
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/config/targets.js
@@ -0,0 +1,18 @@
+'use strict';
+
+const browsers = [
+  'last 1 Chrome versions',
+  'last 1 Firefox versions',
+  'last 1 Safari versions'
+];
+
+const isCI = !!process.env.CI;
+const isProduction = process.env.EMBER_ENV === 'production';
+
+if (isCI || isProduction) {
+  browsers.push('ie 11');
+}
+
+module.exports = {
+  browsers
+};
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/dist/assets/quickstart.js
@@ -0,0 +1,277 @@
+"use strict";
+
+
+
+define('quickstart/app', ['exports', 'quickstart/resolver', 'ember-load-initializers', 'quickstart/config/environment'], function (exports, _resolver, _emberLoadInitializers, _environment) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+
+
+  const App = Ember.Application.extend({
+    modulePrefix: _environment.default.modulePrefix,
+    podModulePrefix: _environment.default.podModulePrefix,
+    Resolver: _resolver.default
+  });
+
+  (0, _emberLoadInitializers.default)(App, _environment.default.modulePrefix);
+
+  exports.default = App;
+});
+
+define('quickstart/components/welcome-page', ['exports', 'ember-welcome-page/components/welcome-page'], function (exports, _welcomePage) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  Object.defineProperty(exports, 'default', {
+    enumerable: true,
+    get: function () {
+      return _welcomePage.default;
+    }
+  });
+});
+
+define('quickstart/helpers/app-version', ['exports', 'quickstart/config/environment', 'ember-cli-app-version/utils/regexp'], function (exports, _environment, _regexp) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.appVersion = appVersion;
+
+
+  const {
+    APP: {
+      version
+    }
+  } = _environment.default;
+
+  function appVersion(_, hash = {}) {
+    if (hash.hideSha) {
+      return version.match(_regexp.versionRegExp)[0];
+    }
+
+    if (hash.hideVersion) {
+      return version.match(_regexp.shaRegExp)[0];
+    }
+
+    return version;
+  }
+
+  exports.default = Ember.Helper.helper(appVersion);
+});
+
+define('quickstart/helpers/pluralize', ['exports', 'ember-inflector/lib/helpers/pluralize'], function (exports, _pluralize) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = _pluralize.default;
+});
+
+define('quickstart/helpers/singularize', ['exports', 'ember-inflector/lib/helpers/singularize'], function (exports, _singularize) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = _singularize.default;
+});
+
+define('quickstart/initializers/app-version', ['exports', 'ember-cli-app-version/initializer-factory', 'quickstart/config/environment'], function (exports, _initializerFactory, _environment) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+
+
+  let name, version;
+  if (_environment.default.APP) {
+    name = _environment.default.APP.name;
+    version = _environment.default.APP.version;
+  }
+
+  exports.default = {
+    name: 'App Version',
+    initialize: (0, _initializerFactory.default)(name, version)
+  };
+});
+
+define('quickstart/initializers/container-debug-adapter', ['exports', 'ember-resolver/resolvers/classic/container-debug-adapter'], function (exports, _containerDebugAdapter) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = {
+    name: 'container-debug-adapter',
+
+    initialize() {
+      let app = arguments[1] || arguments[0];
+
+      app.register('container-debug-adapter:main', _containerDebugAdapter.default);
+      app.inject('container-debug-adapter:main', 'namespace', 'application:main');
+    }
+  };
+});
+
+define('quickstart/initializers/ember-data', ['exports', 'ember-data/setup-container', 'ember-data'], function (exports, _setupContainer) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = {
+    name: 'ember-data',
+    initialize: _setupContainer.default
+  };
+});
+
+define('quickstart/initializers/export-application-global', ['exports', 'quickstart/config/environment'], function (exports, _environment) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.initialize = initialize;
+  function initialize() {
+    var application = arguments[1] || arguments[0];
+    if (_environment.default.exportApplicationGlobal !== false) {
+      var theGlobal;
+      if (typeof window !== 'undefined') {
+        theGlobal = window;
+      } else if (typeof global !== 'undefined') {
+        theGlobal = global;
+      } else if (typeof self !== 'undefined') {
+        theGlobal = self;
+      } else {
+        // no reasonable global, just bail
+        return;
+      }
+
+      var value = _environment.default.exportApplicationGlobal;
+      var globalName;
+
+      if (typeof value === 'string') {
+        globalName = value;
+      } else {
+        globalName = Ember.String.classify(_environment.default.modulePrefix);
+      }
+
+      if (!theGlobal[globalName]) {
+        theGlobal[globalName] = application;
+
+        application.reopen({
+          willDestroy: function () {
+            this._super.apply(this, arguments);
+            delete theGlobal[globalName];
+          }
+        });
+      }
+    }
+  }
+
+  exports.default = {
+    name: 'export-application-global',
+
+    initialize: initialize
+  };
+});
+
+define("quickstart/instance-initializers/ember-data", ["exports", "ember-data/initialize-store-service"], function (exports, _initializeStoreService) {
+  "use strict";
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = {
+    name: "ember-data",
+    initialize: _initializeStoreService.default
+  };
+});
+
+define('quickstart/resolver', ['exports', 'ember-resolver'], function (exports, _emberResolver) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = _emberResolver.default;
+});
+
+define('quickstart/router', ['exports', 'quickstart/config/environment'], function (exports, _environment) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+
+
+  const Router = Ember.Router.extend({
+    location: _environment.default.locationType,
+    rootURL: _environment.default.rootURL
+  });
+
+  Router.map(function () {});
+
+  window.mapTestFunction = () => {
+    window.console.log("pause here", _environment.default, Router);
+  };
+
+  exports.default = Router;
+});
+
+define('quickstart/services/ajax', ['exports', 'ember-ajax/services/ajax'], function (exports, _ajax) {
+  'use strict';
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  Object.defineProperty(exports, 'default', {
+    enumerable: true,
+    get: function () {
+      return _ajax.default;
+    }
+  });
+});
+
+define("quickstart/templates/application", ["exports"], function (exports) {
+  "use strict";
+
+  Object.defineProperty(exports, "__esModule", {
+    value: true
+  });
+  exports.default = Ember.HTMLBars.template({ "id": "d4rGKf2G", "block": "{\"symbols\":[],\"statements\":[[1,[18,\"welcome-page\"],false],[0,\"\\n\"],[0,\"\\n\"],[1,[18,\"outlet\"],false]],\"hasEval\":false}", "meta": { "moduleName": "quickstart/templates/application.hbs" } });
+});
+
+
+
+define('quickstart/config/environment', [], function() {
+  var prefix = 'quickstart';
+try {
+  var metaName = prefix + '/config/environment';
+  var rawConfig = document.querySelector('meta[name="' + metaName + '"]').getAttribute('content');
+  var config = JSON.parse(unescape(rawConfig));
+
+  var exports = { 'default': config };
+
+  Object.defineProperty(exports, '__esModule', { value: true });
+
+  return exports;
+}
+catch(err) {
+  throw new Error('Could not read config from meta tag with name "' + metaName + '".');
+}
+
+});
+
+if (!runningTests) {
+  require("quickstart/app")["default"].create({"name":"quickstart","version":"0.0.0+1dde1d7f"});
+}
+//# sourceMappingURL=quickstart.map
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/dist/assets/quickstart.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["vendor/ember-cli/app-prefix.js","quickstart/app.js","quickstart/components/welcome-page.js","quickstart/helpers/app-version.js","quickstart/helpers/pluralize.js","quickstart/helpers/singularize.js","quickstart/initializers/app-version.js","quickstart/initializers/container-debug-adapter.js","quickstart/initializers/ember-data.js","quickstart/initializers/export-application-global.js","quickstart/instance-initializers/ember-data.js","quickstart/resolver.js","quickstart/router.js","quickstart/services/ajax.js","quickstart/templates/application.js","vendor/ember-cli/app-suffix.js","vendor/ember-cli/app-config.js","vendor/ember-cli/app-boot.js"],"sourcesContent":["\"use strict\";\n\n\n","import Application from '@ember/application';\nimport Resolver from './resolver';\nimport loadInitializers from 'ember-load-initializers';\nimport config from './config/environment';\n\nconst App = Application.extend({\n  modulePrefix: config.modulePrefix,\n  podModulePrefix: config.podModulePrefix,\n  Resolver\n});\n\nloadInitializers(App, config.modulePrefix);\n\nexport default App;\n","export { default } from 'ember-welcome-page/components/welcome-page';","import Ember from 'ember';\nimport config from '../config/environment';\nimport { shaRegExp, versionRegExp } from 'ember-cli-app-version/utils/regexp';\n\nconst {\n  APP: {\n    version\n  }\n} = config;\n\nexport function appVersion(_, hash = {}) {\n  if (hash.hideSha) {\n    return version.match(versionRegExp)[0];\n  }\n\n  if (hash.hideVersion) {\n    return version.match(shaRegExp)[0];\n  }\n\n  return version;\n}\n\nexport default Ember.Helper.helper(appVersion);\n","define('quickstart/helpers/pluralize', ['exports', 'ember-inflector/lib/helpers/pluralize'], function (exports, _pluralize) {\n  'use strict';\n\n  Object.defineProperty(exports, \"__esModule\", {\n    value: true\n  });\n  exports.default = _pluralize.default;\n});\n","define('quickstart/helpers/singularize', ['exports', 'ember-inflector/lib/helpers/singularize'], function (exports, _singularize) {\n  'use strict';\n\n  Object.defineProperty(exports, \"__esModule\", {\n    value: true\n  });\n  exports.default = _singularize.default;\n});\n","import initializerFactory from 'ember-cli-app-version/initializer-factory';\nimport config from '../config/environment';\n\nlet name, version;\nif (config.APP) {\n  name = config.APP.name;\n  version = config.APP.version;\n}\n\nexport default {\n  name: 'App Version',\n  initialize: initializerFactory(name, version)\n};\n","import ContainerDebugAdapter from 'ember-resolver/resolvers/classic/container-debug-adapter';\n\nexport default {\n  name: 'container-debug-adapter',\n\n  initialize() {\n    let app = arguments[1] || arguments[0];\n\n    app.register('container-debug-adapter:main', ContainerDebugAdapter);\n    app.inject('container-debug-adapter:main', 'namespace', 'application:main');\n  }\n};\n","import setupContainer from 'ember-data/setup-container';\nimport 'ember-data';\n\n/*\n\n  This code initializes Ember-Data onto an Ember application.\n\n  If an Ember.js developer defines a subclass of DS.Store on their application,\n  as `App.StoreService` (or via a module system that resolves to `service:store`)\n  this code will automatically instantiate it and make it available on the\n  router.\n\n  Additionally, after an application's controllers have been injected, they will\n  each have the store made available to them.\n\n  For example, imagine an Ember.js application with the following classes:\n\n  ```app/services/store.js\n  import DS from 'ember-data';\n\n  export default DS.Store.extend({\n    adapter: 'custom'\n  });\n  ```\n\n  ```app/controllers/posts.js\n  import { Controller } from '@ember/controller';\n\n  export default Controller.extend({\n    // ...\n  });\n\n  When the application is initialized, `ApplicationStore` will automatically be\n  instantiated, and the instance of `PostsController` will have its `store`\n  property set to that instance.\n\n  Note that this code will only be run if the `ember-application` package is\n  loaded. If Ember Data is being used in an environment other than a\n  typical application (e.g., node.js where only `ember-runtime` is available),\n  this code will be ignored.\n*/\n\nexport default {\n  name: 'ember-data',\n  initialize: setupContainer\n};\n","import Ember from 'ember';\nimport config from '../config/environment';\n\nexport function initialize() {\n  var application = arguments[1] || arguments[0];\n  if (config.exportApplicationGlobal !== false) {\n    var theGlobal;\n    if (typeof window !== 'undefined') {\n        theGlobal = window;\n    } else if (typeof global !== 'undefined') {\n        theGlobal = global\n    } else if (typeof self !== 'undefined') {\n        theGlobal = self;\n    } else {\n       // no reasonable global, just bail\n       return;\n    }\n\n    var value = config.exportApplicationGlobal;\n    var globalName;\n\n    if (typeof value === 'string') {\n      globalName = value;\n    } else {\n      globalName = Ember.String.classify(config.modulePrefix);\n    }\n\n    if (!theGlobal[globalName]) {\n      theGlobal[globalName] = application;\n\n      application.reopen({\n        willDestroy: function() {\n          this._super.apply(this, arguments);\n          delete theGlobal[globalName];\n        }\n      });\n    }\n  }\n}\n\nexport default {\n  name: 'export-application-global',\n\n  initialize: initialize\n};\n","import initializeStoreService from 'ember-data/initialize-store-service';\n\nexport default {\n  name: \"ember-data\",\n  initialize: initializeStoreService\n};\n","define('quickstart/resolver', ['exports', 'ember-resolver'], function (exports, _emberResolver) {\n  'use strict';\n\n  Object.defineProperty(exports, \"__esModule\", {\n    value: true\n  });\n  exports.default = _emberResolver.default;\n});\n","import EmberRouter from '@ember/routing/router';\nimport config from './config/environment';\n\nconst Router = EmberRouter.extend({\n  location: config.locationType,\n  rootURL: config.rootURL\n});\n\nRouter.map(function() {\n});\n\nwindow.mapTestFunction = () => {\n  window.console.log(\"pause here\", config, Router);\n};\n\nexport default Router;\n","export { default } from 'ember-ajax/services/ajax';\n","export default Ember.HTMLBars.template({\"id\":\"d4rGKf2G\",\"block\":\"{\\\"symbols\\\":[],\\\"statements\\\":[[1,[18,\\\"welcome-page\\\"],false],[0,\\\"\\\\n\\\"],[0,\\\"\\\\n\\\"],[1,[18,\\\"outlet\\\"],false]],\\\"hasEval\\\":false}\",\"meta\":{\"moduleName\":\"quickstart/templates/application.hbs\"}});","\n","define('quickstart/config/environment', [], function() {\n  var prefix = 'quickstart';\ntry {\n  var metaName = prefix + '/config/environment';\n  var rawConfig = document.querySelector('meta[name=\"' + metaName + '\"]').getAttribute('content');\n  var config = JSON.parse(unescape(rawConfig));\n\n  var exports = { 'default': config };\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n  return exports;\n}\ncatch(err) {\n  throw new Error('Could not read config from meta tag with name \"' + metaName + '\".');\n}\n\n});\n","if (!runningTests) {\n  require(\"quickstart/app\")[\"default\"].create({\"name\":\"quickstart\",\"version\":\"0.0.0+1dde1d7f\"});\n}\n"],"names":["App","Application","extend","modulePrefix","podModulePrefix","Resolver","default","appVersion","APP","version","_","hash","hideSha","match","hideVersion","Ember","Helper","helper","name","version","APP","initialize","name","initialize","app","arguments","register","inject","name","initialize","initialize","application","arguments","exportApplicationGlobal","theGlobal","window","global","self","value","globalName","Ember","String","classify","modulePrefix","reopen","willDestroy","_super","apply","name","name","initialize","Router","EmberRouter","extend","location","locationType","rootURL","map","window","mapTestFunction","console","log","default","Ember","HTMLBars","template"],"mappings":"AAAA;AACA;AACA;;;;;;;;;;ACGA,QAAMA,MAAMC,kBAAYC,MAAZ,CAAmB;AAC7BC,kBAAc,qBAAOA,YADQ;AAE7BC,qBAAiB,qBAAOA,eAFK;AAG7BC;AAH6B,GAAnB,CAAZ;;AAMA,sCAAiBL,GAAjB,EAAsB,qBAAOG,YAA7B;;oBAEeH,G;;;;;;;;;;;;0BCbNM,O;;;;;;;;;;;UCUOC,U,GAAAA,U;;;AANhB,QAAM;AACJC,SAAK;AACHC;AADG;AADD,0BAAN;;AAMO,WAASF,UAAT,CAAoBG,CAApB,EAAuBC,OAAO,EAA9B,EAAkC;AACvC,QAAIA,KAAKC,OAAT,EAAkB;AAChB,aAAOH,QAAQI,KAAR,wBAA6B,CAA7B,CAAP;AACD;;AAED,QAAIF,KAAKG,WAAT,EAAsB;AACpB,aAAOL,QAAQI,KAAR,oBAAyB,CAAzB,CAAP;AACD;;AAED,WAAOJ,OAAP;AACD;;oBAEcM,MAAMC,MAAN,CAAaC,MAAb,CAAoBV,UAApB,C;;;ACtBf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;ACJA,MAAIW,IAAJ,EAAUC,OAAV;AACA,MAAI,qBAAOC,GAAX,EAAgB;AACdF,WAAO,qBAAOE,GAAP,CAAWF,IAAlB;AACAC,cAAU,qBAAOC,GAAP,CAAWD,OAArB;AACD;;oBAEc;AACbD,UAAM,aADO;AAEbG,gBAAY,iCAAmBH,IAAnB,EAAyBC,OAAzB;AAFC,G;;;;;;;;;oBCPA;AACbG,UAAM,yBADO;;AAGbC,iBAAa;AACX,UAAIC,MAAMC,UAAU,CAAV,KAAgBA,UAAU,CAAV,CAA1B;;AAEAD,UAAIE,QAAJ,CAAa,8BAAb;AACAF,UAAIG,MAAJ,CAAW,8BAAX,EAA2C,WAA3C,EAAwD,kBAAxD;AACD;AARY,G;;;;;;;;;oBCwCA;AACbC,UAAM,YADO;AAEbC;AAFa,G;;;;;;;;;UCvCCC,U,GAAAA,U;AAAT,WAASA,UAAT,GAAsB;AAC3B,QAAIC,cAAcC,UAAU,CAAV,KAAgBA,UAAU,CAAV,CAAlC;AACA,QAAI,qBAAOC,uBAAP,KAAmC,KAAvC,EAA8C;AAC5C,UAAIC,SAAJ;AACA,UAAI,OAAOC,MAAP,KAAkB,WAAtB,EAAmC;AAC/BD,oBAAYC,MAAZ;AACH,OAFD,MAEO,IAAI,OAAOC,MAAP,KAAkB,WAAtB,EAAmC;AACtCF,oBAAYE,MAAZ;AACH,OAFM,MAEA,IAAI,OAAOC,IAAP,KAAgB,WAApB,EAAiC;AACpCH,oBAAYG,IAAZ;AACH,OAFM,MAEA;AACJ;AACA;AACF;;AAED,UAAIC,QAAQ,qBAAOL,uBAAnB;AACA,UAAIM,UAAJ;;AAEA,UAAI,OAAOD,KAAP,KAAiB,QAArB,EAA+B;AAC7BC,qBAAaD,KAAb;AACD,OAFD,MAEO;AACLC,qBAAaC,MAAMC,MAAN,CAAaC,QAAb,CAAsB,qBAAOC,YAA7B,CAAb;AACD;;AAED,UAAI,CAACT,UAAUK,UAAV,CAAL,EAA4B;AAC1BL,kBAAUK,UAAV,IAAwBR,WAAxB;;AAEAA,oBAAYa,MAAZ,CAAmB;AACjBC,uBAAa,YAAW;AACtB,iBAAKC,MAAL,CAAYC,KAAZ,CAAkB,IAAlB,EAAwBf,SAAxB;AACA,mBAAOE,UAAUK,UAAV,CAAP;AACD;AAJgB,SAAnB;AAMD;AACF;AACF;;oBAEc;AACbS,UAAM,2BADO;;AAGblB,gBAAYA;AAHC,G;;;;;;;;;oBCtCA;AACbmB,UAAM,YADO;AAEbC;AAFa,G;;;ACFf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;ACJA,QAAMC,SAASC,aAAYC,MAAZ,CAAmB;AAChCC,cAAU,qBAAOC,YADe;AAEhCC,aAAS,qBAAOA;AAFgB,GAAnB,CAAf;;AAKAL,SAAOM,GAAP,CAAW,YAAW,CACrB,CADD;;AAGAC,SAAOC,eAAP,GAAyB,MAAM;AAC7BD,WAAOE,OAAP,CAAeC,GAAf,CAAmB,YAAnB,wBAAyCV,MAAzC;AACD,GAFD;;oBAIeA,M;;;;;;;;;;;;mBCfNW,O;;;;;;;;;;;oBCAMC,MAAMC,QAAN,CAAeC,QAAf,CAAwB,EAAC,MAAK,UAAN,EAAiB,SAAQ,uIAAzB,EAAiK,QAAO,EAAC,cAAa,sCAAd,EAAxK,EAAxB,C;;;ACAf;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACjBA;AACA;AACA;","file":"quickstart.js"}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/dist/assets/test-support.css
@@ -0,0 +1,471 @@
+/*!
+ * QUnit 2.5.1
+ * https://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2018-02-28T01:37Z
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
+	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+	margin: 0;
+	padding: 0;
+}
+
+
+/** Header (excluding toolbar) */
+
+#qunit-header {
+	padding: 0.5em 0 0.5em 1em;
+
+	color: #8699A4;
+	background-color: #0D3349;
+
+	font-size: 1.5em;
+	line-height: 1em;
+	font-weight: 400;
+
+	border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+	text-decoration: none;
+	color: #C2CCD1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+	color: #FFF;
+}
+
+#qunit-banner {
+	height: 5px;
+}
+
+#qunit-filteredTest {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #366097;
+	background-color: #F4FF77;
+}
+
+#qunit-userAgent {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #FFF;
+	background-color: #2B81AF;
+	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #5E740B;
+	background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+	height: 0;
+	clear: both;
+}
+
+#qunit-testrunner-toolbar label {
+	display: inline-block;
+}
+
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+	margin: 3px;
+	vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+	box-sizing: border-box;
+	height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+	display: inline-block;
+	line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
+	float: right;
+	position: relative;
+	margin-left: 1em;
+}
+
+.qunit-url-config label {
+	margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+	box-sizing: border-box;
+	width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+	position: absolute;
+	right: 0.3em;
+	content: "\25bc";
+	color: black;
+}
+
+#qunit-modulefilter-dropdown {
+	/* align with #qunit-modulefilter-search */
+	box-sizing: border-box;
+	width: 400px;
+	position: absolute;
+	right: 0;
+	top: 50%;
+	margin-top: 0.8em;
+
+	border: 1px solid #D3D3D3;
+	border-top: none;
+	border-radius: 0 0 .25em .25em;
+	color: #000;
+	background-color: #F5F5F5;
+	z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+	color: inherit;
+	text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+	font-weight: bold;
+	color: #000;
+	background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+	color: #FFF;
+	background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+	display: block;
+	overflow: auto;
+
+	/* align with #qunit-modulefilter-dropdown-list */
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+	box-sizing: border-box;
+	max-height: 2.8em;
+	display: block;
+	padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+	float: right;
+	font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+	/* insert padding to align with checkbox margins */
+	padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+	max-height: 200px;
+	overflow-y: auto;
+	margin: 0;
+	border-top: 2px groove threedhighlight;
+	padding: 0.4em 0 0;
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+	display: block;
+	padding-left: 0.15em;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+	list-style-position: inside;
+}
+
+#qunit-tests li {
+	padding: 0.4em 1em 0.4em 1em;
+	border-bottom: 1px solid #FFF;
+	list-style-position: inside;
+}
+
+#qunit-tests > li {
+	display: none;
+}
+
+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
+	display: list-item;
+}
+
+#qunit-tests.hidepass {
+	position: relative;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass:not(.todo) {
+	visibility: hidden;
+	position: absolute;
+	width:   0;
+	height:  0;
+	padding: 0;
+	border:  0;
+	margin:  0;
+}
+
+#qunit-tests li strong {
+	cursor: pointer;
+}
+
+#qunit-tests li.skipped strong {
+	cursor: default;
+}
+
+#qunit-tests li a {
+	padding: 0.5em;
+	color: #C2CCD1;
+	text-decoration: none;
+}
+
+#qunit-tests li p a {
+	padding: 0.25em;
+	color: #6B6464;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+	color: #000;
+}
+
+#qunit-tests li .runtime {
+	float: right;
+	font-size: smaller;
+}
+
+.qunit-assert-list {
+	margin-top: 0.5em;
+	padding: 0.5em;
+
+	background-color: #FFF;
+
+	border-radius: 5px;
+}
+
+.qunit-source {
+	margin: 0.6em 0 0.3em;
+}
+
+.qunit-collapsed {
+	display: none;
+}
+
+#qunit-tests table {
+	border-collapse: collapse;
+	margin-top: 0.2em;
+}
+
+#qunit-tests th {
+	text-align: right;
+	vertical-align: top;
+	padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+	vertical-align: top;
+}
+
+#qunit-tests pre {
+	margin: 0;
+	white-space: pre-wrap;
+	word-wrap: break-word;
+}
+
+#qunit-tests del {
+	color: #374E0C;
+	background-color: #E0F2BE;
+	text-decoration: none;
+}
+
+#qunit-tests ins {
+	color: #500;
+	background-color: #FFCACA;
+	text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts                       { color: #000; }
+#qunit-tests b.passed                       { color: #5E740B; }
+#qunit-tests b.failed                       { color: #710909; }
+
+#qunit-tests li li {
+	padding: 5px;
+	background-color: #FFF;
+	border-bottom: none;
+	list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+	color: #3C510C;
+	background-color: #FFF;
+	border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name               { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected           { color: #999; }
+
+#qunit-banner.qunit-pass                    { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+	color: #710909;
+	background-color: #FFF;
+	border-left: 10px solid #EE5757;
+	white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+	border-radius: 0 0 5px 5px;
+}
+
+#qunit-tests .fail                          { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name             { color: #000; }
+
+#qunit-tests .fail .test-actual             { color: #EE5757; }
+#qunit-tests .fail .test-expected           { color: #008000; }
+
+#qunit-banner.qunit-fail                    { background-color: #EE5757; }
+
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
+/*** Skipped tests */
+
+#qunit-tests .skipped {
+	background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-todo-label,
+#qunit-tests .qunit-skipped-label {
+	background-color: #F4FF77;
+	display: inline-block;
+	font-style: normal;
+	color: #366097;
+	line-height: 1.8em;
+	padding: 0 0.5em;
+	margin: -0.4em 0.4em -0.4em 0;
+}
+
+#qunit-tests .qunit-todo-label {
+	background-color: #EEE;
+}
+
+/** Result */
+
+#qunit-testresult {
+	color: #2B81AF;
+	background-color: #D2E0E6;
+
+	border-bottom: 1px solid #FFF;
+}
+#qunit-testresult .clearfix {
+	height: 0;
+	clear: both;
+}
+#qunit-testresult .module-name {
+	font-weight: 700;
+}
+#qunit-testresult-display {
+	padding: 0.5em 1em 0.5em 1em;
+	width: 85%;
+	float:left;
+}
+#qunit-testresult-controls {
+	padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+	float:left;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+	position: absolute;
+	top: -10000px;
+	left: -10000px;
+	width: 1000px;
+	height: 1000px;
+}
+
+#ember-testing-container {
+  position: relative;
+  background: white;
+  bottom: 0;
+  right: 0;
+  width: 640px;
+  height: 384px;
+  overflow: auto;
+  z-index: 98;
+  border: 1px solid #ccc;
+  margin: 0 auto;
+}
+
+#ember-testing-container.full-screen {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+  z-index: 98;
+  border: none;
+}
+
+#ember-testing {
+  width: 200%;
+  height: 200%;
+  transform: scale(0.5);
+  transform-origin: top left;
+}
+
+.full-screen #ember-testing {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  transform: scale(1);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/ember/quickstart/dist/assets/test-support.js
@@ -0,0 +1,12050 @@
+
+
+(function() {
+/*!
+ * @overview  Ember - JavaScript Application Framework
+ * @copyright Copyright 2011-2018 Tilde Inc. and contributors
+ *            Portions Copyright 2006-2011 Strobe Inc.
+ *            Portions Copyright 2008-2011 Apple Inc. All rights reserved.
+ * @license   Licensed under MIT license
+ *            See https://raw.github.com/emberjs/ember.js/master/LICENSE
+ * @version   3.0.0
+ */
+
+/*globals process */
+var enifed, requireModule, Ember;
+
+// Used in ember-environment/lib/global.js
+mainContext = this; // eslint-disable-line no-undef
+
+(function() {
+    function missingModule(name, referrerName) {
+      if (referrerName) {
+        throw new Error('Could not find module ' + name + ' required by: ' + referrerName);
+      } else {
+        throw new Error('Could not find module ' + name);
+      }
+    }
+
+    function internalRequire(_name, referrerName) {
+      var name = _name;
+      var mod = registry[name];
+
+      if (!mod) {
+        name = name + '/index';
+        mod = registry[name];
+      }
+
+      var exports = seen[name];
+
+      if (exports !== undefined) {
+        return exports;
+      }
+
+      exports = seen[name] = {};
+
+      if (!mod) {
+        missingModule(_name, referrerName);
+      }
+
+      var deps = mod.deps;
+      var callback = mod.callback;
+      var reified = new Array(deps.length);
+
+      for (var i = 0; i < deps.length; i++) {
+        if (deps[i] === 'exports') {
+          reified[i] = exports;
+        } else if (deps[i] === 'require') {
+          reified[i] = requireModule;
+        } else {
+          reified[i] = internalRequire(deps[i], name);
+        }
+      }
+
+      callback.apply(this, reified);
+
+      return exports;
+    }
+
+  var isNode = typeof window === 'undefined' &&
+    typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
+
+  if (!isNode) {
+    Ember = this.Ember = this.Ember || {};
+  }
+
+  if (typeof Ember === 'undefined') { Ember = {}; }
+
+  if (typeof Ember.__loader === 'undefined') {
+    var registry = {};
+    var seen = {};
+
+    enifed = function(name, deps, callback) {
+      var value = { };
+
+      if (!callback) {
+        value.deps = [];
+        value.callback = deps;
+      } else {
+        value.deps = deps;
+        value.callback = callback;
+      }
+
+      registry[name] = value;
+    };
+
+    requireModule = function(name) {
+      return internalRequire(name, null);
+    };
+
+    // setup `require` module
+    requireModule['default'] = requireModule;
+
+    requireModule.has = function registryHas(moduleName) {
+      return !!registry[moduleName] || !!registry[moduleName + '/index'];
+    };
+
+    requireModule._eak_seen = registry;
+
+    Ember.__loader = {
+      define: enifed,
+      require: requireModule,
+      registry: registry
+    };
+  } else {
+    enifed = Ember.__loader.define;
+    requireModule = Ember.__loader.require;
+  }
+})();
+
+enifed('ember-babel', ['exports'], function (exports) {
+  'use strict';
+
+  exports.classCallCheck = classCallCheck;
+  exports.inherits = inherits;
+  exports.taggedTemplateLiteralLoose = taggedTemplateLiteralLoose;
+  exports.createClass = createClass;
+  exports.defaults = defaults;
+  function classCallCheck(instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError('Cannot call a class as a function');
+    }
+  }
+
+  function inherits(subClass, superClass) {
+    if (typeof superClass !== 'function' && superClass !== null) {
+      throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass);
+    }
+
+    subClass.prototype = Object.create(superClass && superClass.prototype, {
+      constructor: {
+        value: subClass,
+        enumerable: false,
+        writable: true,
+        configurable: true
+      }
+    });
+
+    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : defaults(subClass, superClass);
+  }
+
+  function taggedTemplateLiteralLoose(strings, raw) {
+    strings.raw = raw;
+    return strings;
+  }
+
+  function defineProperties(target, props) {
+    for (var i = 0; i < props.length; i++) {
+      var descriptor = props[i];
+      descriptor.enumerable = descriptor.enumerable || false;
+      descriptor.configurable = true;
+      if ('value' in descriptor) descriptor.writable = true;
+      Object.defineProperty(target, descriptor.key, descriptor);
+    }
+  }
+
+  function createClass(Constructor, protoProps, staticProps) {
+    if (protoProps) defineProperties(Constructor.prototype, protoProps);
+    if (staticProps) defineProperties(Constructor, staticProps);
+    return Constructor;
+  }
+
+  function defaults(obj, defaults) {
+    var keys = Object.getOwnPropertyNames(defaults);
+    for (var i = 0; i < keys.length; i++) {
+      var key = keys[i];
+      var value = Object.getOwnPropertyDescriptor(defaults, key);
+      if (value && value.configurable && obj[key] === undefined) {
+        Object.defineProperty(obj, key, value);
+      }
+    }
+    return obj;
+  }
+
+  var possibleConstructorReturn = exports.possibleConstructorReturn = function (self, call) {
+    if (!self) {
+      throw new ReferenceError('this hasn\'t been initialized - super() hasn\'t been called');
+    }
+    return call && (typeof call === 'object' || typeof call === 'function') ? call : self;
+  };
+
+  var slice = exports.slice = Array.prototype.slice;
+});
+enifed('ember-debug/deprecate', ['exports', 'ember-debug/error', 'ember-console', 'ember-environment', 'ember-debug/index', 'ember-debug/handlers'], function (exports, _error, _emberConsole, _emberEnvironment, _index, _handlers) {
+  'use strict';
+
+  exports.missingOptionsUntilDeprecation = exports.missingOptionsIdDeprecation = exports.missingOptionsDeprecation = exports.registerHandler = undefined;
+
+  /**
+   @module @ember/debug
+   @public
+  */
+  /**
+    Allows for runtime registration of handler functions that override the default deprecation behavior.
+    Deprecations are invoked by calls to [@ember/application/deprecations/deprecate](https://emberjs.com/api/ember/release/classes/@ember%2Fapplication%2Fdeprecations/methods/deprecate?anchor=deprecate).
+    The following example demonstrates its usage by registering a handler that throws an error if the
+    message contains the word "should", otherwise defers to the default handler.
+  
+    ```javascript
+    import { registerDeprecationHandler } from '@ember/debug';
+  
+    registerDeprecationHandler((message, options, next) => {
+      if (message.indexOf('should') !== -1) {
+        throw new Error(`Deprecation message with should: ${message}`);
+      } else {
+        // defer to whatever handler was registered before this one
+        next(message, options);
+      }
+    });
+    ```
+  
+    The handler function takes the following arguments:
+  
+    <ul>
+      <li> <code>message</code> - The message received from the deprecation call.</li>
+      <li> <code>options</code> - An object passed in with the deprecation call containing additional information including:</li>
+        <ul>
+          <li> <code>id</code> - An id of the deprecation in the form of <code>package-name.specific-deprecation</code>.</li>
+          <li> <code>until</code> - The Ember version number the feature and deprecation will be removed in.</li>
+        </ul>
+      <li> <code>next</code> - A function that calls into the previously registered handler.</li>
+    </ul>
+  
+    @public
+    @static
+    @method registerDeprecationHandler
+    @for @ember/debug
+    @param handler {Function} A function to handle deprecation calls.
+    @since 2.1.0
+  */
+  /*global __fail__*/
+  var registerHandler = function () {};
+  var missingOptionsDeprecation = void 0,
+      missingOptionsIdDeprecation = void 0,
+      missingOptionsUntilDeprecation = void 0,
+      deprecate = void 0;
+
+  if (true) {
+    exports.registerHandler = registerHandler = function registerHandler(handler) {
+      (0, _handlers.registerHandler)('deprecate', handler);
+    };
+
+    var formatMessage = function formatMessage(_message, options) {
+      var message = _message;
+
+      if (options && options.id) {
+        message = message + (' [deprecation id: ' + options.id + ']');
+      }
+
+      if (options && options.url) {
+        message += ' See ' + options.url + ' for more details.';
+      }
+
+      return message;
+    };
+
+    registerHandler(function logDeprecationToConsole(message, options) {
+      var updatedMessage = formatMessage(message, options);
+
+      _emberConsole.default.warn('DEPRECATION: ' + updatedMessage);
+    });
+
+    var captureErrorForStack = void 0;
+
+    if (new Error().stack) {
+      captureErrorForStack = function () {
+        return new Error();
+      };
+    } else {
+      captureErrorForStack = function () {
+        try {
+          __fail__.fail();
+        } catch (e) {
+          return e;
+        }
+      };
+    }
+
+    registerHandler(function logDeprecationStackTrace(message, options, next) {
+      if (_emberEnvironment.ENV.LOG_STACKTRACE_ON_DEPRECATION) {
+        var stackStr = '';
+        var error = captureErrorForStack();
+        var stack = void 0;
+
+        if (error.stack) {
+          if (error['arguments']) {
+            // Chrome
+            stack = error.stack.replace(/^\s+at\s+/gm, '').replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
+            stack.shift();
+          } else {
+            // Firefox
+            stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
+          }
+
+          stackStr = '\n    ' + stack.slice(2).join('\n    ');
+        }
+
+        var updatedMessage = formatMessage(message, options);
+
+        _emberConsole.default.warn('DEPRECATION: ' + updatedMessage + stackStr);
+      } else {
+        next.apply(undefined, arguments);
+      }
+    });
+
+    registerHandler(function raiseOnDeprecation(message, options, next) {
+      if (_emberEnvironment.ENV.RAISE_ON_DEPRECATION) {
+        var updatedMessage = formatMessage(message);
+
+        throw new _error.default(updatedMessage);
+      } else {
+        next.apply(undefined, arguments);
+      }
+    });
+
+    exports.missingOptionsDeprecation = missingOptionsDeprecation = 'When calling `deprecate` you ' + 'must provide an `options` hash as the third parameter.  ' + '`options` should include `id` and `until` properties.';
+    exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation = 'When calling `deprecate` you must provide `id` in options.';
+    exports.missingOptionsUntilDeprecation = missingOptionsUntilDeprecation = 'When calling `deprecate` you must provide `until` in options.';
+    /**
+     @module @ember/application
+     @public
+     */
+    /**
+      Display a deprecation warning with the provided message and a stack trace
+      (Chrome and Firefox only).
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       @method deprecate
+      @for @ember/application/deprecations
+      @param {String} message A description of the deprecation.
+      @param {Boolean} test A boolean. If falsy, the deprecation will be displayed.
+      @param {Object} options
+      @param {String} options.id A unique id for this deprecation. The id can be
+        used by Ember debugging tools to change the behavior (raise, log or silence)
+        for that specific deprecation. The id should be namespaced by dots, e.g.
+        "view.helper.select".
+      @param {string} options.until The version of Ember when this deprecation
+        warning will be removed.
+      @param {String} [options.url] An optional url to the transition guide on the
+        emberjs.com website.
+      @static
+      @public
+      @since 1.0.0
+    */
+    deprecate = function deprecate(message, test, options) {
+      if (_emberEnvironment.ENV._ENABLE_DEPRECATION_OPTIONS_SUPPORT !== true) {
+        (0, _index.assert)(missingOptionsDeprecation, options && (options.id || options.until));
+        (0, _index.assert)(missingOptionsIdDeprecation, options.id);
+        (0, _index.assert)(missingOptionsUntilDeprecation, options.until);
+      }
+
+      if ((!options || !options.id && !options.until) && _emberEnvironment.ENV._ENABLE_DEPRECATION_OPTIONS_SUPPORT === true) {
+        deprecate(missingOptionsDeprecation, false, {
+          id: 'ember-debug.deprecate-options-missing',
+          until: '3.0.0',
+          url: 'https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
+        });
+      }
+
+      if (options && !options.id && _emberEnvironment.ENV._ENABLE_DEPRECATION_OPTIONS_SUPPORT === true) {
+        deprecate(missingOptionsIdDeprecation, false, {
+          id: 'ember-debug.deprecate-id-missing',
+          until: '3.0.0',
+          url: 'https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
+        });
+      }
+
+      if (options && !options.until && _emberEnvironment.ENV._ENABLE_DEPRECATION_OPTIONS_SUPPORT === true) {
+        deprecate(missingOptionsUntilDeprecation, options && options.until, {
+          id: 'ember-debug.deprecate-until-missing',
+          until: '3.0.0',
+          url: 'https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
+        });
+      }
+
+      _handlers.invoke.apply(undefined, ['deprecate'].concat(Array.prototype.slice.call(arguments)));
+    };
+  }
+
+  exports.default = deprecate;
+  exports.registerHandler = registerHandler;
+  exports.missingOptionsDeprecation = missingOptionsDeprecation;
+  exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation;
+  exports.missingOptionsUntilDeprecation = missingOptionsUntilDeprecation;
+});
+enifed("ember-debug/error", ["exports", "ember-babel"], function (exports, _emberBabel) {
+  "use strict";
+
+  /**
+   @module @ember/error
+  */
+  function ExtendBuiltin(klass) {
+    function ExtendableBuiltin() {
+      klass.apply(this, arguments);
+    }
+
+    ExtendableBuiltin.prototype = Object.create(klass.prototype);
+    ExtendableBuiltin.prototype.constructor = ExtendableBuiltin;
+    return ExtendableBuiltin;
+  }
+
+  /**
+    A subclass of the JavaScript Error object for use in Ember.
+  
+    @class EmberError
+    @extends Error
+    @constructor
+    @public
+  */
+
+  var EmberError = function (_ExtendBuiltin) {
+    (0, _emberBabel.inherits)(EmberError, _ExtendBuiltin);
+
+    function EmberError(message) {
+      (0, _emberBabel.classCallCheck)(this, EmberError);
+
+      var _this = (0, _emberBabel.possibleConstructorReturn)(this, _ExtendBuiltin.call(this));
+
+      if (!(_this instanceof EmberError)) {
+        var _ret;
+
+        return _ret = new EmberError(message), (0, _emberBabel.possibleConstructorReturn)(_this, _ret);
+      }
+
+      var error = Error.call(_this, message);
+      _this.stack = error.stack;
+      _this.description = error.description;
+      _this.fileName = error.fileName;
+      _this.lineNumber = error.lineNumber;
+      _this.message = error.message;
+      _this.name = error.name;
+      _this.number = error.number;
+      _this.code = error.code;
+      return _this;
+    }
+
+    return EmberError;
+  }(ExtendBuiltin(Error));
+
+  exports.default = EmberError;
+});
+enifed('ember-debug/features', ['exports', 'ember-environment', 'ember/features'], function (exports, _emberEnvironment, _features) {
+  'use strict';
+
+  exports.default = isEnabled;
+  var FEATURES = _features.FEATURES;
+
+
+  /**
+   @module ember
+  */
+
+  /**
+    The hash of enabled Canary features. Add to this, any canary features
+    before creating your application.
+  
+    Alternatively (and recommended), you can also define `EmberENV.FEATURES`
+    if you need to enable features flagged at runtime.
+  
+    @class FEATURES
+    @namespace Ember
+    @static
+    @since 1.1.0
+    @public
+  */
+
+  // Auto-generated
+
+  /**
+    Determine whether the specified `feature` is enabled. Used by Ember's
+    build tools to exclude experimental features from beta/stable builds.
+  
+    You can define the following configuration options:
+  
+    * `EmberENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly
+      enabled/disabled.
+  
+    @method isEnabled
+    @param {String} feature The feature to check
+    @return {Boolean}
+    @for Ember.FEATURES
+    @since 1.1.0
+    @public
+  */
+  function isEnabled(feature) {
+    var featureValue = FEATURES[feature];
+
+    if (featureValue === true || featureValue === false || featureValue === undefined) {
+      return featureValue;
+    } else if (_emberEnvironment.ENV.ENABLE_OPTIONAL_FEATURES) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+});
+enifed('ember-debug/handlers', ['exports'], function (exports) {
+  'use strict';
+
+  var HANDLERS = exports.HANDLERS = {};
+
+  var registerHandler = function () {};
+  var invoke = function () {};
+
+  if (true) {
+    exports.registerHandler = registerHandler = function registerHandler(type, callback) {
+      var nextHandler = HANDLERS[type] || function () {};
+
+      HANDLERS[type] = function (message, options) {
+        callback(message, options, nextHandler);
+      };
+    };
+
+    exports.invoke = invoke = function invoke(type, message, test, options) {
+      if (test) {
+        return;
+      }
+
+      var handlerForType = HANDLERS[type];
+
+      if (handlerForType) {
+        handlerForType(message, options);
+      }
+    };
+  }
+
+  exports.registerHandler = registerHandler;
+  exports.invoke = invoke;
+});
+enifed('ember-debug/index', ['exports', 'ember-debug/warn', 'ember-debug/deprecate', 'ember-debug/features', 'ember-debug/error', 'ember-debug/testing', 'ember-environment', 'ember-console', 'ember/features'], function (exports, _warn2, _deprecate2, _features, _error, _testing, _emberEnvironment, _emberConsole, _features2) {
+  'use strict';
+
+  exports._warnIfUsingStrippedFeatureFlags = exports.getDebugFunction = exports.setDebugFunction = exports.deprecateFunc = exports.runInDebug = exports.debugFreeze = exports.debugSeal = exports.deprecate = exports.debug = exports.warn = exports.info = exports.assert = exports.setTesting = exports.isTesting = exports.Error = exports.isFeatureEnabled = exports.registerDeprecationHandler = exports.registerWarnHandler = undefined;
+  Object.defineProperty(exports, 'registerWarnHandler', {
+    enumerable: true,
+    get: function () {
+      return _warn2.registerHandler;
+    }
+  });
+  Object.defineProperty(exports, 'registerDeprecationHandler', {
+    enumerable: true,
+    get: function () {
+      return _deprecate2.registerHandler;
+    }
+  });
+  Object.defineProperty(exports, 'isFeatureEnabled', {
+    enumerable: true,
+    get: function () {
+      return _features.default;
+    }
+  });
+  Object.defineProperty(exports, 'Error', {
+    enumerable: true,
+    get: function () {
+      return _error.default;
+    }
+  });
+  Object.defineProperty(exports, 'isTesting', {
+    enumerable: true,
+    get: function () {
+      return _testing.isTesting;
+    }
+  });
+  Object.defineProperty(exports, 'setTesting', {
+    enumerable: true,
+    get: function () {
+      return _testing.setTesting;
+    }
+  });
+  var DEFAULT_FEATURES = _features2.DEFAULT_FEATURES,
+      FEATURES = _features2.FEATURES;
+
+
+  // These are the default production build versions:
+  var noop = function () {};
+
+  var assert = noop;
+  var info = noop;
+  var warn = noop;
+  var debug = noop;
+  var deprecate = noop;
+  var debugSeal = noop;
+  var debugFreeze = noop;
+  var runInDebug = noop;
+  var setDebugFunction = noop;
+  var getDebugFunction = noop;
+
+  var deprecateFunc = function () {
+    return arguments[arguments.length - 1];
+  };
+
+  if (true) {
+    exports.setDebugFunction = setDebugFunction = function (type, callback) {
+      switch (type) {
+        case 'assert':
+          return exports.assert = assert = callback;
+        case 'info':
+          return exports.info = info = callback;
+        case 'warn':
+          return exports.warn = warn = callback;
+        case 'debug':
+          return exports.debug = debug = callback;
+        case 'deprecate':
+          return exports.deprecate = deprecate = callback;
+        case 'debugSeal':
+          return exports.debugSeal = debugSeal = callback;
+        case 'debugFreeze':
+          return exports.debugFreeze = debugFreeze = callback;
+        case 'runInDebug':
+          return exports.runInDebug = runInDebug = callback;
+        case 'deprecateFunc':
+          return exports.deprecateFunc = deprecateFunc = callback;
+      }
+    };
+
+    exports.getDebugFunction = getDebugFunction = function (type) {
+      switch (type) {
+        case 'assert':
+          return assert;
+        case 'info':
+          return info;
+        case 'warn':
+          return warn;
+        case 'debug':
+          return debug;
+        case 'deprecate':
+          return deprecate;
+        case 'debugSeal':
+          return debugSeal;
+        case 'debugFreeze':
+          return debugFreeze;
+        case 'runInDebug':
+          return runInDebug;
+        case 'deprecateFunc':
+          return deprecateFunc;
+      }
+    };
+  }
+
+  /**
+  @module @ember/debug
+  */
+
+  if (true) {
+    /**
+      Define an assertion that will throw an exception if the condition is not met.
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       ```javascript
+      import { assert } from '@ember/debug';
+       // Test for truthiness
+      assert('Must pass a valid object', obj);
+       // Fail unconditionally
+      assert('This code path should never be run');
+      ```
+       @method assert
+      @static
+      @for @ember/debug
+      @param {String} desc A description of the assertion. This will become
+        the text of the Error thrown if the assertion fails.
+      @param {Boolean} test Must be truthy for the assertion to pass. If
+        falsy, an exception will be thrown.
+      @public
+      @since 1.0.0
+    */
+    setDebugFunction('assert', function assert(desc, test) {
+      if (!test) {
+        throw new _error.default('Assertion Failed: ' + desc);
+      }
+    });
+
+    /**
+      Display a debug notice.
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       ```javascript
+      import { debug } from '@ember/debug';
+       debug('I\'m a debug notice!');
+      ```
+       @method debug
+      @for @ember/debug
+      @static
+      @param {String} message A debug message to display.
+      @public
+    */
+    setDebugFunction('debug', function debug(message) {
+      _emberConsole.default.debug('DEBUG: ' + message);
+    });
+
+    /**
+      Display an info notice.
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       @method info
+      @private
+    */
+    setDebugFunction('info', function info() {
+      _emberConsole.default.info.apply(undefined, arguments);
+    });
+
+    /**
+     @module @ember/application
+     @public
+    */
+
+    /**
+      Alias an old, deprecated method with its new counterpart.
+       Display a deprecation warning with the provided message and a stack trace
+      (Chrome and Firefox only) when the assigned method is called.
+       * In a production build, this method is defined as an empty function (NOP).
+       ```javascript
+      import { deprecateFunc } from '@ember/application/deprecations';
+       Ember.oldMethod = deprecateFunc('Please use the new, updated method', options, Ember.newMethod);
+      ```
+       @method deprecateFunc
+      @static
+      @for @ember/application/deprecations
+      @param {String} message A description of the deprecation.
+      @param {Object} [options] The options object for `deprecate`.
+      @param {Function} func The new function called to replace its deprecated counterpart.
+      @return {Function} A new function that wraps the original function with a deprecation warning
+      @private
+    */
+    setDebugFunction('deprecateFunc', function deprecateFunc() {
+      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+        args[_key] = arguments[_key];
+      }
+
+      if (args.length === 3) {
+        var message = args[0],
+            options = args[1],
+            func = args[2];
+
+        return function () {
+          deprecate(message, false, options);
+          return func.apply(this, arguments);
+        };
+      } else {
+        var _message = args[0],
+            _func = args[1];
+
+        return function () {
+          deprecate(_message);
+          return _func.apply(this, arguments);
+        };
+      }
+    });
+
+    /**
+     @module @ember/debug
+     @public
+    */
+    /**
+      Run a function meant for debugging.
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       ```javascript
+      import Component from '@ember/component';
+      import { runInDebug } from '@ember/debug';
+       runInDebug(() => {
+        Component.reopen({
+          didInsertElement() {
+            console.log("I'm happy");
+          }
+        });
+      });
+      ```
+       @method runInDebug
+      @for @ember/debug
+      @static
+      @param {Function} func The function to be executed.
+      @since 1.5.0
+      @public
+    */
+    setDebugFunction('runInDebug', function runInDebug(func) {
+      func();
+    });
+
+    setDebugFunction('debugSeal', function debugSeal(obj) {
+      Object.seal(obj);
+    });
+
+    setDebugFunction('debugFreeze', function debugFreeze(obj) {
+      Object.freeze(obj);
+    });
+
+    setDebugFunction('deprecate', _deprecate2.default);
+
+    setDebugFunction('warn', _warn2.default);
+  }
+
+  var _warnIfUsingStrippedFeatureFlags = void 0;
+
+  if (true && !(0, _testing.isTesting)()) {
+    /**
+       Will call `warn()` if ENABLE_OPTIONAL_FEATURES or
+       any specific FEATURES flag is truthy.
+        This method is called automatically in debug canary builds.
+        @private
+       @method _warnIfUsingStrippedFeatureFlags
+       @return {void}
+    */
+    exports._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags = function _warnIfUsingStrippedFeatureFlags(FEATURES, knownFeatures, featuresWereStripped) {
+      if (featuresWereStripped) {
+        warn('Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.', !_emberEnvironment.ENV.ENABLE_OPTIONAL_FEATURES, { id: 'ember-debug.feature-flag-with-features-stripped' });
+
+        var keys = Object.keys(FEATURES || {});
+        for (var i = 0; i < keys.length; i++) {
+          var key = keys[i];
+          if (key === 'isEnabled' || !(key in knownFeatures)) {
+            continue;
+          }
+
+          warn('FEATURE["' + key + '"] is set as enabled, but FEATURE flags are only available in canary builds.', !FEATURES[key], { id: 'ember-debug.feature-flag-with-features-stripped' });
+        }
+      }
+    };
+
+    // Complain if they're using FEATURE flags in builds other than canary
+    FEATURES['features-stripped-test'] = true;
+    var featuresWereStripped = true;
+
+    if ((0, _features.default)('features-stripped-test')) {
+      featuresWereStripped = false;
+    }
+
+    delete FEATURES['features-stripped-test'];
+    _warnIfUsingStrippedFeatureFlags(_emberEnvironment.ENV.FEATURES, DEFAULT_FEATURES, featuresWereStripped);
+
+    // Inform the developer about the Ember Inspector if not installed.
+    var isFirefox = _emberEnvironment.environment.isFirefox;
+    var isChrome = _emberEnvironment.environment.isChrome;
+
+    if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) {
+      window.addEventListener('load', function () {
+        if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) {
+          var downloadURL = void 0;
+
+          if (isChrome) {
+            downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi';
+          } else if (isFirefox) {
+            downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/';
+          }
+
+          debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL);
+        }
+      }, false);
+    }
+  }
+
+  exports.assert = assert;
+  exports.info = info;
+  exports.warn = warn;
+  exports.debug = debug;
+  exports.deprecate = deprecate;
+  exports.debugSeal = debugSeal;
+  exports.debugFreeze = debugFreeze;
+  exports.runInDebug = runInDebug;
+  exports.deprecateFunc = deprecateFunc;
+  exports.setDebugFunction = setDebugFunction;
+  exports.getDebugFunction = getDebugFunction;
+  exports._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags;
+});
+enifed("ember-debug/testing", ["exports"], function (exports) {
+  "use strict";
+
+  exports.isTesting = isTesting;
+  exports.setTesting = setTesting;
+  var testing = false;
+
+  function isTesting() {
+    return testing;
+  }
+
+  function setTesting(value) {
+    testing = !!value;
+  }
+});
+enifed('ember-debug/warn', ['exports', 'ember-environment', 'ember-console', 'ember-debug/deprecate', 'ember-debug/index', 'ember-debug/handlers'], function (exports, _emberEnvironment, _emberConsole, _deprecate, _index, _handlers) {
+  'use strict';
+
+  exports.missingOptionsDeprecation = exports.missingOptionsIdDeprecation = exports.registerHandler = undefined;
+
+
+  var registerHandler = function () {};
+  var warn = function () {};
+  var missingOptionsDeprecation = void 0,
+      missingOptionsIdDeprecation = void 0;
+
+  /**
+  @module @ember/debug
+  */
+
+  if (true) {
+    /**
+      Allows for runtime registration of handler functions that override the default warning behavior.
+      Warnings are invoked by calls made to [@ember/debug/warn](https://emberjs.com/api/ember/release/classes/@ember%2Fdebug/methods/warn?anchor=warn).
+      The following example demonstrates its usage by registering a handler that does nothing overriding Ember's
+      default warning behavior.
+       ```javascript
+      import { registerWarnHandler } from '@ember/debug';
+       // next is not called, so no warnings get the default behavior
+      registerWarnHandler(() => {});
+      ```
+       The handler function takes the following arguments:
+       <ul>
+        <li> <code>message</code> - The message received from the warn call. </li>
+        <li> <code>options</code> - An object passed in with the warn call containing additional information including:</li>
+          <ul>
+            <li> <code>id</code> - An id of the warning in the form of <code>package-name.specific-warning</code>.</li>
+          </ul>
+        <li> <code>next</code> - A function that calls into the previously registered handler.</li>
+      </ul>
+       @public
+      @static
+      @method registerWarnHandler
+      @for @ember/debug
+      @param handler {Function} A function to handle warnings.
+      @since 2.1.0
+    */
+    exports.registerHandler = registerHandler = function registerHandler(handler) {
+      (0, _handlers.registerHandler)('warn', handler);
+    };
+
+    registerHandler(function logWarning(message) {
+      _emberConsole.default.warn('WARNING: ' + message);
+      if ('trace' in _emberConsole.default) {
+        _emberConsole.default.trace();
+      }
+    });
+
+    exports.missingOptionsDeprecation = missingOptionsDeprecation = 'When calling `warn` you ' + 'must provide an `options` hash as the third parameter.  ' + '`options` should include an `id` property.';
+    exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation = 'When calling `warn` you must provide `id` in options.';
+
+    /**
+      Display a warning with the provided message.
+       * In a production build, this method is defined as an empty function (NOP).
+      Uses of this method in Ember itself are stripped from the ember.prod.js build.
+       @method warn
+      @for @ember/debug
+      @static
+      @param {String} message A warning to display.
+      @param {Boolean} test An optional boolean. If falsy, the warning
+        will be displayed.
+      @param {Object} options An object that can be used to pass a unique
+        `id` for this warning.  The `id` can be used by Ember debugging tools
+        to change the behavior (raise, log, or silence) for that specific warning.
+        The `id` should be namespaced by dots, e.g. "ember-debug.feature-flag-with-features-stripped"
+      @public
+      @since 1.0.0
+    */
+    warn = function warn(message, test, options) {
+      if (arguments.length === 2 && typeof test === 'object') {
+        options = test;
+        test = false;
+      }
+
+      if (_emberEnvironment.ENV._ENABLE_WARN_OPTIONS_SUPPORT !== true) {
+        (0, _index.assert)(missingOptionsDeprecation, options);
+        (0, _index.assert)(missingOptionsIdDeprecation, options && options.id);
+      }
+
+      if (!options && _emberEnvironment.ENV._ENABLE_WARN_OPTIONS_SUPPORT === true) {
+        (0, _deprecate.default)(missingOptionsDeprecation, false, {
+          id: 'ember-debug.warn-options-missing',
+          until: '3.0.0',
+          url: 'https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
+        });
+      }
+
+      if (options && !options.id && _emberEnvironment.ENV._ENABLE_WARN_OPTIONS_SUPPORT === true) {
+        (0, _deprecate.default)(missingOptionsIdDeprecation, false, {
+          id: 'ember-debug.warn-id-missing',
+          until: '3.0.0',
+          url: 'https://emberjs.com/deprecations/v2.x/#toc_ember-debug-function-options'
+        });
+      }
+
+      (0, _handlers.invoke)('warn', message, test, options);
+    };
+  }
+
+  exports.default = warn;
+  exports.registerHandler = registerHandler;
+  exports.missingOptionsIdDeprecation = missingOptionsIdDeprecation;
+  exports.missingOptionsDeprecation = missingOptionsDeprecation;
+});
+enifed('ember-testing/adapters/adapter', ['exports', 'ember-runtime'], function (exports, _emberRuntime) {
+  'use strict';
+
+  function K() {
+    return this;
+  }
+
+  /**
+   @module @ember/test
+  */
+
+  /**
+    The primary purpose of this class is to create hooks that can be implemented
+    by an adapter for various test frameworks.
+  
+    @class TestAdapter
+    @public
+  */
+  exports.default = _emberRuntime.Object.extend({
+    /**
+      This callback will be called whenever an async operation is about to start.
+       Override this to call your framework's methods that handle async
+      operations.
+       @public
+      @method asyncStart
+    */
+    asyncStart: K,
+
+    /**
+      This callback will be called whenever an async operation has completed.
+       @public
+      @method asyncEnd
+    */
+    asyncEnd: K,
+
+    /**
+      Override this method with your testing framework's false assertion.
+      This function is called whenever an exception occurs causing the testing
+      promise to fail.
+       QUnit example:
+       ```javascript
+        exception: function(error) {
+          ok(false, error);
+        };
+      ```
+       @public
+      @method exception
+      @param {String} error The exception to be raised.
+    */
+    exception: function (error) {
+      throw error;
+    }
+  });
+});
+enifed('ember-testing/adapters/qunit', ['exports', 'ember-utils', 'ember-testing/adapters/adapter'], function (exports, _emberUtils, _adapter) {
+  'use strict';
+
+  exports.default = _adapter.default.extend({
+    asyncStart: function () {
+      QUnit.stop();
+    },
+    asyncEnd: function () {
+      QUnit.start();
+    },
+    exception: function (error) {
+      QUnit.config.current.assert.ok(false, (0, _emberUtils.inspect)(error));
+    }
+  });
+});
+enifed('ember-testing/events', ['exports', 'ember-views', 'ember-metal'], function (exports, _emberViews, _emberMetal) {
+  'use strict';
+
+  exports.focus = focus;
+  exports.fireEvent = fireEvent;
+
+
+  var DEFAULT_EVENT_OPTIONS = { canBubble: true, cancelable: true };
+  var KEYBOARD_EVENT_TYPES = ['keydown', 'keypress', 'keyup'];
+  var MOUSE_EVENT_TYPES = ['click', 'mousedown', 'mouseup', 'dblclick', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover'];
+
+  function focus(el) {
+    if (!el) {
+      return;
+    }
+    var $el = (0, _emberViews.jQuery)(el);
+    if ($el.is(':input, [contenteditable=true]')) {
+      var type = $el.prop('type');
+      if (type !== 'checkbox' && type !== 'radio' && type !== 'hidden') {
+        (0, _emberMetal.run)(null, function () {
+          // Firefox does not trigger the `focusin` event if the window
+          // does not have focus. If the document doesn't have focus just
+          // use trigger('focusin') instead.
+
+          if (!document.hasFocus || document.hasFocus()) {
+            el.focus();
+          } else {
+            $el.trigger('focusin');
+          }
+        });
+      }
+    }
+  }
+
+  function fireEvent(element, type) {
+    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+
+    if (!element) {
+      return;
+    }
+    var event = void 0;
+    if (KEYBOARD_EVENT_TYPES.indexOf(type) > -1) {
+      event = buildKeyboardEvent(type, options);
+    } else if (MOUSE_EVENT_TYPES.indexOf(type) > -1) {
+      var rect = element.getBoundingClientRect();
+      var x = rect.left + 1;
+      var y = rect.top + 1;
+      var simulatedCoordinates = {
+        screenX: x + 5,
+        screenY: y + 95,
+        clientX: x,
+        clientY: y
+      };
+      event = buildMouseEvent(type, _emberViews.jQuery.extend(simulatedCoordinates, options));
+    } else {
+      event = buildBasicEvent(type, options);
+    }
+    element.dispatchEvent(event);
+  }
+
+  function buildBasicEvent(type) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+    var event = document.createEvent('Events');
+    event.initEvent(type, true, true);
+    _emberViews.jQuery.extend(event, options);
+    return event;
+  }
+
+  function buildMouseEvent(type) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+    var event = void 0;
+    try {
+      event = document.createEvent('MouseEvents');
+      var eventOpts = _emberViews.jQuery.extend({}, DEFAULT_EVENT_OPTIONS, options);
+      event.initMouseEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.detail, eventOpts.screenX, eventOpts.screenY, eventOpts.clientX, eventOpts.clientY, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.button, eventOpts.relatedTarget);
+    } catch (e) {
+      event = buildBasicEvent(type, options);
+    }
+    return event;
+  }
+
+  function buildKeyboardEvent(type) {
+    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+    var event = void 0;
+    try {
+      event = document.createEvent('KeyEvents');
+      var eventOpts = _emberViews.jQuery.extend({}, DEFAULT_EVENT_OPTIONS, options);
+      event.initKeyEvent(type, eventOpts.canBubble, eventOpts.cancelable, window, eventOpts.ctrlKey, eventOpts.altKey, eventOpts.shiftKey, eventOpts.metaKey, eventOpts.keyCode, eventOpts.charCode);
+    } catch (e) {
+      event = buildBasicEvent(type, options);
+    }
+    return event;
+  }
+});
+enifed('ember-testing/ext/application', ['ember-application', 'ember-testing/setup_for_testing', 'ember-testing/test/helpers', 'ember-testing/test/promise', 'ember-testing/test/run', 'ember-testing/test/on_inject_helpers', 'ember-testing/test/adapter'], function (_emberApplication, _setup_for_testing, _helpers, _promise, _run, _on_inject_helpers, _adapter) {
+  'use strict';
+
+  _emberApplication.Application.reopen({
+    /**
+     This property contains the testing helpers for the current application. These
+     are created once you call `injectTestHelpers` on your `Application`
+     instance. The included helpers are also available on the `window` object by
+     default, but can be used from this object on the individual application also.
+       @property testHelpers
+      @type {Object}
+      @default {}
+      @public
+    */
+    testHelpers: {},
+
+    /**
+     This property will contain the original methods that were registered
+     on the `helperContainer` before `injectTestHelpers` is called.
+      When `removeTestHelpers` is called, these methods are restored to the
+     `helperContainer`.
+       @property originalMethods
+      @type {Object}
+      @default {}
+      @private
+      @since 1.3.0
+    */
+    originalMethods: {},
+
+    /**
+    This property indicates whether or not this application is currently in
+    testing mode. This is set when `setupForTesting` is called on the current
+    application.
+     @property testing
+    @type {Boolean}
+    @default false
+    @since 1.3.0
+    @public
+    */
+    testing: false,
+
+    setupForTesting: function () {
+      (0, _setup_for_testing.default)();
+
+      this.testing = true;
+
+      this.resolveRegistration('router:main').reopen({
+        location: 'none'
+      });
+    },
+
+
+    /**
+      This will be used as the container to inject the test helpers into. By
+      default the helpers are injected into `window`.
+       @property helperContainer
+      @type {Object} The object to be used for test helpers.
+      @default window
+      @since 1.2.0
+      @private
+    */
+    helperContainer: null,
+
+    injectTestHelpers: function (helperContainer) {
+      if (helperContainer) {
+        this.helperContainer = helperContainer;
+      } else {
+        this.helperContainer = window;
+      }
+
+      this.reopen({
+        willDestroy: function () {
+          this._super.apply(this, arguments);
+          this.removeTestHelpers();
+        }
+      });
+
+      this.testHelpers = {};
+      for (var name in _helpers.helpers) {
+        this.originalMethods[name] = this.helperContainer[name];
+        this.testHelpers[name] = this.helperContainer[name] = helper(this, name);
+        protoWrap(_promise.default.prototype, name, helper(this, name), _helpers.helpers[name].meta.wait);
+      }
+
+      (0, _on_inject_helpers.invokeInjectHelpersCallbacks)(this);
+    },
+    removeTestHelpers: function () {
+      if (!this.helperContainer) {
+        return;
+      }
+
+      for (var name in _helpers.helpers) {
+        this.helperContainer[name] = this.originalMethods[name];
+        delete _promise.default.prototype[name];
+        delete this.testHelpers[name];
+        delete this.originalMethods[name];
+      }
+    }
+  });
+
+  // This method is no longer needed
+  // But still here for backwards compatibility
+  // of helper chaining
+  function protoWrap(proto, name, callback, isAsync) {
+    proto[name] = function () {
+      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+        args[_key] = arguments[_key];
+      }
+
+      if (isAsync) {
+        return callback.apply(this, args);
+      } else {
+        return this.then(function () {
+          return callback.apply(this, args);
+        });
+      }
+    };
+  }
+
+  function helper(app, name) {
+    var fn = _helpers.helpers[name].method;
+    var meta = _helpers.helpers[name].meta;
+    if (!meta.wait) {
+      return function () {
+        for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+          args[_key2] = arguments[_key2];
+        }
+
+        return fn.apply(app, [app].concat(args));
+      };
+    }
+
+    return function () {
+      for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+        args[_key3] = arguments[_key3];
+      }
+
+      var lastPromise = (0, _run.default)(function () {
+        return (0, _promise.resolve)((0, _promise.getLastPromise)());
+      });
+
+      // wait for last helper's promise to resolve and then
+      // execute. To be safe, we need to tell the adapter we're going
+      // asynchronous here, because fn may not be invoked before we
+      // return.
+      (0, _adapter.asyncStart)();
+      return lastPromise.then(function () {
+        return fn.apply(app, [app].concat(args));
+      }).finally(_adapter.asyncEnd);
+    };
+  }
+});
+enifed('ember-testing/ext/rsvp', ['exports', 'ember-runtime', 'ember-metal', 'ember-debug', 'ember-testing/test/adapter'], function (exports, _emberRuntime, _emberMetal, _emberDebug, _adapter) {
+  'use strict';
+
+  _emberRuntime.RSVP.configure('async', function (callback, promise) {
+    // if schedule will cause autorun, we need to inform adapter
+    if ((0, _emberDebug.isTesting)() && !_emberMetal.run.backburner.currentInstance) {
+      (0, _adapter.asyncStart)();
+      _emberMetal.run.backburner.schedule('actions', function () {
+        (0, _adapter.asyncEnd)();
+        callback(promise);
+      });
+    } else {
+      _emberMetal.run.backburner.schedule('actions', function () {
+        return callback(promise);
+      });
+    }
+  });
+
+  exports.default = _emberRuntime.RSVP;
+});
+enifed('ember-testing/helpers', ['ember-testing/test/helpers', 'ember-testing/helpers/and_then', 'ember-testing/helpers/click', 'ember-testing/helpers/current_path', 'ember-testing/helpers/current_route_name', 'ember-testing/helpers/current_url', 'ember-testing/helpers/fill_in', 'ember-testing/helpers/find', 'ember-testing/helpers/find_with_assert', 'ember-testing/helpers/key_event', 'ember-testing/helpers/pause_test', 'ember-testing/helpers/trigger_event', 'ember-testing/helpers/visit', 'ember-testing/helpers/wait'], function (_helpers, _and_then, _click, _current_path, _current_route_name, _current_url, _fill_in, _find, _find_with_assert, _key_event, _pause_test, _trigger_event, _visit, _wait) {
+  'use strict';
+
+  (0, _helpers.registerAsyncHelper)('visit', _visit.default);
+  (0, _helpers.registerAsyncHelper)('click', _click.default);
+  (0, _helpers.registerAsyncHelper)('keyEvent', _key_event.default);
+  (0, _helpers.registerAsyncHelper)('fillIn', _fill_in.default);
+  (0, _helpers.registerAsyncHelper)('wait', _wait.default);
+  (0, _helpers.registerAsyncHelper)('andThen', _and_then.default);
+  (0, _helpers.registerAsyncHelper)('pauseTest', _pause_test.pauseTest);
+  (0, _helpers.registerAsyncHelper)('triggerEvent', _trigger_event.default);
+
+  (0, _helpers.registerHelper)('find', _find.default);
+  (0, _helpers.registerHelper)('findWithAssert', _find_with_assert.default);
+  (0, _helpers.registerHelper)('currentRouteName', _current_route_name.default);
+  (0, _helpers.registerHelper)('currentPath', _current_path.default);
+  (0, _helpers.registerHelper)('currentURL', _current_url.default);
+  (0, _helpers.registerHelper)('resumeTest', _pause_test.resumeTest);
+});
+enifed("ember-testing/helpers/and_then", ["exports"], function (exports) {
+  "use strict";
+
+  exports.default = andThen;
+  function andThen(app, callback) {
+    return app.testHelpers.wait(callback(app));
+  }
+});
+enifed('ember-testing/helpers/click', ['exports', 'ember-testing/events'], function (exports, _events) {
+  'use strict';
+
+  exports.default = click;
+
+
+  /**
+    Clicks an element and triggers any actions triggered by the element's `click`
+    event.
+  
+    Example:
+  
+    ```javascript
+    click('.some-jQuery-selector').then(function() {
+      // assert something
+    });
+    ```
+  
+    @method click
+    @param {String} selector jQuery selector for finding element on the DOM
+    @param {Object} context A DOM Element, Document, or jQuery to use as context
+    @return {RSVP.Promise<undefined>}
+    @public
+  */
+  function click(app, selector, context) {
+    var $el = app.testHelpers.findWithAssert(selector, context);
+    var el = $el[0];
+
+    (0, _events.fireEvent)(el, 'mousedown');
+
+    (0, _events.focus)(el);
+
+    (0, _events.fireEvent)(el, 'mouseup');
+    (0, _events.fireEvent)(el, 'click');
+
+    return app.testHelpers.wait();
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/current_path', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = currentPath;
+
+
+  /**
+    Returns the current path.
+  
+  Example:
+  
+  ```javascript
+  function validateURL() {
+    equal(currentPath(), 'some.path.index', "correct path was transitioned into.");
+  }
+  
+  click('#some-link-id').then(validateURL);
+  ```
+  
+  @method currentPath
+  @return {Object} The currently active path.
+  @since 1.5.0
+  @public
+  */
+  function currentPath(app) {
+    var routingService = app.__container__.lookup('service:-routing');
+    return (0, _emberMetal.get)(routingService, 'currentPath');
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/current_route_name', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = currentRouteName;
+
+  /**
+    Returns the currently active route name.
+  
+  Example:
+  
+  ```javascript
+  function validateRouteName() {
+    equal(currentRouteName(), 'some.path', "correct route was transitioned into.");
+  }
+  visit('/some/path').then(validateRouteName)
+  ```
+  
+  @method currentRouteName
+  @return {Object} The name of the currently active route.
+  @since 1.5.0
+  @public
+  */
+  function currentRouteName(app) {
+    var routingService = app.__container__.lookup('service:-routing');
+    return (0, _emberMetal.get)(routingService, 'currentRouteName');
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/current_url', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = currentURL;
+
+
+  /**
+    Returns the current URL.
+  
+  Example:
+  
+  ```javascript
+  function validateURL() {
+    equal(currentURL(), '/some/path', "correct URL was transitioned into.");
+  }
+  
+  click('#some-link-id').then(validateURL);
+  ```
+  
+  @method currentURL
+  @return {Object} The currently active URL.
+  @since 1.5.0
+  @public
+  */
+  function currentURL(app) {
+    var router = app.__container__.lookup('router:main');
+    return (0, _emberMetal.get)(router, 'location').getURL();
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/fill_in', ['exports', 'ember-testing/events'], function (exports, _events) {
+  'use strict';
+
+  exports.default = fillIn;
+
+
+  /**
+    Fills in an input element with some text.
+  
+    Example:
+  
+    ```javascript
+    fillIn('#email', 'you@example.com').then(function() {
+      // assert something
+    });
+    ```
+  
+    @method fillIn
+    @param {String} selector jQuery selector finding an input element on the DOM
+    to fill text with
+    @param {String} text text to place inside the input element
+    @return {RSVP.Promise<undefined>}
+    @public
+  */
+  function fillIn(app, selector, contextOrText, text) {
+    var $el = void 0,
+        el = void 0,
+        context = void 0;
+    if (text === undefined) {
+      text = contextOrText;
+    } else {
+      context = contextOrText;
+    }
+    $el = app.testHelpers.findWithAssert(selector, context);
+    el = $el[0];
+    (0, _events.focus)(el);
+
+    $el.eq(0).val(text);
+    (0, _events.fireEvent)(el, 'input');
+    (0, _events.fireEvent)(el, 'change');
+
+    return app.testHelpers.wait();
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/find', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = find;
+
+
+  /**
+    Finds an element in the context of the app's container element. A simple alias
+    for `app.$(selector)`.
+  
+    Example:
+  
+    ```javascript
+    var $el = find('.my-selector');
+    ```
+  
+    With the `context` param:
+  
+    ```javascript
+    var $el = find('.my-selector', '.parent-element-class');
+    ```
+  
+    @method find
+    @param {String} selector jQuery string selector for element lookup
+    @param {String} [context] (optional) jQuery selector that will limit the selector
+                              argument to find only within the context's children
+    @return {Object} jQuery object representing the results of the query
+    @public
+  */
+  function find(app, selector, context) {
+    var $el = void 0;
+    context = context || (0, _emberMetal.get)(app, 'rootElement');
+    $el = app.$(selector, context);
+    return $el;
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/find_with_assert', ['exports'], function (exports) {
+  'use strict';
+
+  exports.default = findWithAssert;
+  /**
+  @module ember
+  */
+  /**
+    Like `find`, but throws an error if the element selector returns no results.
+  
+    Example:
+  
+    ```javascript
+    var $el = findWithAssert('.doesnt-exist'); // throws error
+    ```
+  
+    With the `context` param:
+  
+    ```javascript
+    var $el = findWithAssert('.selector-id', '.parent-element-class'); // assert will pass
+    ```
+  
+    @method findWithAssert
+    @param {String} selector jQuery selector string for finding an element within
+    the DOM
+    @param {String} [context] (optional) jQuery selector that will limit the
+    selector argument to find only within the context's children
+    @return {Object} jQuery object representing the results of the query
+    @throws {Error} throws error if jQuery object returned has a length of 0
+    @public
+  */
+  function findWithAssert(app, selector, context) {
+    var $el = app.testHelpers.find(selector, context);
+    if ($el.length === 0) {
+      throw new Error('Element ' + selector + ' not found.');
+    }
+    return $el;
+  }
+});
+enifed("ember-testing/helpers/key_event", ["exports"], function (exports) {
+  "use strict";
+
+  exports.default = keyEvent;
+  /**
+  @module ember
+  */
+  /**
+    Simulates a key event, e.g. `keypress`, `keydown`, `keyup` with the desired keyCode
+    Example:
+    ```javascript
+    keyEvent('.some-jQuery-selector', 'keypress', 13).then(function() {
+     // assert something
+    });
+    ```
+    @method keyEvent
+    @param {String} selector jQuery selector for finding element on the DOM
+    @param {String} type the type of key event, e.g. `keypress`, `keydown`, `keyup`
+    @param {Number} keyCode the keyCode of the simulated key event
+    @return {RSVP.Promise<undefined>}
+    @since 1.5.0
+    @public
+  */
+  function keyEvent(app, selector, contextOrType, typeOrKeyCode, keyCode) {
+    var context = void 0,
+        type = void 0;
+
+    if (keyCode === undefined) {
+      context = null;
+      keyCode = typeOrKeyCode;
+      type = contextOrType;
+    } else {
+      context = contextOrType;
+      type = typeOrKeyCode;
+    }
+
+    return app.testHelpers.triggerEvent(selector, context, type, { keyCode: keyCode, which: keyCode });
+  }
+});
+enifed('ember-testing/helpers/pause_test', ['exports', 'ember-runtime', 'ember-console', 'ember-debug'], function (exports, _emberRuntime, _emberConsole, _emberDebug) {
+  'use strict';
+
+  exports.resumeTest = resumeTest;
+  exports.pauseTest = pauseTest;
+
+
+  var resume = void 0;
+
+  /**
+   Resumes a test paused by `pauseTest`.
+  
+   @method resumeTest
+   @return {void}
+   @public
+  */
+  /**
+  @module ember
+  */
+  function resumeTest() {
+    (true && !(resume) && (0, _emberDebug.assert)('Testing has not been paused. There is nothing to resume.', resume));
+
+    resume();
+    resume = undefined;
+  }
+
+  /**
+   Pauses the current test - this is useful for debugging while testing or for test-driving.
+   It allows you to inspect the state of your application at any point.
+   Example (The test will pause before clicking the button):
+  
+   ```javascript
+   visit('/')
+   return pauseTest();
+   click('.btn');
+   ```
+  
+   You may want to turn off the timeout before pausing.
+  
+   qunit (as of 2.4.0):
+  
+   ```
+   visit('/');
+   assert.timeout(0);
+   return pauseTest();
+   click('.btn');
+   ```
+  
+   mocha:
+  
+   ```
+   visit('/');
+   this.timeout(0);
+   return pauseTest();
+   click('.btn');
+   ```
+  
+  
+   @since 1.9.0
+   @method pauseTest
+   @return {Object} A promise that will never resolve
+   @public
+  */
+  function pauseTest() {
+    _emberConsole.default.info('Testing paused. Use `resumeTest()` to continue.');
+
+    return new _emberRuntime.RSVP.Promise(function (resolve) {
+      resume = resolve;
+    }, 'TestAdapter paused promise');
+  }
+});
+enifed('ember-testing/helpers/trigger_event', ['exports', 'ember-testing/events'], function (exports, _events) {
+  'use strict';
+
+  exports.default = triggerEvent;
+
+  /**
+    Triggers the given DOM event on the element identified by the provided selector.
+    Example:
+    ```javascript
+    triggerEvent('#some-elem-id', 'blur');
+    ```
+    This is actually used internally by the `keyEvent` helper like so:
+    ```javascript
+    triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 });
+    ```
+   @method triggerEvent
+   @param {String} selector jQuery selector for finding element on the DOM
+   @param {String} [context] jQuery selector that will limit the selector
+                             argument to find only within the context's children
+   @param {String} type The event type to be triggered.
+   @param {Object} [options] The options to be passed to jQuery.Event.
+   @return {RSVP.Promise<undefined>}
+   @since 1.5.0
+   @public
+  */
+  function triggerEvent(app, selector, contextOrType, typeOrOptions, possibleOptions) {
+    var arity = arguments.length;
+    var context = void 0,
+        type = void 0,
+        options = void 0;
+
+    if (arity === 3) {
+      // context and options are optional, so this is
+      // app, selector, type
+      context = null;
+      type = contextOrType;
+      options = {};
+    } else if (arity === 4) {
+      // context and options are optional, so this is
+      if (typeof typeOrOptions === 'object') {
+        // either
+        // app, selector, type, options
+        context = null;
+        type = contextOrType;
+        options = typeOrOptions;
+      } else {
+        // or
+        // app, selector, context, type
+        context = contextOrType;
+        type = typeOrOptions;
+        options = {};
+      }
+    } else {
+      context = contextOrType;
+      type = typeOrOptions;
+      options = possibleOptions;
+    }
+
+    var $el = app.testHelpers.findWithAssert(selector, context);
+    var el = $el[0];
+
+    (0, _events.fireEvent)(el, type, options);
+
+    return app.testHelpers.wait();
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/visit', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = visit;
+
+
+  /**
+    Loads a route, sets up any controllers, and renders any templates associated
+    with the route as though a real user had triggered the route change while
+    using your app.
+  
+    Example:
+  
+    ```javascript
+    visit('posts/index').then(function() {
+      // assert something
+    });
+    ```
+  
+    @method visit
+    @param {String} url the name of the route
+    @return {RSVP.Promise<undefined>}
+    @public
+  */
+  function visit(app, url) {
+    var router = app.__container__.lookup('router:main');
+    var shouldHandleURL = false;
+
+    app.boot().then(function () {
+      router.location.setURL(url);
+
+      if (shouldHandleURL) {
+        (0, _emberMetal.run)(app.__deprecatedInstance__, 'handleURL', url);
+      }
+    });
+
+    if (app._readinessDeferrals > 0) {
+      router['initialURL'] = url;
+      (0, _emberMetal.run)(app, 'advanceReadiness');
+      delete router['initialURL'];
+    } else {
+      shouldHandleURL = true;
+    }
+
+    return app.testHelpers.wait();
+  } /**
+    @module ember
+    */
+});
+enifed('ember-testing/helpers/wait', ['exports', 'ember-testing/test/waiters', 'ember-runtime', 'ember-metal', 'ember-testing/test/pending_requests'], function (exports, _waiters, _emberRuntime, _emberMetal, _pending_requests) {
+  'use strict';
+
+  exports.default = wait;
+
+
+  /**
+    Causes the run loop to process any pending events. This is used to ensure that
+    any async operations from other helpers (or your assertions) have been processed.
+  
+    This is most often used as the return value for the helper functions (see 'click',
+    'fillIn','visit',etc). However, there is a method to register a test helper which
+    utilizes this method without the need to actually call `wait()` in your helpers.
+  
+    The `wait` helper is built into `registerAsyncHelper` by default. You will not need
+    to `return app.testHelpers.wait();` - the wait behavior is provided for you.
+  
+    Example:
+  
+    ```javascript
+    import { registerAsyncHelper } from '@ember/test';
+  
+    registerAsyncHelper('loginUser', function(app, username, password) {
+      visit('secured/path/here')
+        .fillIn('#username', username)
+        .fillIn('#password', password)
+        .click('.submit');
+    });
+    ```
+  
+    @method wait
+    @param {Object} value The value to be returned.
+    @return {RSVP.Promise<any>} Promise that resolves to the passed value.
+    @public
+    @since 1.0.0
+  */
+  /**
+  @module ember
+  */
+  function wait(app, value) {
+    return new _emberRuntime.RSVP.Promise(function (resolve) {
+      var router = app.__container__.lookup('router:main');
+
+      // Every 10ms, poll for the async thing to have finished
+      var watcher = setInterval(function () {
+        // 1. If the router is loading, keep polling
+        var routerIsLoading = router._routerMicrolib && !!router._routerMicrolib.activeTransition;
+        if (routerIsLoading) {
+          return;
+        }
+
+        // 2. If there are pending Ajax requests, keep polling
+        if ((0, _pending_requests.pendingRequests)()) {
+          return;
+        }
+
+        // 3. If there are scheduled timers or we are inside of a run loop, keep polling
+        if (_emberMetal.run.hasScheduledTimers() || _emberMetal.run.currentRunLoop) {
+          return;
+        }
+
+        if ((0, _waiters.checkWaiters)()) {
+          return;
+        }
+
+        // Stop polling
+        clearInterval(watcher);
+
+        // Synchronously resolve the promise
+        (0, _emberMetal.run)(null, resolve, value);
+      }, 10);
+    });
+  }
+});
+enifed('ember-testing/index', ['exports', 'ember-testing/test', 'ember-testing/adapters/adapter', 'ember-testing/setup_for_testing', 'ember-testing/adapters/qunit', 'ember-testing/support', 'ember-testing/ext/application', 'ember-testing/ext/rsvp', 'ember-testing/helpers', 'ember-testing/initializers'], function (exports, _test, _adapter, _setup_for_testing, _qunit) {
+  'use strict';
+
+  exports.QUnitAdapter = exports.setupForTesting = exports.Adapter = exports.Test = undefined;
+  Object.defineProperty(exports, 'Test', {
+    enumerable: true,
+    get: function () {
+      return _test.default;
+    }
+  });
+  Object.defineProperty(exports, 'Adapter', {
+    enumerable: true,
+    get: function () {
+      return _adapter.default;
+    }
+  });
+  Object.defineProperty(exports, 'setupForTesting', {
+    enumerable: true,
+    get: function () {
+      return _setup_for_testing.default;
+    }
+  });
+  Object.defineProperty(exports, 'QUnitAdapter', {
+    enumerable: true,
+    get: function () {
+      return _qunit.default;
+    }
+  });
+});
+enifed('ember-testing/initializers', ['ember-runtime'], function (_emberRuntime) {
+  'use strict';
+
+  var name = 'deferReadiness in `testing` mode';
+
+  (0, _emberRuntime.onLoad)('Ember.Application', function (Application) {
+    if (!Application.initializers[name]) {
+      Application.initializer({
+        name: name,
+
+        initialize: function (application) {
+          if (application.testing) {
+            application.deferReadiness();
+          }
+        }
+      });
+    }
+  });
+});
+enifed('ember-testing/setup_for_testing', ['exports', 'ember-debug', 'ember-views', 'ember-testing/test/adapter', 'ember-testing/test/pending_requests', 'ember-testing/adapters/adapter', 'ember-testing/adapters/qunit'], function (exports, _emberDebug, _emberViews, _adapter, _pending_requests, _adapter2, _qunit) {
+  'use strict';
+
+  exports.default = setupForTesting;
+
+
+  /**
+    Sets Ember up for testing. This is useful to perform
+    basic setup steps in order to unit test.
+  
+    Use `App.setupForTesting` to perform integration tests (full
+    application testing).
+  
+    @method setupForTesting
+    @namespace Ember
+    @since 1.5.0
+    @private
+  */
+  /* global self */
+
+  function setupForTesting() {
+    (0, _emberDebug.setTesting)(true);
+
+    var adapter = (0, _adapter.getAdapter)();
+    // if adapter is not manually set default to QUnit
+    if (!adapter) {
+      (0, _adapter.setAdapter)(typeof self.QUnit === 'undefined' ? new _adapter2.default() : new _qunit.default());
+    }
+
+    if (_emberViews.jQuery) {
+      (0, _emberViews.jQuery)(document).off('ajaxSend', _pending_requests.incrementPendingRequests);
+      (0, _emberViews.jQuery)(document).off('ajaxComplete', _pending_requests.decrementPendingRequests);
+
+      (0, _pending_requests.clearPendingRequests)();
+
+      (0, _emberViews.jQuery)(document).on('ajaxSend', _pending_requests.incrementPendingRequests);
+      (0, _emberViews.jQuery)(document).on('ajaxComplete', _pending_requests.decrementPendingRequests);
+    }
+  }
+});
+enifed('ember-testing/support', ['ember-debug', 'ember-views', 'ember-environment'], function (_emberDebug, _emberViews, _emberEnvironment) {
+  'use strict';
+
+  /**
+    @module ember
+  */
+
+  var $ = _emberViews.jQuery;
+
+  /**
+    This method creates a checkbox and triggers the click event to fire the
+    passed in handler. It is used to correct for a bug in older versions
+    of jQuery (e.g 1.8.3).
+  
+    @private
+    @method testCheckboxClick
+  */
+  function testCheckboxClick(handler) {
+    var input = document.createElement('input');
+    $(input).attr('type', 'checkbox').css({ position: 'absolute', left: '-1000px', top: '-1000px' }).appendTo('body').on('click', handler).trigger('click').remove();
+  }
+
+  if (_emberEnvironment.environment.hasDOM && typeof $ === 'function') {
+    $(function () {
+      /*
+        Determine whether a checkbox checked using jQuery's "click" method will have
+        the correct value for its checked property.
+         If we determine that the current jQuery version exhibits this behavior,
+        patch it to work correctly as in the commit for the actual fix:
+        https://github.com/jquery/jquery/commit/1fb2f92.
+      */
+      testCheckboxClick(function () {
+        if (!this.checked && !$.event.special.click) {
+          $.event.special.click = {
+            trigger: function () {
+              if ($.nodeName(this, 'input') && this.type === 'checkbox' && this.click) {
+                this.click();
+                return false;
+              }
+            }
+          };
+        }
+      });
+
+      // Try again to verify that the patch took effect or blow up.
+      testCheckboxClick(function () {
+        (true && (0, _emberDebug.warn)('clicked checkboxes should be checked! the jQuery patch didn\'t work', this.checked, { id: 'ember-testing.test-checkbox-click' }));
+      });
+    });
+  }
+});
+enifed('ember-testing/test', ['exports', 'ember-testing/test/helpers', 'ember-testing/test/on_inject_helpers', 'ember-testing/test/promise', 'ember-testing/test/waiters', 'ember-testing/test/adapter'], function (exports, _helpers, _on_inject_helpers, _promise, _waiters, _adapter) {
+  'use strict';
+
+  /**
+    This is a container for an assortment of testing related functionality:
+  
+    * Choose your default test adapter (for your framework of choice).
+    * Register/Unregister additional test helpers.
+    * Setup callbacks to be fired when the test helpers are injected into
+      your application.
+  
+    @class Test
+    @namespace Ember
+    @public
+  */
+  var Test = {
+    /**
+      Hash containing all known test helpers.
+       @property _helpers
+      @private
+      @since 1.7.0
+    */
+    _helpers: _helpers.helpers,
+
+    registerHelper: _helpers.registerHelper,
+    registerAsyncHelper: _helpers.registerAsyncHelper,
+    unregisterHelper: _helpers.unregisterHelper,
+    onInjectHelpers: _on_inject_helpers.onInjectHelpers,
+    Promise: _promise.default,
+    promise: _promise.promise,
+    resolve: _promise.resolve,
+    registerWaiter: _waiters.registerWaiter,
+    unregisterWaiter: _waiters.unregisterWaiter,
+    checkWaiters: _waiters.checkWaiters
+  };
+
+  /**
+   Used to allow ember-testing to communicate with a specific testing
+   framework.
+  
+   You can manually set it before calling `App.setupForTesting()`.
+  
+   Example:
+  
+   ```javascript
+   Ember.Test.adapter = MyCustomAdapter.create()
+   ```
+  
+   If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`.
+  
+   @public
+   @for Ember.Test
+   @property adapter
+   @type {Class} The adapter to be used.
+   @default Ember.Test.QUnitAdapter
+  */
+  /**
+    @module ember
+  */
+  Object.defineProperty(Test, 'adapter', {
+    get: _adapter.getAdapter,
+    set: _adapter.setAdapter
+  });
+
+  exports.default = Test;
+});
+enifed('ember-testing/test/adapter', ['exports', 'ember-console', 'ember-metal'], function (exports, _emberConsole, _emberMetal) {
+  'use strict';
+
+  exports.getAdapter = getAdapter;
+  exports.setAdapter = setAdapter;
+  exports.asyncStart = asyncStart;
+  exports.asyncEnd = asyncEnd;
+
+
+  var adapter = void 0;
+  function getAdapter() {
+    return adapter;
+  }
+
+  function setAdapter(value) {
+    adapter = value;
+    if (value && typeof value.exception === 'function') {
+      (0, _emberMetal.setDispatchOverride)(adapterDispatch);
+    } else {
+      (0, _emberMetal.setDispatchOverride)(null);
+    }
+  }
+
+  function asyncStart() {
+    if (adapter) {
+      adapter.asyncStart();
+    }
+  }
+
+  function asyncEnd() {
+    if (adapter) {
+      adapter.asyncEnd();
+    }
+  }
+
+  function adapterDispatch(error) {
+    adapter.exception(error);
+    _emberConsole.default.error(error.stack);
+  }
+});
+enifed('ember-testing/test/helpers', ['exports', 'ember-testing/test/promise'], function (exports, _promise) {
+  'use strict';
+
+  exports.helpers = undefined;
+  exports.registerHelper = registerHelper;
+  exports.registerAsyncHelper = registerAsyncHelper;
+  exports.unregisterHelper = unregisterHelper;
+  var helpers = exports.helpers = {};
+  /**
+   @module @ember/test
+  */
+
+  /**
+    `registerHelper` is used to register a test helper that will be injected
+    when `App.injectTestHelpers` is called.
+  
+    The helper method will always be called with the current Application as
+    the first parameter.
+  
+    For example:
+  
+    ```javascript
+    import { registerHelper } from '@ember/test';
+    import { run } from '@ember/runloop';
+  
+    registerHelper('boot', function(app) {
+      run(app, app.advanceReadiness);
+    });
+    ```
+  
+    This helper can later be called without arguments because it will be
+    called with `app` as the first parameter.
+  
+    ```javascript
+    import Application from '@ember/application';
+  
+    App = Application.create();
+    App.injectTestHelpers();
+    boot();
+    ```
+  
+    @public
+    @for @ember/test
+    @static
+    @method registerHelper
+    @param {String} name The name of the helper method to add.
+    @param {Function} helperMethod
+    @param options {Object}
+  */
+  function registerHelper(name, helperMethod) {
+    helpers[name] = {
+      method: helperMethod,
+      meta: { wait: false }
+    };
+  }
+
+  /**
+    `registerAsyncHelper` is used to register an async test helper that will be injected
+    when `App.injectTestHelpers` is called.
+  
+    The helper method will always be called with the current Application as
+    the first parameter.
+  
+    For example:
+  
+    ```javascript
+    import { registerAsyncHelper } from '@ember/test';
+    import { run } from '@ember/runloop';
+  
+    registerAsyncHelper('boot', function(app) {
+      run(app, app.advanceReadiness);
+    });
+    ```
+  
+    The advantage of an async helper is that it will not run
+    until the last async helper has completed.  All async helpers
+    after it will wait for it complete before running.
+  
+  
+    For example:
+  
+    ```javascript
+    import { registerAsyncHelper } from '@ember/test';
+  
+    registerAsyncHelper('deletePost', function(app, postId) {
+      click('.delete-' + postId);
+    });
+  
+    // ... in your test
+    visit('/post/2');
+    deletePost(2);
+    visit('/post/3');
+    deletePost(3);
+    ```
+  
+    @public
+    @for @ember/test
+    @method registerAsyncHelper
+    @param {String} name The name of the helper method to add.
+    @param {Function} helperMethod
+    @since 1.2.0
+  */
+  function registerAsyncHelper(name, helperMethod) {
+    helpers[name] = {
+      method: helperMethod,
+      meta: { wait: true }
+    };
+  }
+
+  /**
+    Remove a previously added helper method.
+  
+    Example:
+  
+    ```javascript
+    import { unregisterHelper } from '@ember/test';
+  
+    unregisterHelper('wait');
+    ```
+  
+    @public
+    @method unregisterHelper
+    @static
+    @for @ember/test
+    @param {String} name The helper to remove.
+  */
+  function unregisterHelper(name) {
+    delete helpers[name];
+    delete _promise.default.prototype[name];
+  }
+});
+enifed("ember-testing/test/on_inject_helpers", ["exports"], function (exports) {
+  "use strict";
+
+  exports.onInjectHelpers = onInjectHelpers;
+  exports.invokeInjectHelpersCallbacks = invokeInjectHelpersCallbacks;
+  var callbacks = exports.callbacks = [];
+
+  /**
+    Used to register callbacks to be fired whenever `App.injectTestHelpers`
+    is called.
+  
+    The callback will receive the current application as an argument.
+  
+    Example:
+  
+    ```javascript
+    import $ from 'jquery';
+  
+    Ember.Test.onInjectHelpers(function() {
+      $(document).ajaxSend(function() {
+        Test.pendingRequests++;
+      });
+  
+      $(document).ajaxComplete(function() {
+        Test.pendingRequests--;
+      });
+    });
+    ```
+  
+    @public
+    @for Ember.Test
+    @method onInjectHelpers
+    @param {Function} callback The function to be called.
+  */
+  function onInjectHelpers(callback) {
+    callbacks.push(callback);
+  }
+
+  function invokeInjectHelpersCallbacks(app) {
+    for (var i = 0; i < callbacks.length; i++) {
+      callbacks[i](app);
+    }
+  }
+});
+enifed("ember-testing/test/pending_requests", ["exports"], function (exports) {
+  "use strict";
+
+  exports.pendingRequests = pendingRequests;
+  exports.clearPendingRequests = clearPendingRequests;
+  exports.incrementPendingRequests = incrementPendingRequests;
+  exports.decrementPendingRequests = decrementPendingRequests;
+  var requests = [];
+
+  function pendingRequests() {
+    return requests.length;
+  }
+
+  function clearPendingRequests() {
+    requests.length = 0;
+  }
+
+  function incrementPendingRequests(_, xhr) {
+    requests.push(xhr);
+  }
+
+  function decrementPendingRequests(_, xhr) {
+    for (var i = 0; i < requests.length; i++) {
+      if (xhr === requests[i]) {
+        requests.splice(i, 1);
+        break;
+      }
+    }
+  }
+});
+enifed('ember-testing/test/promise', ['exports', 'ember-babel', 'ember-runtime', 'ember-testing/test/run'], function (exports, _emberBabel, _emberRuntime, _run) {
+  'use strict';
+
+  exports.promise = promise;
+  exports.resolve = resolve;
+  exports.getLastPromise = getLastPromise;
+
+
+  var lastPromise = void 0;
+
+  var TestPromise = function (_RSVP$Promise) {
+    (0, _emberBabel.inherits)(TestPromise, _RSVP$Promise);
+
+    function TestPromise() {
+      (0, _emberBabel.classCallCheck)(this, TestPromise);
+
+      var _this = (0, _emberBabel.possibleConstructorReturn)(this, _RSVP$Promise.apply(this, arguments));
+
+      lastPromise = _this;
+      return _this;
+    }
+
+    TestPromise.prototype.then = function then(_onFulfillment) {
+      var _RSVP$Promise$prototy;
+
+      var onFulfillment = typeof _onFulfillment === 'function' ? function (result) {
+        return isolate(_onFulfillment, result);
+      } : undefined;
+
+      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+        args[_key - 1] = arguments[_key];
+      }
+
+      return (_RSVP$Promise$prototy = _RSVP$Promise.prototype.then).call.apply(_RSVP$Promise$prototy, [this, onFulfillment].concat(args));
+    };
+
+    return TestPromise;
+  }(_emberRuntime.RSVP.Promise);
+
+  exports.default = TestPromise;
+
+
+  /**
+    This returns a thenable tailored for testing.  It catches failed
+    `onSuccess` callbacks and invokes the `Ember.Test.adapter.exception`
+    callback in the last chained then.
+  
+    This method should be returned by async helpers such as `wait`.
+  
+    @public
+    @for Ember.Test
+    @method promise
+    @param {Function} resolver The function used to resolve the promise.
+    @param {String} label An optional string for identifying the promise.
+  */
+  function promise(resolver, label) {
+    var fullLabel = 'Ember.Test.promise: ' + (label || '<Unknown Promise>');
+    return new TestPromise(resolver, fullLabel);
+  }
+
+  /**
+    Replacement for `Ember.RSVP.resolve`
+    The only difference is this uses
+    an instance of `Ember.Test.Promise`
+  
+    @public
+    @for Ember.Test
+    @method resolve
+    @param {Mixed} The value to resolve
+    @since 1.2.0
+  */
+  function resolve(result, label) {
+    return TestPromise.resolve(result, label);
+  }
+
+  function getLastPromise() {
+    return lastPromise;
+  }
+
+  // This method isolates nested async methods
+  // so that they don't conflict with other last promises.
+  //
+  // 1. Set `Ember.Test.lastPromise` to null
+  // 2. Invoke method
+  // 3. Return the last promise created during method
+  function isolate(onFulfillment, result) {
+    // Reset lastPromise for nested helpers
+    lastPromise = null;
+
+    var value = onFulfillment(result);
+
+    var promise = lastPromise;
+    lastPromise = null;
+
+    // If the method returned a promise
+    // return that promise. If not,
+    // return the last async helper's promise
+    if (value && value instanceof TestPromise || !promise) {
+      return value;
+    } else {
+      return (0, _run.default)(function () {
+        return resolve(promise).then(function () {
+          return value;
+        });
+      });
+    }
+  }
+});
+enifed('ember-testing/test/run', ['exports', 'ember-metal'], function (exports, _emberMetal) {
+  'use strict';
+
+  exports.default = run;
+  function run(fn) {
+    if (!_emberMetal.run.currentRunLoop) {
+      return (0, _emberMetal.run)(fn);
+    } else {
+      return fn();
+    }
+  }
+});
+enifed("ember-testing/test/waiters", ["exports"], function (exports) {
+  "use strict";
+
+  exports.registerWaiter = registerWaiter;
+  exports.unregisterWaiter = unregisterWaiter;
+  exports.checkWaiters = checkWaiters;
+  /**
+   @module @ember/test
+  */
+  var contexts = [];
+  var callbacks = [];
+
+  /**
+     This allows ember-testing to play nicely with other asynchronous
+     events, such as an application that is waiting for a CSS3
+     transition or an IndexDB transaction. The waiter runs periodically
+     after each async helper (i.e. `click`, `andThen`, `visit`, etc) has executed,
+     until the returning result is truthy. After the waiters finish, the next async helper
+     is executed and the process repeats.
+  
+     For example:
+  
+     ```javascript
+     import { registerWaiter } from '@ember/test';
+  
+     registerWaiter(function() {
+       return myPendingTransactions() == 0;
+     });
+     ```
+     The `context` argument allows you to optionally specify the `this`
+     with which your callback will be invoked.
+  
+     For example:
+  
+     ```javascript
+     import { registerWaiter } from '@ember/test';
+  
+     registerWaiter(MyDB, MyDB.hasPendingTransactions);
+     ```
+  
+     @public
+     @for @ember/test
+     @static
+     @method registerWaiter
+     @param {Object} context (optional)
+     @param {Function} callback
+     @since 1.2.0
+  */
+  function registerWaiter(context, callback) {
+    if (arguments.length === 1) {
+      callback = context;
+      context = null;
+    }
+    if (indexOf(context, callback) > -1) {
+      return;
+    }
+    contexts.push(context);
+    callbacks.push(callback);
+  }
+
+  /**
+     `unregisterWaiter` is used to unregister a callback that was
+     registered with `registerWaiter`.
+  
+     @public
+     @for @ember/test
+     @static
+     @method unregisterWaiter
+     @param {Object} context (optional)
+     @param {Function} callback
+     @since 1.2.0
+  */
+  function unregisterWaiter(context, callback) {
+    if (!callbacks.length) {
+      return;
+    }
+    if (arguments.length === 1) {
+      callback = context;
+      context = null;
+    }
+    var i = indexOf(context, callback);
+    if (i === -1) {
+      return;
+    }
+    contexts.splice(i, 1);
+    callbacks.splice(i, 1);
+  }
+
+  /**
+    Iterates through each registered test waiter, and invokes
+    its callback. If any waiter returns false, this method will return
+    true indicating that the waiters have not settled yet.
+  
+    This is generally used internally from the acceptance/integration test
+    infrastructure.
+  
+    @public
+    @for @ember/test
+    @static
+    @method checkWaiters
+  */
+  function checkWaiters() {
+    if (!callbacks.length) {
+      return false;
+    }
+    for (var i = 0; i < callbacks.length; i++) {
+      var context = contexts[i];
+      var callback = callbacks[i];
+      if (!callback.call(context)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  function indexOf(context, callback) {
+    for (var i = 0; i < callbacks.length; i++) {
+      if (callbacks[i] === callback && contexts[i] === context) {
+        return i;
+      }
+    }
+    return -1;
+  }
+});
+/*global enifed */
+enifed('node-module', ['exports'], function(_exports) {
+  var IS_NODE = typeof module === 'object' && typeof module.require === 'function';
+  if (IS_NODE) {
+    _exports.require = module.require;
+    _exports.module = module;
+    _exports.IS_NODE = IS_NODE;
+  } else {
+    _exports.require = null;
+    _exports.module = null;
+    _exports.IS_NODE = IS_NODE;
+  }
+});
+var testing = requireModule('ember-testing');
+Ember.Test = testing.Test;
+Ember.Test.Adapter = testing.Adapter;
+Ember.Test.QUnitAdapter = testing.QUnitAdapter;
+Ember.setupForTesting = testing.setupForTesting;
+}());
+
+/* globals require, Ember, jQuery */
+
+(() => {
+  if (typeof jQuery !== 'undefined') {
+    let _Ember;
+    if (typeof Ember !== 'undefined') {
+      _Ember = Ember;
+    } else {
+      _Ember = require('ember').default;
+    }
+
+    let pendingRequests;
+    if (Ember.__loader.registry['ember-testing/test/pending_requests']) {
+      pendingRequests = Ember.__loader.require('ember-testing/test/pending_requests');
+    }
+
+    if (pendingRequests) {
+      // This exists to ensure that the AJAX listeners setup by Ember itself
+      // (which as of 2.17 are not properly torn down) get cleared and released
+      // when the application is destroyed. Without this, any AJAX requests
+      // that happen _between_ acceptance tests will always share
+      // `pendingRequests`.
+      _Ember.Application.reopen({
+        willDestroy() {
+          jQuery(document).off('ajaxSend', pendingRequests.incrementPendingRequests);
+          jQuery(document).off('ajaxComplete', pendingRequests.decrementPendingRequests);
+
+          pendingRequests.clearPendingRequests();
+
+          this._super(...arguments);
+        }
+      });
+    }
+  }
+})();
+/*!
+ * QUnit 2.5.1
+ * https://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2018-02-28T01:37Z
+ */
+(function (global$1) {
+  'use strict';
+
+  global$1 = global$1 && global$1.hasOwnProperty('default') ? global$1['default'] : global$1;
+
+  var window = global$1.window;
+  var self$1 = global$1.self;
+  var console = global$1.console;
+  var setTimeout = global$1.setTimeout;
+  var clearTimeout = global$1.clearTimeout;
+
+  var document = window && window.document;
+  var navigator = window && window.navigator;
+
+  var localSessionStorage = function () {
+  	var x = "qunit-test-string";
+  	try {
+  		global$1.sessionStorage.setItem(x, x);
+  		global$1.sessionStorage.removeItem(x);
+  		return global$1.sessionStorage;
+  	} catch (e) {
+  		return 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 && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
+
+
+
+
+
+
+
+
+
+
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  var toConsumableArray = function (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);
+    }
+  };
+
+  var toString = Object.prototype.toString;
+  var hasOwn = Object.prototype.hasOwnProperty;
+  var now = Date.now || function () {
+  	return new Date().getTime();
+  };
+
+  var defined = {
+  	document: window && window.document !== undefined,
+  	setTimeout: setTimeout !== undefined
+  };
+
+  // Returns a new Array with the elements that are in a but not in b
+  function diff(a, b) {
+  	var i,
+  	    j,
+  	    result = a.slice();
+
+  	for (i = 0; i < result.length; i++) {
+  		for (j = 0; j < b.length; j++) {
+  			if (result[i] === b[j]) {
+  				result.splice(i, 1);
+  				i--;
+  				break;
+  			}
+  		}
+  	}
+  	return result;
+  }
+
+  /**
+   * Determines whether an element exists in a given array or not.
+   *
+   * @method inArray
+   * @param {Any} elem
+   * @param {Array} array
+   * @return {Boolean}
+   */
+  function inArray(elem, array) {
+  	return array.indexOf(elem) !== -1;
+  }
+
+  /**
+   * Makes a clone of an object using only Array or Object as base,
+   * and copies over the own enumerable properties.
+   *
+   * @param {Object} obj
+   * @return {Object} New object with only the own properties (recursively).
+   */
+  function objectValues(obj) {
+  	var key,
+  	    val,
+  	    vals = is("array", obj) ? [] : {};
+  	for (key in obj) {
+  		if (hasOwn.call(obj, key)) {
+  			val = obj[key];
+  			vals[key] = val === Object(val) ? objectValues(val) : val;
+  		}
+  	}
+  	return vals;
+  }
+
+  function extend(a, b, undefOnly) {
+  	for (var prop in b) {
+  		if (hasOwn.call(b, prop)) {
+  			if (b[prop] === undefined) {
+  				delete a[prop];
+  			} else if (!(undefOnly && typeof a[prop] !== "undefined")) {
+  				a[prop] = b[prop];
+  			}
+  		}
+  	}
+
+  	return a;
+  }
+
+  function objectType(obj) {
+  	if (typeof obj === "undefined") {
+  		return "undefined";
+  	}
+
+  	// Consider: typeof null === object
+  	if (obj === null) {
+  		return "null";
+  	}
+
+  	var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
+  	    type = match && match[1];
+
+  	switch (type) {
+  		case "Number":
+  			if (isNaN(obj)) {
+  				return "nan";
+  			}
+  			return "number";
+  		case "String":
+  		case "Boolean":
+  		case "Array":
+  		case "Set":
+  		case "Map":
+  		case "Date":
+  		case "RegExp":
+  		case "Function":
+  		case "Symbol":
+  			return type.toLowerCase();
+  		default:
+  			return typeof obj === "undefined" ? "undefined" : _typeof(obj);
+  	}
+  }
+
+  // Safe object type checking
+  function is(type, obj) {
+  	return objectType(obj) === type;
+  }
+
+  // Based on Java's String.hashCode, a simple but not
+  // rigorously collision resistant hashing function
+  function generateHash(module, testName) {
+  	var str = module + "\x1C" + testName;
+  	var hash = 0;
+
+  	for (var i = 0; i < str.length; i++) {
+  		hash = (hash << 5) - hash + str.charCodeAt(i);
+  		hash |= 0;
+  	}
+
+  	// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+  	// strictly necessary but increases user understanding that the id is a SHA-like hash
+  	var hex = (0x100000000 + hash).toString(16);
+  	if (hex.length < 8) {
+  		hex = "0000000" + hex;
+  	}
+
+  	return hex.slice(-8);
+  }
+
+  // Test for equality any JavaScript type.
+  // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
+  var equiv = (function () {
+
+  	// Value pairs queued for comparison. Used for breadth-first processing order, recursion
+  	// detection and avoiding repeated comparison (see below for details).
+  	// Elements are { a: val, b: val }.
+  	var pairs = [];
+
+  	var getProto = Object.getPrototypeOf || function (obj) {
+  		return obj.__proto__;
+  	};
+
+  	function useStrictEquality(a, b) {
+
+  		// This only gets called if a and b are not strict equal, and is used to compare on
+  		// the primitive values inside object wrappers. For example:
+  		// `var i = 1;`
+  		// `var j = new Number(1);`
+  		// Neither a nor b can be null, as a !== b and they have the same type.
+  		if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
+  			a = a.valueOf();
+  		}
+  		if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
+  			b = b.valueOf();
+  		}
+
+  		return a === b;
+  	}
+
+  	function compareConstructors(a, b) {
+  		var protoA = getProto(a);
+  		var protoB = getProto(b);
+
+  		// Comparing constructors is more strict than using `instanceof`
+  		if (a.constructor === b.constructor) {
+  			return true;
+  		}
+
+  		// Ref #851
+  		// If the obj prototype descends from a null constructor, treat it
+  		// as a null prototype.
+  		if (protoA && protoA.constructor === null) {
+  			protoA = null;
+  		}
+  		if (protoB && protoB.constructor === null) {
+  			protoB = null;
+  		}
+
+  		// Allow objects with no prototype to be equivalent to
+  		// objects with Object as their constructor.
+  		if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
+  			return true;
+  		}
+
+  		return false;
+  	}
+
+  	function getRegExpFlags(regexp) {
+  		return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
+  	}
+
+  	function isContainer(val) {
+  		return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
+  	}
+
+  	function breadthFirstCompareChild(a, b) {
+
+  		// If a is a container not reference-equal to b, postpone the comparison to the
+  		// end of the pairs queue -- unless (a, b) has been seen before, in which case skip
+  		// over the pair.
+  		if (a === b) {
+  			return true;
+  		}
+  		if (!isContainer(a)) {
+  			return typeEquiv(a, b);
+  		}
+  		if (pairs.every(function (pair) {
+  			return pair.a !== a || pair.b !== b;
+  		})) {
+
+  			// Not yet started comparing this pair
+  			pairs.push({ a: a, b: b });
+  		}
+  		return true;
+  	}
+
+  	var callbacks = {
+  		"string": useStrictEquality,
+  		"boolean": useStrictEquality,
+  		"number": useStrictEquality,
+  		"null": useStrictEquality,
+  		"undefined": useStrictEquality,
+  		"symbol": useStrictEquality,
+  		"date": useStrictEquality,
+
+  		"nan": function nan() {
+  			return true;
+  		},
+
+  		"regexp": function regexp(a, b) {
+  			return a.source === b.source &&
+
+  			// Include flags in the comparison
+  			getRegExpFlags(a) === getRegExpFlags(b);
+  		},
+
+  		// abort (identical references / instance methods were skipped earlier)
+  		"function": function _function() {
+  			return false;
+  		},
+
+  		"array": function array(a, b) {
+  			var i, len;
+
+  			len = a.length;
+  			if (len !== b.length) {
+
+  				// Safe and faster
+  				return false;
+  			}
+
+  			for (i = 0; i < len; i++) {
+
+  				// Compare non-containers; queue non-reference-equal containers
+  				if (!breadthFirstCompareChild(a[i], b[i])) {
+  					return false;
+  				}
+  			}
+  			return true;
+  		},
+
+  		// Define sets a and b to be equivalent if for each element aVal in a, there
+  		// is some element bVal in b such that aVal and bVal are equivalent. Element
+  		// repetitions are not counted, so these are equivalent:
+  		// a = new Set( [ {}, [], [] ] );
+  		// b = new Set( [ {}, {}, [] ] );
+  		"set": function set$$1(a, b) {
+  			var innerEq,
+  			    outerEq = true;
+
+  			if (a.size !== b.size) {
+
+  				// This optimization has certain quirks because of the lack of
+  				// repetition counting. For instance, adding the same
+  				// (reference-identical) element to two equivalent sets can
+  				// make them non-equivalent.
+  				return false;
+  			}
+
+  			a.forEach(function (aVal) {
+
+  				// Short-circuit if the result is already known. (Using for...of
+  				// with a break clause would be cleaner here, but it would cause
+  				// a syntax error on older Javascript implementations even if
+  				// Set is unused)
+  				if (!outerEq) {
+  					return;
+  				}
+
+  				innerEq = false;
+
+  				b.forEach(function (bVal) {
+  					var parentPairs;
+
+  					// Likewise, short-circuit if the result is already known
+  					if (innerEq) {
+  						return;
+  					}
+
+  					// Swap out the global pairs list, as the nested call to
+  					// innerEquiv will clobber its contents
+  					parentPairs = pairs;
+  					if (innerEquiv(bVal, aVal)) {
+  						innerEq = true;
+  					}
+
+  					// Replace the global pairs list
+  					pairs = parentPairs;
+  				});
+
+  				if (!innerEq) {
+  					outerEq = false;
+  				}
+  			});
+
+  			return outerEq;
+  		},
+
+  		// Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
+  		// in a, there is some key-value pair (bKey, bVal) in b such that
+  		// [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
+  		// counted, so these are equivalent:
+  		// a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
+  		// b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
+  		"map": function map(a, b) {
+  			var innerEq,
+  			    outerEq = true;
+
+  			if (a.size !== b.size) {
+
+  				// This optimization has certain quirks because of the lack of
+  				// repetition counting. For instance, adding the same
+  				// (reference-identical) key-value pair to two equivalent maps
+  				// can make them non-equivalent.
+  				return false;
+  			}
+
+  			a.forEach(function (aVal, aKey) {
+
+  				// Short-circuit if the result is already known. (Using for...of
+  				// with a break clause would be cleaner here, but it would cause
+  				// a syntax error on older Javascript implementations even if
+  				// Map is unused)
+  				if (!outerEq) {
+  					return;
+  				}
+
+  				innerEq = false;
+
+  				b.forEach(function (bVal, bKey) {
+  					var parentPairs;
+
+  					// Likewise, short-circuit if the result is already known
+  					if (innerEq) {
+  						return;
+  					}
+
+  					// Swap out the global pairs list, as the nested call to
+  					// innerEquiv will clobber its contents
+  					parentPairs = pairs;
+  					if (innerEquiv([bVal, bKey], [aVal, aKey])) {
+  						innerEq = true;
+  					}
+
+  					// Replace the global pairs list
+  					pairs = parentPairs;
+  				});
+
+  				if (!innerEq) {
+  					outerEq = false;
+  				}
+  			});
+
+  			return outerEq;
+  		},
+
+  		"object": function object(a, b) {
+  			var i,
+  			    aProperties = [],
+  			    bProperties = [];
+
+  			if (compareConstructors(a, b) === false) {
+  				return false;
+  			}
+
+  			// Be strict: don't ensure hasOwnProperty and go deep
+  			for (i in a) {
+
+  				// Collect a's properties
+  				aProperties.push(i);
+
+  				// Skip OOP methods that look the same
+  				if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
+  					continue;
+  				}
+
+  				// Compare non-containers; queue non-reference-equal containers
+  				if (!breadthFirstCompareChild(a[i], b[i])) {
+  					return false;
+  				}
+  			}
+
+  			for (i in b) {
+
+  				// Collect b's properties
+  				bProperties.push(i);
+  			}
+
+  			// Ensures identical properties name
+  			return typeEquiv(aProperties.sort(), bProperties.sort());
+  		}
+  	};
+
+  	function typeEquiv(a, b) {
+  		var type = objectType(a);
+
+  		// Callbacks for containers will append to the pairs queue to achieve breadth-first
+  		// search order. The pairs queue is also used to avoid reprocessing any pair of
+  		// containers that are reference-equal to a previously visited pair (a special case
+  		// this being recursion detection).
+  		//
+  		// Because of this approach, once typeEquiv returns a false value, it should not be
+  		// called again without clearing the pair queue else it may wrongly report a visited
+  		// pair as being equivalent.
+  		return objectType(b) === type && callbacks[type](a, b);
+  	}
+
+  	function innerEquiv(a, b) {
+  		var i, pair;
+
+  		// We're done when there's nothing more to compare
+  		if (arguments.length < 2) {
+  			return true;
+  		}
+
+  		// Clear the global pair queue and add the top-level values being compared
+  		pairs = [{ a: a, b: b }];
+
+  		for (i = 0; i < pairs.length; i++) {
+  			pair = pairs[i];
+
+  			// Perform type-specific comparison on any pairs that are not strictly
+  			// equal. For container types, that comparison will postpone comparison
+  			// of any sub-container pair to the end of the pair queue. This gives
+  			// breadth-first search order. It also avoids the reprocessing of
+  			// reference-equal siblings, cousins etc, which can have a significant speed
+  			// impact when comparing a container of small objects each of which has a
+  			// reference to the same (singleton) large object.
+  			if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
+  				return false;
+  			}
+  		}
+
+  		// ...across all consecutive argument pairs
+  		return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
+  	}
+
+  	return function () {
+  		var result = innerEquiv.apply(undefined, arguments);
+
+  		// Release any retained objects
+  		pairs.length = 0;
+  		return result;
+  	};
+  })();
+
+  /**
+   * Config object: Maintain internal state
+   * Later exposed as QUnit.config
+   * `config` initialized at top of scope
+   */
+  var config = {
+
+  	// The queue of tests to run
+  	queue: [],
+
+  	// Block until document ready
+  	blocking: true,
+
+  	// By default, run previously failed tests first
+  	// very useful in combination with "Hide passed tests" checked
+  	reorder: true,
+
+  	// By default, modify document.title when suite is done
+  	altertitle: true,
+
+  	// HTML Reporter: collapse every test except the first failing test
+  	// If false, all failing tests will be expanded
+  	collapse: true,
+
+  	// By default, scroll to top of the page when suite is done
+  	scrolltop: true,
+
+  	// Depth up-to which object will be dumped
+  	maxDepth: 5,
+
+  	// When enabled, all tests must call expect()
+  	requireExpects: false,
+
+  	// Placeholder for user-configurable form-exposed URL parameters
+  	urlConfig: [],
+
+  	// Set of all modules.
+  	modules: [],
+
+  	// The first unnamed module
+  	currentModule: {
+  		name: "",
+  		tests: [],
+  		childModules: [],
+  		testsRun: 0,
+  		unskippedTestsRun: 0,
+  		hooks: {
+  			before: [],
+  			beforeEach: [],
+  			afterEach: [],
+  			after: []
+  		}
+  	},
+
+  	callbacks: {},
+
+  	// The storage module to use for reordering tests
+  	storage: localSessionStorage
+  };
+
+  // take a predefined QUnit.config and extend the defaults
+  var globalConfig = window && window.QUnit && window.QUnit.config;
+
+  // only extend the global config if there is no QUnit overload
+  if (window && window.QUnit && !window.QUnit.version) {
+  	extend(config, globalConfig);
+  }
+
+  // Push a loose unnamed module to the modules collection
+  config.modules.push(config.currentModule);
+
+  // Based on jsDump by Ariel Flesler
+  // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+  var dump = (function () {
+  	function quote(str) {
+  		return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+  	}
+  	function literal(o) {
+  		return o + "";
+  	}
+  	function join(pre, arr, post) {
+  		var s = dump.separator(),
+  		    base = dump.indent(),
+  		    inner = dump.indent(1);
+  		if (arr.join) {
+  			arr = arr.join("," + s + inner);
+  		}
+  		if (!arr) {
+  			return pre + post;
+  		}
+  		return [pre, inner + arr, base + post].join(s);
+  	}
+  	function array(arr, stack) {
+  		var i = arr.length,
+  		    ret = new Array(i);
+
+  		if (dump.maxDepth && dump.depth > dump.maxDepth) {
+  			return "[object Array]";
+  		}
+
+  		this.up();
+  		while (i--) {
+  			ret[i] = this.parse(arr[i], undefined, stack);
+  		}
+  		this.down();
+  		return join("[", ret, "]");
+  	}
+
+  	function isArray(obj) {
+  		return (
+
+  			//Native Arrays
+  			toString.call(obj) === "[object Array]" ||
+
+  			// NodeList objects
+  			typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
+  		);
+  	}
+
+  	var reName = /^function (\w+)/,
+  	    dump = {
+
+  		// The objType is used mostly internally, you can fix a (custom) type in advance
+  		parse: function parse(obj, objType, stack) {
+  			stack = stack || [];
+  			var res,
+  			    parser,
+  			    parserType,
+  			    objIndex = stack.indexOf(obj);
+
+  			if (objIndex !== -1) {
+  				return "recursion(" + (objIndex - stack.length) + ")";
+  			}
+
+  			objType = objType || this.typeOf(obj);
+  			parser = this.parsers[objType];
+  			parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
+
+  			if (parserType === "function") {
+  				stack.push(obj);
+  				res = parser.call(this, obj, stack);
+  				stack.pop();
+  				return res;
+  			}
+  			return parserType === "string" ? parser : this.parsers.error;
+  		},
+  		typeOf: function typeOf(obj) {
+  			var type;
+
+  			if (obj === null) {
+  				type = "null";
+  			} else if (typeof obj === "undefined") {
+  				type = "undefined";
+  			} else if (is("regexp", obj)) {
+  				type = "regexp";
+  			} else if (is("date", obj)) {
+  				type = "date";
+  			} else if (is("function", obj)) {
+  				type = "function";
+  			} else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
+  				type = "window";
+  			} else if (obj.nodeType === 9) {
+  				type = "document";
+  			} else if (obj.nodeType) {
+  				type = "node";
+  			} else if (isArray(obj)) {
+  				type = "array";
+  			} else if (obj.constructor === Error.prototype.constructor) {
+  				type = "error";
+  			} else {
+  				type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
+  			}
+  			return type;
+  		},
+
+  		separator: function separator() {
+  			if (this.multiline) {
+  				return this.HTML ? "<br />" : "\n";
+  			} else {
+  				return this.HTML ? "&#160;" : " ";
+  			}
+  		},
+
+  		// Extra can be a number, shortcut for increasing-calling-decreasing
+  		indent: function indent(extra) {
+  			if (!this.multiline) {
+  				return "";
+  			}
+  			var chr = this.indentChar;
+  			if (this.HTML) {
+  				chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
+  			}
+  			return new Array(this.depth + (extra || 0)).join(chr);
+  		},
+  		up: function up(a) {
+  			this.depth += a || 1;
+  		},
+  		down: function down(a) {
+  			this.depth -= a || 1;
+  		},
+  		setParser: function setParser(name, parser) {
+  			this.parsers[name] = parser;
+  		},
+
+  		// The next 3 are exposed so you can use them
+  		quote: quote,
+  		literal: literal,
+  		join: join,
+  		depth: 1,
+  		maxDepth: config.maxDepth,
+
+  		// This is the list of parsers, to modify them, use dump.setParser
+  		parsers: {
+  			window: "[Window]",
+  			document: "[Document]",
+  			error: function error(_error) {
+  				return "Error(\"" + _error.message + "\")";
+  			},
+  			unknown: "[Unknown]",
+  			"null": "null",
+  			"undefined": "undefined",
+  			"function": function _function(fn) {
+  				var ret = "function",
+
+
+  				// Functions never have name in IE
+  				name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+  				if (name) {
+  					ret += " " + name;
+  				}
+  				ret += "(";
+
+  				ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
+  				return join(ret, dump.parse(fn, "functionCode"), "}");
+  			},
+  			array: array,
+  			nodelist: array,
+  			"arguments": array,
+  			object: function object(map, stack) {
+  				var keys,
+  				    key,
+  				    val,
+  				    i,
+  				    nonEnumerableProperties,
+  				    ret = [];
+
+  				if (dump.maxDepth && dump.depth > dump.maxDepth) {
+  					return "[object Object]";
+  				}
+
+  				dump.up();
+  				keys = [];
+  				for (key in map) {
+  					keys.push(key);
+  				}
+
+  				// Some properties are not always enumerable on Error objects.
+  				nonEnumerableProperties = ["message", "name"];
+  				for (i in nonEnumerableProperties) {
+  					key = nonEnumerableProperties[i];
+  					if (key in map && !inArray(key, keys)) {
+  						keys.push(key);
+  					}
+  				}
+  				keys.sort();
+  				for (i = 0; i < keys.length; i++) {
+  					key = keys[i];
+  					val = map[key];
+  					ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
+  				}
+  				dump.down();
+  				return join("{", ret, "}");
+  			},
+  			node: function node(_node) {
+  				var len,
+  				    i,
+  				    val,
+  				    open = dump.HTML ? "&lt;" : "<",
+  				    close = dump.HTML ? "&gt;" : ">",
+  				    tag = _node.nodeName.toLowerCase(),
+  				    ret = open + tag,
+  				    attrs = _node.attributes;
+
+  				if (attrs) {
+  					for (i = 0, len = attrs.length; i < len; i++) {
+  						val = attrs[i].nodeValue;
+
+  						// IE6 includes all attributes in .attributes, even ones not explicitly
+  						// set. Those have values like undefined, null, 0, false, "" or
+  						// "inherit".
+  						if (val && val !== "inherit") {
+  							ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
+  						}
+  					}
+  				}
+  				ret += close;
+
+  				// Show content of TextNode or CDATASection
+  				if (_node.nodeType === 3 || _node.nodeType === 4) {
+  					ret += _node.nodeValue;
+  				}
+
+  				return ret + open + "/" + tag + close;
+  			},
+
+  			// Function calls it internally, it's the arguments part of the function
+  			functionArgs: function functionArgs(fn) {
+  				var args,
+  				    l = fn.length;
+
+  				if (!l) {
+  					return "";
+  				}
+
+  				args = new Array(l);
+  				while (l--) {
+
+  					// 97 is 'a'
+  					args[l] = String.fromCharCode(97 + l);
+  				}
+  				return " " + args.join(", ") + " ";
+  			},
+
+  			// Object calls it internally, the key part of an item in a map
+  			key: quote,
+
+  			// Function calls it internally, it's the content of the function
+  			functionCode: "[code]",
+
+  			// Node calls it internally, it's a html attribute value
+  			attribute: quote,
+  			string: quote,
+  			date: quote,
+  			regexp: literal,
+  			number: literal,
+  			"boolean": literal,
+  			symbol: function symbol(sym) {
+  				return sym.toString();
+  			}
+  		},
+
+  		// If true, entities are escaped ( <, >, \t, space and \n )
+  		HTML: false,
+
+  		// Indentation unit
+  		indentChar: "  ",
+
+  		// If true, items in a collection, are separated by a \n, else just a space.
+  		multiline: true
+  	};
+
+  	return dump;
+  })();
+
+  var LISTENERS = Object.create(null);
+  var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
+
+  /**
+   * Emits an event with the specified data to all currently registered listeners.
+   * Callbacks will fire in the order in which they are registered (FIFO). This
+   * function is not exposed publicly; it is used by QUnit internals to emit
+   * logging events.
+   *
+   * @private
+   * @method emit
+   * @param {String} eventName
+   * @param {Object} data
+   * @return {Void}
+   */
+  function emit(eventName, data) {
+  	if (objectType(eventName) !== "string") {
+  		throw new TypeError("eventName must be a string when emitting an event");
+  	}
+
+  	// Clone the callbacks in case one of them registers a new callback
+  	var originalCallbacks = LISTENERS[eventName];
+  	var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
+
+  	for (var i = 0; i < callbacks.length; i++) {
+  		callbacks[i](data);
+  	}
+  }
+
+  /**
+   * Registers a callback as a listener to the specified event.
+   *
+   * @public
+   * @method on
+   * @param {String} eventName
+   * @param {Function} callback
+   * @return {Void}
+   */
+  function on(eventName, callback) {
+  	if (objectType(eventName) !== "string") {
+  		throw new TypeError("eventName must be a string when registering a listener");
+  	} else if (!inArray(eventName, SUPPORTED_EVENTS)) {
+  		var events = SUPPORTED_EVENTS.join(", ");
+  		throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
+  	} else if (objectType(callback) !== "function") {
+  		throw new TypeError("callback must be a function when registering a listener");
+  	}
+
+  	if (!LISTENERS[eventName]) {
+  		LISTENERS[eventName] = [];
+  	}
+
+  	// Don't register the same callback more than once
+  	if (!inArray(callback, LISTENERS[eventName])) {
+  		LISTENERS[eventName].push(callback);
+  	}
+  }
+
+  // Register logging callbacks
+  function registerLoggingCallbacks(obj) {
+  	var i,
+  	    l,
+  	    key,
+  	    callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
+
+  	function registerLoggingCallback(key) {
+  		var loggingCallback = function loggingCallback(callback) {
+  			if (objectType(callback) !== "function") {
+  				throw new Error("QUnit logging methods require a callback function as their first parameters.");
+  			}
+
+  			config.callbacks[key].push(callback);
+  		};
+
+  		return loggingCallback;
+  	}
+
+  	for (i = 0, l = callbackNames.length; i < l; i++) {
+  		key = callbackNames[i];
+
+  		// Initialize key collection of logging callback
+  		if (objectType(config.callbacks[key]) === "undefined") {
+  			config.callbacks[key] = [];
+  		}
+
+  		obj[key] = registerLoggingCallback(key);
+  	}
+  }
+
+  function runLoggingCallbacks(key, args) {
+  	var i, l, callbacks;
+
+  	callbacks = config.callbacks[key];
+  	for (i = 0, l = callbacks.length; i < l; i++) {
+  		callbacks[i](args);
+  	}
+  }
+
+  // Doesn't support IE9, it will return undefined on these browsers
+  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+  var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
+
+  function extractStacktrace(e, offset) {
+  	offset = offset === undefined ? 4 : offset;
+
+  	var stack, include, i;
+
+  	if (e && e.stack) {
+  		stack = e.stack.split("\n");
+  		if (/^error$/i.test(stack[0])) {
+  			stack.shift();
+  		}
+  		if (fileName) {
+  			include = [];
+  			for (i = offset; i < stack.length; i++) {
+  				if (stack[i].indexOf(fileName) !== -1) {
+  					break;
+  				}
+  				include.push(stack[i]);
+  			}
+  			if (include.length) {
+  				return include.join("\n");
+  			}
+  		}
+  		return stack[offset];
+  	}
+  }
+
+  function sourceFromStacktrace(offset) {
+  	var error = new Error();
+
+  	// Support: Safari <=7 only, IE <=10 - 11 only
+  	// Not all browsers generate the `stack` property for `new Error()`, see also #636
+  	if (!error.stack) {
+  		try {
+  			throw error;
+  		} catch (err) {
+  			error = err;
+  		}
+  	}
+
+  	return extractStacktrace(error, offset);
+  }
+
+  var priorityCount = 0;
+  var unitSampler = void 0;
+
+  /**
+   * Advances the ProcessingQueue to the next item if it is ready.
+   * @param {Boolean} last
+   */
+  function advance() {
+  	var start = now();
+  	config.depth = (config.depth || 0) + 1;
+
+  	while (config.queue.length && !config.blocking) {
+  		var elapsedTime = now() - start;
+
+  		if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
+  			if (priorityCount > 0) {
+  				priorityCount--;
+  			}
+
+  			config.queue.shift()();
+  		} else {
+  			setTimeout(advance);
+  			break;
+  		}
+  	}
+
+  	config.depth--;
+
+  	if (!config.blocking && !config.queue.length && config.depth === 0) {
+  		done();
+  	}
+  }
+
+  function addToQueueImmediate(callback) {
+  	if (objectType(callback) === "array") {
+  		while (callback.length) {
+  			addToQueueImmediate(callback.pop());
+  		}
+
+  		return;
+  	}
+
+  	config.queue.unshift(callback);
+  	priorityCount++;
+  }
+
+  /**
+   * Adds a function to the ProcessingQueue for execution.
+   * @param {Function|Array} callback
+   * @param {Boolean} priority
+   * @param {String} seed
+   */
+  function addToQueue(callback, prioritize, seed) {
+  	if (prioritize) {
+  		config.queue.splice(priorityCount++, 0, callback);
+  	} else if (seed) {
+  		if (!unitSampler) {
+  			unitSampler = unitSamplerGenerator(seed);
+  		}
+
+  		// Insert into a random position after all prioritized items
+  		var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
+  		config.queue.splice(priorityCount + index, 0, callback);
+  	} else {
+  		config.queue.push(callback);
+  	}
+  }
+
+  /**
+   * Creates a seeded "sample" generator which is used for randomizing tests.
+   */
+  function unitSamplerGenerator(seed) {
+
+  	// 32-bit xorshift, requires only a nonzero seed
+  	// http://excamera.com/sphinx/article-xorshift.html
+  	var sample = parseInt(generateHash(seed), 16) || -1;
+  	return function () {
+  		sample ^= sample << 13;
+  		sample ^= sample >>> 17;
+  		sample ^= sample << 5;
+
+  		// ECMAScript has no unsigned number type
+  		if (sample < 0) {
+  			sample += 0x100000000;
+  		}
+
+  		return sample / 0x100000000;
+  	};
+  }
+
+  /**
+   * This function is called when the ProcessingQueue is done processing all
+   * items. It handles emitting the final run events.
+   */
+  function done() {
+  	var storage = config.storage;
+
+  	ProcessingQueue.finished = true;
+
+  	var runtime = now() - config.started;
+  	var passed = config.stats.all - config.stats.bad;
+
+  	emit("runEnd", globalSuite.end(true));
+  	runLoggingCallbacks("done", {
+  		passed: passed,
+  		failed: config.stats.bad,
+  		total: config.stats.all,
+  		runtime: runtime
+  	});
+
+  	// Clear own storage items if all tests passed
+  	if (storage && config.stats.bad === 0) {
+  		for (var i = storage.length - 1; i >= 0; i--) {
+  			var key = storage.key(i);
+
+  			if (key.indexOf("qunit-test-") === 0) {
+  				storage.removeItem(key);
+  			}
+  		}
+  	}
+  }
+
+  var ProcessingQueue = {
+  	finished: false,
+  	add: addToQueue,
+  	addImmediate: addToQueueImmediate,
+  	advance: advance
+  };
+
+  var TestReport = function () {
+  	function TestReport(name, suite, options) {
+  		classCallCheck(this, TestReport);
+
+  		this.name = name;
+  		this.suiteName = suite.name;
+  		this.fullName = suite.fullName.concat(name);
+  		this.runtime = 0;
+  		this.assertions = [];
+
+  		this.skipped = !!options.skip;
+  		this.todo = !!options.todo;
+
+  		this.valid = options.valid;
+
+  		this._startTime = 0;
+  		this._endTime = 0;
+
+  		suite.pushTest(this);
+  	}
+
+  	createClass(TestReport, [{
+  		key: "start",
+  		value: function start(recordTime) {
+  			if (recordTime) {
+  				this._startTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				suiteName: this.suiteName,
+  				fullName: this.fullName.slice()
+  			};
+  		}
+  	}, {
+  		key: "end",
+  		value: function end(recordTime) {
+  			if (recordTime) {
+  				this._endTime = Date.now();
+  			}
+
+  			return extend(this.start(), {
+  				runtime: this.getRuntime(),
+  				status: this.getStatus(),
+  				errors: this.getFailedAssertions(),
+  				assertions: this.getAssertions()
+  			});
+  		}
+  	}, {
+  		key: "pushAssertion",
+  		value: function pushAssertion(assertion) {
+  			this.assertions.push(assertion);
+  		}
+  	}, {
+  		key: "getRuntime",
+  		value: function getRuntime() {
+  			return this._endTime - this._startTime;
+  		}
+  	}, {
+  		key: "getStatus",
+  		value: function getStatus() {
+  			if (this.skipped) {
+  				return "skipped";
+  			}
+
+  			var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
+
+  			if (!testPassed) {
+  				return "failed";
+  			} else if (this.todo) {
+  				return "todo";
+  			} else {
+  				return "passed";
+  			}
+  		}
+  	}, {
+  		key: "getFailedAssertions",
+  		value: function getFailedAssertions() {
+  			return this.assertions.filter(function (assertion) {
+  				return !assertion.passed;
+  			});
+  		}
+  	}, {
+  		key: "getAssertions",
+  		value: function getAssertions() {
+  			return this.assertions.slice();
+  		}
+
+  		// Remove actual and expected values from assertions. This is to prevent
+  		// leaking memory throughout a test suite.
+
+  	}, {
+  		key: "slimAssertions",
+  		value: function slimAssertions() {
+  			this.assertions = this.assertions.map(function (assertion) {
+  				delete assertion.actual;
+  				delete assertion.expected;
+  				return assertion;
+  			});
+  		}
+  	}]);
+  	return TestReport;
+  }();
+
+  var focused$1 = false;
+
+  function Test(settings) {
+  	var i, l;
+
+  	++Test.count;
+
+  	this.expected = null;
+  	this.assertions = [];
+  	this.semaphore = 0;
+  	this.module = config.currentModule;
+  	this.stack = sourceFromStacktrace(3);
+  	this.steps = [];
+  	this.timeout = undefined;
+
+  	// If a module is skipped, all its tests and the tests of the child suites
+  	// should be treated as skipped even if they are defined as `only` or `todo`.
+  	// As for `todo` module, all its tests will be treated as `todo` except for
+  	// tests defined as `skip` which will be left intact.
+  	//
+  	// So, if a test is defined as `todo` and is inside a skipped module, we should
+  	// then treat that test as if was defined as `skip`.
+  	if (this.module.skip) {
+  		settings.skip = true;
+  		settings.todo = false;
+
+  		// Skipped tests should be left intact
+  	} else if (this.module.todo && !settings.skip) {
+  		settings.todo = true;
+  	}
+
+  	extend(this, settings);
+
+  	this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
+  		todo: settings.todo,
+  		skip: settings.skip,
+  		valid: this.valid()
+  	});
+
+  	// Register unique strings
+  	for (i = 0, l = this.module.tests; i < l.length; i++) {
+  		if (this.module.tests[i].name === this.testName) {
+  			this.testName += " ";
+  		}
+  	}
+
+  	this.testId = generateHash(this.module.name, this.testName);
+
+  	this.module.tests.push({
+  		name: this.testName,
+  		testId: this.testId,
+  		skip: !!settings.skip
+  	});
+
+  	if (settings.skip) {
+
+  		// Skipped tests will fully ignore any sent callback
+  		this.callback = function () {};
+  		this.async = false;
+  		this.expected = 0;
+  	} else {
+  		if (typeof this.callback !== "function") {
+  			var method = this.todo ? "todo" : "test";
+
+  			// eslint-disable-next-line max-len
+  			throw new TypeError("You must provide a function as a test callback to QUnit." + method + "(\"" + settings.testName + "\")");
+  		}
+
+  		this.assert = new Assert(this);
+  	}
+  }
+
+  Test.count = 0;
+
+  function getNotStartedModules(startModule) {
+  	var module = startModule,
+  	    modules = [];
+
+  	while (module && module.testsRun === 0) {
+  		modules.push(module);
+  		module = module.parentModule;
+  	}
+
+  	return modules;
+  }
+
+  Test.prototype = {
+  	before: function before() {
+  		var i,
+  		    startModule,
+  		    module = this.module,
+  		    notStartedModules = getNotStartedModules(module);
+
+  		for (i = notStartedModules.length - 1; i >= 0; i--) {
+  			startModule = notStartedModules[i];
+  			startModule.stats = { all: 0, bad: 0, started: now() };
+  			emit("suiteStart", startModule.suiteReport.start(true));
+  			runLoggingCallbacks("moduleStart", {
+  				name: startModule.name,
+  				tests: startModule.tests
+  			});
+  		}
+
+  		config.current = this;
+
+  		this.testEnvironment = extend({}, module.testEnvironment);
+
+  		this.started = now();
+  		emit("testStart", this.testReport.start(true));
+  		runLoggingCallbacks("testStart", {
+  			name: this.testName,
+  			module: module.name,
+  			testId: this.testId,
+  			previousFailure: this.previousFailure
+  		});
+
+  		if (!config.pollution) {
+  			saveGlobal();
+  		}
+  	},
+
+  	run: function run() {
+  		var promise;
+
+  		config.current = this;
+
+  		this.callbackStarted = now();
+
+  		if (config.notrycatch) {
+  			runTest(this);
+  			return;
+  		}
+
+  		try {
+  			runTest(this);
+  		} catch (e) {
+  			this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
+
+  			// Else next test will carry the responsibility
+  			saveGlobal();
+
+  			// Restart the tests if they're blocking
+  			if (config.blocking) {
+  				internalRecover(this);
+  			}
+  		}
+
+  		function runTest(test) {
+  			promise = test.callback.call(test.testEnvironment, test.assert);
+  			test.resolvePromise(promise);
+
+  			// If the test has a "lock" on it, but the timeout is 0, then we push a
+  			// failure as the test should be synchronous.
+  			if (test.timeout === 0 && test.semaphore !== 0) {
+  				pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
+  			}
+  		}
+  	},
+
+  	after: function after() {
+  		checkPollution();
+  	},
+
+  	queueHook: function queueHook(hook, hookName, hookOwner) {
+  		var _this = this;
+
+  		var callHook = function callHook() {
+  			var promise = hook.call(_this.testEnvironment, _this.assert);
+  			_this.resolvePromise(promise, hookName);
+  		};
+
+  		var runHook = function runHook() {
+  			if (hookName === "before") {
+  				if (hookOwner.unskippedTestsRun !== 0) {
+  					return;
+  				}
+
+  				_this.preserveEnvironment = true;
+  			}
+
+  			if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
+  				return;
+  			}
+
+  			config.current = _this;
+  			if (config.notrycatch) {
+  				callHook();
+  				return;
+  			}
+  			try {
+  				callHook();
+  			} catch (error) {
+  				_this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+  			}
+  		};
+
+  		return runHook;
+  	},
+
+
+  	// Currently only used for module level hooks, can be used to add global level ones
+  	hooks: function hooks(handler) {
+  		var hooks = [];
+
+  		function processHooks(test, module) {
+  			if (module.parentModule) {
+  				processHooks(test, module.parentModule);
+  			}
+
+  			if (module.hooks[handler].length) {
+  				for (var i = 0; i < module.hooks[handler].length; i++) {
+  					hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
+  				}
+  			}
+  		}
+
+  		// Hooks are ignored on skipped tests
+  		if (!this.skip) {
+  			processHooks(this, this.module);
+  		}
+
+  		return hooks;
+  	},
+
+
+  	finish: function finish() {
+  		config.current = this;
+
+  		if (this.steps.length) {
+  			var stepsList = this.steps.join(", ");
+  			this.pushFailure("Expected assert.verifySteps() to be called before end of test " + ("after using assert.step(). Unverified steps: " + stepsList), this.stack);
+  		}
+
+  		if (config.requireExpects && this.expected === null) {
+  			this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
+  		} else if (this.expected !== null && this.expected !== this.assertions.length) {
+  			this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
+  		} else if (this.expected === null && !this.assertions.length) {
+  			this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
+  		}
+
+  		var i,
+  		    module = this.module,
+  		    moduleName = module.name,
+  		    testName = this.testName,
+  		    skipped = !!this.skip,
+  		    todo = !!this.todo,
+  		    bad = 0,
+  		    storage = config.storage;
+
+  		this.runtime = now() - this.started;
+
+  		config.stats.all += this.assertions.length;
+  		module.stats.all += this.assertions.length;
+
+  		for (i = 0; i < this.assertions.length; i++) {
+  			if (!this.assertions[i].result) {
+  				bad++;
+  				config.stats.bad++;
+  				module.stats.bad++;
+  			}
+  		}
+
+  		notifyTestsRan(module, skipped);
+
+  		// Store result when possible
+  		if (storage) {
+  			if (bad) {
+  				storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
+  			} else {
+  				storage.removeItem("qunit-test-" + moduleName + "-" + testName);
+  			}
+  		}
+
+  		// After emitting the js-reporters event we cleanup the assertion data to
+  		// avoid leaking it. It is not used by the legacy testDone callbacks.
+  		emit("testEnd", this.testReport.end(true));
+  		this.testReport.slimAssertions();
+
+  		runLoggingCallbacks("testDone", {
+  			name: testName,
+  			module: moduleName,
+  			skipped: skipped,
+  			todo: todo,
+  			failed: bad,
+  			passed: this.assertions.length - bad,
+  			total: this.assertions.length,
+  			runtime: skipped ? 0 : this.runtime,
+
+  			// HTML Reporter use
+  			assertions: this.assertions,
+  			testId: this.testId,
+
+  			// Source of Test
+  			source: this.stack
+  		});
+
+  		if (module.testsRun === numberOfTests(module)) {
+  			logSuiteEnd(module);
+
+  			// Check if the parent modules, iteratively, are done. If that the case,
+  			// we emit the `suiteEnd` event and trigger `moduleDone` callback.
+  			var parent = module.parentModule;
+  			while (parent && parent.testsRun === numberOfTests(parent)) {
+  				logSuiteEnd(parent);
+  				parent = parent.parentModule;
+  			}
+  		}
+
+  		config.current = undefined;
+
+  		function logSuiteEnd(module) {
+  			emit("suiteEnd", module.suiteReport.end(true));
+  			runLoggingCallbacks("moduleDone", {
+  				name: module.name,
+  				tests: module.tests,
+  				failed: module.stats.bad,
+  				passed: module.stats.all - module.stats.bad,
+  				total: module.stats.all,
+  				runtime: now() - module.stats.started
+  			});
+  		}
+  	},
+
+  	preserveTestEnvironment: function preserveTestEnvironment() {
+  		if (this.preserveEnvironment) {
+  			this.module.testEnvironment = this.testEnvironment;
+  			this.testEnvironment = extend({}, this.module.testEnvironment);
+  		}
+  	},
+
+  	queue: function queue() {
+  		var test = this;
+
+  		if (!this.valid()) {
+  			return;
+  		}
+
+  		function runTest() {
+
+  			// Each of these can by async
+  			ProcessingQueue.addImmediate([function () {
+  				test.before();
+  			}, test.hooks("before"), function () {
+  				test.preserveTestEnvironment();
+  			}, test.hooks("beforeEach"), function () {
+  				test.run();
+  			}, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
+  				test.after();
+  			}, function () {
+  				test.finish();
+  			}]);
+  		}
+
+  		var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
+
+  		// Prioritize previously failed tests, detected from storage
+  		var prioritize = config.reorder && !!previousFailCount;
+
+  		this.previousFailure = !!previousFailCount;
+
+  		ProcessingQueue.add(runTest, prioritize, config.seed);
+
+  		// If the queue has already finished, we manually process the new test
+  		if (ProcessingQueue.finished) {
+  			ProcessingQueue.advance();
+  		}
+  	},
+
+
+  	pushResult: function pushResult(resultInfo) {
+  		if (this !== config.current) {
+  			throw new Error("Assertion occurred after test had finished.");
+  		}
+
+  		// Destructure of resultInfo = { result, actual, expected, message, negative }
+  		var source,
+  		    details = {
+  			module: this.module.name,
+  			name: this.testName,
+  			result: resultInfo.result,
+  			message: resultInfo.message,
+  			actual: resultInfo.actual,
+  			testId: this.testId,
+  			negative: resultInfo.negative || false,
+  			runtime: now() - this.started,
+  			todo: !!this.todo
+  		};
+
+  		if (hasOwn.call(resultInfo, "expected")) {
+  			details.expected = resultInfo.expected;
+  		}
+
+  		if (!resultInfo.result) {
+  			source = resultInfo.source || sourceFromStacktrace();
+
+  			if (source) {
+  				details.source = source;
+  			}
+  		}
+
+  		this.logAssertion(details);
+
+  		this.assertions.push({
+  			result: !!resultInfo.result,
+  			message: resultInfo.message
+  		});
+  	},
+
+  	pushFailure: function pushFailure(message, source, actual) {
+  		if (!(this instanceof Test)) {
+  			throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
+  		}
+
+  		this.pushResult({
+  			result: false,
+  			message: message || "error",
+  			actual: actual || null,
+  			source: source
+  		});
+  	},
+
+  	/**
+    * Log assertion details using both the old QUnit.log interface and
+    * QUnit.on( "assertion" ) interface.
+    *
+    * @private
+    */
+  	logAssertion: function logAssertion(details) {
+  		runLoggingCallbacks("log", details);
+
+  		var assertion = {
+  			passed: details.result,
+  			actual: details.actual,
+  			expected: details.expected,
+  			message: details.message,
+  			stack: details.source,
+  			todo: details.todo
+  		};
+  		this.testReport.pushAssertion(assertion);
+  		emit("assertion", assertion);
+  	},
+
+
+  	resolvePromise: function resolvePromise(promise, phase) {
+  		var then,
+  		    resume,
+  		    message,
+  		    test = this;
+  		if (promise != null) {
+  			then = promise.then;
+  			if (objectType(then) === "function") {
+  				resume = internalStop(test);
+  				if (config.notrycatch) {
+  					then.call(promise, function () {
+  						resume();
+  					});
+  				} else {
+  					then.call(promise, function () {
+  						resume();
+  					}, function (error) {
+  						message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
+  						test.pushFailure(message, extractStacktrace(error, 0));
+
+  						// Else next test will carry the responsibility
+  						saveGlobal();
+
+  						// Unblock
+  						internalRecover(test);
+  					});
+  				}
+  			}
+  		}
+  	},
+
+  	valid: function valid() {
+  		var filter = config.filter,
+  		    regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
+  		    module = config.module && config.module.toLowerCase(),
+  		    fullName = this.module.name + ": " + this.testName;
+
+  		function moduleChainNameMatch(testModule) {
+  			var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
+  			if (testModuleName === module) {
+  				return true;
+  			} else if (testModule.parentModule) {
+  				return moduleChainNameMatch(testModule.parentModule);
+  			} else {
+  				return false;
+  			}
+  		}
+
+  		function moduleChainIdMatch(testModule) {
+  			return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
+  		}
+
+  		// Internally-generated tests are always valid
+  		if (this.callback && this.callback.validTest) {
+  			return true;
+  		}
+
+  		if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
+
+  			return false;
+  		}
+
+  		if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
+
+  			return false;
+  		}
+
+  		if (module && !moduleChainNameMatch(this.module)) {
+  			return false;
+  		}
+
+  		if (!filter) {
+  			return true;
+  		}
+
+  		return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
+  	},
+
+  	regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
+  		var regex = new RegExp(pattern, flags);
+  		var match = regex.test(fullName);
+
+  		return match !== exclude;
+  	},
+
+  	stringFilter: function stringFilter(filter, fullName) {
+  		filter = filter.toLowerCase();
+  		fullName = fullName.toLowerCase();
+
+  		var include = filter.charAt(0) !== "!";
+  		if (!include) {
+  			filter = filter.slice(1);
+  		}
+
+  		// If the filter matches, we need to honour include
+  		if (fullName.indexOf(filter) !== -1) {
+  			return include;
+  		}
+
+  		// Otherwise, do the opposite
+  		return !include;
+  	}
+  };
+
+  function pushFailure() {
+  	if (!config.current) {
+  		throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
+  	}
+
+  	// Gets current test obj
+  	var currentTest = config.current;
+
+  	return currentTest.pushFailure.apply(currentTest, arguments);
+  }
+
+  function saveGlobal() {
+  	config.pollution = [];
+
+  	if (config.noglobals) {
+  		for (var key in global$1) {
+  			if (hasOwn.call(global$1, key)) {
+
+  				// In Opera sometimes DOM element ids show up here, ignore them
+  				if (/^qunit-test-output/.test(key)) {
+  					continue;
+  				}
+  				config.pollution.push(key);
+  			}
+  		}
+  	}
+  }
+
+  function checkPollution() {
+  	var newGlobals,
+  	    deletedGlobals,
+  	    old = config.pollution;
+
+  	saveGlobal();
+
+  	newGlobals = diff(config.pollution, old);
+  	if (newGlobals.length > 0) {
+  		pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
+  	}
+
+  	deletedGlobals = diff(old, config.pollution);
+  	if (deletedGlobals.length > 0) {
+  		pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
+  	}
+  }
+
+  // Will be exposed as QUnit.test
+  function test(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback
+  	});
+
+  	newTest.queue();
+  }
+
+  function todo(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback,
+  		todo: true
+  	});
+
+  	newTest.queue();
+  }
+
+  // Will be exposed as QUnit.skip
+  function skip(testName) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var test = new Test({
+  		testName: testName,
+  		skip: true
+  	});
+
+  	test.queue();
+  }
+
+  // Will be exposed as QUnit.only
+  function only(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	config.queue.length = 0;
+  	focused$1 = true;
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback
+  	});
+
+  	newTest.queue();
+  }
+
+  // Put a hold on processing and return a function that will release it.
+  function internalStop(test) {
+  	test.semaphore += 1;
+  	config.blocking = true;
+
+  	// Set a recovery timeout, if so configured.
+  	if (defined.setTimeout) {
+  		var timeoutDuration = void 0;
+
+  		if (typeof test.timeout === "number") {
+  			timeoutDuration = test.timeout;
+  		} else if (typeof config.testTimeout === "number") {
+  			timeoutDuration = config.testTimeout;
+  		}
+
+  		if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
+  			clearTimeout(config.timeout);
+  			config.timeout = setTimeout(function () {
+  				pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
+  				internalRecover(test);
+  			}, timeoutDuration);
+  		}
+  	}
+
+  	var released = false;
+  	return function resume() {
+  		if (released) {
+  			return;
+  		}
+
+  		released = true;
+  		test.semaphore -= 1;
+  		internalStart(test);
+  	};
+  }
+
+  // Forcefully release all processing holds.
+  function internalRecover(test) {
+  	test.semaphore = 0;
+  	internalStart(test);
+  }
+
+  // Release a processing hold, scheduling a resumption attempt if no holds remain.
+  function internalStart(test) {
+
+  	// If semaphore is non-numeric, throw error
+  	if (isNaN(test.semaphore)) {
+  		test.semaphore = 0;
+
+  		pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
+  		return;
+  	}
+
+  	// Don't start until equal number of stop-calls
+  	if (test.semaphore > 0) {
+  		return;
+  	}
+
+  	// Throw an Error if start is called more often than stop
+  	if (test.semaphore < 0) {
+  		test.semaphore = 0;
+
+  		pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
+  		return;
+  	}
+
+  	// Add a slight delay to allow more assertions etc.
+  	if (defined.setTimeout) {
+  		if (config.timeout) {
+  			clearTimeout(config.timeout);
+  		}
+  		config.timeout = setTimeout(function () {
+  			if (test.semaphore > 0) {
+  				return;
+  			}
+
+  			if (config.timeout) {
+  				clearTimeout(config.timeout);
+  			}
+
+  			begin();
+  		});
+  	} else {
+  		begin();
+  	}
+  }
+
+  function collectTests(module) {
+  	var tests = [].concat(module.tests);
+  	var modules = [].concat(toConsumableArray(module.childModules));
+
+  	// Do a breadth-first traversal of the child modules
+  	while (modules.length) {
+  		var nextModule = modules.shift();
+  		tests.push.apply(tests, nextModule.tests);
+  		modules.push.apply(modules, toConsumableArray(nextModule.childModules));
+  	}
+
+  	return tests;
+  }
+
+  function numberOfTests(module) {
+  	return collectTests(module).length;
+  }
+
+  function numberOfUnskippedTests(module) {
+  	return collectTests(module).filter(function (test) {
+  		return !test.skip;
+  	}).length;
+  }
+
+  function notifyTestsRan(module, skipped) {
+  	module.testsRun++;
+  	if (!skipped) {
+  		module.unskippedTestsRun++;
+  	}
+  	while (module = module.parentModule) {
+  		module.testsRun++;
+  		if (!skipped) {
+  			module.unskippedTestsRun++;
+  		}
+  	}
+  }
+
+  /**
+   * Returns a function that proxies to the given method name on the globals
+   * console object. The proxy will also detect if the console doesn't exist and
+   * will appropriately no-op. This allows support for IE9, which doesn't have a
+   * console if the developer tools are not open.
+   */
+  function consoleProxy(method) {
+  	return function () {
+  		if (console) {
+  			console[method].apply(console, arguments);
+  		}
+  	};
+  }
+
+  var Logger = {
+  	warn: consoleProxy("warn")
+  };
+
+  var Assert = function () {
+  	function Assert(testContext) {
+  		classCallCheck(this, Assert);
+
+  		this.test = testContext;
+  	}
+
+  	// Assert helpers
+
+  	createClass(Assert, [{
+  		key: "timeout",
+  		value: function timeout(duration) {
+  			if (typeof duration !== "number") {
+  				throw new Error("You must pass a number as the duration to assert.timeout");
+  			}
+
+  			this.test.timeout = duration;
+  		}
+
+  		// Documents a "step", which is a string value, in a test as a passing assertion
+
+  	}, {
+  		key: "step",
+  		value: function step(message) {
+  			var result = !!message;
+
+  			this.test.steps.push(message);
+
+  			return this.pushResult({
+  				result: result,
+  				message: message || "You must provide a message to assert.step"
+  			});
+  		}
+
+  		// Verifies the steps in a test match a given array of string values
+
+  	}, {
+  		key: "verifySteps",
+  		value: function verifySteps(steps, message) {
+  			this.deepEqual(this.test.steps, steps, message);
+  			this.test.steps.length = 0;
+  		}
+
+  		// Specify the number of expected assertions to guarantee that failed test
+  		// (no assertions are run at all) don't slip through.
+
+  	}, {
+  		key: "expect",
+  		value: function expect(asserts) {
+  			if (arguments.length === 1) {
+  				this.test.expected = asserts;
+  			} else {
+  				return this.test.expected;
+  			}
+  		}
+
+  		// Put a hold on processing and return a function that will release it a maximum of once.
+
+  	}, {
+  		key: "async",
+  		value: function async(count) {
+  			var test$$1 = this.test;
+
+  			var popped = false,
+  			    acceptCallCount = count;
+
+  			if (typeof acceptCallCount === "undefined") {
+  				acceptCallCount = 1;
+  			}
+
+  			var resume = internalStop(test$$1);
+
+  			return function done() {
+  				if (config.current !== test$$1) {
+  					throw Error("assert.async callback called after test finished.");
+  				}
+
+  				if (popped) {
+  					test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
+  					return;
+  				}
+
+  				acceptCallCount -= 1;
+  				if (acceptCallCount > 0) {
+  					return;
+  				}
+
+  				popped = true;
+  				resume();
+  			};
+  		}
+
+  		// Exports test.push() to the user API
+  		// Alias of pushResult.
+
+  	}, {
+  		key: "push",
+  		value: function push(result, actual, expected, message, negative) {
+  			Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
+
+  			var currentAssert = this instanceof Assert ? this : config.current.assert;
+  			return currentAssert.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: negative
+  			});
+  		}
+  	}, {
+  		key: "pushResult",
+  		value: function pushResult(resultInfo) {
+
+  			// Destructure of resultInfo = { result, actual, expected, message, negative }
+  			var assert = this;
+  			var currentTest = assert instanceof Assert && assert.test || config.current;
+
+  			// Backwards compatibility fix.
+  			// Allows the direct use of global exported assertions and QUnit.assert.*
+  			// Although, it's use is not recommended as it can leak assertions
+  			// to other tests from async tests, because we only get a reference to the current test,
+  			// not exactly the test where assertion were intended to be called.
+  			if (!currentTest) {
+  				throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
+  			}
+
+  			if (!(assert instanceof Assert)) {
+  				assert = currentTest.assert;
+  			}
+
+  			return assert.test.pushResult(resultInfo);
+  		}
+  	}, {
+  		key: "ok",
+  		value: function ok(result, message) {
+  			if (!message) {
+  				message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
+  			}
+
+  			this.pushResult({
+  				result: !!result,
+  				actual: result,
+  				expected: true,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notOk",
+  		value: function notOk(result, message) {
+  			if (!message) {
+  				message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
+  			}
+
+  			this.pushResult({
+  				result: !result,
+  				actual: result,
+  				expected: false,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "equal",
+  		value: function equal(actual, expected, message) {
+
+  			// eslint-disable-next-line eqeqeq
+  			var result = expected == actual;
+
+  			this.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notEqual",
+  		value: function notEqual(actual, expected, message) {
+
+  			// eslint-disable-next-line eqeqeq
+  			var result = expected != actual;
+
+  			this.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "propEqual",
+  		value: function propEqual(actual, expected, message) {
+  			actual = objectValues(actual);
+  			expected = objectValues(expected);
+
+  			this.pushResult({
+  				result: equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notPropEqual",
+  		value: function notPropEqual(actual, expected, message) {
+  			actual = objectValues(actual);
+  			expected = objectValues(expected);
+
+  			this.pushResult({
+  				result: !equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "deepEqual",
+  		value: function deepEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notDeepEqual",
+  		value: function notDeepEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: !equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "strictEqual",
+  		value: function strictEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: expected === actual,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notStrictEqual",
+  		value: function notStrictEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: expected !== actual,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "throws",
+  		value: function throws(block, expected, message) {
+  			var actual = void 0,
+  			    result = false;
+
+  			var currentTest = this instanceof Assert && this.test || config.current;
+
+  			// 'expected' is optional unless doing string comparison
+  			if (objectType(expected) === "string") {
+  				if (message == null) {
+  					message = expected;
+  					expected = null;
+  				} else {
+  					throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
+  				}
+  			}
+
+  			currentTest.ignoreGlobalErrors = true;
+  			try {
+  				block.call(currentTest.testEnvironment);
+  			} catch (e) {
+  				actual = e;
+  			}
+  			currentTest.ignoreGlobalErrors = false;
+
+  			if (actual) {
+  				var expectedType = objectType(expected);
+
+  				// We don't want to validate thrown error
+  				if (!expected) {
+  					result = true;
+  					expected = null;
+
+  					// Expected is a regexp
+  				} else if (expectedType === "regexp") {
+  					result = expected.test(errorString(actual));
+
+  					// Expected is a constructor, maybe an Error constructor
+  				} else if (expectedType === "function" && actual instanceof expected) {
+  					result = true;
+
+  					// Expected is an Error object
+  				} else if (expectedType === "object") {
+  					result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+  					// Expected is a validation function which returns true if validation passed
+  				} else if (expectedType === "function" && expected.call({}, actual) === true) {
+  					expected = null;
+  					result = true;
+  				}
+  			}
+
+  			currentTest.assert.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "rejects",
+  		value: function rejects(promise, expected, message) {
+  			var result = false;
+
+  			var currentTest = this instanceof Assert && this.test || config.current;
+
+  			// 'expected' is optional unless doing string comparison
+  			if (objectType(expected) === "string") {
+  				if (message === undefined) {
+  					message = expected;
+  					expected = undefined;
+  				} else {
+  					message = "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary.";
+
+  					currentTest.assert.pushResult({
+  						result: false,
+  						message: message
+  					});
+
+  					return;
+  				}
+  			}
+
+  			var then = promise && promise.then;
+  			if (objectType(then) !== "function") {
+  				var _message = "The value provided to `assert.rejects` in " + "\"" + currentTest.testName + "\" was not a promise.";
+
+  				currentTest.assert.pushResult({
+  					result: false,
+  					message: _message,
+  					actual: promise
+  				});
+
+  				return;
+  			}
+
+  			var done = this.async();
+
+  			return then.call(promise, function handleFulfillment() {
+  				var message = "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest.testName + "\" did not reject.";
+
+  				currentTest.assert.pushResult({
+  					result: false,
+  					message: message,
+  					actual: promise
+  				});
+
+  				done();
+  			}, function handleRejection(actual) {
+  				if (actual) {
+  					var expectedType = objectType(expected);
+
+  					// We don't want to validate
+  					if (expected === undefined) {
+  						result = true;
+  						expected = null;
+
+  						// Expected is a regexp
+  					} else if (expectedType === "regexp") {
+  						result = expected.test(errorString(actual));
+
+  						// Expected is a constructor, maybe an Error constructor
+  					} else if (expectedType === "function" && actual instanceof expected) {
+  						result = true;
+
+  						// Expected is an Error object
+  					} else if (expectedType === "object") {
+  						result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+  						// Expected is a validation function which returns true if validation passed
+  					} else {
+  						if (expectedType === "function") {
+  							result = expected.call({}, actual) === true;
+  							expected = null;
+
+  							// Expected is some other invalid type
+  						} else {
+  							result = false;
+  							message = "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest.testName + "\": " + expectedType + ".";
+  						}
+  					}
+  				}
+
+  				currentTest.assert.pushResult({
+  					result: result,
+  					actual: actual,
+  					expected: expected,
+  					message: message
+  				});
+
+  				done();
+  			});
+  		}
+  	}]);
+  	return Assert;
+  }();
+
+  // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+  // Known to us are: Closure Compiler, Narwhal
+  // eslint-disable-next-line dot-notation
+
+
+  Assert.prototype.raises = Assert.prototype["throws"];
+
+  /**
+   * Converts an error into a simple string for comparisons.
+   *
+   * @param {Error} error
+   * @return {String}
+   */
+  function errorString(error) {
+  	var resultErrorString = error.toString();
+
+  	if (resultErrorString.substring(0, 7) === "[object") {
+  		var name = error.name ? error.name.toString() : "Error";
+  		var message = error.message ? error.message.toString() : "";
+
+  		if (name && message) {
+  			return name + ": " + message;
+  		} else if (name) {
+  			return name;
+  		} else if (message) {
+  			return message;
+  		} else {
+  			return "Error";
+  		}
+  	} else {
+  		return resultErrorString;
+  	}
+  }
+
+  /* global module, exports, define */
+  function exportQUnit(QUnit) {
+
+  	if (defined.document) {
+
+  		// QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
+  		if (window.QUnit && window.QUnit.version) {
+  			throw new Error("QUnit has already been defined.");
+  		}
+
+  		window.QUnit = QUnit;
+  	}
+
+  	// For nodejs
+  	if (typeof module !== "undefined" && module && module.exports) {
+  		module.exports = QUnit;
+
+  		// For consistency with CommonJS environments' exports
+  		module.exports.QUnit = QUnit;
+  	}
+
+  	// For CommonJS with exports, but without module.exports, like Rhino
+  	if (typeof exports !== "undefined" && exports) {
+  		exports.QUnit = QUnit;
+  	}
+
+  	if (typeof define === "function" && define.amd) {
+  		define(function () {
+  			return QUnit;
+  		});
+  		QUnit.config.autostart = false;
+  	}
+
+  	// For Web/Service Workers
+  	if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
+  		self$1.QUnit = QUnit;
+  	}
+  }
+
+  var SuiteReport = function () {
+  	function SuiteReport(name, parentSuite) {
+  		classCallCheck(this, SuiteReport);
+
+  		this.name = name;
+  		this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
+
+  		this.tests = [];
+  		this.childSuites = [];
+
+  		if (parentSuite) {
+  			parentSuite.pushChildSuite(this);
+  		}
+  	}
+
+  	createClass(SuiteReport, [{
+  		key: "start",
+  		value: function start(recordTime) {
+  			if (recordTime) {
+  				this._startTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				fullName: this.fullName.slice(),
+  				tests: this.tests.map(function (test) {
+  					return test.start();
+  				}),
+  				childSuites: this.childSuites.map(function (suite) {
+  					return suite.start();
+  				}),
+  				testCounts: {
+  					total: this.getTestCounts().total
+  				}
+  			};
+  		}
+  	}, {
+  		key: "end",
+  		value: function end(recordTime) {
+  			if (recordTime) {
+  				this._endTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				fullName: this.fullName.slice(),
+  				tests: this.tests.map(function (test) {
+  					return test.end();
+  				}),
+  				childSuites: this.childSuites.map(function (suite) {
+  					return suite.end();
+  				}),
+  				testCounts: this.getTestCounts(),
+  				runtime: this.getRuntime(),
+  				status: this.getStatus()
+  			};
+  		}
+  	}, {
+  		key: "pushChildSuite",
+  		value: function pushChildSuite(suite) {
+  			this.childSuites.push(suite);
+  		}
+  	}, {
+  		key: "pushTest",
+  		value: function pushTest(test) {
+  			this.tests.push(test);
+  		}
+  	}, {
+  		key: "getRuntime",
+  		value: function getRuntime() {
+  			return this._endTime - this._startTime;
+  		}
+  	}, {
+  		key: "getTestCounts",
+  		value: function getTestCounts() {
+  			var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
+
+  			counts = this.tests.reduce(function (counts, test) {
+  				if (test.valid) {
+  					counts[test.getStatus()]++;
+  					counts.total++;
+  				}
+
+  				return counts;
+  			}, counts);
+
+  			return this.childSuites.reduce(function (counts, suite) {
+  				return suite.getTestCounts(counts);
+  			}, counts);
+  		}
+  	}, {
+  		key: "getStatus",
+  		value: function getStatus() {
+  			var _getTestCounts = this.getTestCounts(),
+  			    total = _getTestCounts.total,
+  			    failed = _getTestCounts.failed,
+  			    skipped = _getTestCounts.skipped,
+  			    todo = _getTestCounts.todo;
+
+  			if (failed) {
+  				return "failed";
+  			} else {
+  				if (skipped === total) {
+  					return "skipped";
+  				} else if (todo === total) {
+  					return "todo";
+  				} else {
+  					return "passed";
+  				}
+  			}
+  		}
+  	}]);
+  	return SuiteReport;
+  }();
+
+  // Handle an unhandled exception. By convention, returns true if further
+  // error handling should be suppressed and false otherwise.
+  // In this case, we will only suppress further error handling if the
+  // "ignoreGlobalErrors" configuration option is enabled.
+  function onError(error) {
+  	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+  		args[_key - 1] = arguments[_key];
+  	}
+
+  	if (config.current) {
+  		if (config.current.ignoreGlobalErrors) {
+  			return true;
+  		}
+  		pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+  	} else {
+  		test("global failure", extend(function () {
+  			pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+  		}, { validTest: true }));
+  	}
+
+  	return false;
+  }
+
+  // Handle an unhandled rejection
+  function onUnhandledRejection(reason) {
+  	var resultInfo = {
+  		result: false,
+  		message: reason.message || "error",
+  		actual: reason,
+  		source: reason.stack || sourceFromStacktrace(3)
+  	};
+
+  	var currentTest = config.current;
+  	if (currentTest) {
+  		currentTest.assert.pushResult(resultInfo);
+  	} else {
+  		test("global failure", extend(function (assert) {
+  			assert.pushResult(resultInfo);
+  		}, { validTest: true }));
+  	}
+  }
+
+  var focused = false;
+  var QUnit = {};
+  var globalSuite = new SuiteReport();
+
+  // The initial "currentModule" represents the global (or top-level) module that
+  // is not explicitly defined by the user, therefore we add the "globalSuite" to
+  // it since each module has a suiteReport associated with it.
+  config.currentModule.suiteReport = globalSuite;
+
+  var moduleStack = [];
+  var globalStartCalled = false;
+  var runStarted = false;
+
+  // Figure out if we're running the tests from a server or not
+  QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+
+  // Expose the current QUnit version
+  QUnit.version = "2.5.1";
+
+  function createModule(name, testEnvironment, modifiers) {
+  	var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
+  	var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
+  	var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
+
+  	var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
+  	var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
+
+  	var module = {
+  		name: moduleName,
+  		parentModule: parentModule,
+  		tests: [],
+  		moduleId: generateHash(moduleName),
+  		testsRun: 0,
+  		unskippedTestsRun: 0,
+  		childModules: [],
+  		suiteReport: new SuiteReport(name, parentSuite),
+
+  		// Pass along `skip` and `todo` properties from parent module, in case
+  		// there is one, to childs. And use own otherwise.
+  		// This property will be used to mark own tests and tests of child suites
+  		// as either `skipped` or `todo`.
+  		skip: skip$$1,
+  		todo: skip$$1 ? false : todo$$1
+  	};
+
+  	var env = {};
+  	if (parentModule) {
+  		parentModule.childModules.push(module);
+  		extend(env, parentModule.testEnvironment);
+  	}
+  	extend(env, testEnvironment);
+  	module.testEnvironment = env;
+
+  	config.modules.push(module);
+  	return module;
+  }
+
+  function processModule(name, options, executeNow) {
+  	var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+  	var module = createModule(name, options, modifiers);
+
+  	// Move any hooks to a 'hooks' object
+  	var testEnvironment = module.testEnvironment;
+  	var hooks = module.hooks = {};
+
+  	setHookFromEnvironment(hooks, testEnvironment, "before");
+  	setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
+  	setHookFromEnvironment(hooks, testEnvironment, "afterEach");
+  	setHookFromEnvironment(hooks, testEnvironment, "after");
+
+  	function setHookFromEnvironment(hooks, environment, name) {
+  		var potentialHook = environment[name];
+  		hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
+  		delete environment[name];
+  	}
+
+  	var moduleFns = {
+  		before: setHookFunction(module, "before"),
+  		beforeEach: setHookFunction(module, "beforeEach"),
+  		afterEach: setHookFunction(module, "afterEach"),
+  		after: setHookFunction(module, "after")
+  	};
+
+  	var currentModule = config.currentModule;
+  	if (objectType(executeNow) === "function") {
+  		moduleStack.push(module);
+  		config.currentModule = module;
+  		executeNow.call(module.testEnvironment, moduleFns);
+  		moduleStack.pop();
+  		module = module.parentModule || currentModule;
+  	}
+
+  	config.currentModule = module;
+  }
+
+  // TODO: extract this to a new file alongside its related functions
+  function module$1(name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow);
+  }
+
+  module$1.only = function () {
+  	if (focused) {
+  		return;
+  	}
+
+  	config.modules.length = 0;
+  	config.queue.length = 0;
+
+  	module$1.apply(undefined, arguments);
+
+  	focused = true;
+  };
+
+  module$1.skip = function (name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow, { skip: true });
+  };
+
+  module$1.todo = function (name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow, { todo: true });
+  };
+
+  extend(QUnit, {
+  	on: on,
+
+  	module: module$1,
+
+  	test: test,
+
+  	todo: todo,
+
+  	skip: skip,
+
+  	only: only,
+
+  	start: function start(count) {
+  		var globalStartAlreadyCalled = globalStartCalled;
+
+  		if (!config.current) {
+  			globalStartCalled = true;
+
+  			if (runStarted) {
+  				throw new Error("Called start() while test already started running");
+  			} else if (globalStartAlreadyCalled || count > 1) {
+  				throw new Error("Called start() outside of a test context too many times");
+  			} else if (config.autostart) {
+  				throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
+  			} else if (!config.pageLoaded) {
+
+  				// The page isn't completely loaded yet, so we set autostart and then
+  				// load if we're in Node or wait for the browser's load event.
+  				config.autostart = true;
+
+  				// Starts from Node even if .load was not previously called. We still return
+  				// early otherwise we'll wind up "beginning" twice.
+  				if (!defined.document) {
+  					QUnit.load();
+  				}
+
+  				return;
+  			}
+  		} else {
+  			throw new Error("QUnit.start cannot be called inside a test context.");
+  		}
+
+  		scheduleBegin();
+  	},
+
+  	config: config,
+
+  	is: is,
+
+  	objectType: objectType,
+
+  	extend: extend,
+
+  	load: function load() {
+  		config.pageLoaded = true;
+
+  		// Initialize the configuration options
+  		extend(config, {
+  			stats: { all: 0, bad: 0 },
+  			started: 0,
+  			updateRate: 1000,
+  			autostart: true,
+  			filter: ""
+  		}, true);
+
+  		if (!runStarted) {
+  			config.blocking = false;
+
+  			if (config.autostart) {
+  				scheduleBegin();
+  			}
+  		}
+  	},
+
+  	stack: function stack(offset) {
+  		offset = (offset || 0) + 2;
+  		return sourceFromStacktrace(offset);
+  	},
+
+  	onError: onError,
+
+  	onUnhandledRejection: onUnhandledRejection
+  });
+
+  QUnit.pushFailure = pushFailure;
+  QUnit.assert = Assert.prototype;
+  QUnit.equiv = equiv;
+  QUnit.dump = dump;
+
+  registerLoggingCallbacks(QUnit);
+
+  function scheduleBegin() {
+
+  	runStarted = true;
+
+  	// Add a slight delay to allow definition of more modules and tests.
+  	if (defined.setTimeout) {
+  		setTimeout(function () {
+  			begin();
+  		});
+  	} else {
+  		begin();
+  	}
+  }
+
+  function begin() {
+  	var i,
+  	    l,
+  	    modulesLog = [];
+
+  	// If the test run hasn't officially begun yet
+  	if (!config.started) {
+
+  		// Record the time of the test run's beginning
+  		config.started = now();
+
+  		// Delete the loose unnamed module if unused.
+  		if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
+  			config.modules.shift();
+  		}
+
+  		// Avoid unnecessary information by not logging modules' test environments
+  		for (i = 0, l = config.modules.length; i < l; i++) {
+  			modulesLog.push({
+  				name: config.modules[i].name,
+  				tests: config.modules[i].tests
+  			});
+  		}
+
+  		// The test run is officially beginning now
+  		emit("runStart", globalSuite.start(true));
+  		runLoggingCallbacks("begin", {
+  			totalTests: Test.count,
+  			modules: modulesLog
+  		});
+  	}
+
+  	config.blocking = false;
+  	ProcessingQueue.advance();
+  }
+
+  function setHookFunction(module, hookName) {
+  	return function setHook(callback) {
+  		module.hooks[hookName].push(callback);
+  	};
+  }
+
+  exportQUnit(QUnit);
+
+  (function () {
+
+  	if (typeof window === "undefined" || typeof document === "undefined") {
+  		return;
+  	}
+
+  	var config = QUnit.config,
+  	    hasOwn = Object.prototype.hasOwnProperty;
+
+  	// Stores fixture HTML for resetting later
+  	function storeFixture() {
+
+  		// Avoid overwriting user-defined values
+  		if (hasOwn.call(config, "fixture")) {
+  			return;
+  		}
+
+  		var fixture = document.getElementById("qunit-fixture");
+  		if (fixture) {
+  			config.fixture = fixture.cloneNode(true);
+  		}
+  	}
+
+  	QUnit.begin(storeFixture);
+
+  	// Resets the fixture DOM element if available.
+  	function resetFixture() {
+  		if (config.fixture == null) {
+  			return;
+  		}
+
+  		var fixture = document.getElementById("qunit-fixture");
+  		var resetFixtureType = _typeof(config.fixture);
+  		if (resetFixtureType === "string") {
+
+  			// support user defined values for `config.fixture`
+  			var newFixture = document.createElement("div");
+  			newFixture.setAttribute("id", "qunit-fixture");
+  			newFixture.innerHTML = config.fixture;
+  			fixture.parentNode.replaceChild(newFixture, fixture);
+  		} else {
+  			var clonedFixture = config.fixture.cloneNode(true);
+  			fixture.parentNode.replaceChild(clonedFixture, fixture);
+  		}
+  	}
+
+  	QUnit.testStart(resetFixture);
+  })();
+
+  (function () {
+
+  	// Only interact with URLs via window.location
+  	var location = typeof window !== "undefined" && window.location;
+  	if (!location) {
+  		return;
+  	}
+
+  	var urlParams = getUrlParams();
+
+  	QUnit.urlParams = urlParams;
+
+  	// Match module/test by inclusion in an array
+  	QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
+  	QUnit.config.testId = [].concat(urlParams.testId || []);
+
+  	// Exact case-insensitive match of the module name
+  	QUnit.config.module = urlParams.module;
+
+  	// Regular expression or case-insenstive substring match against "moduleName: testName"
+  	QUnit.config.filter = urlParams.filter;
+
+  	// Test order randomization
+  	if (urlParams.seed === true) {
+
+  		// Generate a random seed if the option is specified without a value
+  		QUnit.config.seed = Math.random().toString(36).slice(2);
+  	} else if (urlParams.seed) {
+  		QUnit.config.seed = urlParams.seed;
+  	}
+
+  	// Add URL-parameter-mapped config values with UI form rendering data
+  	QUnit.config.urlConfig.push({
+  		id: "hidepassed",
+  		label: "Hide passed tests",
+  		tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+  	}, {
+  		id: "noglobals",
+  		label: "Check for Globals",
+  		tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
+  	}, {
+  		id: "notrycatch",
+  		label: "No try-catch",
+  		tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
+  	});
+
+  	QUnit.begin(function () {
+  		var i,
+  		    option,
+  		    urlConfig = QUnit.config.urlConfig;
+
+  		for (i = 0; i < urlConfig.length; i++) {
+
+  			// Options can be either strings or objects with nonempty "id" properties
+  			option = QUnit.config.urlConfig[i];
+  			if (typeof option !== "string") {
+  				option = option.id;
+  			}
+
+  			if (QUnit.config[option] === undefined) {
+  				QUnit.config[option] = urlParams[option];
+  			}
+  		}
+  	});
+
+  	function getUrlParams() {
+  		var i, param, name, value;
+  		var urlParams = Object.create(null);
+  		var params = location.search.slice(1).split("&");
+  		var length = params.length;
+
+  		for (i = 0; i < length; i++) {
+  			if (params[i]) {
+  				param = params[i].split("=");
+  				name = decodeQueryParam(param[0]);
+
+  				// Allow just a key to turn on a flag, e.g., test.html?noglobals
+  				value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
+  				if (name in urlParams) {
+  					urlParams[name] = [].concat(urlParams[name], value);
+  				} else {
+  					urlParams[name] = value;
+  				}
+  			}
+  		}
+
+  		return urlParams;
+  	}
+
+  	function decodeQueryParam(param) {
+  		return decodeURIComponent(param.replace(/\+/g, "%20"));
+  	}
+  })();
+
+  var stats = {
+  	passedTests: 0,
+  	failedTests: 0,
+  	skippedTests: 0,
+  	todoTests: 0
+  };
+
+  // Escape text for attribute or text content.
+  function escapeText(s) {
+  	if (!s) {
+  		return "";
+  	}
+  	s = s + "";
+
+  	// Both single quotes and double quotes (for attributes)
+  	return s.replace(/['"<>&]/g, function (s) {
+  		switch (s) {
+  			case "'":
+  				return "&#039;";
+  			case "\"":
+  				return "&quot;";
+  			case "<":
+  				return "&lt;";
+  			case ">":
+  				return "&gt;";
+  			case "&":
+  				return "&amp;";
+  		}
+  	});
+  }
+
+  (function () {
+
+  	// Don't load the HTML Reporter on non-browser environments
+  	if (typeof window === "undefined" || !window.document) {
+  		return;
+  	}
+
+  	var config = QUnit.config,
+  	    document$$1 = window.document,
+  	    collapseNext = false,
+  	    hasOwn = Object.prototype.hasOwnProperty,
+  	    unfilteredUrl = setUrl({ filter: undefined, module: undefined,
+  		moduleId: undefined, testId: undefined }),
+  	    modulesList = [];
+
+  	function addEvent(elem, type, fn) {
+  		elem.addEventListener(type, fn, false);
+  	}
+
+  	function removeEvent(elem, type, fn) {
+  		elem.removeEventListener(type, fn, false);
+  	}
+
+  	function addEvents(elems, type, fn) {
+  		var i = elems.length;
+  		while (i--) {
+  			addEvent(elems[i], type, fn);
+  		}
+  	}
+
+  	function hasClass(elem, name) {
+  		return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
+  	}
+
+  	function addClass(elem, name) {
+  		if (!hasClass(elem, name)) {
+  			elem.className += (elem.className ? " " : "") + name;
+  		}
+  	}
+
+  	function toggleClass(elem, name, force) {
+  		if (force || typeof force === "undefined" && !hasClass(elem, name)) {
+  			addClass(elem, name);
+  		} else {
+  			removeClass(elem, name);
+  		}
+  	}
+
+  	function removeClass(elem, name) {
+  		var set = " " + elem.className + " ";
+
+  		// Class name may appear multiple times
+  		while (set.indexOf(" " + name + " ") >= 0) {
+  			set = set.replace(" " + name + " ", " ");
+  		}
+
+  		// Trim for prettiness
+  		elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+  	}
+
+  	function id(name) {
+  		return document$$1.getElementById && document$$1.getElementById(name);
+  	}
+
+  	function abortTests() {
+  		var abortButton = id("qunit-abort-tests-button");
+  		if (abortButton) {
+  			abortButton.disabled = true;
+  			abortButton.innerHTML = "Aborting...";
+  		}
+  		QUnit.config.queue.length = 0;
+  		return false;
+  	}
+
+  	function interceptNavigation(ev) {
+  		applyUrlParams();
+
+  		if (ev && ev.preventDefault) {
+  			ev.preventDefault();
+  		}
+
+  		return false;
+  	}
+
+  	function getUrlConfigHtml() {
+  		var i,
+  		    j,
+  		    val,
+  		    escaped,
+  		    escapedTooltip,
+  		    selection = false,
+  		    urlConfig = config.urlConfig,
+  		    urlConfigHtml = "";
+
+  		for (i = 0; i < urlConfig.length; i++) {
+
+  			// Options can be either strings or objects with nonempty "id" properties
+  			val = config.urlConfig[i];
+  			if (typeof val === "string") {
+  				val = {
+  					id: val,
+  					label: val
+  				};
+  			}
+
+  			escaped = escapeText(val.id);
+  			escapedTooltip = escapeText(val.tooltip);
+
+  			if (!val.value || typeof val.value === "string") {
+  				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
+  			} else {
+  				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+  				if (QUnit.is("array", val.value)) {
+  					for (j = 0; j < val.value.length; j++) {
+  						escaped = escapeText(val.value[j]);
+  						urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
+  					}
+  				} else {
+  					for (j in val.value) {
+  						if (hasOwn.call(val.value, j)) {
+  							urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
+  						}
+  					}
+  				}
+  				if (config[val.id] && !selection) {
+  					escaped = escapeText(config[val.id]);
+  					urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+  				}
+  				urlConfigHtml += "</select>";
+  			}
+  		}
+
+  		return urlConfigHtml;
+  	}
+
+  	// Handle "click" events on toolbar checkboxes and "change" for select menus.
+  	// Updates the URL with the new state of `config.urlConfig` values.
+  	function toolbarChanged() {
+  		var updatedUrl,
+  		    value,
+  		    tests,
+  		    field = this,
+  		    params = {};
+
+  		// Detect if field is a select menu or a checkbox
+  		if ("selectedIndex" in field) {
+  			value = field.options[field.selectedIndex].value || undefined;
+  		} else {
+  			value = field.checked ? field.defaultValue || true : undefined;
+  		}
+
+  		params[field.name] = value;
+  		updatedUrl = setUrl(params);
+
+  		// Check if we can apply the change without a page refresh
+  		if ("hidepassed" === field.name && "replaceState" in window.history) {
+  			QUnit.urlParams[field.name] = value;
+  			config[field.name] = value || false;
+  			tests = id("qunit-tests");
+  			if (tests) {
+  				toggleClass(tests, "hidepass", value || false);
+  			}
+  			window.history.replaceState(null, "", updatedUrl);
+  		} else {
+  			window.location = updatedUrl;
+  		}
+  	}
+
+  	function setUrl(params) {
+  		var key,
+  		    arrValue,
+  		    i,
+  		    querystring = "?",
+  		    location = window.location;
+
+  		params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
+
+  		for (key in params) {
+
+  			// Skip inherited or undefined properties
+  			if (hasOwn.call(params, key) && params[key] !== undefined) {
+
+  				// Output a parameter for each value of this key
+  				// (but usually just one)
+  				arrValue = [].concat(params[key]);
+  				for (i = 0; i < arrValue.length; i++) {
+  					querystring += encodeURIComponent(key);
+  					if (arrValue[i] !== true) {
+  						querystring += "=" + encodeURIComponent(arrValue[i]);
+  					}
+  					querystring += "&";
+  				}
+  			}
+  		}
+  		return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
+  	}
+
+  	function applyUrlParams() {
+  		var i,
+  		    selectedModules = [],
+  		    modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
+  		    filter = id("qunit-filter-input").value;
+
+  		for (i = 0; i < modulesList.length; i++) {
+  			if (modulesList[i].checked) {
+  				selectedModules.push(modulesList[i].value);
+  			}
+  		}
+
+  		window.location = setUrl({
+  			filter: filter === "" ? undefined : filter,
+  			moduleId: selectedModules.length === 0 ? undefined : selectedModules,
+
+  			// Remove module and testId filter
+  			module: undefined,
+  			testId: undefined
+  		});
+  	}
+
+  	function toolbarUrlConfigContainer() {
+  		var urlConfigContainer = document$$1.createElement("span");
+
+  		urlConfigContainer.innerHTML = getUrlConfigHtml();
+  		addClass(urlConfigContainer, "qunit-url-config");
+
+  		addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
+  		addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
+
+  		return urlConfigContainer;
+  	}
+
+  	function abortTestsButton() {
+  		var button = document$$1.createElement("button");
+  		button.id = "qunit-abort-tests-button";
+  		button.innerHTML = "Abort";
+  		addEvent(button, "click", abortTests);
+  		return button;
+  	}
+
+  	function toolbarLooseFilter() {
+  		var filter = document$$1.createElement("form"),
+  		    label = document$$1.createElement("label"),
+  		    input = document$$1.createElement("input"),
+  		    button = document$$1.createElement("button");
+
+  		addClass(filter, "qunit-filter");
+
+  		label.innerHTML = "Filter: ";
+
+  		input.type = "text";
+  		input.value = config.filter || "";
+  		input.name = "filter";
+  		input.id = "qunit-filter-input";
+
+  		button.innerHTML = "Go";
+
+  		label.appendChild(input);
+
+  		filter.appendChild(label);
+  		filter.appendChild(document$$1.createTextNode(" "));
+  		filter.appendChild(button);
+  		addEvent(filter, "submit", interceptNavigation);
+
+  		return filter;
+  	}
+
+  	function moduleListHtml() {
+  		var i,
+  		    checked,
+  		    html = "";
+
+  		for (i = 0; i < config.modules.length; i++) {
+  			if (config.modules[i].name !== "") {
+  				checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
+  				html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
+  			}
+  		}
+
+  		return html;
+  	}
+
+  	function toolbarModuleFilter() {
+  		var allCheckbox,
+  		    commit,
+  		    reset,
+  		    moduleFilter = document$$1.createElement("form"),
+  		    label = document$$1.createElement("label"),
+  		    moduleSearch = document$$1.createElement("input"),
+  		    dropDown = document$$1.createElement("div"),
+  		    actions = document$$1.createElement("span"),
+  		    dropDownList = document$$1.createElement("ul"),
+  		    dirty = false;
+
+  		moduleSearch.id = "qunit-modulefilter-search";
+  		addEvent(moduleSearch, "input", searchInput);
+  		addEvent(moduleSearch, "input", searchFocus);
+  		addEvent(moduleSearch, "focus", searchFocus);
+  		addEvent(moduleSearch, "click", searchFocus);
+
+  		label.id = "qunit-modulefilter-search-container";
+  		label.innerHTML = "Module: ";
+  		label.appendChild(moduleSearch);
+
+  		actions.id = "qunit-modulefilter-actions";
+  		actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+  		allCheckbox = actions.lastChild.firstChild;
+  		commit = actions.firstChild;
+  		reset = commit.nextSibling;
+  		addEvent(commit, "click", applyUrlParams);
+
+  		dropDownList.id = "qunit-modulefilter-dropdown-list";
+  		dropDownList.innerHTML = moduleListHtml();
+
+  		dropDown.id = "qunit-modulefilter-dropdown";
+  		dropDown.style.display = "none";
+  		dropDown.appendChild(actions);
+  		dropDown.appendChild(dropDownList);
+  		addEvent(dropDown, "change", selectionChange);
+  		selectionChange();
+
+  		moduleFilter.id = "qunit-modulefilter";
+  		moduleFilter.appendChild(label);
+  		moduleFilter.appendChild(dropDown);
+  		addEvent(moduleFilter, "submit", interceptNavigation);
+  		addEvent(moduleFilter, "reset", function () {
+
+  			// Let the reset happen, then update styles
+  			window.setTimeout(selectionChange);
+  		});
+
+  		// Enables show/hide for the dropdown
+  		function searchFocus() {
+  			if (dropDown.style.display !== "none") {
+  				return;
+  			}
+
+  			dropDown.style.display = "block";
+  			addEvent(document$$1, "click", hideHandler);
+  			addEvent(document$$1, "keydown", hideHandler);
+
+  			// Hide on Escape keydown or outside-container click
+  			function hideHandler(e) {
+  				var inContainer = moduleFilter.contains(e.target);
+
+  				if (e.keyCode === 27 || !inContainer) {
+  					if (e.keyCode === 27 && inContainer) {
+  						moduleSearch.focus();
+  					}
+  					dropDown.style.display = "none";
+  					removeEvent(document$$1, "click", hideHandler);
+  					removeEvent(document$$1, "keydown", hideHandler);
+  					moduleSearch.value = "";
+  					searchInput();
+  				}
+  			}
+  		}
+
+  		// Processes module search box input
+  		function searchInput() {
+  			var i,
+  			    item,
+  			    searchText = moduleSearch.value.toLowerCase(),
+  			    listItems = dropDownList.children;
+
+  			for (i = 0; i < listItems.length; i++) {
+  				item = listItems[i];
+  				if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
+  					item.style.display = "";
+  				} else {
+  					item.style.display = "none";
+  				}
+  			}
+  		}
+
+  		// Processes selection changes
+  		function selectionChange(evt) {
+  			var i,
+  			    item,
+  			    checkbox = evt && evt.target || allCheckbox,
+  			    modulesList = dropDownList.getElementsByTagName("input"),
+  			    selectedNames = [];
+
+  			toggleClass(checkbox.parentNode, "checked", checkbox.checked);
+
+  			dirty = false;
+  			if (checkbox.checked && checkbox !== allCheckbox) {
+  				allCheckbox.checked = false;
+  				removeClass(allCheckbox.parentNode, "checked");
+  			}
+  			for (i = 0; i < modulesList.length; i++) {
+  				item = modulesList[i];
+  				if (!evt) {
+  					toggleClass(item.parentNode, "checked", item.checked);
+  				} else if (checkbox === allCheckbox && checkbox.checked) {
+  					item.checked = false;
+  					removeClass(item.parentNode, "checked");
+  				}
+  				dirty = dirty || item.checked !== item.defaultChecked;
+  				if (item.checked) {
+  					selectedNames.push(item.parentNode.textContent);
+  				}
+  			}
+
+  			commit.style.display = reset.style.display = dirty ? "" : "none";
+  			moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
+  			moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
+  		}
+
+  		return moduleFilter;
+  	}
+
+  	function appendToolbar() {
+  		var toolbar = id("qunit-testrunner-toolbar");
+
+  		if (toolbar) {
+  			toolbar.appendChild(toolbarUrlConfigContainer());
+  			toolbar.appendChild(toolbarModuleFilter());
+  			toolbar.appendChild(toolbarLooseFilter());
+  			toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+  		}
+  	}
+
+  	function appendHeader() {
+  		var header = id("qunit-header");
+
+  		if (header) {
+  			header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
+  		}
+  	}
+
+  	function appendBanner() {
+  		var banner = id("qunit-banner");
+
+  		if (banner) {
+  			banner.className = "";
+  		}
+  	}
+
+  	function appendTestResults() {
+  		var tests = id("qunit-tests"),
+  		    result = id("qunit-testresult"),
+  		    controls;
+
+  		if (result) {
+  			result.parentNode.removeChild(result);
+  		}
+
+  		if (tests) {
+  			tests.innerHTML = "";
+  			result = document$$1.createElement("p");
+  			result.id = "qunit-testresult";
+  			result.className = "result";
+  			tests.parentNode.insertBefore(result, tests);
+  			result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
+  			controls = id("qunit-testresult-controls");
+  		}
+
+  		if (controls) {
+  			controls.appendChild(abortTestsButton());
+  		}
+  	}
+
+  	function appendFilteredTest() {
+  		var testId = QUnit.config.testId;
+  		if (!testId || testId.length <= 0) {
+  			return "";
+  		}
+  		return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
+  	}
+
+  	function appendUserAgent() {
+  		var userAgent = id("qunit-userAgent");
+
+  		if (userAgent) {
+  			userAgent.innerHTML = "";
+  			userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+  		}
+  	}
+
+  	function appendInterface() {
+  		var qunit = id("qunit");
+
+  		if (qunit) {
+  			qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+  		}
+
+  		appendHeader();
+  		appendBanner();
+  		appendTestResults();
+  		appendUserAgent();
+  		appendToolbar();
+  	}
+
+  	function appendTestsList(modules) {
+  		var i, l, x, z, test, moduleObj;
+
+  		for (i = 0, l = modules.length; i < l; i++) {
+  			moduleObj = modules[i];
+
+  			for (x = 0, z = moduleObj.tests.length; x < z; x++) {
+  				test = moduleObj.tests[x];
+
+  				appendTest(test.name, test.testId, moduleObj.name);
+  			}
+  		}
+  	}
+
+  	function appendTest(name, testId, moduleName) {
+  		var title,
+  		    rerunTrigger,
+  		    testBlock,
+  		    assertList,
+  		    tests = id("qunit-tests");
+
+  		if (!tests) {
+  			return;
+  		}
+
+  		title = document$$1.createElement("strong");
+  		title.innerHTML = getNameHtml(name, moduleName);
+
+  		rerunTrigger = document$$1.createElement("a");
+  		rerunTrigger.innerHTML = "Rerun";
+  		rerunTrigger.href = setUrl({ testId: testId });
+
+  		testBlock = document$$1.createElement("li");
+  		testBlock.appendChild(title);
+  		testBlock.appendChild(rerunTrigger);