diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/base/test/webextensions/browser_extension_sideloading.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | comm/mail/base/test/webextensions/browser_extension_sideloading.js | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/comm/mail/base/test/webextensions/browser_extension_sideloading.js b/comm/mail/base/test/webextensions/browser_extension_sideloading.js new file mode 100644 index 0000000000..eb0754a922 --- /dev/null +++ b/comm/mail/base/test/webextensions/browser_extension_sideloading.js @@ -0,0 +1,352 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +const { AddonManagerPrivate } = ChromeUtils.importESModule( + "resource://gre/modules/AddonManager.sys.mjs" +); + +var { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +AddonTestUtils.initMochitest(this); + +AddonTestUtils.hookAMTelemetryEvents(); + +const kSideloaded = true; + +async function createWebExtension(details) { + let options = { + manifest: { + applications: { gecko: { id: details.id } }, + + name: details.name, + + permissions: details.permissions, + }, + }; + + if (details.iconURL) { + options.manifest.icons = { 64: details.iconURL }; + } + + let xpi = AddonTestUtils.createTempWebExtensionFile(options); + + await AddonTestUtils.manuallyInstall(xpi); +} + +function promiseEvent(eventEmitter, event) { + return new Promise(resolve => { + eventEmitter.once(event, resolve); + }); +} + +function getAddonElement(managerWindow, addonId) { + return BrowserTestUtils.waitForCondition( + () => + managerWindow.document.querySelector(`addon-card[addon-id="${addonId}"]`), + `Found entry for sideload extension addon "${addonId}" in HTML about:addons` + ); +} + +function assertSideloadedAddonElementState(addonElement, pressed) { + const enableBtn = addonElement.querySelector('[action="toggle-disabled"]'); + is( + enableBtn.pressed, + pressed, + `The enable button is ${!pressed ? " not " : ""} pressed` + ); + is(enableBtn.localName, "moz-toggle", "The enable button is a toggle"); +} + +function clickEnableExtension(addonElement) { + addonElement.querySelector('[action="toggle-disabled"]').click(); +} + +add_task(async function test_sideloading() { + const DEFAULT_ICON_URL = + "chrome://mozapps/skin/extensions/extensionGeneric.svg"; + + await SpecialPowers.pushPrefEnv({ + set: [ + ["xpinstall.signatures.required", false], + ["extensions.autoDisableScopes", 15], + ["extensions.ui.ignoreUnsigned", true], + ["extensions.allowPrivateBrowsingByDefault", false], + ], + }); + + const ID1 = "addon1@tests.mozilla.org"; + await createWebExtension({ + id: ID1, + name: "Test 1", + userDisabled: true, + permissions: ["accountsRead", "https://*/*"], + iconURL: "foo-icon.png", + }); + + const ID2 = "addon2@tests.mozilla.org"; + await createWebExtension({ + id: ID2, + name: "Test 2", + permissions: ["<all_urls>"], + }); + + const ID3 = "addon3@tests.mozilla.org"; + await createWebExtension({ + id: ID3, + name: "Test 3", + permissions: ["<all_urls>"], + }); + + testCleanup = async function () { + // clear out ExtensionsUI state about sideloaded extensions so + // subsequent tests don't get confused. + ExtensionsUI.sideloaded.clear(); + ExtensionsUI.emit("change"); + }; + + let changePromise = new Promise(resolve => { + ExtensionsUI.on("change", function listener() { + ExtensionsUI.off("change", listener); + resolve(); + }); + }); + ExtensionsUI._checkForSideloaded(); + await changePromise; + + // Check for the addons badge on the hamburger menu + let menuButton = document.getElementById("button-appmenu"); + is( + menuButton.getAttribute("badge-status"), + "addon-alert", + "Should have addon alert badge" + ); + + // Find the menu entries for sideloaded extensions + await gCUITestUtils.openMainMenu(); + + let addons = PanelUI.addonNotificationContainer; + is( + addons.children.length, + 3, + "Have 3 menu entries for sideloaded extensions" + ); + + info( + "Test disabling sideloaded addon 1 using the permission prompt secondary button" + ); + + // Click the first sideloaded extension + let popupPromise = promisePopupNotificationShown("addon-webext-permissions"); + addons.children[0].click(); + + // The click should hide the main menu. This is currently synchronous. + ok(PanelUI.panel.state != "open", "Main menu is closed or closing."); + + let panel = await popupPromise; + + // Check the contents of the notification, then choose "Cancel" + await checkNotification( + panel, + /\/foo-icon\.png$/, + [ + ["webext-perms-host-description-all-urls"], + ["webext-perms-description-accountsRead"], + ], + kSideloaded + ); + + panel.secondaryButton.click(); + + let [addon1, addon2, addon3] = await AddonManager.getAddonsByIDs([ + ID1, + ID2, + ID3, + ]); + ok(addon1.seen, "Addon should be marked as seen"); + is(addon1.userDisabled, true, "Addon 1 should still be disabled"); + is(addon2.userDisabled, true, "Addon 2 should still be disabled"); + is(addon3.userDisabled, true, "Addon 3 should still be disabled"); + + // Should still have 2 entries in the hamburger menu + await gCUITestUtils.openMainMenu(); + + addons = PanelUI.addonNotificationContainer; + is( + addons.children.length, + 2, + "Have 2 menu entries for sideloaded extensions" + ); + + // Close the hamburger menu and go directly to the addons manager + await gCUITestUtils.hideMainMenu(); + + const VIEW = "addons://list/extension"; + let win = await openAddonsMgr(VIEW); + + await waitAboutAddonsViewLoaded(win.document); + + // about:addons addon entry element. + const addonElement = await getAddonElement(win, ID2); + + assertSideloadedAddonElementState(addonElement, false); + + info("Test enabling sideloaded addon 2 from about:addons enable button"); + + // When clicking enable we should see the permissions notification + popupPromise = promisePopupNotificationShown("addon-webext-permissions"); + clickEnableExtension(addonElement); + panel = await popupPromise; + await checkNotification( + panel, + DEFAULT_ICON_URL, + [["webext-perms-host-description-all-urls"]], + kSideloaded + ); + + // Setup async test for post install notification on addon 2 + popupPromise = promisePopupNotificationShown("addon-installed"); + + // Accept the permissions + panel.button.click(); + await promiseEvent(ExtensionsUI, "change"); + + addon2 = await AddonManager.getAddonByID(ID2); + is(addon2.userDisabled, false, "Addon 2 should be enabled"); + assertSideloadedAddonElementState(addonElement, true); + + // Test post install notification on addon 2. + panel = await popupPromise; + panel.button.click(); + + let tabmail = document.getElementById("tabmail"); + tabmail.closeTab(tabmail.currentTabInfo); + + // Should still have 1 entry in the hamburger menu + await gCUITestUtils.openMainMenu(); + + addons = PanelUI.addonNotificationContainer; + is(addons.children.length, 1, "Have 1 menu entry for sideloaded extensions"); + + PanelUI.hide(); + + // Open the Add-Ons Manager + win = await openAddonsMgr(`addons://detail/${encodeURIComponent(ID3)}`); + + info("Test enabling sideloaded addon 3 from app menu"); + // Trigger addon 3 install as triggered from the app menu, to be able to cover the + // post install notification that should be triggered when the permission + // dialog is accepted from that flow. + popupPromise = promisePopupNotificationShown("addon-webext-permissions"); + ExtensionsUI.showSideloaded(tabmail, addon3); + + panel = await popupPromise; + await checkNotification( + panel, + DEFAULT_ICON_URL, + [["webext-perms-host-description-all-urls"]], + kSideloaded + ); + + // Setup async test for post install notification on addon 3 + popupPromise = promisePopupNotificationShown("addon-installed"); + + // Accept the permissions + panel.button.click(); + await promiseEvent(ExtensionsUI, "change"); + + addon3 = await AddonManager.getAddonByID(ID3); + is(addon3.userDisabled, false, "Addon 3 should be enabled"); + + // Test post install notification on addon 3. + panel = await popupPromise; + panel.button.click(); + + isnot( + menuButton.getAttribute("badge-status"), + "addon-alert", + "Should no longer have addon alert badge" + ); + + await new Promise(resolve => setTimeout(resolve, 100)); + + for (let addon of [addon1, addon2, addon3]) { + await addon.uninstall(); + } + + tabmail.closeTab(tabmail.currentTabInfo); + + // Assert that the expected AddonManager telemetry are being recorded. + const expectedExtra = { source: "app-profile", method: "sideload" }; + + const baseEvent = { object: "extension", extra: expectedExtra }; + const createBaseEventAddon = n => ({ + ...baseEvent, + value: `addon${n}@tests.mozilla.org`, + }); + const getEventsForAddonId = (events, addonId) => + events.filter(ev => ev.value === addonId); + + const amEvents = AddonTestUtils.getAMTelemetryEvents(); + + // Test telemetry events for addon1 (1 permission and 1 origin). + info("Test telemetry events collected for addon1"); + + const baseEventAddon1 = createBaseEventAddon(1); + const collectedEventsAddon1 = getEventsForAddonId( + amEvents, + baseEventAddon1.value + ); + const expectedEventsAddon1 = [ + { + ...baseEventAddon1, + method: "sideload_prompt", + extra: { ...expectedExtra, num_strings: "2" }, + }, + { ...baseEventAddon1, method: "uninstall" }, + ]; + + let i = 0; + for (let event of collectedEventsAddon1) { + Assert.deepEqual( + event, + expectedEventsAddon1[i++], + "Got the expected telemetry event" + ); + } + + is( + collectedEventsAddon1.length, + expectedEventsAddon1.length, + "Got the expected number of telemetry events for addon1" + ); + + const baseEventAddon2 = createBaseEventAddon(2); + const collectedEventsAddon2 = getEventsForAddonId( + amEvents, + baseEventAddon2.value + ); + const expectedEventsAddon2 = [ + { + ...baseEventAddon2, + method: "sideload_prompt", + extra: { ...expectedExtra, num_strings: "1" }, + }, + { ...baseEventAddon2, method: "enable" }, + { ...baseEventAddon2, method: "uninstall" }, + ]; + + i = 0; + for (let event of collectedEventsAddon2) { + Assert.deepEqual( + event, + expectedEventsAddon2[i++], + "Got the expected telemetry event" + ); + } + + is( + collectedEventsAddon2.length, + expectedEventsAddon2.length, + "Got the expected number of telemetry events for addon2" + ); +}); |