diff options
Diffstat (limited to 'browser/components/translations/tests/browser/browser_translations_full_page_panel_fuzzing.js')
-rw-r--r-- | browser/components/translations/tests/browser/browser_translations_full_page_panel_fuzzing.js | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/browser/components/translations/tests/browser/browser_translations_full_page_panel_fuzzing.js b/browser/components/translations/tests/browser/browser_translations_full_page_panel_fuzzing.js new file mode 100644 index 0000000000..0accc3db60 --- /dev/null +++ b/browser/components/translations/tests/browser/browser_translations_full_page_panel_fuzzing.js @@ -0,0 +1,240 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Manually destroy the engine while a page is in the background, and test that the page + * is still translated after switching back to it. + */ +add_task(async function test_translations_panel_fuzzing() { + const { + cleanup, + runInPage: runInSpanishPage, + tab: spanishTab, + } = await loadTestPage({ + page: SPANISH_PAGE_URL, + languagePairs: LANGUAGE_PAIRS, + autoDownloadFromRemoteSettings: true, + }); + + /** + * @typedef {object} Tab + */ + + /** @type {Tab?} */ + let englishTab; + /** @type {Function?} */ + let removeEnglishTab; + /** @type {boolean} */ + let isSpanishPageTranslated = false; + /** @type {"spanish" | "english"} */ + let activeTab = "spanish"; + /** @type {boolean} */ + let isEngineMaybeDestroyed = true; + /** @type {boolean} */ + let isTitleMutated = false; + /** @type {boolean} */ + let hasVerifiedMutation = true; + + function reportOperation(name) { + info( + `\n\nOperation: ${name} ` + + JSON.stringify({ + activeTab, + englishTab: !!englishTab, + isSpanishPageTranslated, + isEngineMaybeDestroyed, + isTitleMutated, + }) + ); + } + + /** + * A list of fuzzing operations. They return false when they are a noop given the + * conditions. + * + * @type {object} - Record<string, () => Promise<boolean>> + */ + const operations = { + async addEnglishTab() { + if (!englishTab) { + reportOperation("addEnglishTab"); + const { removeTab, tab } = await addTab( + ENGLISH_PAGE_URL, + "Creating a new tab for a page in English." + ); + + englishTab = tab; + removeEnglishTab = removeTab; + activeTab = "english"; + return true; + } + return false; + }, + + async removeEnglishTab() { + if (removeEnglishTab) { + reportOperation("removeEnglishTab"); + await removeEnglishTab(); + + englishTab = null; + removeEnglishTab = null; + activeTab = "spanish"; + return true; + } + return false; + }, + + async translateSpanishPage() { + if (!isSpanishPageTranslated) { + reportOperation("translateSpanishPage"); + if (activeTab === "english") { + await switchTab(spanishTab, "spanish tab"); + } + await FullPageTranslationsTestUtils.assertTranslationsButton( + { button: true }, + "The button is available." + ); + await FullPageTranslationsTestUtils.openPanel({ + onOpenPanel: FullPageTranslationsTestUtils.assertPanelViewDefault, + }); + + await FullPageTranslationsTestUtils.clickTranslateButton(); + + await FullPageTranslationsTestUtils.assertTranslationsButton( + { button: true, circleArrows: false, locale: true, icon: true }, + "Translations button is fully loaded." + ); + + await FullPageTranslationsTestUtils.assertPageIsTranslated( + "es", + "en", + runInSpanishPage + ); + + isSpanishPageTranslated = true; + isEngineMaybeDestroyed = false; + activeTab = "spanish"; + return true; + } + return false; + }, + + async destroyEngineProcess() { + if ( + !isEngineMaybeDestroyed && + // Don't destroy the engine process until the mutation has been verified. + // There is an artifical race (e.g. only in tests) that happens from a new + // engine being requested, and forcefully destroyed before the it can be + // initialized. + hasVerifiedMutation + ) { + reportOperation("destroyEngineProcess"); + await EngineProcess.destroyTranslationsEngine(); + isEngineMaybeDestroyed = true; + } + return true; + }, + + async mutateSpanishPage() { + if (isSpanishPageTranslated && !isTitleMutated) { + reportOperation("mutateSpanishPage"); + + info("Mutate the page's content to re-trigger a translation."); + await runInSpanishPage(async TranslationsTest => { + const { getH1 } = TranslationsTest.getSelectors(); + getH1().innerText = "New text for the H1"; + }); + + if (isEngineMaybeDestroyed) { + info("The engine may be recreated now."); + } + + isEngineMaybeDestroyed = false; + isTitleMutated = true; + hasVerifiedMutation = false; + return true; + } + return false; + }, + + async switchToSpanishTab() { + if (activeTab !== "spanish") { + reportOperation("switchToSpanishTab"); + await switchTab(spanishTab, "spanish tab"); + activeTab = "spanish"; + + if (isTitleMutated) { + await runInSpanishPage(async TranslationsTest => { + const { getH1 } = TranslationsTest.getSelectors(); + await TranslationsTest.assertTranslationResult( + "The mutated content should be translated.", + getH1, + "NEW TEXT FOR THE H1 [es to en]" + ); + }); + hasVerifiedMutation = true; + } + + return true; + } + return false; + }, + + async switchToEnglishTab() { + if (activeTab !== "english" && englishTab) { + reportOperation("switchToEnglishTab"); + await switchTab(englishTab, "english tab"); + activeTab = "english"; + return true; + } + return false; + }, + + async restoreSpanishPage() { + if (activeTab === "spanish" && isSpanishPageTranslated) { + reportOperation("restoreSpanishPage"); + await FullPageTranslationsTestUtils.openPanel({ + onOpenPanel: FullPageTranslationsTestUtils.assertPanelViewRevisit, + }); + + await FullPageTranslationsTestUtils.clickRestoreButton(); + + await FullPageTranslationsTestUtils.assertPageIsUntranslated( + runInSpanishPage + ); + + await FullPageTranslationsTestUtils.assertTranslationsButton( + { button: true, circleArrows: false, locale: false, icon: true }, + "The button is reverted to have an icon." + ); + + isSpanishPageTranslated = false; + isTitleMutated = false; + return true; + } + return false; + }, + }; + + const fuzzSteps = 100; + info(`Starting the fuzzing with ${fuzzSteps} operations.`); + const opsArray = Object.values(operations); + + for (let i = 0; i < fuzzSteps; i++) { + // Pick a random operation and check if that it was not a noop, otherwise continue + // trying to find a valid operation. + while (true) { + const operation = opsArray[Math.floor(Math.random() * opsArray.length)]; + if (await operation()) { + break; + } + } + } + + if (removeEnglishTab) { + await removeEnglishTab(); + } + await cleanup(); +}); |