Bug 1314057 - Move listtabs and listaddons tests to shared. r=loganfsmyth
☠☠ backed out by f5681e1f56e6 ☠ ☠
authorDavid Walsh <dwalsh@mozilla.com>
Mon, 08 Oct 2018 13:02:13 -0500
changeset 498824 fa0d46e77437a4cb89fe0d6178d8ec7b7d933b23
parent 498823 241f876d557f679b81c2aa6d73f0ec598cc49545
child 498825 90480b4b4c43db2437ec4ad7185cd6fafbf821e7
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersloganfsmyth
bugs1314057
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1314057 - Move listtabs and listaddons tests to shared. r=loganfsmyth
devtools/client/debugger/test/mochitest/addon1.xpi
devtools/client/debugger/test/mochitest/addon2.xpi
devtools/client/debugger/test/mochitest/browser.ini
devtools/client/debugger/test/mochitest/browser2.ini
devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
devtools/client/shared/test/addon1.xpi
devtools/client/shared/test/addon2.xpi
devtools/client/shared/test/browser.ini
devtools/client/shared/test/browser_dbg_listaddons.js
devtools/client/shared/test/browser_dbg_listtabs-01.js
devtools/client/shared/test/browser_dbg_listtabs-02.js
devtools/client/shared/test/browser_dbg_listtabs-03.js
devtools/client/shared/test/doc_empty-tab-01.html
devtools/client/shared/test/doc_empty-tab-02.html
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser2.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
@@ -195,20 +193,12 @@ skip-if = e10s && debug
 [browser_dbg_host-layout.js]
 skip-if = e10s && debug
 [browser_dbg_jump-to-function-definition.js]
 skip-if = e10s && debug
 [browser_dbg_iframes.js]
 skip-if = e10s # TODO
 [browser_dbg_interrupts.js]
 skip-if = e10s && debug
-[browser_dbg_listaddons.js]
-skip-if = e10s && debug
-tags = addons
-[browser_dbg_listtabs-01.js]
-[browser_dbg_listtabs-02.js]
-skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
-[browser_dbg_listtabs-03.js]
-skip-if = e10s && debug
 [browser_dbg_listworkers.js]
 [browser_dbg_multiple-windows.js]
 [browser_dbg_navigation.js]
 skip-if = e10s && debug
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -1,18 +1,16 @@
 # Tests in this directory are split into two manifests (this and browser.ini)
 # to facilitate better chunking; see bug 1294489.
 
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon1.xpi
-  addon2.xpi
   addon4.xpi
   addon5.xpi
   addon-webext-contentscript.xpi
   addon-source/browser_dbg_addon5/*
   code_binary_search.coffee
   code_binary_search.js
   code_binary_search.map
   code_breakpoints-break-on-last-line-of-script-on-reload.js
rename from devtools/client/debugger/test/mochitest/addon1.xpi
rename to devtools/client/shared/test/addon1.xpi
rename from devtools/client/debugger/test/mochitest/addon2.xpi
rename to devtools/client/shared/test/addon2.xpi
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -1,15 +1,19 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
+  addon1.xpi
+  addon2.xpi
   browser_devices.json
   doc_cubic-bezier-01.html
   doc_cubic-bezier-02.html
+  doc_empty-tab-01.html
+  doc_empty-tab-02.html
   doc_filter-editor-01.html
   doc_html_tooltip-02.xul
   doc_html_tooltip-03.xul
   doc_html_tooltip-04.xul
   doc_html_tooltip-05.xul
   doc_html_tooltip.xul
   doc_html_tooltip_arrow-01.xul
   doc_html_tooltip_arrow-02.xul
@@ -202,8 +206,16 @@ skip-if = !e10s || os == "win" # RDM onl
 [browser_telemetry_toolboxtabs_webaudioeditor.js]
 [browser_telemetry_toolboxtabs_webconsole.js]
 [browser_treeWidget_basic.js]
 [browser_treeWidget_keyboard_interaction.js]
 [browser_treeWidget_mouse_interaction.js]
 [browser_devices.js]
 skip-if = verify
 [browser_theme_switching.js]
+[browser_dbg_listaddons.js]
+skip-if = e10s && debug
+tags = addons
+[browser_dbg_listtabs-01.js]
+[browser_dbg_listtabs-02.js]
+skip-if = true # Never worked for remote frames, needs a mock DebuggerServerConnection
+[browser_dbg_listtabs-03.js]
+skip-if = e10s && debug
rename from devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
rename to devtools/client/shared/test/browser_dbg_listaddons.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listaddons.js
+++ b/devtools/client/shared/test/browser_dbg_listaddons.js
@@ -1,110 +1,163 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const chromeRegistry =
+  Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
+
 /**
  * Make sure the listAddons request works as specified.
  */
 const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
 const ADDON1_PATH = "addon1.xpi";
 const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
 const ADDON2_PATH = "addon2.xpi";
 
-var gAddon1, gAddon1Actor, gAddon2, gAddon2Actor, gClient;
+var gAddon1, gAddon1Actor, gAddon2, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstAddon)
       .then(testSecondAddon)
       .then(testRemoveFirstAddon)
       .then(testRemoveSecondAddon)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstAddon() {
   let addonListChanged = false;
   gClient.addOneTimeListener("addonListChanged", () => {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON1_PATH).then(aAddon => {
-    gAddon1 = aAddon;
+  return addTemporaryAddon(ADDON1_PATH).then(addon => {
+    gAddon1 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(!addonListChanged, "Should not yet be notified that list of addons changed.");
-      ok(aGrip, "Should find an addon actor for addon1.");
-      gAddon1Actor = aGrip.actor;
+      ok(grip, "Should find an addon actor for addon1.");
+      gAddon1Actor = grip.actor;
     });
   });
 }
 
 function testSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
-  return addTemporaryAddon(ADDON2_PATH).then(aAddon => {
-    gAddon2 = aAddon;
+  return addTemporaryAddon(ADDON2_PATH).then(addon => {
+    gAddon2 = addon;
 
-    return getAddonActorForId(gClient, ADDON1_ID).then(aFirstGrip => {
-      return getAddonActorForId(gClient, ADDON2_ID).then(aSecondGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(fistGrip => {
+      return getAddonActorForId(gClient, ADDON2_ID).then(secondGrip => {
         ok(addonListChanged, "Should be notified that list of addons changed.");
-        is(aFirstGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a addon actor for the second addon.");
-        gAddon2Actor = aSecondGrip.actor;
+        is(fistGrip.actor, gAddon1Actor, "First addon's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a addon actor for the second addon.");
       });
     });
   });
 }
 
 function testRemoveFirstAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon1).then(() => {
-    return getAddonActorForId(gClient, ADDON1_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON1_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the first addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the first addon anymore.");
     });
   });
 }
 
 function testRemoveSecondAddon() {
   let addonListChanged = false;
-  gClient.addOneTimeListener("addonListChanged", function () {
+  gClient.addOneTimeListener("addonListChanged", function() {
     addonListChanged = true;
   });
 
   return removeAddon(gAddon2).then(() => {
-    return getAddonActorForId(gClient, ADDON2_ID).then(aGrip => {
+    return getAddonActorForId(gClient, ADDON2_ID).then(grip => {
       ok(addonListChanged, "Should be notified that list of addons changed.");
-      ok(!aGrip, "Shouldn't find a addon actor for the second addon anymore.");
+      ok(!grip, "Shouldn't find a addon actor for the second addon anymore.");
     });
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gAddon1 = null;
   gAddon1Actor = null;
   gAddon2 = null;
-  gAddon2Actor = null;
   gClient = null;
 });
+
+function getAddonURIFromPath(path) {
+  const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
+  return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
+}
+
+function addTemporaryAddon(path) {
+  const addonFile = getAddonURIFromPath(path).file;
+  info("Installing addon: " + addonFile.path);
+
+  return AddonManager.installTemporaryAddon(addonFile);
+}
+
+function getAddonActorForId(client, addonId) {
+  info("Get addon actor for ID: " + addonId);
+  const deferred = promise.defer();
+
+  client.listAddons(response => {
+    const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
+    info("got addon actor for ID: " + addonId);
+    deferred.resolve(addonTargetActor);
+  });
+
+  return deferred.promise;
+}
+
+function removeAddon(addon) {
+  info("Removing addon.");
+
+  const deferred = promise.defer();
+
+  const listener = {
+    onUninstalled: function(uninstalledAddon) {
+      if (uninstalledAddon != addon) {
+        return;
+      }
+      AddonManager.removeAddonListener(listener);
+      deferred.resolve();
+    }
+  };
+  AddonManager.addAddonListener(listener);
+  addon.uninstall();
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
rename to devtools/client/shared/test/browser_dbg_listtabs-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-01.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-01.js
@@ -1,96 +1,112 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
 
 var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     promise.resolve(null)
       .then(testFirstTab)
       .then(testSecondTab)
       .then(testRemoveTab)
       .then(testAttachRemovedTab)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testFirstTab() {
-  return addTab(TAB1_URL).then(aTab => {
-    gTab1 = aTab;
+  return addTab(TAB1_URL).then(tab => {
+    gTab1 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(aGrip, "Should find a target actor for the first tab.");
-      gTab1Actor = aGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(grip, "Should find a target actor for the first tab.");
+      gTab1Actor = grip.actor;
     });
   });
 }
 
 function testSecondTab() {
-  return addTab(TAB2_URL).then(aTab => {
-    gTab2 = aTab;
+  return addTab(TAB2_URL).then(tab => {
+    gTab2 = tab;
 
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aFirstGrip => {
-      return getTargetActorForUrl(gClient, TAB2_URL).then(aSecondGrip => {
-        is(aFirstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
-        ok(aSecondGrip, "Should find a target actor for the second tab.");
-        gTab2Actor = aSecondGrip.actor;
+    return getTargetActorForUrl(gClient, TAB1_URL).then(firstGrip => {
+      return getTargetActorForUrl(gClient, TAB2_URL).then(secondGrip => {
+        is(firstGrip.actor, gTab1Actor, "First tab's actor shouldn't have changed.");
+        ok(secondGrip, "Should find a target actor for the second tab.");
+        gTab2Actor = secondGrip.actor;
       });
     });
   });
 }
 
 function testRemoveTab() {
   return removeTab(gTab1).then(() => {
-    return getTargetActorForUrl(gClient, TAB1_URL).then(aGrip => {
-      ok(!aGrip, "Shouldn't find a target actor for the first tab anymore.");
+    return getTargetActorForUrl(gClient, TAB1_URL).then(grip => {
+      ok(!grip, "Shouldn't find a target actor for the first tab anymore.");
     });
   });
 }
 
 function testAttachRemovedTab() {
   return removeTab(gTab2).then(() => {
-    let deferred = promise.defer();
+    const deferred = promise.defer();
 
-    gClient.addListener("paused", (aEvent, aPacket) => {
+    gClient.addListener("paused", () => {
       ok(false, "Attaching to an exited target actor shouldn't generate a pause.");
       deferred.reject();
     });
 
-    gClient.request({ to: gTab2Actor, type: "attach" }, aResponse => {
-      is(aResponse.error, "connectionClosed",
+    gClient.request({ to: gTab2Actor, type: "attach" }, response => {
+      is(response.error, "connectionClosed",
          "Connection is gone since the tab was removed.");
       deferred.resolve();
     });
 
     return deferred.promise;
   });
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gTab1 = null;
   gTab1Actor = null;
   gTab2 = null;
   gTab2Actor = null;
   gClient = null;
 });
+
+function getTargetActorForUrl(client, url) {
+  const deferred = promise.defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
rename to devtools/client/shared/test/browser_dbg_listtabs-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-02.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-02.js
@@ -3,16 +3,17 @@
 
 "use strict";
 
 /**
  * Make sure the root actor's live tab list implementation works as specified.
  */
 
 var { BrowserTabList } = require("devtools/server/actors/webbrowser");
+var { DebuggerServer } = require("devtools/server/main");
 
 var gTestPage = "data:text/html;charset=utf-8," + encodeURIComponent(
   "<title>JS Debugger BrowserTabList test page</title><body>Yo.</body>");
 
 // The tablist object whose behavior we observe.
 var gTabList;
 var gFirstActor, gActorA;
 var gTabA, gTabB, gTabC;
@@ -49,62 +50,70 @@ function test() {
     .then(checkSingleTab)
     .then(finishUp);
 }
 
 function checkSingleTab() {
   return gTabList.getList().then(targetActors => {
     is(targetActors.length, 1, "initial tab list: contains initial tab");
     gFirstActor = targetActors[0];
-    is(gFirstActor.url, "about:blank", "initial tab list: initial tab URL is 'about:blank'");
+    is(
+      gFirstActor.url,
+      "about:blank",
+      "initial tab list: initial tab URL is 'about:blank'"
+    );
     is(gFirstActor.title, "New Tab", "initial tab list: initial tab title is 'New Tab'");
   });
 }
 
 function addTabA() {
-  return addTab(gTestPage).then(aTab => {
-    gTabA = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabA = tab;
   });
 }
 
 function testTabA() {
   is(onListChangedCount, 1, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA opened: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA opened: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA opened: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA opened: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA opened: new tab title"
+    );
   });
 }
 
 function addTabB() {
-  return addTab(gTestPage).then(aTab => {
-    gTabB = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabB = tab;
   });
 }
 
 function testTabB() {
   is(onListChangedCount, 2, "onListChanged handler call count");
 
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabB opened: three tabs in list");
   });
 }
 
 function removeTabA() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabA);
   return deferred.promise;
 }
@@ -115,40 +124,44 @@ function testTabClosed() {
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gTabA closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gTabA closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabA closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabA closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabA closed: new tab title"
+    );
   });
 }
 
 function addTabC() {
-  return addTab(gTestPage).then(aTab => {
-    gTabC = aTab;
+  return addTab(gTestPage).then(tab => {
+    gTabC = tab;
   });
 }
 
 function testTabC() {
   is(onListChangedCount, 4, "onListChanged handler call count");
 
   gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC opened: three tabs in list");
   });
 }
 
 function removeTabC() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(aEvent.detail.adoptedBy, "This was a tab closed by moving");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(event.detail.adoptedBy, "This was a tab closed by moving");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow = gBrowser.replaceTabWithWindow(gTabC);
   return deferred.promise;
 }
@@ -159,25 +172,29 @@ function testNewWindow() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 3, "gTabC closed: three tabs in list");
     ok(targetActors.has(gFirstActor), "gTabC closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gTabC closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gTabC closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gTabC closed: new tab title"
+    );
   });
 }
 
 function removeNewWindow() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gNewWindow, "unload").then(aEvent => {
-    ok(!aEvent.detail, "This was a normal window close");
+  once(gNewWindow, "unload").then(event => {
+    ok(!event.detail, "This was a normal window close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   gNewWindow.close();
   return deferred.promise;
 }
@@ -188,25 +205,29 @@ function testWindowClosed() {
   return gTabList.getList().then(targetActors => {
     targetActors = new Set(targetActors);
     is(targetActors.size, 2, "gNewWindow closed: two tabs in list");
     ok(targetActors.has(gFirstActor), "gNewWindow closed: initial tab present");
 
     info("actors: " + [...targetActors].map(a => a.url));
     gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
     ok(gActorA.url.match(/^data:text\/html;/), "gNewWindow closed: new tab URL");
-    is(gActorA.title, "JS Debugger BrowserTabList test page", "gNewWindow closed: new tab title");
+    is(
+      gActorA.title,
+      "JS Debugger BrowserTabList test page",
+      "gNewWindow closed: new tab title"
+    );
   });
 }
 
 function removeTabB() {
-  let deferred = promise.defer();
+  const deferred = promise.defer();
 
-  once(gBrowser.tabContainer, "TabClose").then(aEvent => {
-    ok(!aEvent.detail.adoptedBy, "This was a normal tab close");
+  once(gBrowser.tabContainer, "TabClose").then(event => {
+    ok(!event.detail.adoptedBy, "This was a normal tab close");
 
     // Let the actor's TabClose handler finish first.
     executeSoon(deferred.resolve);
   }, false);
 
   removeTab(gTabB);
   return deferred.promise;
 }
rename from devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
rename to devtools/client/shared/test/browser_dbg_listtabs-03.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listtabs-03.js
+++ b/devtools/client/shared/test/browser_dbg_listtabs-03.js
@@ -1,59 +1,61 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Make sure the listTabs request works as specified.
  */
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+var { Task } = require("devtools/shared/task");
+
 const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
 
-var gTab1, gTab1Actor, gTab2, gTab2Actor, gClient;
+var gClient;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(Task.async(function* ([aType, aTraits]) {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
-    let tab = yield addTab(TAB1_URL);
+    const tab = yield addTab(TAB1_URL);
 
     let { tabs } = yield gClient.listTabs();
     is(tabs.length, 2, "Should be two tabs");
-    let tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const tabGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     ok(tabGrip, "Should have an actor for the tab");
 
     let response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
 
     response = yield gClient.listTabs();
     tabs = response.tabs;
 
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
-    let newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
+    const newGrip = tabs.filter(a => a.url == TAB1_URL).pop();
     is(newGrip.actor, tabGrip.actor, "Should have the same actor for the same tab");
 
     response = yield gClient.request({ to: tabGrip.actor, type: "attach" });
     is(response.type, "tabAttached", "Should have attached");
     response = yield gClient.request({ to: tabGrip.actor, type: "detach" });
     is(response.type, "detached", "Should have detached");
 
     yield removeTab(tab);
     yield gClient.close();
     finish();
   }));
 }
 
-registerCleanupFunction(function () {
-  gTab1 = null;
-  gTab1Actor = null;
-  gTab2 = null;
-  gTab2Actor = null;
+registerCleanupFunction(function() {
   gClient = null;
 });
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-01.html
copy to devtools/client/shared/test/doc_empty-tab-01.html
copy from devtools/client/debugger/test/mochitest/doc_empty-tab-02.html
copy to devtools/client/shared/test/doc_empty-tab-02.html