summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/browser/browser_html_list_view_recommendations.js
diff options
context:
space:
mode:
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.js293
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();
+});