Bug 1506581 - Add mochitest to test updates of the addon list for USB runtimes;r=daisuke
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 13 Nov 2018 18:13:35 +0000
changeset 446540 0a38b015f9531af367ef48cc9033ae4f4e13394d
parent 446539 3808928279aadfa8c73e9d6e59af6a4e5b34219e
child 446541 dfaa72cb3f7a16ea8bbedd78563157e6bef61a11
push id35043
push userebalazs@mozilla.com
push dateThu, 15 Nov 2018 16:12:36 +0000
treeherdermozilla-central@59026ada59bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaisuke
bugs1506581
milestone65.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 1506581 - Add mochitest to test updates of the addon list for USB runtimes;r=daisuke Depends on D11651, Depends on D11643 Differential Revision: https://phabricator.services.mozilla.com/D11663
devtools/client/aboutdebugging-new/test/browser/browser.ini
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_addons_usb_runtime.js
devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_debug-target-pane_usb_runtime.js
devtools/client/aboutdebugging-new/test/browser/head.js
devtools/client/aboutdebugging-new/test/browser/mocks/head-client-wrapper-mock.js
devtools/client/aboutdebugging-new/test/browser/mocks/head-usb-mocks.js
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -8,16 +8,17 @@ support-files =
   mocks/*
   resources/test-adb-extension/*
   resources/test-temporary-extension/*
   test-tab-favicons.html
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
 
+[browser_aboutdebugging_addons_usb_runtime.js]
 [browser_aboutdebugging_connect_networklocations.js]
 [browser_aboutdebugging_connect_toggle_usb_devices.js]
 skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
 [browser_aboutdebugging_connection_prompt_setting.js]
 [browser_aboutdebugging_debug-target-pane_collapsibilities_interaction.js]
 [browser_aboutdebugging_debug-target-pane_collapsibilities_preference.js]
 [browser_aboutdebugging_debug-target-pane_empty.js]
 [browser_aboutdebugging_debug-target-pane_usb_runtime.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_addons_usb_runtime.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const RUNTIME_ID = "test-runtime-id";
+const RUNTIME_DEVICE_NAME = "test device name";
+const RUNTIME_APP_NAME = "TestApp";
+
+/* import-globals-from mocks/head-usb-mocks.js */
+Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "mocks/head-usb-mocks.js", this);
+
+// Test that addons are displayed and updated for USB runtimes when expected.
+add_task(async function() {
+  const usbMocks = new UsbMocks();
+  usbMocks.enableMocks();
+  registerCleanupFunction(() => usbMocks.disableMocks());
+
+  const { document, tab } = await openAboutDebugging();
+
+  const usbClient = usbMocks.createRuntime(RUNTIME_ID, {
+    appName: RUNTIME_APP_NAME,
+    deviceName: RUNTIME_DEVICE_NAME,
+  });
+  usbMocks.emitUpdate();
+
+  await connectToRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
+
+  const extensionPane = getDebugTargetPane("Extensions", document);
+  info("Check an empty target pane message is displayed");
+  ok(extensionPane.querySelector(".js-debug-target-list-empty"),
+    "Extensions list is empty");
+
+  info("Add an extension to the remote client");
+  const addon = { name: "Test extension name", debuggable: true };
+  usbClient.listAddons = () => ({ addons: [addon] });
+  usbClient._eventEmitter.emit("addonListChanged");
+
+  info("Wait until the extension appears");
+  await waitUntil(() => !extensionPane.querySelector(".js-debug-target-list-empty"));
+
+  const extensionTarget = findDebugTargetByText("Test extension name", document);
+  ok(extensionTarget, "Extension target appeared for the USB runtime");
+
+  // The goal here is to check that USB runtimes addons are only updated when the USB
+  // runtime is sending addonListChanged events. The reason for this test is because the
+  // previous implementation was updating the USB runtime extensions list when the _local_
+  // AddonManager was updated.
+  info("Remove the extension from the remote client WITHOUT sending an event");
+  usbClient.listAddons = () => ({ addons: [] });
+
+  info("Simulate an addon update on the ThisFirefox client");
+  usbMocks.thisFirefoxClient._eventEmitter.emit("addonListChanged");
+
+  // To avoid wait for a set period of time we trigger another async update, adding a new
+  // tab. We assume that if the addon update mechanism had started, it would also be done
+  // when the new tab was processed.
+  info("Wait until the tab target for 'http://some.random/url.com' appears");
+  const testTab = { outerWindowID: 0, url: "http://some.random/url.com" };
+  usbClient.listTabs = () => ({ tabs: [testTab] });
+  usbClient._eventEmitter.emit("tabListChanged");
+  await waitUntil(() => findDebugTargetByText("http://some.random/url.com", document));
+
+  ok(findDebugTargetByText("Test extension name", document),
+    "The test extension is still visible");
+
+  info("Emit `addonListChanged` on usbClient and wait for the target list to update");
+  usbClient._eventEmitter.emit("addonListChanged");
+  await waitUntil(() => !findDebugTargetByText("Test extension name", document));
+
+  await removeTab(tab);
+});
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_debug-target-pane_usb_runtime.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_debug-target-pane_usb_runtime.js
@@ -21,32 +21,17 @@ add_task(async function() {
   const { document, tab } = await openAboutDebugging();
 
   usbMocks.createRuntime(RUNTIME_ID, {
     appName: RUNTIME_APP_NAME,
     deviceName: RUNTIME_DEVICE_NAME,
   });
   usbMocks.emitUpdate();
 
-  info("Wait until the USB sidebar item appears");
-  await waitUntil(() => findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
-  const usbRuntimeSidebarItem = findSidebarItemByText(RUNTIME_DEVICE_NAME, document);
-  const connectButton = usbRuntimeSidebarItem.querySelector(".js-connect-button");
-  ok(connectButton, "Connect button is displayed for the USB runtime");
-
-  info("Click on the connect button and wait until it disappears");
-  connectButton.click();
-  await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button"));
-
-  usbRuntimeSidebarItem.querySelector(".js-sidebar-link").click();
-
-  await waitUntil(() => {
-    const runtimeInfo = document.querySelector(".js-runtime-info");
-    return runtimeInfo.textContent.includes(RUNTIME_APP_NAME);
-  });
+  await connectToRuntime(RUNTIME_DEVICE_NAME, RUNTIME_APP_NAME, document);
 
   const SUPPORTED_TARGET_PANES = [
     "Extensions",
     "Tabs",
   ];
 
   for (const { title } of TARGET_PANES) {
     const debugTargetPaneEl = getDebugTargetPane(title, document);
--- a/devtools/client/aboutdebugging-new/test/browser/head.js
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -98,8 +98,27 @@ function findDebugTargetByText(text, doc
 }
 
 function findSidebarItemByText(text, document) {
   const sidebarItems = document.querySelectorAll(".js-sidebar-item");
   return [...sidebarItems].find(element => {
     return element.textContent.includes(text);
   });
 }
+
+async function connectToRuntime(deviceName, appName, document) {
+  info(`Wait until the sidebar item for ${deviceName} appears`);
+  await waitUntil(() => findSidebarItemByText(deviceName, document));
+  const sidebarItem = findSidebarItemByText(deviceName, document);
+  const connectButton = sidebarItem.querySelector(".js-connect-button");
+  ok(connectButton, `Connect button is displayed for the runtime ${deviceName}`);
+
+  info("Click on the connect button and wait until it disappears");
+  connectButton.click();
+  await waitUntil(() => !sidebarItem.querySelector(".js-connect-button"));
+
+  sidebarItem.querySelector(".js-sidebar-link").click();
+
+  await waitUntil(() => {
+    const runtimeInfo = document.querySelector(".js-runtime-info");
+    return runtimeInfo.textContent.includes(appName);
+  });
+}
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/head-client-wrapper-mock.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/head-client-wrapper-mock.js
@@ -13,21 +13,33 @@ const { RUNTIME_PREFERENCE } =
 // Sensible default values for runtime preferences that should be usable in most
 // situations
 const DEFAULT_PREFERENCES = {
   [RUNTIME_PREFERENCE.CONNECTION_PROMPT]: true,
 };
 
 // Creates a simple mock ClientWrapper.
 function createClientMock() {
+  const EventEmitter = require("devtools/shared/event-emitter");
+  const eventEmitter = {};
+  EventEmitter.decorate(eventEmitter);
+
   return {
-    // no-op
-    addOneTimeListener: () => {},
-    // no-op
-    addListener: () => {},
+    // add a reference to the internal event emitter so that consumers can fire client
+    // events.
+    _eventEmitter: eventEmitter,
+    addOneTimeListener: (evt, listener) => {
+      eventEmitter.once(evt, listener);
+    },
+    addListener: (evt, listener) => {
+      eventEmitter.on(evt, listener);
+    },
+    removeListener: (evt, listener) => {
+      eventEmitter.off(evt, listener);
+    },
     // no-op
     close: () => {},
     // no-op
     connect: () => {},
     // no-op
     getDeviceDescription: () => {},
     // Return default preference value or null if no match.
     getPreference: (prefName) => {
@@ -42,18 +54,16 @@ function createClientMock() {
     listTabs: () => ({ tabs: []}),
     // Empty arrays of workers
     listWorkers: () => ({
       otherWorkers: [],
       serviceWorkers: [],
       sharedWorkers: [],
     }),
     // no-op
-    removeListener: () => {},
-    // no-op
     setPreference: () => {},
   };
 }
 
 // Create a ClientWrapper mock that can be used to replace the this-firefox runtime.
 function createThisFirefoxClientMock() {
   const mockThisFirefoxDescription = {
     name: "Firefox",
--- a/devtools/client/aboutdebugging-new/test/browser/mocks/head-usb-mocks.js
+++ b/devtools/client/aboutdebugging-new/test/browser/mocks/head-usb-mocks.js
@@ -35,17 +35,22 @@ class UsbMocks {
     this.runtimeClientFactoryMock = createRuntimeClientFactoryMock();
     this._clients = {};
     this.runtimeClientFactoryMock.createClientForRuntime = runtime => {
       return { client: this._clients[runtime.id] };
     };
 
     // Add a client for THIS_FIREFOX, since about:debugging will start on the This Firefox
     // page.
-    this._clients[RUNTIMES.THIS_FIREFOX] = createThisFirefoxClientMock();
+    this._thisFirefoxClient = createThisFirefoxClientMock();
+    this._clients[RUNTIMES.THIS_FIREFOX] = this._thisFirefoxClient;
+  }
+
+  get thisFirefoxClient() {
+    return this._thisFirefoxClient;
   }
 
   enableMocks() {
     enableUsbRuntimesMock(this.usbRuntimesMock);
     enableRuntimeClientFactoryMock(this.runtimeClientFactoryMock);
   }
 
   disableMocks() {