summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm')
-rw-r--r--comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm423
1 files changed, 423 insertions, 0 deletions
diff --git a/comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm b/comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm
new file mode 100644
index 0000000000..3d7ba3ee2c
--- /dev/null
+++ b/comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm
@@ -0,0 +1,423 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const EXPORTED_SYMBOLS = [
+ "open_content_tab_with_url",
+ "open_content_tab_with_click",
+ "plan_for_content_tab_load",
+ "wait_for_content_tab_load",
+ "assert_content_tab_has_favicon",
+ "content_tab_e",
+ "get_content_tab_element_display",
+ "assert_content_tab_element_hidden",
+ "assert_content_tab_element_visible",
+ "wait_for_content_tab_element_display",
+ "get_element_by_text",
+ "assert_content_tab_text_present",
+ "assert_content_tab_text_absent",
+ "NotificationWatcher",
+ "get_notification_bar_for_tab",
+ "updateBlocklist",
+ "setAndUpdateBlocklist",
+ "resetBlocklist",
+ "gMockExtProtSvcReg",
+ "gMockExtProtSvc",
+];
+
+var utils = ChromeUtils.import("resource://testing-common/mozmill/utils.jsm");
+
+var folderDisplayHelper = ChromeUtils.import(
+ "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
+);
+var { MockObjectReplacer } = ChromeUtils.import(
+ "resource://testing-common/mozmill/MockObjectHelpers.jsm"
+);
+var wh = ChromeUtils.import(
+ "resource://testing-common/mozmill/WindowHelpers.jsm"
+);
+
+var { Assert } = ChromeUtils.importESModule(
+ "resource://testing-common/Assert.sys.mjs"
+);
+
+var FAST_TIMEOUT = 1000;
+var FAST_INTERVAL = 100;
+var EXT_PROTOCOL_SVC_CID = "@mozilla.org/uriloader/external-protocol-service;1";
+
+var mc = folderDisplayHelper.mc;
+
+var _originalBlocklistURL = null;
+
+var gMockExtProtSvcReg = new MockObjectReplacer(
+ EXT_PROTOCOL_SVC_CID,
+ MockExtProtConstructor
+);
+
+/**
+ * gMockExtProtocolSvc allows us to capture (most if not all) attempts to
+ * open links in the default browser.
+ */
+var gMockExtProtSvc = {
+ _loadedURLs: [],
+ QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]),
+
+ externalProtocolHandlerExists(aProtocolScheme) {},
+
+ getApplicationDescription(aScheme) {},
+
+ getProtocolHandlerInfo(aProtocolScheme) {},
+
+ getProtocolHandlerInfoFromOS(aProtocolScheme, aFound) {},
+
+ isExposedProtocol(aProtocolScheme) {},
+
+ loadURI(aURI, aWindowContext) {
+ this._loadedURLs.push(aURI.spec);
+ },
+
+ setProtocolHandlerDefaults(aHandlerInfo, aOSHandlerExists) {},
+
+ urlLoaded(aURL) {
+ return this._loadedURLs.includes(aURL);
+ },
+};
+
+function MockExtProtConstructor() {
+ return gMockExtProtSvc;
+}
+
+/* Allows for planning / capture of notification events within
+ * content tabs, for example: plugin crash notifications, theme
+ * install notifications.
+ */
+var ALERT_TIMEOUT = 10000;
+
+var NotificationWatcher = {
+ planForNotification(aController) {
+ this.alerted = false;
+ aController.window.document.addEventListener(
+ "AlertActive",
+ this.alertActive
+ );
+ },
+ waitForNotification(aController) {
+ if (!this.alerted) {
+ utils.waitFor(
+ () => this.alerted,
+ "Timeout waiting for alert",
+ ALERT_TIMEOUT,
+ 100
+ );
+ }
+ // Double check the notification box has finished animating.
+ let notificationBox = mc.window.document
+ .getElementById("tabmail")
+ .selectedTab.panel.querySelector("notificationbox");
+ if (notificationBox && notificationBox._animating) {
+ utils.waitFor(
+ () => !notificationBox._animating,
+ "Timeout waiting for notification box animation to finish",
+ ALERT_TIMEOUT,
+ 100
+ );
+ }
+
+ aController.window.document.removeEventListener(
+ "AlertActive",
+ this.alertActive
+ );
+ },
+ alerted: false,
+ alertActive() {
+ NotificationWatcher.alerted = true;
+ },
+};
+
+/**
+ * Opens a content tab with the given URL.
+ *
+ * @param {string} aURL - The URL to load.
+ * @param {string} [aLinkHandler=null] - See specialTabs.contentTabType.openTab.
+ * @param {boolean} [aBackground=false] Whether the tab is opened in the background.
+ *
+ * @returns {object} The newly-opened tab.
+ */
+function open_content_tab_with_url(
+ aURL,
+ aLinkHandler = null,
+ aBackground = false
+) {
+ let tabmail = mc.window.document.getElementById("tabmail");
+ let preCount = tabmail.tabContainer.allTabs.length;
+ tabmail.openTab("contentTab", {
+ url: aURL,
+ background: aBackground,
+ linkHandler: aLinkHandler,
+ });
+ utils.waitFor(
+ () => tabmail.tabContainer.allTabs.length == preCount + 1,
+ "Timeout waiting for the content tab to open with URL: " + aURL,
+ FAST_TIMEOUT,
+ FAST_INTERVAL
+ );
+
+ // We append new tabs at the end, so check the last one.
+ let expectedNewTab = tabmail.tabInfo[preCount];
+ folderDisplayHelper.assert_selected_tab(expectedNewTab);
+ wait_for_content_tab_load(expectedNewTab, aURL);
+ return expectedNewTab;
+}
+
+/**
+ * Opens a content tab with a click on the given element. The tab is expected to
+ * be opened in the foreground. The element is expected to be associated with
+ * the given controller.
+ *
+ * @param aElem The element to click or a function that causes the tab to open.
+ * @param aExpectedURL The URL that is expected to be opened (string).
+ * @param [aController] The controller the element is associated with. Defaults
+ * to |mc|.
+ * @param [aTabType] Optional tab type to expect (string).
+ * @returns The newly-opened tab.
+ */
+function open_content_tab_with_click(
+ aElem,
+ aExpectedURL,
+ aController,
+ aTabType = "contentTab"
+) {
+ if (aController === undefined) {
+ aController = mc;
+ }
+
+ let preCount =
+ aController.window.document.getElementById("tabmail").tabContainer.allTabs
+ .length;
+ if (typeof aElem != "function") {
+ EventUtils.synthesizeMouseAtCenter(aElem, {}, aElem.ownerGlobal);
+ } else {
+ aElem();
+ }
+
+ utils.waitFor(
+ () =>
+ aController.window.document.getElementById("tabmail").tabContainer.allTabs
+ .length ==
+ preCount + 1,
+ "Timeout waiting for the content tab to open",
+ FAST_TIMEOUT,
+ FAST_INTERVAL
+ );
+
+ // We append new tabs at the end, so check the last one.
+ let expectedNewTab =
+ aController.window.document.getElementById("tabmail").tabInfo[preCount];
+ folderDisplayHelper.assert_selected_tab(expectedNewTab);
+ folderDisplayHelper.assert_tab_mode_name(expectedNewTab, aTabType);
+ wait_for_content_tab_load(expectedNewTab, aExpectedURL);
+ return expectedNewTab;
+}
+
+/**
+ * Call this before triggering a page load that you are going to wait for using
+ * |wait_for_content_tab_load|. This ensures that if a page is already displayed
+ * in the given tab that state is sufficiently cleaned up so it doesn't trick us
+ * into thinking that there is no need to wait.
+ *
+ * @param [aTab] optional tab, defaulting to the current tab.
+ */
+function plan_for_content_tab_load(aTab) {
+ if (aTab === undefined) {
+ aTab = mc.window.document.getElementById("tabmail").currentTabInfo;
+ }
+ aTab.pageLoaded = false;
+}
+
+/**
+ * Waits for the given content tab to load completely with the given URL. This
+ * is expected to be accompanied by a |plan_for_content_tab_load| right before
+ * the action triggering the page load takes place.
+ *
+ * Note that you cannot call |plan_for_content_tab_load| if you're opening a new
+ * tab. That is fine, because pageLoaded is initially false.
+ *
+ * @param [aTab] Optional tab, defaulting to the current tab.
+ * @param aURL The URL being loaded in the tab.
+ * @param [aTimeout] Optional time to wait for the load.
+ */
+function wait_for_content_tab_load(aTab, aURL, aTimeout) {
+ if (aTab === undefined) {
+ aTab = mc.window.document.getElementById("tabmail").currentTabInfo;
+ }
+
+ function isLoadedChecker() {
+ // Require that the progress listener think that the page is loaded.
+ if (!aTab.pageLoaded) {
+ return false;
+ }
+ // Also require that our tab infrastructure thinks that the page is loaded.
+ return !aTab.busy;
+ }
+
+ utils.waitFor(
+ isLoadedChecker,
+ "Timeout waiting for the content tab page to load.",
+ aTimeout
+ );
+ // The above may return immediately, meaning the event queue might not get a
+ // chance. Give it a chance now.
+ utils.sleep(0);
+ // Finally, require that the tab's browser thinks that no page is being loaded.
+ wh.wait_for_browser_load(aTab.browser, aURL);
+}
+
+/**
+ * Gets the element with the given ID from the content tab's displayed page.
+ */
+function content_tab_e(aTab, aId) {
+ return aTab.browser.contentDocument.getElementById(aId);
+}
+
+/**
+ * Assert that the given content tab has the given URL loaded as a favicon.
+ */
+function assert_content_tab_has_favicon(aTab, aURL) {
+ Assert.equal(aTab.favIconUrl, aURL, "Checking tab favicon");
+}
+
+/**
+ * Returns the current "display" style property of an element.
+ */
+function get_content_tab_element_display(aTab, aElem) {
+ let style = aTab.browser.contentWindow.getComputedStyle(aElem);
+ return style.getPropertyValue("display");
+}
+
+/**
+ * Asserts that the given element is hidden from view on the page.
+ */
+function assert_content_tab_element_hidden(aTab, aElem) {
+ let display = get_content_tab_element_display(aTab, aElem);
+ Assert.equal(display, "none", "Element should be hidden");
+}
+
+/**
+ * Asserts that the given element is visible on the page.
+ */
+function assert_content_tab_element_visible(aTab, aElem) {
+ let display = get_content_tab_element_display(aTab, aElem);
+ Assert.notEqual(display, "none", "Element should be visible");
+}
+
+/**
+ * Waits for the element's display property indicate it is visible.
+ */
+function wait_for_content_tab_element_display(aTab, aElem) {
+ function isValue() {
+ return get_content_tab_element_display(aTab, aElem) != "none";
+ }
+ try {
+ utils.waitFor(isValue);
+ } catch (e) {
+ if (e instanceof utils.TimeoutError) {
+ Assert.report(
+ true,
+ undefined,
+ undefined,
+ "Timeout waiting for element to become visible"
+ );
+ } else {
+ throw e;
+ }
+ }
+}
+
+/**
+ * Finds element in document fragment, containing only the specified text
+ * as its textContent value.
+ *
+ * @param aRootNode Root node of the node tree where search should start.
+ * @param aText The string to search.
+ */
+function get_element_by_text(aRootNode, aText) {
+ // Check every node existing.
+ let nodes = aRootNode.querySelectorAll("*");
+ for (let node of nodes) {
+ // We ignore surrounding whitespace.
+ if (node.textContent.trim() == aText) {
+ return node;
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Finds element containing only the specified text in the content tab's page.
+ */
+function get_content_tab_element_by_text(aTab, aText) {
+ let doc = aTab.browser.contentDocument.documentElement;
+ return get_element_by_text(doc, aText);
+}
+
+/**
+ * Asserts that the given text is present on the content tab's page.
+ */
+function assert_content_tab_text_present(aTab, aText) {
+ Assert.ok(
+ get_content_tab_element_by_text(aTab, aText),
+ `String "${aText}" should be on the content tab's page`
+ );
+}
+
+/**
+ * Asserts that the given text is absent on the content tab's page.
+ */
+function assert_content_tab_text_absent(aTab, aText) {
+ Assert.ok(
+ !get_content_tab_element_by_text(aTab, aText),
+ `String "${aText}" should not be on the content tab's page`
+ );
+}
+
+/**
+ * Returns the notification bar for a tab if one is currently visible,
+ * null if otherwise.
+ */
+function get_notification_bar_for_tab(aTab) {
+ let notificationBoxEls = mc.window.document
+ .getElementById("tabmail")
+ .selectedTab.panel.querySelector("notificationbox");
+ if (!notificationBoxEls) {
+ return null;
+ }
+
+ return notificationBoxEls;
+}
+
+function updateBlocklist(aController, aCallback) {
+ let observer = function () {
+ Services.obs.removeObserver(observer, "blocklist-updated");
+ aController.window.setTimeout(aCallback, 0);
+ };
+ Services.obs.addObserver(observer, "blocklist-updated");
+ Services.blocklist.QueryInterface(Ci.nsITimerCallback).notify(null);
+}
+
+function setAndUpdateBlocklist(aController, aURL, aCallback) {
+ if (!_originalBlocklistURL) {
+ _originalBlocklistURL = Services.prefs.getCharPref(
+ "extensions.blocklist.url"
+ );
+ }
+ Services.prefs.setCharPref("extensions.blocklist.url", aURL);
+ updateBlocklist(aController, aCallback);
+}
+
+function resetBlocklist(aController, aCallback) {
+ Services.prefs.setCharPref("extensions.blocklist.url", _originalBlocklistURL);
+ updateBlocklist(aController, aCallback);
+}