diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js b/toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js new file mode 100644 index 0000000000..db4067ab35 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js @@ -0,0 +1,293 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint max-len: ["error", 80] */ +"use strict"; + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +AddonTestUtils.initMochitest(this); + +function makeResult({ guid, type }) { + return { + addon: { + authors: [{ name: "Some author" }], + current_version: { + files: [{ platform: "all", url: "data:," }], + }, + url: "data:,", + guid, + type, + }, + }; +} + +function mockResults() { + let types = ["extension", "theme", "extension", "extension", "theme"]; + return { + results: types.map((type, i) => + makeResult({ + guid: `${type}${i}@mochi.test`, + type, + }) + ), + }; +} + +add_setup(async function () { + let results = btoa(JSON.stringify(mockResults())); + await SpecialPowers.pushPrefEnv({ + set: [ + // Disable personalized recommendations, they will break the data URI. + ["browser.discovery.enabled", false], + ["extensions.getAddons.discovery.api_url", `data:;base64,${results}`], + [ + "extensions.recommendations.themeRecommendationUrl", + "https://example.com/theme", + ], + ], + }); +}); + +function checkExtraContents(doc, type, opts = {}) { + let { showThemeRecommendationFooter = type === "theme" } = opts; + let footer = doc.querySelector("footer"); + let amoButton = footer.querySelector('[action="open-amo"]'); + let privacyPolicyLink = footer.querySelector(".privacy-policy-link"); + let themeRecommendationFooter = footer.querySelector(".theme-recommendation"); + let themeRecommendationLink = + themeRecommendationFooter && themeRecommendationFooter.querySelector("a"); + let taarNotice = doc.querySelector("taar-notice"); + + is_element_visible(footer, "The footer is visible"); + + if (type == "extension") { + ok(taarNotice, "There is a TAAR notice"); + is_element_visible(amoButton, "The AMO button is shown"); + is_element_visible(privacyPolicyLink, "The privacy policy is visible"); + } else if (type == "theme") { + ok(!taarNotice, "There is no TAAR notice"); + ok(amoButton, "AMO button is shown"); + ok(!privacyPolicyLink, "There is no privacy policy"); + } else { + throw new Error(`Unknown type ${type}`); + } + + if (showThemeRecommendationFooter) { + is_element_visible( + themeRecommendationFooter, + "There's a theme recommendation footer" + ); + is_element_visible(themeRecommendationLink, "There's a link to the theme"); + is(themeRecommendationLink.target, "_blank", "The link opens in a new tab"); + is( + themeRecommendationLink.href, + "https://example.com/theme", + "The link goes to the pref's URL" + ); + is( + doc.l10n.getAttributes(themeRecommendationFooter).id, + "recommended-theme-1", + "The recommendation has the right l10n-id" + ); + } else { + ok( + !themeRecommendationFooter || themeRecommendationFooter.hidden, + "There's no theme recommendation" + ); + } +} + +async function installAddon({ card, recommendedList, manifestExtra = {} }) { + // Install an add-on to hide the card. + let hidden = BrowserTestUtils.waitForEvent( + recommendedList, + "card-hidden", + false, + e => e.detail.card == card + ); + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + browser_specific_settings: { gecko: { id: card.addonId } }, + ...manifestExtra, + }, + useAddonManager: "temporary", + }); + await extension.startup(); + await hidden; + return extension; +} + +async function testListRecommendations({ type, manifestExtra = {} }) { + let win = await loadInitialView(type); + let doc = win.document; + + // Wait for the list to render, rendering is tested with the discovery pane. + let recommendedList = doc.querySelector("recommended-addon-list"); + await recommendedList.cardsReady; + + checkExtraContents(doc, type); + + // Check that the cards are all for the right type. + let cards = doc.querySelectorAll("recommended-addon-card"); + ok(!!cards.length, "There were some cards found"); + for (let card of cards) { + is(card.discoAddon.type, type, `The card is for a ${type}`); + is_element_visible(card, "The card is visible"); + } + + // Install an add-on for the first card, verify it is hidden. + let { addonId } = cards[0]; + ok(addonId, "The card has an addonId"); + + // Installing the add-on will fail since the URL doesn't point to a valid + // XPI. + let installButton = cards[0].querySelector('[action="install-addon"]'); + let { panel } = PopupNotifications; + let popupId = "addon-install-failed-notification"; + let failPromise = TestUtils.topicObserved("addon-install-failed"); + installButton.click(); + await failPromise; + // Wait for the installing popup to be hidden and leave just the error popup. + await BrowserTestUtils.waitForCondition(() => { + return panel.children.length == 1 && panel.firstElementChild.id == popupId; + }); + + // Dismiss the popup. + panel.firstElementChild.button.click(); + await BrowserTestUtils.waitForPopupEvent(panel, "hidden"); + + let extension = await installAddon({ card: cards[0], recommendedList }); + is_element_hidden(cards[0], "The card is now hidden"); + + // Switch away and back, there should still be a hidden card. + await closeView(win); + win = await loadInitialView(type); + doc = win.document; + recommendedList = doc.querySelector("recommended-addon-list"); + await recommendedList.cardsReady; + + cards = Array.from(doc.querySelectorAll("recommended-addon-card")); + + let hiddenCard = cards.pop(); + is(hiddenCard.addonId, addonId, "The expected card was found"); + is_element_hidden(hiddenCard, "The card is still hidden"); + + ok(!!cards.length, "There are still some visible cards"); + for (let card of cards) { + is(card.discoAddon.type, type, `The card is for a ${type}`); + is_element_visible(card, "The card is visible"); + } + + // Uninstall the add-on, verify the card is shown again. + let shown = BrowserTestUtils.waitForEvent(recommendedList, "card-shown"); + await extension.unload(); + await shown; + + is_element_visible(hiddenCard, "The card is now shown"); + + await closeView(win); +} + +add_task(async function testExtensionList() { + await testListRecommendations({ type: "extension" }); +}); + +add_task(async function testThemeList() { + await testListRecommendations({ + type: "theme", + manifestExtra: { theme: {} }, + }); +}); + +add_task(async function testInstallAllExtensions() { + let type = "extension"; + let win = await loadInitialView(type); + let doc = win.document; + + // Wait for the list to render, rendering is tested with the discovery pane. + let recommendedList = doc.querySelector("recommended-addon-list"); + await recommendedList.cardsReady; + + // Find more button is shown. + checkExtraContents(doc, type); + + let cards = Array.from(doc.querySelectorAll("recommended-addon-card")); + is(cards.length, 3, "We found some cards"); + + let extensions = await Promise.all( + cards.map(card => installAddon({ card, recommendedList })) + ); + + // The find more on AMO button is shown. + checkExtraContents(doc, type); + + // Uninstall one of the extensions, the button should still be shown. + let extension = extensions.pop(); + let shown = BrowserTestUtils.waitForEvent(recommendedList, "card-shown"); + await extension.unload(); + await shown; + + // The find more on AMO button is shown. + checkExtraContents(doc, type); + + await Promise.all(extensions.map(extension => extension.unload())); + await closeView(win); +}); + +add_task(async function testError() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.getAddons.discovery.api_url", "data:,"]], + }); + + let win = await loadInitialView("extension"); + let doc = win.document; + + // Wait for the list to render, rendering is tested with the discovery pane. + let recommendedList = doc.querySelector("recommended-addon-list"); + await recommendedList.cardsReady; + + checkExtraContents(doc, "extension"); + + await closeView(win); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function testThemesNoRecommendationUrl() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.recommendations.themeRecommendationUrl", ""]], + }); + + let win = await loadInitialView("theme"); + let doc = win.document; + + // Wait for the list to render, rendering is tested with the discovery pane. + let recommendedList = doc.querySelector("recommended-addon-list"); + await recommendedList.cardsReady; + + checkExtraContents(doc, "theme", { showThemeRecommendationFooter: false }); + + await closeView(win); + await SpecialPowers.popPrefEnv(); +}); + +add_task(async function testRecommendationsDisabled() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.htmlaboutaddons.recommendations.enabled", false]], + }); + + let types = ["extension", "theme"]; + + for (let type of types) { + let win = await loadInitialView(type); + let doc = win.document; + + let recommendedList = doc.querySelector("recommended-addon-list"); + ok(!recommendedList, `There are no recommendations on the ${type} page`); + + await closeView(win); + } + + await SpecialPowers.popPrefEnv(); +}); |