diff options
Diffstat (limited to 'comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm')
-rw-r--r-- | comm/mail/test/browser/shared-modules/ContentTabHelpers.jsm | 423 |
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); +} |