From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../test/browser/browser_html_abuse_report.js | 1029 ++++++++++++++++++++ 1 file changed, 1029 insertions(+) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js (limited to 'toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js') diff --git a/toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js b/toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js new file mode 100644 index 0000000000..b325f87511 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js @@ -0,0 +1,1029 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* eslint max-len: ["error", 80] */ + +loadTestSubscript("head_abuse_report.js"); + +add_setup(async function () { + await AbuseReportTestUtils.setup(); +}); + +/** + * Base tests on abuse report panel webcomponents. + */ + +// This test case verified that the abuse report panels contains a radio +// button for all the expected "abuse report reasons", they are grouped +// together under the same form field named "reason". +add_task(async function test_abusereport_issuelist() { + const extension = await installTestExtension(); + + const abuseReportEl = await AbuseReportTestUtils.openReport(extension.id); + + const reasonsPanel = abuseReportEl._reasonsPanel; + const radioButtons = reasonsPanel.querySelectorAll("[type=radio]"); + const selectedRadios = reasonsPanel.querySelectorAll("[type=radio]:checked"); + + is(selectedRadios.length, 1, "Expect only one radio button selected"); + is( + selectedRadios[0], + radioButtons[0], + "Expect the first radio button to be selected" + ); + + is( + abuseReportEl.reason, + radioButtons[0].value, + `The reason property has the expected value: ${radioButtons[0].value}` + ); + + const reasons = Array.from(radioButtons).map(el => el.value); + Assert.deepEqual( + reasons.sort(), + AbuseReportTestUtils.getReasons(abuseReportEl).sort(), + `Got a radio button for the expected reasons` + ); + + for (const radio of radioButtons) { + const reasonInfo = AbuseReportTestUtils.getReasonInfo( + abuseReportEl, + radio.value + ); + const expectExampleHidden = + reasonInfo && reasonInfo.isExampleHidden("extension"); + is( + radio.parentNode.querySelector(".reason-example").hidden, + expectExampleHidden, + `Got expected visibility on the example for reason "${radio.value}"` + ); + } + + info("Change the selected reason to " + radioButtons[3].value); + radioButtons[3].checked = true; + is( + abuseReportEl.reason, + radioButtons[3].value, + "The reason property has the expected value" + ); + + await extension.unload(); + await closeAboutAddons(); +}); + +// This test case verifies that the abuse report panel: +// - switches from its "reasons list" mode to its "submit report" mode when the +// "next" button is clicked +// - goes back to the "reasons list" mode when the "go back" button is clicked +// - the abuse report panel is closed when the "close" icon is clicked +add_task(async function test_abusereport_submitpanel() { + const extension = await installTestExtension(); + + const abuseReportEl = await AbuseReportTestUtils.openReport(extension.id); + + ok( + !abuseReportEl._reasonsPanel.hidden, + "The list of abuse reasons is the currently visible" + ); + ok( + abuseReportEl._submitPanel.hidden, + "The submit panel is the currently hidden" + ); + + let onceUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "submit" + ); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnNext); + await onceUpdated; + + onceUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "reasons" + ); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnGoBack); + await onceUpdated; + + const onceReportClosed = + AbuseReportTestUtils.promiseReportClosed(abuseReportEl); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnCancel); + await onceReportClosed; + + await extension.unload(); + await closeAboutAddons(); +}); + +// This test case verifies that the abuse report panel sends the expected data +// in the "abuse-report:submit" event detail. +add_task(async function test_abusereport_submit() { + // Reset the timestamp of the last report between tests. + AbuseReporter._lastReportTimestamp = null; + const extension = await installTestExtension(); + + const abuseReportEl = await AbuseReportTestUtils.openReport(extension.id); + + ok( + !abuseReportEl._reasonsPanel.hidden, + "The list of abuse reasons is the currently visible" + ); + + let onceUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "submit" + ); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnNext); + await onceUpdated; + + is(abuseReportEl.message, "", "The abuse report message is initially empty"); + + info("Test typing a message in the abuse report submit panel textarea"); + const typedMessage = "Description of the extension abuse report"; + + EventUtils.synthesizeComposition( + { + data: typedMessage, + type: "compositioncommit", + }, + abuseReportEl.ownerGlobal + ); + + is( + abuseReportEl.message, + typedMessage, + "Got the expected typed message in the abuse report" + ); + + const expectedDetail = { + addonId: extension.id, + }; + + const expectedReason = abuseReportEl.reason; + const expectedMessage = abuseReportEl.message; + + function handleSubmitRequest({ request, response }) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/json", false); + response.write("{}"); + } + + let reportSubmitted; + const onReportSubmitted = AbuseReportTestUtils.promiseReportSubmitHandled( + ({ data, request, response }) => { + reportSubmitted = JSON.parse(data); + handleSubmitRequest({ request, response }); + } + ); + + const onceReportClosed = + AbuseReportTestUtils.promiseReportClosed(abuseReportEl); + + const onMessageBarsCreated = AbuseReportTestUtils.promiseMessageBars(2); + + const onceSubmitEvent = BrowserTestUtils.waitForEvent( + abuseReportEl, + "abuse-report:submit" + ); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnSubmit); + const submitEvent = await onceSubmitEvent; + + const actualDetail = { + addonId: submitEvent.detail.addonId, + }; + Assert.deepEqual( + actualDetail, + expectedDetail, + "Got the expected detail in the abuse-report:submit event" + ); + + ok( + submitEvent.detail.report, + "Got a report object in the abuse-report:submit event detail" + ); + + // Verify that, when the "abuse-report:submit" has been sent, + // the abuse report panel has been hidden, the report has been + // submitted and the expected message bar is created in the + // HTML about:addons page. + info("Wait the report to be submitted to the api server"); + await onReportSubmitted; + info("Wait the report panel to be closed"); + await onceReportClosed; + + is( + reportSubmitted.addon, + ADDON_ID, + "Got the expected addon in the submitted report" + ); + is( + reportSubmitted.reason, + expectedReason, + "Got the expected reason in the submitted report" + ); + is( + reportSubmitted.message, + expectedMessage, + "Got the expected message in the submitted report" + ); + is( + reportSubmitted.report_entry_point, + REPORT_ENTRY_POINT, + "Got the expected report_entry_point in the submitted report" + ); + + info("Waiting the expected message bars to be created"); + const barDetails = await onMessageBarsCreated; + is(barDetails.length, 2, "Expect two message bars to have been created"); + is( + barDetails[0].definitionId, + "submitting", + "Got a submitting message bar as expected" + ); + is( + barDetails[1].definitionId, + "submitted", + "Got a submitted message bar as expected" + ); + + await extension.unload(); + await closeAboutAddons(); +}); + +// This helper does verify that the abuse report panel contains the expected +// suggestions when the selected reason requires it (and urls are being set +// on the links elements included in the suggestions when expected). +async function test_abusereport_suggestions(addonId) { + const addon = await AddonManager.getAddonByID(addonId); + + const abuseReportEl = await AbuseReportTestUtils.openReport(addonId); + + const { + _btnNext, + _btnGoBack, + _reasonsPanel, + _submitPanel, + _submitPanel: { _suggestions }, + } = abuseReportEl; + + for (const reason of AbuseReportTestUtils.getReasons(abuseReportEl)) { + const reasonInfo = AbuseReportTestUtils.getReasonInfo( + abuseReportEl, + reason + ); + + // TODO(Bug 1789718): Remove after the deprecated XPIProvider-based + // implementation is also removed. + const addonType = + addon.type === "sitepermission-deprecated" + ? "sitepermission" + : addon.type; + + if (reasonInfo.isReasonHidden(addonType)) { + continue; + } + + info(`Test suggestions for abuse reason "${reason}"`); + + // Select a reason with suggestions. + let radioEl = abuseReportEl.querySelector(`#abuse-reason-${reason}`); + ok(radioEl, `Found radio button for "${reason}"`); + radioEl.checked = true; + + // Make sure the element localization is completed before + // checking the content isn't empty. + await document.l10n.translateFragment(radioEl); + + // Verify each radio button has a non-empty localized string. + const localizedRadioContent = Array.from( + radioEl.closest("label").querySelectorAll("[data-l10n-id]") + ).filter(el => !el.hidden); + + for (let el of localizedRadioContent) { + isnot( + el.textContent, + "", + `Fluent string id '${el.getAttribute("data-l10n-id")}' missing` + ); + } + + // Switch to the submit form with the current reason radio selected. + let oncePanelUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "submit" + ); + await AbuseReportTestUtils.clickPanelButton(_btnNext); + await oncePanelUpdated; + + const localizedSuggestionsContent = Array.from( + _suggestions.querySelectorAll("[data-l10n-id]") + ).filter(el => !el.hidden); + + is( + !_suggestions.hidden, + !!reasonInfo.hasSuggestions, + `Suggestions block has the expected visibility for "${reason}"` + ); + if (reasonInfo.hasSuggestions) { + ok( + !!localizedSuggestionsContent.length, + `Category suggestions should not be empty for "${reason}"` + ); + } else { + ok( + localizedSuggestionsContent.length === 0, + `Category suggestions should be empty for "${reason}"` + ); + } + + const extSupportLink = _suggestions.querySelector( + ".extension-support-link" + ); + if (extSupportLink) { + is( + extSupportLink.getAttribute("href"), + BASE_TEST_MANIFEST.homepage_url, + "Got the expected extension-support-url" + ); + } + + const learnMoreLinks = []; + for (const linkClass of _suggestions.LEARNMORE_LINKS) { + learnMoreLinks.push(..._suggestions.querySelectorAll(linkClass)); + } + + if (learnMoreLinks.length) { + ok( + learnMoreLinks.every(el => el.getAttribute("target") === "_blank"), + "All the learn more links have target _blank" + ); + ok( + learnMoreLinks.every(el => el.hasAttribute("href")), + "All the learn more links have a url set" + ); + } + + oncePanelUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "reasons" + ); + await AbuseReportTestUtils.clickPanelButton(_btnGoBack); + await oncePanelUpdated; + ok(!_reasonsPanel.hidden, "Reasons panel should be visible"); + ok(_submitPanel.hidden, "Submit panel should be hidden"); + } + + await closeAboutAddons(); +} + +add_task(async function test_abusereport_suggestions_extension() { + const EXT_ID = "test-extension-suggestions@mochi.test"; + const extension = await installTestExtension(EXT_ID); + await test_abusereport_suggestions(EXT_ID); + await extension.unload(); +}); + +add_task(async function test_abusereport_suggestions_theme() { + const THEME_ID = "theme@mochi.test"; + const theme = await installTestExtension(THEME_ID, "theme"); + await test_abusereport_suggestions(THEME_ID); + await theme.unload(); +}); + +// TODO(Bug 1789718): adapt to SitePermAddonProvider implementation. +add_task(async function test_abusereport_suggestions_sitepermission() { + const SITEPERM_ADDON_ID = "webmidi@mochi.test"; + const sitePermAddon = await installTestExtension( + SITEPERM_ADDON_ID, + "sitepermission-deprecated" + ); + await test_abusereport_suggestions(SITEPERM_ADDON_ID); + await sitePermAddon.unload(); +}); + +// This test case verifies the message bars created on other +// scenarios (e.g. report creation and submissions errors). +// +// TODO(Bug 1789718): adapt to SitePermAddonProvider implementation. +add_task(async function test_abusereport_messagebars() { + const EXT_ID = "test-extension-report@mochi.test"; + const EXT_ID2 = "test-extension-report-2@mochi.test"; + const THEME_ID = "test-theme-report@mochi.test"; + const SITEPERM_ADDON_ID = "webmidi-report@mochi.test"; + const extension = await installTestExtension(EXT_ID); + const extension2 = await installTestExtension(EXT_ID2); + const theme = await installTestExtension(THEME_ID, "theme"); + const sitePermAddon = await installTestExtension( + SITEPERM_ADDON_ID, + "sitepermission-deprecated" + ); + + async function assertMessageBars( + expectedMessageBarIds, + testSetup, + testMessageBarDetails + ) { + await openAboutAddons(); + const expectedLength = expectedMessageBarIds.length; + const onMessageBarsCreated = + AbuseReportTestUtils.promiseMessageBars(expectedLength); + // Reset the timestamp of the last report between tests. + AbuseReporter._lastReportTimestamp = null; + await testSetup(); + info(`Waiting for ${expectedLength} message-bars to be created`); + const barDetails = await onMessageBarsCreated; + Assert.deepEqual( + barDetails.map(d => d.definitionId), + expectedMessageBarIds, + "Got the expected message bars" + ); + if (testMessageBarDetails) { + await testMessageBarDetails(barDetails); + } + await closeAboutAddons(); + } + + function setTestRequestHandler(responseStatus, responseData) { + AbuseReportTestUtils.promiseReportSubmitHandled(({ request, response }) => { + response.setStatusLine(request.httpVersion, responseStatus, "Error"); + response.write(responseData); + }); + } + + await assertMessageBars(["ERROR_ADDON_NOTFOUND"], async () => { + info("Test message bars on addon not found"); + AbuseReportTestUtils.triggerNewReport( + "non-existend-addon-id@mochi.test", + REPORT_ENTRY_POINT + ); + }); + + await assertMessageBars(["submitting", "ERROR_RECENT_SUBMIT"], async () => { + info("Test message bars on recent submission"); + const promiseRendered = AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await promiseRendered; + AbuseReporter.updateLastReportTimestamp(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + await assertMessageBars(["submitting", "ERROR_ABORTED_SUBMIT"], async () => { + info("Test message bars on aborted submission"); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await AbuseReportTestUtils.promiseReportRendered(); + const { _report } = AbuseReportTestUtils.getReportPanel(); + _report.abort(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + await assertMessageBars(["submitting", "ERROR_SERVER"], async () => { + info("Test message bars on server error"); + setTestRequestHandler(500); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + await assertMessageBars(["submitting", "ERROR_CLIENT"], async () => { + info("Test message bars on client error"); + setTestRequestHandler(400); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + await assertMessageBars(["submitting", "ERROR_UNKNOWN"], async () => { + info("Test message bars on unexpected status code"); + setTestRequestHandler(604); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + await assertMessageBars(["submitting", "ERROR_UNKNOWN"], async () => { + info("Test message bars on invalid json in the response data"); + setTestRequestHandler(200, ""); + AbuseReportTestUtils.triggerNewReport(EXT_ID, REPORT_ENTRY_POINT); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + // Verify message bar on add-on without perm_can_uninstall. + await assertMessageBars( + ["submitting", "submitted-no-remove-action"], + async () => { + info("Test message bars on report submitted on an addon without remove"); + setTestRequestHandler(200, "{}"); + AbuseReportTestUtils.triggerNewReport(THEME_NO_UNINSTALL_ID, "menu"); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + } + ); + + // Verify the 3 expected entry points: + // menu, toolbar_context_menu and uninstall + // (See https://addons-server.readthedocs.io/en/latest/topics/api/abuse.html). + await assertMessageBars(["submitting", "submitted"], async () => { + info("Test message bars on report opened from addon options menu"); + setTestRequestHandler(200, "{}"); + AbuseReportTestUtils.triggerNewReport(EXT_ID, "menu"); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }); + + for (const extId of [EXT_ID, THEME_ID]) { + await assertMessageBars( + ["submitting", "submitted"], + async () => { + info(`Test message bars on ${extId} reported from toolbar contextmenu`); + setTestRequestHandler(200, "{}"); + AbuseReportTestUtils.triggerNewReport(extId, "toolbar_context_menu"); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }, + ([submittingDetails, submittedDetails]) => { + const buttonsL10nId = Array.from( + submittedDetails.messagebar.querySelectorAll("button") + ).map(el => el.getAttribute("data-l10n-id")); + if (extId === THEME_ID) { + ok( + buttonsL10nId.every(id => id.endsWith("-theme")), + "submitted bar actions should use the Fluent id for themes" + ); + } else { + ok( + buttonsL10nId.every(id => id.endsWith("-extension")), + "submitted bar actions should use the Fluent id for extensions" + ); + } + } + ); + } + + for (const extId of [EXT_ID2, THEME_ID, SITEPERM_ADDON_ID]) { + const testFn = async () => { + info(`Test message bars on ${extId} reported opened from addon removal`); + setTestRequestHandler(200, "{}"); + AbuseReportTestUtils.triggerNewReport(extId, "uninstall"); + await AbuseReportTestUtils.promiseReportRendered(); + const addon = await AddonManager.getAddonByID(extId); + // Ensure that the test extension is pending uninstall as it would be + // when a user trigger this scenario on an actual addon uninstall. + await addon.uninstall(true); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }; + const assertMessageBarDetails = async ([ + submittingDetails, + submittedDetails, + ]) => AbuseReportTestUtils.assertFluentStrings(submittedDetails.messagebar); + await assertMessageBars( + ["submitting", "submitted-and-removed"], + testFn, + assertMessageBarDetails + ); + } + + // Verify message bar on sitepermission add-on type. + await assertMessageBars( + ["submitting", "submitted"], + async () => { + info( + "Test message bars for report submitted on an sitepermission addon type" + ); + setTestRequestHandler(200, "{}"); + AbuseReportTestUtils.triggerNewReport(SITEPERM_ADDON_ID, "menu"); + await AbuseReportTestUtils.promiseReportRendered(); + AbuseReportTestUtils.triggerSubmit("fake-reason", "fake-message"); + }, + ([submittingDetails, submittedDetails]) => + AbuseReportTestUtils.assertFluentStrings(submittedDetails.messagebar) + ); + + await extension.unload(); + await extension2.unload(); + await theme.unload(); + await sitePermAddon.unload(); +}); + +add_task(async function test_abusereport_from_aboutaddons_menu() { + const EXT_ID = "test-report-from-aboutaddons-menu@mochi.test"; + const extension = await installTestExtension(EXT_ID); + + await openAboutAddons(); + + AbuseReportTestUtils.assertReportPanelHidden(); + + const addonCard = gManagerWindow.document.querySelector( + `addon-list addon-card[addon-id="${extension.id}"]` + ); + ok(addonCard, "Got the addon-card for the test extension"); + + const reportButton = addonCard.querySelector("[action=report]"); + ok(reportButton, "Got the report action for the test extension"); + + info("Click the report action and wait for the 'abuse-report:new' event"); + + let onceReportOpened = AbuseReportTestUtils.promiseReportOpened({ + addonId: extension.id, + reportEntryPoint: "menu", + }); + reportButton.click(); + const panelEl = await onceReportOpened; + + await AbuseReportTestUtils.closeReportPanel(panelEl); + + await closeAboutAddons(); + await extension.unload(); +}); + +add_task(async function test_abusereport_from_aboutaddons_remove() { + const EXT_ID = "test-report-from-aboutaddons-remove@mochi.test"; + + // Test on a theme addon to cover the report checkbox included in the + // uninstall dialog also on a theme. + const extension = await installTestExtension(EXT_ID, "theme"); + + await openAboutAddons("theme"); + + AbuseReportTestUtils.assertReportPanelHidden(); + + const addonCard = gManagerWindow.document.querySelector( + `addon-list addon-card[addon-id="${extension.id}"]` + ); + ok(addonCard, "Got the addon-card for the test theme extension"); + + const removeButton = addonCard.querySelector("[action=remove]"); + ok(removeButton, "Got the remove action for the test theme extension"); + + // Prepare the mocked prompt service. + const promptService = mockPromptService(); + promptService.confirmEx = createPromptConfirmEx({ + remove: true, + report: true, + }); + + info("Click the report action and wait for the 'abuse-report:new' event"); + + const onceReportOpened = AbuseReportTestUtils.promiseReportOpened({ + addonId: extension.id, + reportEntryPoint: "uninstall", + }); + removeButton.click(); + const panelEl = await onceReportOpened; + + await AbuseReportTestUtils.closeReportPanel(panelEl); + + await closeAboutAddons(); + await extension.unload(); +}); + +add_task(async function test_abusereport_from_browserAction_remove() { + const EXT_ID = "test-report-from-browseraction-remove@mochi.test"; + const xpiFile = AddonTestUtils.createTempWebExtensionFile({ + manifest: { + ...BASE_TEST_MANIFEST, + browser_action: { + default_area: "navbar", + }, + browser_specific_settings: { gecko: { id: EXT_ID } }, + }, + }); + const addon = await AddonManager.installTemporaryAddon(xpiFile); + + const buttonId = `${makeWidgetId(EXT_ID)}-browser-action`; + + async function promiseAnimationFrame() { + await new Promise(resolve => window.requestAnimationFrame(resolve)); + + let { tm } = Services; + return new Promise(resolve => tm.dispatchToMainThread(resolve)); + } + + async function reportFromContextMenuRemove() { + const menu = document.getElementById("toolbar-context-menu"); + const node = document.getElementById(CSS.escape(buttonId)); + const shown = BrowserTestUtils.waitForEvent( + menu, + "popupshown", + "Wair for contextmenu popup" + ); + + // Wait for an animation frame as we do for the other mochitest-browser + // tests related to the browserActions. + await promiseAnimationFrame(); + EventUtils.synthesizeMouseAtCenter(node, { type: "contextmenu" }); + await shown; + + info(`Clicking on "Remove Extension" context menu item`); + let removeExtension = menu.querySelector( + ".customize-context-removeExtension" + ); + removeExtension.click(); + + return menu; + } + + // Prepare the mocked prompt service. + const promptService = mockPromptService(); + promptService.confirmEx = createPromptConfirmEx({ + remove: true, + report: true, + }); + + await BrowserTestUtils.withNewTab("about:blank", async function () { + info(`Open browserAction context menu in toolbar context menu`); + let promiseMenu = reportFromContextMenuRemove(); + + // Wait about:addons to be loaded. + let browser = gBrowser.selectedBrowser; + await BrowserTestUtils.browserLoaded(browser); + + let onceReportOpened = AbuseReportTestUtils.promiseReportOpened({ + addonId: EXT_ID, + reportEntryPoint: "uninstall", + managerWindow: browser.contentWindow, + }); + + is( + browser.currentURI.spec, + "about:addons", + "about:addons tab currently selected" + ); + + let menu = await promiseMenu; + menu.hidePopup(); + + let panelEl = await onceReportOpened; + + await AbuseReportTestUtils.closeReportPanel(panelEl); + + let onceExtStarted = AddonTestUtils.promiseWebExtensionStartup(EXT_ID); + addon.cancelUninstall(); + await onceExtStarted; + + // Reload the tab to verify Bug 1559124 didn't regressed. + browser.contentWindow.location.reload(); + await BrowserTestUtils.browserLoaded(browser); + is( + browser.currentURI.spec, + "about:addons", + "about:addons tab currently selected" + ); + + onceReportOpened = AbuseReportTestUtils.promiseReportOpened({ + addonId: EXT_ID, + reportEntryPoint: "uninstall", + managerWindow: browser.contentWindow, + }); + + menu = await reportFromContextMenuRemove(); + info("Wait for the report panel"); + panelEl = await onceReportOpened; + + info("Wait for the report panel to be closed"); + await AbuseReportTestUtils.closeReportPanel(panelEl); + + menu.hidePopup(); + + onceExtStarted = AddonTestUtils.promiseWebExtensionStartup(EXT_ID); + addon.cancelUninstall(); + await onceExtStarted; + }); + + await addon.uninstall(); +}); + +/* + * Test report action hidden on non-supported extension types. + */ +add_task(async function test_report_action_hidden_on_builtin_addons() { + await openAboutAddons("theme"); + await AbuseReportTestUtils.assertReportActionHidden( + gManagerWindow, + DEFAULT_BUILTIN_THEME_ID + ); + await closeAboutAddons(); +}); + +add_task(async function test_report_action_hidden_on_system_addons() { + await openAboutAddons("extension"); + await AbuseReportTestUtils.assertReportActionHidden( + gManagerWindow, + EXT_SYSTEM_ADDON_ID + ); + await closeAboutAddons(); +}); + +add_task(async function test_report_action_hidden_on_dictionary_addons() { + await openAboutAddons("dictionary"); + await AbuseReportTestUtils.assertReportActionHidden( + gManagerWindow, + EXT_DICTIONARY_ADDON_ID + ); + await closeAboutAddons(); +}); + +add_task(async function test_report_action_hidden_on_langpack_addons() { + await openAboutAddons("locale"); + await AbuseReportTestUtils.assertReportActionHidden( + gManagerWindow, + EXT_LANGPACK_ADDON_ID + ); + await closeAboutAddons(); +}); + +// This test verifies that triggering a report that would be immediately +// cancelled (e.g. because abuse reports for that extension type are not +// supported) the abuse report is being hidden as expected. +add_task(async function test_report_hidden_on_report_unsupported_addontype() { + await openAboutAddons(); + + let onceCreateReportFailed = AbuseReportTestUtils.promiseMessageBars(1); + + AbuseReportTestUtils.triggerNewReport(EXT_UNSUPPORTED_TYPE_ADDON_ID, "menu"); + + await onceCreateReportFailed; + + ok(!AbuseReporter.getOpenDialog(), "report dialog should not be open"); + + await closeAboutAddons(); +}); + +/* + * Test regression fixes. + */ + +add_task(async function test_no_broken_suggestion_on_missing_supportURL() { + const EXT_ID = "test-no-author@mochi.test"; + const extension = await installTestExtension(EXT_ID, "extension", { + homepage_url: undefined, + }); + + const abuseReportEl = await AbuseReportTestUtils.openReport(EXT_ID); + + info("Select broken as the abuse reason"); + abuseReportEl.querySelector("#abuse-reason-broken").checked = true; + + let oncePanelUpdated = AbuseReportTestUtils.promiseReportUpdated( + abuseReportEl, + "submit" + ); + await AbuseReportTestUtils.clickPanelButton(abuseReportEl._btnNext); + await oncePanelUpdated; + + const suggestionEl = abuseReportEl.querySelector( + "abuse-report-reason-suggestions" + ); + is(suggestionEl.reason, "broken", "Got the expected suggestion element"); + ok(suggestionEl.hidden, "suggestion element should be empty"); + + await closeAboutAddons(); + await extension.unload(); +}); + +// This test verify that the abuse report panel is opening the +// author link using a null triggeringPrincipal. +add_task(async function test_abusereport_open_author_url() { + const abuseReportEl = await AbuseReportTestUtils.openReport( + EXT_WITH_PRIVILEGED_URL_ID + ); + + const authorLink = abuseReportEl._linkAddonAuthor; + ok(authorLink, "Got the author link element"); + is( + authorLink.href, + "about:config", + "Got a privileged url in the link element" + ); + + SimpleTest.waitForExplicitFinish(); + let waitForConsole = new Promise(resolve => { + SimpleTest.monitorConsole(resolve, [ + { + message: + // eslint-disable-next-line max-len + /Security Error: Content at moz-nullprincipal:{.*} may not load or link to about:config/, + }, + ]); + }); + + let tabSwitched = BrowserTestUtils.waitForEvent(gBrowser, "TabSwitchDone"); + authorLink.click(); + await tabSwitched; + + is( + gBrowser.selectedBrowser.currentURI.spec, + "about:blank", + "Got about:blank loaded in the new tab" + ); + + SimpleTest.endMonitorConsole(); + await waitForConsole; + + BrowserTestUtils.removeTab(gBrowser.selectedTab); + await closeAboutAddons(); +}); + +add_task(async function test_no_report_checkbox_for_unsupported_addon_types() { + async function test_report_checkbox_hidden(addon) { + await openAboutAddons(addon.type); + + const addonCard = gManagerWindow.document.querySelector( + `addon-list addon-card[addon-id="${addon.id}"]` + ); + ok(addonCard, "Got the addon-card for the test extension"); + + const removeButton = addonCard.querySelector("[action=remove]"); + ok(removeButton, "Got the remove action for the test extension"); + + // Prepare the mocked prompt service. + const promptService = mockPromptService(); + promptService.confirmEx = createPromptConfirmEx({ + remove: true, + report: false, + expectCheckboxHidden: true, + }); + + info("Click the report action and wait for the addon to be removed"); + const promiseCardRemoved = BrowserTestUtils.waitForEvent( + addonCard.closest("addon-list"), + "remove" + ); + removeButton.click(); + await promiseCardRemoved; + + await closeAboutAddons(); + } + + const reportNotSupportedAddons = [ + { + id: "fake-langpack-to-remove@mochi.test", + name: "This is a fake langpack", + version: "1.1", + type: "locale", + }, + { + id: "fake-dictionary-to-remove@mochi.test", + name: "This is a fake dictionary", + version: "1.1", + type: "dictionary", + }, + ]; + + AbuseReportTestUtils.createMockAddons(reportNotSupportedAddons); + + for (const { id } of reportNotSupportedAddons) { + const addon = await AddonManager.getAddonByID(id); + await test_report_checkbox_hidden(addon); + } +}); + +add_task(async function test_author_hidden_when_missing() { + const EXT_ID = "test-no-author@mochi.test"; + const extension = await installTestExtension(EXT_ID, "extension", { + author: undefined, + }); + + const abuseReportEl = await AbuseReportTestUtils.openReport(EXT_ID); + + const addon = await AddonManager.getAddonByID(EXT_ID); + + ok(!addon.creator, "addon.creator should not be undefined"); + ok( + abuseReportEl._addonAuthorContainer.hidden, + "author container should be hidden" + ); + + await closeAboutAddons(); + await extension.unload(); +}); + +// Verify addon.siteOrigin is used as a fallback when homepage_url/developer.url +// or support url are missing. +// +// TODO(Bug 1789718): adapt to SitePermAddonProvider implementation. +add_task(async function test_siteperm_siteorigin_fallback() { + const SITEPERM_ADDON_ID = "webmidi-site-origin@mochi.test"; + const sitePermAddon = await installTestExtension( + SITEPERM_ADDON_ID, + "sitepermission-deprecated", + { + homepage_url: undefined, + } + ); + + const abuseReportEl = await AbuseReportTestUtils.openReport( + SITEPERM_ADDON_ID + ); + const addon = await AddonManager.getAddonByID(SITEPERM_ADDON_ID); + + ok(addon.siteOrigin, "addon.siteOrigin should not be undefined"); + ok(!addon.supportURL, "addon.supportURL should not be set"); + ok(!addon.homepageURL, "addon.homepageURL should not be set"); + is( + abuseReportEl.supportURL, + addon.siteOrigin, + "Got the expected support_url" + ); + + await closeAboutAddons(); + await sitePermAddon.unload(); +}); -- cgit v1.2.3