Bug 1285500 - Rewrite browser_ext_browserAction_popup test and avoid intermittency on the 'overflow menu' test. r=mixedpuppy
authorLuca Greco <lgreco@mozilla.com>
Wed, 27 Feb 2019 21:44:47 +0000
changeset 519475 a56af9e8313bd344d1f0b497efa6b45b94802c6a
parent 519474 5570cf4346a807801c535758f1b82dae1ca63f3f
child 519476 de132128473b4973ba81fb84a75ee87f2ae55b7b
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmixedpuppy
bugs1285500
milestone67.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 1285500 - Rewrite browser_ext_browserAction_popup test and avoid intermittency on the 'overflow menu' test. r=mixedpuppy This patch rewrites the browser_ext_browserAction_popup.js test file to make it easier to follow the sequence of its test cases (in the previous version of this test file, the logic that was executing the sequence of test cases was fragmented in a bunch of listeners, in both the test extension and the privileged test code) and to make the test able to wait for the popups to be fully loaded or fully destroyed before moving to the next test case. Differential Revision: https://phabricator.services.mozilla.com/D21427
browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -6,261 +6,296 @@ const {GlobalManager} = ChromeUtils.impo
 
 function getBrowserAction(extension) {
   const {global: {browserActionFor}} = Management;
 
   let ext = GlobalManager.extensionMap.get(extension.id);
   return browserActionFor(ext);
 }
 
-function assertViewCount(extension, count) {
+async function assertViewCount(extension, count, waitForPromise) {
   let ext = GlobalManager.extensionMap.get(extension.id);
+
+  if (waitForPromise) {
+    await waitForPromise;
+  }
+
   is(ext.views.size, count, "Should have the expected number of extension views");
 }
 
+function promiseExtensionPageClosed(extension, viewType) {
+  return new Promise(resolve => {
+    const policy = WebExtensionPolicy.getByID(extension.id);
+
+    const listener = (evtType, context) => {
+      if (context.viewType === viewType) {
+        policy.extension.off("extension-proxy-context-load", listener);
+
+        context.callOnClose({
+          close: resolve,
+        });
+      }
+    };
+    policy.extension.on("extension-proxy-context-load", listener);
+  });
+}
+
 let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head><body>${url}</body></html>`;
 
 async function testInArea(area) {
   let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      browser.browserAction.onClicked.addListener(() => {
+        browser.test.sendMessage("browserAction-onClicked");
+      });
+
+      browser.test.onMessage.addListener(async (msg) => {
+        if (msg.type === "setBrowserActionPopup") {
+          let opts = {popup: msg.popup};
+          if (msg.onCurrentWindowId) {
+            let {id} = await browser.windows.getCurrent();
+            opts = {...opts, windowId: id};
+          } else if (msg.onActiveTabId) {
+            let [{id}] = await browser.tabs.query({active: true, currentWindow: true});
+            opts = {...opts, tabId: id};
+          }
+          await browser.browserAction.setPopup(opts);
+          browser.test.sendMessage("setBrowserActionPopup:done");
+        }
+      });
+
+      browser.test.sendMessage("background-page-ready");
+    },
     manifest: {
-      "background": {
-        "page": "data/background.html",
-      },
       "browser_action": {
         "default_popup": "popup-a.html",
         "browser_style": true,
       },
     },
 
     files: {
       "popup-a.html": scriptPage("popup-a.js"),
       "popup-a.js": function() {
-        window.onload = () => {
-          let color = window.getComputedStyle(document.body).color;
-          browser.test.assertEq("rgb(34, 36, 38)", color);
-          browser.runtime.sendMessage("from-popup-a");
-        };
-        browser.runtime.onMessage.addListener(msg => {
+        browser.test.onMessage.addListener(msg => {
           if (msg == "close-popup-using-window.close") {
             window.close();
           }
         });
+
+        window.onload = () => {
+          let color = window.getComputedStyle(document.body).color;
+          browser.test.assertEq("rgb(34, 36, 38)", color);
+          browser.test.sendMessage("from-popup", "popup-a");
+        };
       },
 
       "data/popup-b.html": scriptPage("popup-b.js"),
       "data/popup-b.js": function() {
         window.onload = () => {
-          browser.runtime.sendMessage("from-popup-b");
+          browser.test.sendMessage("from-popup", "popup-b");
         };
       },
 
       "data/popup-c.html": scriptPage("popup-c.js"),
       "data/popup-c.js": function() {
         // Close the popup before the document is fully-loaded to make sure that
         // we handle this case sanely.
-        browser.runtime.sendMessage("from-popup-c");
+        browser.test.sendMessage("from-popup", "popup-c");
         window.close();
       },
-
-      "data/background.html": scriptPage("background.js"),
-
-      "data/background.js": function() {
-        let sendClick;
-        let tests = [
-          () => {
-            browser.test.log(`Click browser action, expect popup "a".`);
-            sendClick({expectEvent: false, expectPopup: "a"});
-          },
-          () => {
-            browser.test.log(`Click browser action again, expect popup "a".`);
-            sendClick({expectEvent: false, expectPopup: "a"});
-          },
-          () => {
-            browser.test.log(`Call triggerAction, expect popup "a" again. Leave popup open.`);
-            sendClick({expectEvent: false, expectPopup: "a",
-                       closePopup: false, containingPopupShouldClose: false}, "trigger-action");
-          },
-          () => {
-            browser.test.log(`Call triggerAction again. Expect remaining popup closed.`);
-            sendClick({expectEvent: false, expectPopup: null}, "trigger-action");
-            browser.test.sendMessage("next-test", {waitUntilClosed: true});
-          },
-          () => {
-            browser.test.log(`Call triggerAction again. Expect popup "a" again.`);
-            sendClick({expectEvent: false, expectPopup: "a"}, "trigger-action");
-          },
-          () => {
-            browser.test.log(`Set popup to "c" and click browser action. Expect popup "c".`);
-            browser.browserAction.setPopup({popup: "popup-c.html"});
-            sendClick({expectEvent: false, expectPopup: "c", waitUntilClosed: true});
-          },
-          () => {
-            browser.test.log(`Set popup to "b" and click browser action. Expect popup "b".`);
-            browser.browserAction.setPopup({popup: "popup-b.html"});
-            sendClick({expectEvent: false, expectPopup: "b"});
-          },
-          () => {
-            browser.test.log(`Click browser action again, expect popup "b".`);
-            sendClick({expectEvent: false, expectPopup: "b"});
-          },
-          () => {
-            browser.test.log(`Clear popup URL. Click browser action. Expect click event.`);
-            browser.browserAction.setPopup({popup: ""});
-            sendClick({expectEvent: true, expectPopup: null});
-          },
-          () => {
-            browser.test.log(`Click browser action again. Expect another click event.`);
-            sendClick({expectEvent: true, expectPopup: null});
-          },
-          () => {
-            browser.test.log(`Call triggerAction. Expect click event.`);
-            sendClick({expectEvent: true, expectPopup: null}, "trigger-action");
-          },
-          async () => {
-            browser.test.log(`Set window-specific popup to "b" and click browser action. Expect popup "b".`);
-            let {id} = await browser.windows.getCurrent();
-            browser.browserAction.setPopup({windowId: id, popup: "popup-b.html"});
-            sendClick({expectEvent: false, expectPopup: "b"});
-          },
-          async () => {
-            browser.test.log(`Set tab-specific popup to "a" and click browser action. Expect popup "a", and leave open.`);
-            let [{id}] = await browser.tabs.query({active: true, currentWindow: true});
-            browser.browserAction.setPopup({tabId: id, popup: "/popup-a.html"});
-            sendClick({expectEvent: false, expectPopup: "a", closePopup: false,
-                       containingPopupShouldClose: false});
-          },
-          () => {
-            browser.test.log(`Tell popup "a" to call window.close(). Expect popup closed.`);
-            browser.test.sendMessage("next-test", {closePopupUsingWindow: true});
-          },
-        ];
-
-        let expect = {};
-        sendClick = ({expectEvent, expectPopup, runNextTest, waitUntilClosed,
-                      closePopup, containingPopupShouldClose = true},
-                     message = "send-click") => {
-          if (closePopup == undefined) {
-            closePopup = !expectEvent;
-          }
-
-          expect = {
-            event: expectEvent, popup: expectPopup, runNextTest,
-            waitUntilClosed, closePopup, containingPopupShouldClose,
-          };
-          browser.test.sendMessage(message);
-        };
-
-        browser.runtime.onMessage.addListener(msg => {
-          if (msg == "close-popup-using-window.close") {
-            return;
-          } else if (expect.popup) {
-            browser.test.assertEq(msg, `from-popup-${expect.popup}`,
-                                  "expected popup opened");
-          } else {
-            browser.test.fail(`unexpected popup: ${msg}`);
-          }
-
-          expect.popup = null;
-          browser.test.sendMessage("next-test", expect);
-        });
-
-        browser.browserAction.onClicked.addListener(() => {
-          if (expect.event) {
-            browser.test.succeed("expected click event received");
-          } else {
-            browser.test.fail("unexpected click event");
-          }
-
-          expect.event = false;
-          browser.test.sendMessage("next-test", expect);
-        });
-
-        browser.test.onMessage.addListener((msg) => {
-          if (msg == "close-popup-using-window.close") {
-            browser.runtime.sendMessage("close-popup-using-window.close");
-            return;
-          }
-
-          if (msg != "next-test") {
-            browser.test.fail("Expecting 'next-test' message");
-          }
-
-          if (tests.length) {
-            let test = tests.shift();
-            test();
-          } else {
-            browser.test.notifyPass("browseraction-tests-done");
-          }
-        });
-
-        browser.test.sendMessage("next-test");
-      },
     },
   });
 
-  extension.onMessage("send-click", () => {
-    clickBrowserAction(extension);
-  });
+  await Promise.all([
+    extension.startup(),
+    extension.awaitMessage("background-page-ready"),
+  ]);
+
+  let widget = getBrowserActionWidget(extension);
+
+  // Move the browserAction widget to the area targeted by this test.
+  CustomizableUI.addWidgetToArea(widget.id, area);
+
+  async function setBrowserActionPopup(opts) {
+    extension.sendMessage({type: "setBrowserActionPopup", ...opts});
+    await extension.awaitMessage("setBrowserActionPopup:done");
+  }
 
-  extension.onMessage("trigger-action", () => {
-    getBrowserAction(extension).triggerAction(window);
-  });
+  async function runTest({
+    actionType,
+    waitForPopupLoaded,
+    expectPopup, expectOnClicked,
+    closePopup,
+  }) {
+    const oncePopupPageClosed = promiseExtensionPageClosed(extension, "popup");
+    const oncePopupLoaded = waitForPopupLoaded ?
+      awaitExtensionPanel(extension) : undefined;
 
-  let widget;
-  extension.onMessage("next-test", async function(expecting = {}) {
-    if (!widget) {
-      widget = getBrowserActionWidget(extension);
-      CustomizableUI.addWidgetToArea(widget.id, area);
+    if (actionType === "click") {
+      clickBrowserAction(extension);
+    } else if (actionType === "trigger") {
+      getBrowserAction(extension).triggerAction(window);
+    }
+
+    if (expectPopup) {
+      info(`Waiting for popup: ${expectPopup}`);
+      is(await extension.awaitMessage("from-popup"), expectPopup, "expected popup opened");
+    } else if (expectOnClicked) {
+      await extension.awaitMessage("browserAction-onClicked");
+    }
+
+    await oncePopupLoaded;
+
+    if (closePopup) {
+      info("Closing popup");
+      await closeBrowserAction(extension);
+      await assertViewCount(extension, 1, oncePopupPageClosed);
     }
-    if (expecting.waitUntilClosed) {
-      await new Promise(resolve => setTimeout(resolve, 0));
+
+    return {oncePopupPageClosed};
+  }
+
+  // Run the sequence of test cases.
+  const tests = [
+    async () => {
+      info(`Click browser action, expect popup "a".`);
+
+      await runTest({
+        actionType: "click",
+        expectPopup: "popup-a",
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Click browser action again, expect popup "a".`);
+
+      await runTest({
+        actionType: "click",
+        expectPopup: "popup-a",
+        waitForPopupLoaded: true,
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Call triggerAction, expect popup "a" again. Leave popup open.`);
+
+      const {oncePopupPageClosed} = await runTest({
+        actionType: "trigger",
+        expectPopup: "popup-a",
+        waitForPopupLoaded: true,
+      });
+
+      await assertViewCount(extension, 2);
+
+      info(`Call triggerAction again. Expect remaining popup closed.`);
+      getBrowserAction(extension).triggerAction(window);
+
+      await assertViewCount(extension, 1, oncePopupPageClosed);
+    },
+    async () => {
+      info(`Call triggerAction again. Expect popup "a" again.`);
+
+      await runTest({
+        actionType: "trigger",
+        expectPopup: "popup-a",
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Set popup to "c" and click browser action. Expect popup "c".`);
+
+      await setBrowserActionPopup({popup: "data/popup-c.html"});
+
+      const {oncePopupPageClosed} = await runTest({
+        actionType: "click",
+        expectPopup: "popup-c",
+      });
+
+      await assertViewCount(extension, 1, oncePopupPageClosed);
+    },
+    async () => {
+      info(`Set popup to "b" and click browser action. Expect popup "b".`);
 
-      let panel = getBrowserActionPopup(extension);
-      if (panel && panel.state != "closed") {
-        info("Popup is open. Waiting for close");
-        await promisePopupHidden(panel);
-      }
+      await setBrowserActionPopup({popup: "data/popup-b.html"});
+
+      await runTest({
+        actionType: "click",
+        expectPopup: "popup-b",
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Click browser action again, expect popup "b".`);
+
+      await runTest({
+        actionType: "click",
+        expectPopup: "popup-b",
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Clear popup URL. Click browser action. Expect click event.`);
+
+      await setBrowserActionPopup({popup: ""});
+
+      await runTest({
+        actionType: "click",
+        expectOnClicked: true,
+      });
+    },
+    async () => {
+      info(`Click browser action again. Expect another click event.`);
 
-      assertViewCount(extension, 1);
-    } else if (expecting.closePopupUsingWindow) {
-      let panel = getBrowserActionPopup(extension);
-      ok(panel, "Expect panel to exist");
-      await promisePopupShown(panel);
+      await runTest({
+        actionType: "click",
+        expectOnClicked: true,
+      });
+    },
+    async () => {
+      info(`Call triggerAction. Expect click event.`);
+
+      await runTest({
+        actionType: "trigger",
+        expectOnClicked: true,
+      });
+    },
+    async () => {
+      info(`Set window-specific popup to "b" and click browser action. Expect popup "b".`);
+
+      await setBrowserActionPopup({popup: "data/popup-b.html", onCurrentWindowId: true});
 
+      await runTest({
+        actionType: "click",
+        expectPopup: "popup-b",
+        closePopup: true,
+      });
+    },
+    async () => {
+      info(`Set tab-specific popup to "a" and click browser action. Expect popup "a", and leave open.`);
+
+      await setBrowserActionPopup({popup: "/popup-a.html", onActiveTabId: true});
+
+      const {oncePopupPageClosed} = await runTest({
+        actionType: "click",
+        expectPopup: "popup-a",
+      });
+      assertViewCount(extension, 2);
+
+      info(`Tell popup "a" to call window.close(). Expect popup closed.`);
       extension.sendMessage("close-popup-using-window.close");
 
-      await promisePopupHidden(panel);
-      ok(true, "Panel is closed");
-
-      assertViewCount(extension, 1);
-    } else if (expecting.closePopup) {
-      if (!getBrowserActionPopup(extension)) {
-        info("Waiting for panel");
-        await awaitExtensionPanel(extension);
-      }
-
-      info("Closing for panel");
-      await closeBrowserAction(extension);
-      assertViewCount(extension, 1);
-    }
+      await assertViewCount(extension, 1, oncePopupPageClosed);
+    },
+  ];
 
-    if (area == getCustomizableUIPanelID() && expecting.containingPopupShouldClose) {
-      let {node} = getBrowserActionWidget(extension).forWindow(window);
-      let panel = node.closest("panel");
-      info(`State of panel ${panel.id} is: ${panel.state}`);
-      ok(!["open", "showing"].includes(panel.state),
-         "Panel containing the action should be closed");
-    }
+  for (let test of tests) {
+    await test();
+  }
 
-    info("Starting next test");
-    extension.sendMessage("next-test");
-  });
-
-  await Promise.all([extension.startup(), extension.awaitFinish("browseraction-tests-done")]);
-
+  // Unload the extension and verify that the browserAction widget is gone.
   await extension.unload();
 
   let view = document.getElementById(widget.viewId);
   is(view, null, "browserAction view removed from document");
 }
 
 add_task(async function testBrowserActionInToolbar() {
   await testInArea(CustomizableUI.AREA_NAVBAR);