diff --git a/toolkit/components/translations/actors/TranslationsParent.sys.mjs b/toolkit/components/translations/actors/TranslationsParent.sys.mjs index fec7927ee205..2a705c35ad29 100644 --- a/toolkit/components/translations/actors/TranslationsParent.sys.mjs +++ b/toolkit/components/translations/actors/TranslationsParent.sys.mjs @@ -62,6 +62,13 @@ export class TranslationsParent extends JSWindowActorParent { /** @type {RemoteSettingsClient | null} */ #wasmRemoteClient = null; + /** + * The translation engine can be mocked for testing. + * + * @type {Array<{ fromLang: string, toLang: string }>} + */ + static #mockedLanguagePairs = null; + async receiveMessage({ name, data }) { switch (name) { case "Translations:GetBergamotWasmArrayBuffer": { @@ -103,7 +110,10 @@ export class TranslationsParent extends JSWindowActorParent { */ async #getSupportedLanguages() { const languages = new Set(); - for (const { fromLang } of (await this.#getModelRecords()).values()) { + const languagePairs = + TranslationsParent.#mockedLanguagePairs ?? + (await this.#getModelRecords()).values(); + for (const { fromLang } of languagePairs) { languages.add(fromLang); } @@ -416,6 +426,15 @@ export class TranslationsParent extends JSWindowActorParent { return results; } + + /** + * For testing purposes, allow the Translations Engine to be mocked. + * @param {Array<{ fromLang: string, toLang: string }>} languagePairs + */ + mock(languagePairs) { + lazy.console.log("Mocking language pairs", languagePairs); + TranslationsParent.#mockedLanguagePairs = languagePairs; + } } /** diff --git a/toolkit/components/translations/tests/browser/browser_about_translations.js b/toolkit/components/translations/tests/browser/browser_about_translations.js index 6f903ab0ee26..4fdf16c50595 100644 --- a/toolkit/components/translations/tests/browser/browser_about_translations.js +++ b/toolkit/components/translations/tests/browser/browser_about_translations.js @@ -55,3 +55,94 @@ add_task(async function test_about_translations_disabled() { }, }); }); + +add_task(async function test_about_translations_dropdowns() { + const { appLocaleAsBCP47 } = Services.locale; + if (!appLocaleAsBCP47.startsWith("en")) { + console.warn( + "This test assumes to be running in an 'en' app locale, however the app locale " + + `is set to ${appLocaleAsBCP47}. Skipping the test.` + ); + ok(true, "Skipping test."); + return; + } + + await openAboutTranslations({ + languagePairs: [ + { fromLang: "en", toLang: "es" }, + { fromLang: "es", toLang: "en" }, + // This is not a bi-directional translation. + { fromLang: "is", toLang: "en" }, + ], + runInPage: async ({ selectors }) => { + const { document } = content; + + /** + * Some languages can be marked as hidden in the dropbdown. This function + * asserts the configuration of the options. + * + * @param {object} args + * @param {string} args.message + * @param {HTMLSelectElement} args.select + * @param {string[]} args.availableOptions + * @param {string} args.selectedValue + */ + function assertOptions({ + message, + select, + availableOptions, + selectedValue, + }) { + const options = [...select.options] + .filter(option => !option.hidden) + .map(option => option.value); + + info(message); + Assert.deepEqual( + options, + availableOptions, + "The available options match." + ); + is(selectedValue, select.value, "The selected value matches."); + } + + /** @type {HTMLSelectElement} */ + const fromSelect = document.querySelector(selectors.fromLanguageSelect); + /** @type {HTMLSelectElement} */ + const toSelect = document.querySelector(selectors.toLanguageSelect); + + assertOptions({ + message: "From languages have English already selected.", + select: fromSelect, + availableOptions: ["", "en", "is", "es"], + selectedValue: "en", + }); + + assertOptions({ + message: + 'The "to" options do not have "en" in the list, and nothing is selected.', + select: toSelect, + availableOptions: ["", "is", "es"], + selectedValue: "", + }); + + info('Switch the "to" language to Spanish.'); + toSelect.value = "es"; + toSelect.dispatchEvent(new Event("input")); + + assertOptions({ + message: 'The "from" languages no longer suggest Spanish.', + select: fromSelect, + availableOptions: ["", "en", "is"], + selectedValue: "en", + }); + + assertOptions({ + message: 'The "to" options remain the same.', + select: toSelect, + availableOptions: ["", "is", "es"], + selectedValue: "es", + }); + }, + }); +}); diff --git a/toolkit/components/translations/tests/browser/head.js b/toolkit/components/translations/tests/browser/head.js index 020c5219475b..cf5f8a1adc5d 100644 --- a/toolkit/components/translations/tests/browser/head.js +++ b/toolkit/components/translations/tests/browser/head.js @@ -22,8 +22,16 @@ * * @param {boolean} [options.disabled] * Disable the panel through a pref. + * + * @param {Array<{ fromLang: string, toLang: string}>} options.languagePairs + * The translation languages pairs to mock for the test. */ -async function openAboutTranslations({ dataForContent, disabled, runInPage }) { +async function openAboutTranslations({ + dataForContent, + disabled, + runInPage, + languagePairs, +}) { await SpecialPowers.pushPrefEnv({ set: [ // Enabled by default. @@ -44,18 +52,38 @@ async function openAboutTranslations({ dataForContent, disabled, runInPage }) { translationResultBlank: "#translation-to-blank", }; + // Start the tab at about:blank. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, - "about:translations", + "about:blank", true // waitForLoad ); + // Before loading about:translations, handle any mocking of the actor. + if (languagePairs) { + const translations = tab.linkedBrowser.browsingContext.currentWindowGlobal.getActor( + "Translations" + ); + translations.mock(languagePairs); + } + + // Now load the about:translations page, since the actor could be mocked. + BrowserTestUtils.loadURIString(tab.linkedBrowser, "about:translations"); + await BrowserTestUtils.browserLoaded(tab.linkedBrowser); + await ContentTask.spawn( tab.linkedBrowser, { dataForContent, selectors }, runInPage ); + if (languagePairs) { + // Revert the mock. + const translations = tab.linkedBrowser.browsingContext.currentWindowGlobal.getActor( + "Translations" + ); + translations.mock(null); + } BrowserTestUtils.removeTab(tab); await SpecialPowers.popPrefEnv(); }