summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/browser/browser_asrouter_cfr.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/browser/browser_asrouter_cfr.js')
-rw-r--r--browser/components/newtab/test/browser/browser_asrouter_cfr.js914
1 files changed, 914 insertions, 0 deletions
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);
+});