summaryrefslogtreecommitdiffstats
path: root/browser/components/translations/tests/browser/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/translations/tests/browser/head.js')
-rw-r--r--browser/components/translations/tests/browser/head.js348
1 files changed, 348 insertions, 0 deletions
diff --git a/browser/components/translations/tests/browser/head.js b/browser/components/translations/tests/browser/head.js
new file mode 100644
index 0000000000..56839971b7
--- /dev/null
+++ b/browser/components/translations/tests/browser/head.js
@@ -0,0 +1,348 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/toolkit/components/translations/tests/browser/shared-head.js",
+ this
+);
+
+/**
+ * Assert some property about the translations button.
+ *
+ * @param {Record<string, boolean>} visibleAssertions
+ * @param {string} message The message for the assertion.
+ * @returns {HTMLElement}
+ */
+async function assertTranslationsButton(visibleAssertions, message) {
+ const elements = {
+ button: document.getElementById("translations-button"),
+ icon: document.getElementById("translations-button-icon"),
+ circleArrows: document.getElementById("translations-button-circle-arrows"),
+ locale: document.getElementById("translations-button-locale"),
+ };
+
+ for (const [name, element] of Object.entries(elements)) {
+ if (!element) {
+ throw new Error("Could not find the " + name);
+ }
+ }
+
+ try {
+ // Test that the visibilities match.
+ await TestUtils.waitForCondition(() => {
+ for (const [name, visible] of Object.entries(visibleAssertions)) {
+ if (elements[name].hidden === visible) {
+ return false;
+ }
+ }
+ return true;
+ }, message);
+ } catch (error) {
+ // On a mismatch, report it.
+ for (const [name, expected] of Object.entries(visibleAssertions)) {
+ is(!elements[name].hidden, expected, `Visibility for "${name}"`);
+ }
+ }
+
+ ok(true, message);
+
+ return elements;
+}
+
+/**
+ * A convenience function to open the settings menu of the
+ * translations panel. Fails the test if the menu cannot be opened.
+ */
+async function openSettingsMenu() {
+ const { button } = await assertTranslationsButton(
+ { button: true },
+ "The button is available."
+ );
+
+ await waitForTranslationsPopupEvent("popupshown", () => {
+ click(button, "Opening the popup");
+ });
+
+ const gearIcon = getByL10nId("translations-panel-settings-button");
+ click(gearIcon, "Open the settings menu");
+}
+
+/**
+ * Simulates the effect of clicking the always-translate-language menuitem.
+ * Requires that the settings menu of the translations panel is open,
+ * otherwise the test will fail.
+ */
+async function toggleAlwaysTranslateLanguage() {
+ const alwaysTranslateLanguage = getByL10nId(
+ "translations-panel-settings-always-translate-language"
+ );
+ info("Toggle the always-translate-language menuitem");
+ await alwaysTranslateLanguage.doCommand();
+}
+
+/**
+ * Simulates the effect of clicking the never-translate-language menuitem.
+ * Requires that the settings menu of the translations panel is open,
+ * otherwise the test will fail.
+ */
+async function toggleNeverTranslateLanguage() {
+ const neverTranslateLanguage = getByL10nId(
+ "translations-panel-settings-never-translate-language"
+ );
+ info("Toggle the never-translate-language menuitem");
+ await neverTranslateLanguage.doCommand();
+}
+
+/**
+ * Simulates the effect of clicking the never-translate-site menuitem.
+ * Requires that the settings menu of the translations panel is open,
+ * otherwise the test will fail.
+ */
+async function toggleNeverTranslateSite() {
+ const neverTranslateSite = getByL10nId(
+ "translations-panel-settings-never-translate-site"
+ );
+ info("Toggle the never-translate-site menuitem");
+ await neverTranslateSite.doCommand();
+}
+
+/**
+ * Asserts that the always-translate-language checkbox matches the expected checked state.
+ *
+ * @param {string} langTag - A BCP-47 language tag
+ * @param {boolean} expectChecked - Whether the checkbox should be checked
+ */
+async function assertIsAlwaysTranslateLanguage(langTag, expectChecked) {
+ await assertCheckboxState(
+ "translations-panel-settings-always-translate-language",
+ expectChecked
+ );
+}
+
+/**
+ * Asserts that the never-translate-language checkbox matches the expected checked state.
+ *
+ * @param {string} langTag - A BCP-47 language tag
+ * @param {boolean} expectChecked - Whether the checkbox should be checked
+ */
+async function assertIsNeverTranslateLanguage(langTag, expectChecked) {
+ await assertCheckboxState(
+ "translations-panel-settings-never-translate-language",
+ expectChecked
+ );
+}
+
+/**
+ * Asserts that the never-translate-site checkbox matches the expected checked state.
+ *
+ * @param {string} url - The url of a website
+ * @param {boolean} expectChecked - Whether the checkbox should be checked
+ */
+async function assertIsNeverTranslateSite(url, expectChecked) {
+ await assertCheckboxState(
+ "translations-panel-settings-never-translate-site",
+ expectChecked
+ );
+}
+
+/**
+ * Asserts that the state of a checkbox with a given dataL10nId is
+ * checked or not, based on the value of expected being true or false.
+ *
+ * @param {string} dataL10nId - The data-l10n-id of the checkbox.
+ * @param {boolean} expectChecked - Whether the checkbox should be checked.
+ */
+async function assertCheckboxState(dataL10nId, expectChecked) {
+ const menuItems = getAllByL10nId(dataL10nId);
+ for (const menuItem of menuItems) {
+ await TestUtils.waitForCondition(
+ () =>
+ menuItem.getAttribute("checked") === (expectChecked ? "true" : "false"),
+ "Waiting for checkbox state"
+ );
+ is(
+ menuItem.getAttribute("checked"),
+ expectChecked ? "true" : "false",
+ `Should match expected checkbox state for ${dataL10nId}`
+ );
+ }
+}
+
+/**
+ * Navigate to a URL and indicate a message as to why.
+ */
+async function navigate(url, message) {
+ info(message);
+
+ // Load a blank page first to ensure that tests don't hang.
+ // I don't know why this is needed, but it appears to be necessary.
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, BLANK_PAGE);
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, url);
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+}
+
+/**
+ * Add a tab to the page
+ *
+ * @param {string} url
+ */
+async function addTab(url) {
+ info(`Adding tab for ` + url);
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ url,
+ true // Wait for laod
+ );
+ return {
+ tab,
+ removeTab() {
+ BrowserTestUtils.removeTab(tab);
+ },
+ };
+}
+
+async function switchTab(tab) {
+ info("Switching tabs");
+ await BrowserTestUtils.switchTab(gBrowser, tab);
+}
+
+function click(button, message) {
+ info(message);
+ EventUtils.synthesizeMouseAtCenter(button, {});
+}
+
+/**
+ * @param {Element} element
+ * @returns {boolean}
+ */
+function isVisible(element) {
+ const win = element.ownerDocument.ownerGlobal;
+ const { visibility, display } = win.getComputedStyle(element);
+ return visibility === "visible" && display !== "none";
+}
+
+/**
+ * Get an element by its l10n id, as this is a user-visible way to find an element.
+ * The `l10nId` represents the text that a user would actually see.
+ *
+ * @param {string} l10nId
+ * @param {Document} doc
+ * @returns {Element}
+ */
+function getByL10nId(l10nId, doc = document) {
+ const elements = doc.querySelectorAll(`[data-l10n-id="${l10nId}"]`);
+ if (elements.length === 0) {
+ throw new Error("Could not find the element by l10n id: " + l10nId);
+ }
+ for (const element of elements) {
+ if (isVisible(element)) {
+ return element;
+ }
+ }
+ throw new Error("The element is not visible in the DOM: " + l10nId);
+}
+
+/**
+ * Get all elements that match the l10n id.
+ *
+ * @param {string} l10nId
+ * @param {Document} doc
+ * @returns {Element}
+ */
+function getAllByL10nId(l10nId, doc = document) {
+ const elements = doc.querySelectorAll(`[data-l10n-id="${l10nId}"]`);
+ if (elements.length === 0) {
+ throw new Error("Could not find the element by l10n id: " + l10nId);
+ }
+ return elements;
+}
+
+/**
+ * @param {string} id
+ * @param {Document} [doc]
+ * @returns {Element}
+ */
+function getById(id, doc = document) {
+ const element = doc.getElementById(id);
+ if (!element) {
+ throw new Error("Could not find the element by id: #" + id);
+ }
+ if (isVisible(element)) {
+ return element;
+ }
+ throw new Error("The element is not visible in the DOM: #" + id);
+}
+
+/**
+ * A non-throwing version of `getByL10nId`.
+ *
+ * @param {string} l10nId
+ * @returns {Element | null}
+ */
+function maybeGetByL10nId(l10nId, doc = document) {
+ const selector = `[data-l10n-id="${l10nId}"]`;
+ const elements = doc.querySelectorAll(selector);
+ for (const element of elements) {
+ if (isVisible(element)) {
+ return element;
+ }
+ }
+ return null;
+}
+
+/**
+ * XUL popups will fire the popupshown and popuphidden events. These will fire for
+ * any type of popup in the browser. This function waits for one of those events, and
+ * checks that the viewId of the popup is PanelUI-profiler
+ *
+ * @param {"popupshown" | "popuphidden"} eventName
+ * @param {Function} callback
+ * @returns {Promise<void>}
+ */
+async function waitForTranslationsPopupEvent(eventName, callback) {
+ const panel = document.getElementById("translations-panel");
+ if (!panel) {
+ throw new Error("Unable to find the translations panel element.");
+ }
+ const promise = BrowserTestUtils.waitForEvent(panel, eventName);
+ callback();
+ info("Waiting for the translations panel popup to be shown");
+ await promise;
+ // Wait a single tick on the event loop.
+ await new Promise(resolve => setTimeout(resolve, 0));
+}
+
+/**
+ * When switching between between views in the popup panel, wait for the view to
+ * be fully shown.
+ *
+ * @param {Function} callback
+ */
+async function waitForViewShown(callback) {
+ const panel = document.getElementById("translations-panel");
+ if (!panel) {
+ throw new Error("Unable to find the translations panel element.");
+ }
+ const promise = BrowserTestUtils.waitForEvent(panel, "ViewShown");
+ callback();
+ info("Waiting for the translations panel view to be shown");
+ await promise;
+ await new Promise(resolve => setTimeout(resolve, 0));
+}
+
+const ENGLISH_PAGE_URL = TRANSLATIONS_TESTER_EN;
+const SPANISH_PAGE_URL = TRANSLATIONS_TESTER_ES;
+const SPANISH_PAGE_URL_2 = TRANSLATIONS_TESTER_ES_2;
+const SPANISH_PAGE_URL_DOT_ORG = TRANSLATIONS_TESTER_ES_DOT_ORG;
+const LANGUAGE_PAIRS = [
+ { fromLang: "es", toLang: "en", isBeta: false },
+ { fromLang: "en", toLang: "es", isBeta: false },
+ { fromLang: "fr", toLang: "en", isBeta: false },
+ { fromLang: "en", toLang: "fr", isBeta: false },
+ { fromLang: "en", toLang: "uk", isBeta: true },
+ { fromLang: "uk", toLang: "en", isBeta: true },
+];