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 --- .../newtab/test/browser/browser_asrouter_cfr.js | 914 +++++++++++++++++++++ 1 file changed, 914 insertions(+) create mode 100644 browser/components/newtab/test/browser/browser_asrouter_cfr.js (limited to 'browser/components/newtab/test/browser/browser_asrouter_cfr.js') diff --git a/browser/components/newtab/test/browser/browser_asrouter_cfr.js b/browser/components/newtab/test/browser/browser_asrouter_cfr.js new file mode 100644 index 0000000000..3c163e2a14 --- /dev/null +++ b/browser/components/newtab/test/browser/browser_asrouter_cfr.js @@ -0,0 +1,914 @@ +const { CFRPageActions } = ChromeUtils.import( + "resource://activity-stream/lib/CFRPageActions.jsm" +); +const { ASRouterTriggerListeners } = ChromeUtils.import( + "resource://activity-stream/lib/ASRouterTriggerListeners.jsm" +); +const { ASRouter } = ChromeUtils.import( + "resource://activity-stream/lib/ASRouter.jsm" +); +const { CFRMessageProvider } = ChromeUtils.importESModule( + "resource://activity-stream/lib/CFRMessageProvider.sys.mjs" +); + +const { TelemetryFeed } = ChromeUtils.import( + "resource://activity-stream/lib/TelemetryFeed.jsm" +); + +const createDummyRecommendation = ({ + action, + category, + heading_text, + layout, + skip_address_bar_notifier, + show_in_private_browsing, + template, +}) => { + let recommendation = { + template, + groups: ["mochitest-group"], + content: { + layout: layout || "addon_recommendation", + category, + anchor_id: "page-action-buttons", + skip_address_bar_notifier, + show_in_private_browsing, + heading_text: heading_text || "Mochitest", + info_icon: { + label: { attributes: { tooltiptext: "Why am I seeing this" } }, + sumo_path: "extensionrecommendations", + }, + icon: "chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg", + icon_dark_theme: + "chrome://activity-stream/content/data/content/assets/glyph-webextension-16.svg", + learn_more: "extensionrecommendations", + addon: { + id: "addon-id", + title: "Addon name", + icon: "chrome://browser/skin/addons/addon-install-downloading.svg", + author: "Author name", + amo_url: "https://example.com", + }, + descriptionDetails: { steps: [] }, + text: "Mochitest", + buttons: { + primary: { + label: { + value: "OK", + attributes: { accesskey: "O" }, + }, + action: { + type: action.type, + data: {}, + }, + }, + secondary: [ + { + label: { + value: "Cancel", + attributes: { accesskey: "C" }, + }, + action: { + type: "CANCEL", + }, + }, + { + label: { + value: "Cancel 1", + attributes: { accesskey: "A" }, + }, + }, + { + label: { + value: "Cancel 2", + attributes: { accesskey: "B" }, + }, + }, + ], + }, + }, + }; + recommendation.content.notification_text = new String("Mochitest"); // eslint-disable-line + recommendation.content.notification_text.attributes = { + tooltiptext: "Mochitest tooltip", + "a11y-announcement": "Mochitest announcement", + }; + return recommendation; +}; + +function checkCFRAddonsElements(notification) { + Assert.ok(notification.hidden === false, "Panel should be visible"); + Assert.equal( + notification.getAttribute("data-notification-category"), + "addon_recommendation", + "Panel have correct data attribute" + ); + Assert.ok( + notification.querySelector("#cfr-notification-footer-text-and-addon-info"), + "Panel should have addon info container" + ); + Assert.ok( + notification.querySelector("#cfr-notification-footer-filled-stars"), + "Panel should have addon rating info" + ); + Assert.ok( + notification.querySelector("#cfr-notification-author"), + "Panel should have author info" + ); +} + +function checkCFRTrackingProtectionMilestone(notification) { + Assert.ok(notification.hidden === false, "Panel should be visible"); + Assert.ok( + notification.getAttribute("data-notification-category") === "short_message", + "Panel have correct data attribute" + ); +} + +function clearNotifications() { + for (let notification of PopupNotifications._currentNotifications) { + notification.remove(); + } + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); +} + +function trigger_cfr_panel( + browser, + trigger, + { + action = { type: "CANCEL" }, + heading_text, + category = "cfrAddons", + layout, + skip_address_bar_notifier = false, + use_single_secondary_button = false, + show_in_private_browsing = false, + template = "cfr_doorhanger", + } = {} +) { + // a fake action type will result in the action being ignored + const recommendation = createDummyRecommendation({ + action, + category, + heading_text, + layout, + skip_address_bar_notifier, + show_in_private_browsing, + template, + }); + if (category !== "cfrAddons") { + delete recommendation.content.addon; + } + if (use_single_secondary_button) { + recommendation.content.buttons.secondary = [ + recommendation.content.buttons.secondary[0], + ]; + } + + clearNotifications(); + return CFRPageActions.addRecommendation( + browser, + trigger, + recommendation, + // Use the real AS dispatch method to trigger real notifications + ASRouter.dispatchCFRAction + ); +} + +add_setup(async function () { + // Store it in order to restore to the original value + const { _fetchLatestAddonVersion } = CFRPageActions; + // Prevent fetching the real addon url and making a network request + CFRPageActions._fetchLatestAddonVersion = x => "http://example.com"; + + registerCleanupFunction(() => { + CFRPageActions._fetchLatestAddonVersion = _fetchLatestAddonVersion; + clearNotifications(); + CFRPageActions.clearRecommendations(); + }); +}); + +add_task(async function test_cfr_notification_show() { + const sendPingStub = sinon.stub( + TelemetryFeed.prototype, + "sendStructuredIngestionEvent" + ); + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + const response = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + const oldFocus = document.activeElement; + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .hidden === false, + "Panel should be visible" + ); + Assert.equal( + document.activeElement, + oldFocus, + "Focus didn't move when panel was shown" + ); + + // Check there is a primary button and click it. It will trigger the callback. + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .button + ); + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); + + Assert.ok(sendPingStub.callCount >= 1, "Recorded some events"); + let cfrPing = sendPingStub.args.find(args => args[2] === "cfr"); + Assert.equal(cfrPing[0].source, "CFR", "Got a CFR event"); + sendPingStub.restore(); +}); + +add_task(async function test_cfr_notification_show() { + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + let response = await trigger_cfr_panel(browser, "example.com", { + heading_text: "First Message", + }); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + + // Try adding another message + response = await trigger_cfr_panel(browser, "example.com", { + heading_text: "Second Message", + }); + Assert.equal( + response, + false, + "Should return false if second call did not add the message" + ); + + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .hidden === false, + "Panel should be visible" + ); + + Assert.equal( + document.getElementById("cfr-notification-header-label").value, + "First Message", + "The first message should be visible" + ); + + // Check there is a primary button and click it. It will trigger the callback. + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .button + ); + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); +}); + +add_task(async function test_cfr_notification_minimize() { + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + let response = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + await BrowserTestUtils.waitForCondition( + () => gURLBar.hasAttribute("cfr-recommendation-state"), + "Wait for the notification to show up and have a state" + ); + Assert.ok( + gURLBar.getAttribute("cfr-recommendation-state") === "expanded", + "CFR recomendation state is correct" + ); + + gURLBar.focus(); + + await BrowserTestUtils.waitForCondition( + () => gURLBar.getAttribute("cfr-recommendation-state") === "collapsed", + "After urlbar focus the CFR notification should collapse" + ); + + // Open the panel and click to dismiss to ensure cleanup + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; +}); + +add_task(async function test_cfr_notification_minimize_2() { + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + let response = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + await BrowserTestUtils.waitForCondition( + () => gURLBar.hasAttribute("cfr-recommendation-state"), + "Wait for the notification to show up and have a state" + ); + Assert.ok( + gURLBar.getAttribute("cfr-recommendation-state") === "expanded", + "CFR recomendation state is correct" + ); + + // Open the panel and click to dismiss to ensure cleanup + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .secondaryButton, + "There should be a cancel button" + ); + + // Click the Not Now button + document + .getElementById("contextual-feature-recommendation-notification") + .secondaryButton.click(); + + await hidePanel; + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification"), + "The notification should not dissapear" + ); + + await BrowserTestUtils.waitForCondition( + () => gURLBar.getAttribute("cfr-recommendation-state") === "collapsed", + "Clicking the secondary button should collapse the notification" + ); + + clearNotifications(); + CFRPageActions.clearRecommendations(); +}); + +add_task(async function test_cfr_addon_install() { + // addRecommendation checks that scheme starts with http and host matches + const browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + const response = await trigger_cfr_panel(browser, "example.com", { + action: { type: "INSTALL_ADDON_FROM_URL" }, + }); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .hidden === false, + "Panel should be visible" + ); + checkCFRAddonsElements( + document.getElementById("contextual-feature-recommendation-notification") + ); + + // Check there is a primary button and click it. It will trigger the callback. + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .button + ); + const hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + + await BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + + let [notification] = PopupNotifications.panel.childNodes; + // Trying to install the addon will trigger a progress popup or an error popup if + // running the test multiple times in a row + Assert.ok( + notification.id === "addon-progress-notification" || + notification.id === "addon-install-failed-notification", + "Should try to install the addon" + ); + + clearNotifications(); +}); + +add_task( + async function test_cfr_tracking_protection_milestone_notification_remove() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.contentblocking.cfr-milestone.milestone-achieved", 1000], + [ + "browser.newtabpage.activity-stream.asrouter.providers.cfr", + `{"id":"cfr","enabled":true,"type":"local","localProvider":"CFRMessageProvider","updateCycleInMs":3600000}`, + ], + ], + }); + + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + + Services.obs.notifyObservers( + { + wrappedJSObject: { + event: "ContentBlockingMilestone", + }, + }, + "SiteProtection:ContentBlockingMilestone" + ); + + await showPanel; + + const notification = document.getElementById( + "contextual-feature-recommendation-notification" + ); + + checkCFRTrackingProtectionMilestone(notification); + + Assert.ok(notification.secondaryButton); + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + + notification.secondaryButton.click(); + await hidePanel; + await SpecialPowers.popPrefEnv(); + clearNotifications(); + } +); + +add_task(async function test_cfr_addon_and_features_show() { + // addRecommendation checks that scheme starts with http and host matches + let browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + // Trigger Feature CFR + let response = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + let showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + const notification = document.getElementById( + "contextual-feature-recommendation-notification" + ); + checkCFRAddonsElements(notification); + + // Check there is a primary button and click it. It will trigger the callback. + Assert.ok(notification.button); + let hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); + + // Trigger Addon CFR + response = await trigger_cfr_panel(browser, "example.com", { + action: { type: "PIN_CURRENT_TAB" }, + category: "cfrAddons", + }); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Open the panel + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + Assert.ok( + document.getElementById("contextual-feature-recommendation-notification") + .hidden === false, + "Panel should be visible" + ); + checkCFRAddonsElements( + document.getElementById("contextual-feature-recommendation-notification") + ); + + // Check there is a primary button and click it. It will trigger the callback. + Assert.ok(notification.button); + hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; + + // Clicking the primary action also removes the notification + Assert.equal( + PopupNotifications._currentNotifications.length, + 0, + "Should have removed the notification" + ); +}); + +add_task(async function test_onLocationChange_cb() { + let count = 0; + const triggerHandler = () => ++count; + const TEST_URL = + "https://example.com/browser/browser/components/newtab/test/browser/blue_page.html"; + const browser = gBrowser.selectedBrowser; + + await ASRouterTriggerListeners.get("openURL").init(triggerHandler, [ + "example.com", + ]); + + BrowserTestUtils.loadURIString(browser, "about:blank"); + await BrowserTestUtils.browserLoaded(browser, false, "about:blank"); + + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + Assert.equal(count, 1, "Count navigation to example.com"); + + // Anchor scroll triggers a location change event with the same document + // https://searchfox.org/mozilla-central/rev/8848b9741fc4ee4e9bc3ae83ea0fc048da39979f/uriloader/base/nsIWebProgressListener.idl#400-403 + BrowserTestUtils.loadURIString(browser, "http://example.com/#foo"); + await BrowserTestUtils.waitForLocationChange( + gBrowser, + "http://example.com/#foo" + ); + + Assert.equal(count, 1, "It should ignore same page navigation"); + + BrowserTestUtils.loadURIString(browser, TEST_URL); + await BrowserTestUtils.browserLoaded(browser, false, TEST_URL); + + Assert.equal(count, 2, "We moved to a new document"); + + registerCleanupFunction(() => { + ASRouterTriggerListeners.get("openURL").uninit(); + }); +}); + +add_task(async function test_matchPattern() { + let count = 0; + const triggerHandler = () => ++count; + const frequentVisitsTrigger = ASRouterTriggerListeners.get("frequentVisits"); + await frequentVisitsTrigger.init(triggerHandler, [], ["*://*.example.com/"]); + + const browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + await BrowserTestUtils.waitForCondition( + () => frequentVisitsTrigger._visits.get("example.com").length === 1, + "Registered pattern matched the current location" + ); + + BrowserTestUtils.loadURIString(browser, "about:config"); + await BrowserTestUtils.browserLoaded(browser, false, "about:config"); + + await BrowserTestUtils.waitForCondition( + () => frequentVisitsTrigger._visits.get("example.com").length === 1, + "Navigated to a new page but not a match" + ); + + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + await BrowserTestUtils.waitForCondition( + () => frequentVisitsTrigger._visits.get("example.com").length === 1, + "Navigated to a location that matches the pattern but within 15 mins" + ); + + BrowserTestUtils.loadURIString(browser, "http://www.example.com/"); + await BrowserTestUtils.browserLoaded( + browser, + false, + "http://www.example.com/" + ); + + await BrowserTestUtils.waitForCondition( + () => frequentVisitsTrigger._visits.get("www.example.com").length === 1, + "www.example.com is a different host that also matches the pattern." + ); + await BrowserTestUtils.waitForCondition( + () => frequentVisitsTrigger._visits.get("example.com").length === 1, + "www.example.com is a different host that also matches the pattern." + ); + + registerCleanupFunction(() => { + ASRouterTriggerListeners.get("frequentVisits").uninit(); + }); +}); + +add_task(async function test_providerNames() { + const providersBranch = + "browser.newtabpage.activity-stream.asrouter.providers."; + const cfrProviderPrefs = Services.prefs.getChildList(providersBranch); + for (const prefName of cfrProviderPrefs) { + const prefValue = JSON.parse(Services.prefs.getStringPref(prefName)); + if (prefValue && prefValue.id) { + // Snippets are disabled in tests and value is set to [] + Assert.equal( + prefValue.id, + prefName.slice(providersBranch.length), + "Provider id and pref name do not match" + ); + } + } +}); + +add_task(async function test_cfr_notification_keyboard() { + // addRecommendation checks that scheme starts with http and host matches + const browser = gBrowser.selectedBrowser; + BrowserTestUtils.loadURIString(browser, "http://example.com/"); + await BrowserTestUtils.browserLoaded(browser, false, "http://example.com/"); + + const response = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + response, + "Should return true if addRecommendation checks were successful" + ); + + // Open the panel with the keyboard. + // Toolbar buttons aren't always focusable; toolbar keyboard navigation + // makes them focusable on demand. Therefore, we must force focus. + const button = document.getElementById("contextual-feature-recommendation"); + button.setAttribute("tabindex", "-1"); + button.focus(); + button.removeAttribute("tabindex"); + + let focused = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "focus", + true + ); + EventUtils.synthesizeKey(" "); + await focused; + Assert.ok(true, "Focus inside panel after button pressed"); + + let hidden = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + EventUtils.synthesizeKey("KEY_Escape"); + await hidden; + Assert.ok(true, "Panel hidden after Escape pressed"); + + const showPanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + // Need to dismiss the notification to clear the RecommendationMap + document.getElementById("contextual-feature-recommendation").click(); + await showPanel; + + const hidePanel = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + document + .getElementById("contextual-feature-recommendation-notification") + .button.click(); + await hidePanel; +}); + +add_task(function test_updateCycleForProviders() { + Services.prefs + .getChildList("browser.newtabpage.activity-stream.asrouter.providers.") + .forEach(provider => { + const prefValue = JSON.parse(Services.prefs.getStringPref(provider, "")); + if (prefValue && prefValue.type === "remote-settings") { + Assert.ok(prefValue.updateCycleInMs); + } + }); +}); + +add_task(async function test_heartbeat_tactic_2() { + clearNotifications(); + registerCleanupFunction(() => { + // Remove the tab opened by clicking the heartbeat message + gBrowser.removeCurrentTab(); + clearNotifications(); + }); + + const msg = (await CFRMessageProvider.getMessages()).find( + m => m.id === "HEARTBEAT_TACTIC_2" + ); + const shown = await CFRPageActions.addRecommendation( + gBrowser.selectedBrowser, + null, + { + ...msg, + id: `HEARTBEAT_MOCHITEST_${Date.now()}`, + groups: ["mochitest-group"], + targeting: true, + }, + // Use the real AS dispatch method to trigger real notifications + ASRouter.dispatchCFRAction + ); + + Assert.ok(shown, "Heartbeat CFR added"); + + // Wait for visibility change + BrowserTestUtils.waitForCondition( + () => document.getElementById("contextual-feature-recommendation"), + "Heartbeat button exists" + ); + + let newTabPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + Services.urlFormatter.formatURL(msg.content.action.url), + true + ); + + document.getElementById("contextual-feature-recommendation").click(); + + await newTabPromise; +}); + +add_task(async function test_cfr_doorhanger_in_private_window() { + const win = await BrowserTestUtils.openNewBrowserWindow({ private: true }); + const sendPingStub = sinon.stub( + TelemetryFeed.prototype, + "sendStructuredIngestionEvent" + ); + + const tab = await BrowserTestUtils.openNewForegroundTab( + win.gBrowser, + "http://example.com/" + ); + const browser = tab.linkedBrowser; + + const response1 = await trigger_cfr_panel(browser, "example.com"); + Assert.ok( + !response1, + "CFR should not be shown in a private window if show_in_private_browsing is false" + ); + + const response2 = await trigger_cfr_panel(browser, "example.com", { + show_in_private_browsing: true, + }); + Assert.ok( + response2, + "CFR should be shown in a private window if show_in_private_browsing is true" + ); + + const shownPromise = BrowserTestUtils.waitForEvent( + win.PopupNotifications.panel, + "popupshown" + ); + win.document.getElementById("contextual-feature-recommendation").click(); + await shownPromise; + + const hiddenPromise = BrowserTestUtils.waitForEvent( + win.PopupNotifications.panel, + "popuphidden" + ); + const button = win.document.getElementById( + "contextual-feature-recommendation-notification" + )?.button; + Assert.ok(button, "CFR doorhanger button found"); + button.click(); + await hiddenPromise; + + Assert.greater(sendPingStub.callCount, 0, "Recorded CFR telemetry"); + const cfrPing = sendPingStub.args.find(args => args[2] === "cfr"); + Assert.equal(cfrPing[0].source, "CFR", "Got a CFR event"); + Assert.equal( + cfrPing[0].message_id, + "n/a", + "Omitted message_id consistent with CFR telemetry policy" + ); + Assert.equal( + cfrPing[0].client_id, + undefined, + "Omitted client_id consistent with CFR telemetry policy" + ); + + sendPingStub.restore(); + await BrowserTestUtils.closeWindow(win); +}); -- cgit v1.2.3