diff options
Diffstat (limited to 'browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js')
-rw-r--r-- | browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js b/browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js new file mode 100644 index 0000000000..a680edb454 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_AMBrowserExtensionsImport.js @@ -0,0 +1,286 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const { AMBrowserExtensionsImport } = ChromeUtils.importESModule( + "resource://gre/modules/AddonManager.sys.mjs" +); + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +// This test verifies the appmenu UI when there are pending imported add-ons. +// The UI in `about:addons` is covered by tests in +// `toolkit/mozapps/extensions/test/browser/browser_AMBrowserExtensionsImport.js`. + +AddonTestUtils.initMochitest(this); + +const TEST_SERVER = AddonTestUtils.createHttpServer(); + +const ADDONS = { + ext1: { + manifest: { + name: "Ext 1", + version: "1.0", + browser_specific_settings: { gecko: { id: "ff@ext-1" } }, + permissions: ["history"], + }, + }, + ext2: { + manifest: { + name: "Ext 2", + version: "1.0", + browser_specific_settings: { gecko: { id: "ff@ext-2" } }, + permissions: ["history"], + }, + }, +}; +// Populated in `setup()`. +const XPIS = {}; +// Populated in `setup()`. +const ADDON_SEARCH_RESULTS = {}; + +const mockAddonRepository = ({ addons = [] }) => { + return { + async getMappedAddons(browserID, extensionIDs) { + return Promise.resolve({ + addons, + matchedIDs: [], + unmatchedIDs: [], + }); + }, + }; +}; + +add_setup(async function setup() { + for (const [name, data] of Object.entries(ADDONS)) { + XPIS[name] = AddonTestUtils.createTempWebExtensionFile(data); + TEST_SERVER.registerFile(`/addons/${name}.xpi`, XPIS[name]); + + ADDON_SEARCH_RESULTS[name] = { + id: data.manifest.browser_specific_settings.gecko.id, + name: data.name, + version: data.version, + sourceURI: Services.io.newURI( + `http://localhost:${TEST_SERVER.identity.primaryPort}/addons/${name}.xpi` + ), + icons: {}, + }; + } + + registerCleanupFunction(() => { + // Clear the add-on repository override. + AMBrowserExtensionsImport._addonRepository = null; + }); +}); + +add_task(async function test_appmenu_notification() { + const browserID = "some-browser-id"; + const extensionIDs = ["ext-1", "ext-2"]; + AMBrowserExtensionsImport._addonRepository = mockAddonRepository({ + addons: Object.values(ADDON_SEARCH_RESULTS), + }); + const menuButton = document.getElementById("PanelUI-menu-button"); + + let promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-pending" + ); + // Start a first import... + await AMBrowserExtensionsImport.stageInstalls(browserID, extensionIDs); + await promiseTopic; + Assert.equal( + menuButton.getAttribute("badge-status"), + "addon-alert", + "expected menu button to have the addon-alert badge" + ); + + // ...then cancel it. + promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-cancelled" + ); + await AMBrowserExtensionsImport.cancelInstalls(); + await promiseTopic; + Assert.ok( + !menuButton.hasAttribute("badge-status"), + "expected menu button to no longer have the addon-alert badge" + ); + + // We start a second import here, then we complete it with the UI. + promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-pending" + ); + const result = await AMBrowserExtensionsImport.stageInstalls( + browserID, + extensionIDs + ); + await promiseTopic; + Assert.equal( + menuButton.getAttribute("badge-status"), + "addon-alert", + "expected menu button to have the addon-alert badge" + ); + + // Open the appmenu panel in order to verify the notification. + const menuPanelShown = BrowserTestUtils.waitForEvent( + PanelUI.panel, + "ViewShown" + ); + menuButton.click(); + await menuPanelShown; + + const notifications = PanelUI.addonNotificationContainer; + Assert.equal( + notifications.children.length, + 1, + "expected a notification about the imported add-ons" + ); + + const endedPromises = result.importedAddonIDs.map(id => + AddonTestUtils.promiseInstallEvent("onInstallEnded") + ); + const menuPanelHidden = BrowserTestUtils.waitForEvent( + PanelUI.panel, + "popuphidden" + ); + promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-complete" + ); + // Complete the installation of the add-ons by clicking on the notification. + notifications.children[0].click(); + await Promise.all([...endedPromises, promiseTopic, menuPanelHidden]); + Assert.ok( + !menuButton.hasAttribute("badge-status"), + "expected menu button to no longer have the addon-alert badge" + ); + + for (const id of result.importedAddonIDs) { + const addon = await AddonManager.getAddonByID(id); + Assert.ok(addon.isActive, `expected add-on "${id}" to be enabled`); + await addon.uninstall(); + } +}); + +add_task(async function test_appmenu_notification_with_sideloaded_addon() { + await SpecialPowers.pushPrefEnv({ + set: [["extensions.autoDisableScopes", 15]], + }); + + const menuButton = document.getElementById("PanelUI-menu-button"); + + // Load a sideloaded add-on, which will be user-disabled by default. + const sideloadedXPI = AddonTestUtils.createTempWebExtensionFile({ + manifest: { + name: "sideloaded add-on", + version: "1.0", + browser_specific_settings: { gecko: { id: "ff@sideloaded" } }, + }, + }); + await AddonTestUtils.manuallyInstall(sideloadedXPI); + const changePromise = new Promise(resolve => + ExtensionsUI.once("change", resolve) + ); + ExtensionsUI._checkForSideloaded(); + await changePromise; + + // We should have a badge for the sideloaded add-on on the appmenu button. + Assert.equal( + menuButton.getAttribute("badge-status"), + "addon-alert", + "expected menu button to have the addon-alert badge" + ); + + // Now, we start an import... + const browserID = "some-browser-id"; + const extensionIDs = ["ext-1", "ext-2"]; + AMBrowserExtensionsImport._addonRepository = mockAddonRepository({ + addons: Object.values(ADDON_SEARCH_RESULTS), + }); + + let promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-pending" + ); + await AMBrowserExtensionsImport.stageInstalls(browserID, extensionIDs); + await promiseTopic; + + // Badge should still be shown. + Assert.equal( + menuButton.getAttribute("badge-status"), + "addon-alert", + "expected menu button to have the addon-alert badge" + ); + + // Open the appmenu panel in order to verify the notifications. + const menuPanelShown = BrowserTestUtils.waitForEvent( + PanelUI.panel, + "ViewShown" + ); + menuButton.click(); + await menuPanelShown; + + // We expect two notifications: one for the imported add-ons (listed first), + // and the second about the sideloaded add-on. + const notifications = PanelUI.addonNotificationContainer; + Assert.equal(notifications.children.length, 2, "expected two notifications"); + Assert.equal( + notifications.children[0].id, + "webext-imported-addons", + "expected a notification for the imported add-ons" + ); + Assert.equal( + notifications.children[1].id, + "webext-perms-sideload-menu-item", + "expected a notification for the sideloaded add-on" + ); + + // Cancel the import to make sure the UI is updated accordingly. + promiseTopic = TestUtils.topicObserved( + "webextension-imported-addons-cancelled" + ); + await AMBrowserExtensionsImport.cancelInstalls(); + await promiseTopic; + + // Badge should still be shown since there is still the sideloaded + // notification but we do not expect the notification for the imported + // add-ons anymore. + Assert.ok( + menuButton.hasAttribute("badge-status"), + "expected menu button to have the addon-alert badge" + ); + Assert.equal(notifications.children.length, 1, "expected a notification"); + Assert.equal( + notifications.children[0].id, + "webext-perms-sideload-menu-item", + "expected a notification for the sideloaded add-on" + ); + + // Click the sideloaded add-on notification. + const menuPanelHidden = BrowserTestUtils.waitForEvent( + PanelUI.panel, + "popuphidden" + ); + const popupPromise = promisePopupNotificationShown( + "addon-webext-permissions" + ); + notifications.children[0].click(); + const [popup] = await Promise.all([popupPromise, menuPanelHidden]); + + // The user should see a new popup asking them about enabling the sideloaded + // add-on. Let's keep the add-on disabled. + popup.secondaryButton.click(); + + // The badge should be hidden now, and there shouldn't be any notification in + // the panel anymore. + Assert.ok( + !menuButton.hasAttribute("badge-status"), + "expected menu button to no longer have the addon-alert badge" + ); + Assert.equal(notifications.children.length, 0, "expected no notification"); + + // Unload the sideloaded add-on. + const addon = await AddonManager.getAddonByID("ff@sideloaded"); + Assert.ok(addon.userDisabled, "expected add-on to be disabled"); + await addon.uninstall(); + + await SpecialPowers.popPrefEnv(); +}); |