/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; XPCOMUtils.defineLazyServiceGetter( this, "TrackingDBService", "@mozilla.org/tracking-db-service;1", "nsITrackingDBService" ); const { AboutProtectionsParent } = ChromeUtils.importESModule( "resource:///actors/AboutProtectionsParent.sys.mjs" ); const LOG = { "https://1.example.com": [ [Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT, true, 1], ], "https://2.example.com": [ [Ci.nsIWebProgressListener.STATE_BLOCKED_FINGERPRINTING_CONTENT, true, 1], ], "https://3.example.com": [ [Ci.nsIWebProgressListener.STATE_BLOCKED_CRYPTOMINING_CONTENT, true, 2], ], "https://4.example.com": [ [Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER, true, 3], ], "https://5.example.com": [ [Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_TRACKER, true, 1], ], // Cookie blocked for other reason, then identified as a tracker "https://6.example.com": [ [ Ci.nsIWebProgressListener.STATE_COOKIES_BLOCKED_ALL | Ci.nsIWebProgressListener.STATE_LOADED_LEVEL_1_TRACKING_CONTENT, true, 4, ], ], }; requestLongerTimeout(2); add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [ ["browser.vpn_promo.enabled", true], ["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"], [ "browser.vpn_promo.disallowed_regions", "ae,by,cn,cu,iq,ir,kp,om,ru,sd,sy,tm,tr,ua", ], // Change the endpoints to prevent non-local network connections when landing on the page. ["browser.contentblocking.report.monitor.url", ""], ["browser.contentblocking.report.monitor.sign_in_url", ""], ["browser.contentblocking.report.social.url", ""], ["browser.contentblocking.report.cookie.url", ""], ["browser.contentblocking.report.tracker.url", ""], ["browser.contentblocking.report.fingerprinter.url", ""], ["browser.contentblocking.report.cryptominer.url", ""], ["browser.contentblocking.report.mobile-ios.url", ""], ["browser.contentblocking.report.mobile-android.url", ""], ["browser.contentblocking.report.monitor.home_page_url", ""], ["browser.contentblocking.report.monitor.preferences_url", ""], ["browser.contentblocking.report.vpn.url", ""], ["browser.contentblocking.report.vpn-promo.url", ""], ["browser.contentblocking.report.vpn-android.url", ""], ["browser.contentblocking.report.vpn-ios.url", ""], ], }); let oldCanRecord = Services.telemetry.canRecordExtended; Services.telemetry.canRecordExtended = true; registerCleanupFunction(() => { Services.telemetry.canRecordExtended = oldCanRecord; // AboutProtectionsParent.setTestOverride(null); }); }); add_task(async function checkTelemetryLoadEvents() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.contentblocking.database.enabled", false], ["browser.contentblocking.report.monitor.enabled", false], ["browser.contentblocking.report.lockwise.enabled", false], ["browser.contentblocking.report.proxy.enabled", false], ["browser.vpn_promo.enabled", false], ], }); await addArbitraryTimeout(); // Clear everything. Services.telemetry.clearEvents(); await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; return !events || !events.length; }); Services.telemetry.setEventRecordingEnabled("security.ui.protections", true); let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections", gBrowser, }); let loadEvents = await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; if (events && events.length) { events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "show" ); if (events.length == 1) { return events; } } return null; }, "recorded telemetry for showing the report"); is(loadEvents.length, 1, `recorded telemetry for showing the report`); await BrowserTestUtils.reloadTab(tab); loadEvents = await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; if (events && events.length) { events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "close" ); if (events.length == 1) { return events; } } return null; }, "recorded telemetry for closing the report"); is(loadEvents.length, 1, `recorded telemetry for closing the report`); await BrowserTestUtils.removeTab(tab); }); function waitForTelemetryEventCount(count) { info("waiting for telemetry event count of " + count); return TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, false ).content; if (!events) { return null; } // Ignore irrelevant events from other parts of the browser. events = events.filter(e => e[1] == "security.ui.protections"); info("got " + (events && events.length) + " events"); if (events.length == count) { return events; } return null; }, "waiting for telemetry event count of: " + count); } let addArbitraryTimeout = async () => { // There's an arbitrary interval of 2 seconds in which the content // processes sync their event data with the parent process, we wait // this out to ensure that we clear everything that is left over from // previous tests and don't receive random events in the middle of our tests. // eslint-disable-next-line mozilla/no-arbitrary-setTimeout await new Promise(c => setTimeout(c, 2000)); }; add_task(async function checkTelemetryClickEvents() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.contentblocking.database.enabled", true], ["browser.contentblocking.report.monitor.enabled", true], ["browser.contentblocking.report.lockwise.enabled", true], ["browser.contentblocking.report.proxy.enabled", true], ["browser.vpn_promo.enabled", false], ], }); await addArbitraryTimeout(); // Clear everything. Services.telemetry.clearEvents(); await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; return !events || !events.length; }); Services.telemetry.setEventRecordingEnabled("security.ui.protections", true); let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections", gBrowser, }); // Add user logins. await Services.logins.addLoginAsync(TEST_LOGIN1); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const managePasswordsButton = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("manage-passwords-button"); }, "Manage passwords button exists" ); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(managePasswordsButton), "manage passwords button is visible" ); managePasswordsButton.click(); }); let events = await waitForTelemetryEventCount(4); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "lw_open_button" && e[4] == "manage_passwords" ); is( events.length, 1, `recorded telemetry for lw_open_button when there are no breached passwords` ); await BrowserTestUtils.removeTab(gBrowser.selectedTab); // Add breached logins. AboutProtectionsParent.setTestOverride( mockGetLoginDataWithSyncedDevices(false, 4) ); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const managePasswordsButton = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("manage-passwords-button"); }, "Manage passwords button exists" ); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(managePasswordsButton), "manage passwords button is visible" ); managePasswordsButton.click(); }); events = await waitForTelemetryEventCount(7); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "lw_open_button" && e[4] == "manage_breached_passwords" ); is( events.length, 1, `recorded telemetry for lw_open_button when there are breached passwords` ); AboutProtectionsParent.setTestOverride(null); Services.logins.removeLogin(TEST_LOGIN1); await BrowserTestUtils.removeTab(gBrowser.selectedTab); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { // Show all elements, so we can click on them, even though our user is not logged in. let hidden_elements = content.document.querySelectorAll(".hidden"); for (let el of hidden_elements) { el.style.display = "block "; } const savePasswordsButton = await ContentTaskUtils.waitForCondition(() => { // Opens an extra tab return content.document.getElementById("save-passwords-button"); }, "Save Passwords button exists"); savePasswordsButton.click(); }); events = await waitForTelemetryEventCount(10); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "lw_open_button" && e[4] == "save_passwords" ); is( events.length, 1, `recorded telemetry for lw_open_button when there are no stored passwords` ); await BrowserTestUtils.removeTab(gBrowser.selectedTab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const lockwiseAboutLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("lockwise-how-it-works"); }, "lockwiseReportLink exists"); lockwiseAboutLink.click(); }); events = await waitForTelemetryEventCount(11); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "lw_about_link" ); is(events.length, 1, `recorded telemetry for lw_about_link`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { let monitorAboutLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("monitor-link"); }, "monitorAboutLink exists"); monitorAboutLink.click(); }); events = await waitForTelemetryEventCount(12); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_about_link" ); is(events.length, 1, `recorded telemetry for mtr_about_link`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const signUpForMonitorLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("sign-up-for-monitor-link"); }, "signUpForMonitorLink exists"); signUpForMonitorLink.click(); }); events = await waitForTelemetryEventCount(13); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_signup_button" ); is(events.length, 1, `recorded telemetry for mtr_signup_button`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const socialLearnMoreLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("social-link"); }, "Learn more link for social tab exists"); socialLearnMoreLink.click(); }); events = await waitForTelemetryEventCount(14); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "trackers_about_link" && e[4] == "social" ); is(events.length, 1, `recorded telemetry for social trackers_about_link`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const cookieLearnMoreLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("cookie-link"); }, "Learn more link for cookie tab exists"); cookieLearnMoreLink.click(); }); events = await waitForTelemetryEventCount(15); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "trackers_about_link" && e[4] == "cookie" ); is(events.length, 1, `recorded telemetry for cookie trackers_about_link`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const trackerLearnMoreLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("tracker-link"); }, "Learn more link for tracker tab exists"); trackerLearnMoreLink.click(); }); events = await waitForTelemetryEventCount(16); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "trackers_about_link" && e[4] == "tracker" ); is( events.length, 1, `recorded telemetry for content tracker trackers_about_link` ); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const fingerprinterLearnMoreLink = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("fingerprinter-link"); }, "Learn more link for fingerprinter tab exists" ); fingerprinterLearnMoreLink.click(); }); events = await waitForTelemetryEventCount(17); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "trackers_about_link" && e[4] == "fingerprinter" ); is( events.length, 1, `recorded telemetry for fingerprinter trackers_about_link` ); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const cryptominerLearnMoreLink = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("cryptominer-link"); }, "Learn more link for cryptominer tab exists" ); cryptominerLearnMoreLink.click(); }); events = await waitForTelemetryEventCount(18); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "trackers_about_link" && e[4] == "cryptominer" ); is( events.length, 1, `recorded telemetry for cryptominer trackers_about_link` ); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const protectionSettings = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("protection-settings"); }, "protection-settings link exists"); protectionSettings.click(); }); events = await waitForTelemetryEventCount(19); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "settings_link" && e[4] == "header-settings" ); is(events.length, 1, `recorded telemetry for settings_link header-settings`); await BrowserTestUtils.removeTab(gBrowser.selectedTab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const customProtectionSettings = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("manage-protections"); }, "manage-protections link exists" ); // Show element so we can click on it customProtectionSettings.style.display = "block"; customProtectionSettings.click(); }); events = await waitForTelemetryEventCount(20); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "settings_link" && e[4] == "custom-card-settings" ); is( events.length, 1, `recorded telemetry for settings_link custom-card-settings` ); await BrowserTestUtils.removeTab(gBrowser.selectedTab); // Add breached logins and some resolved breaches. AboutProtectionsParent.setTestOverride( mockGetMonitorData({ potentiallyBreachedLogins: 4, numBreaches: 3, numBreachesResolved: 1, }) ); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const resolveBreachesButton = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("monitor-partial-breaches-link"); }, "Monitor resolve breaches button exists" ); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(resolveBreachesButton), "Resolve breaches button is visible" ); resolveBreachesButton.click(); }); events = await waitForTelemetryEventCount(23); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "resolve_breaches" ); is(events.length, 1, `recorded telemetry for resolve breaches button`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const monitorKnownBreachesBlock = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("monitor-known-breaches-link"); }, "Monitor card known breaches block exists" ); monitorKnownBreachesBlock.click(); }); events = await waitForTelemetryEventCount(24); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "known_resolved_breaches" ); is(events.length, 1, `recorded telemetry for monitor known breaches block`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const monitorExposedPasswordsBlock = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById( "monitor-exposed-passwords-link" ); }, "Monitor card exposed passwords block exists"); monitorExposedPasswordsBlock.click(); }); events = await waitForTelemetryEventCount(25); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "exposed_passwords_unresolved_breaches" ); is( events.length, 1, `recorded telemetry for monitor exposed passwords block` ); // Add breached logins and no resolved breaches. AboutProtectionsParent.setTestOverride( mockGetMonitorData({ potentiallyBreachedLogins: 4, numBreaches: 3, numBreachesResolved: 0, }) ); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const manageBreachesButton = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("monitor-breaches-link"); }, "Monitor manage breaches button exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(manageBreachesButton), "Manage breaches button is visible" ); manageBreachesButton.click(); }); events = await waitForTelemetryEventCount(28); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "manage_breaches" ); is(events.length, 1, `recorded telemetry for manage breaches button`); // All breaches are resolved. AboutProtectionsParent.setTestOverride( mockGetMonitorData({ potentiallyBreachedLogins: 4, numBreaches: 3, numBreachesResolved: 3, }) ); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const viewReportButton = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("monitor-breaches-link"); }, "Monitor view report button exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(viewReportButton), "View report button is visible" ); viewReportButton.click(); }); events = await waitForTelemetryEventCount(31); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "view_report" ); is(events.length, 1, `recorded telemetry for view report button`); // No breaches are present. AboutProtectionsParent.setTestOverride( mockGetMonitorData({ potentiallyBreachedLogins: 4, numBreaches: 0, numBreachesResolved: 0, }) ); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const viewReportButton = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("monitor-breaches-link"); }, "Monitor view report button exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(viewReportButton), "View report button is visible" ); viewReportButton.click(); }); events = await waitForTelemetryEventCount(34); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "view_report" ); is(events.length, 2, `recorded telemetry for view report button`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const monitorEmailBlock = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("monitor-stored-emails-link"); }, "Monitor card email block exists"); monitorEmailBlock.click(); }); events = await waitForTelemetryEventCount(35); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "stored_emails" ); is(events.length, 1, `recorded telemetry for monitor email block`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const monitorKnownBreachesBlock = await ContentTaskUtils.waitForCondition( () => { return content.document.getElementById("monitor-known-breaches-link"); }, "Monitor card known breaches block exists" ); monitorKnownBreachesBlock.click(); }); events = await waitForTelemetryEventCount(36); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "known_unresolved_breaches" ); is(events.length, 1, `recorded telemetry for monitor known breaches block`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const monitorExposedPasswordsBlock = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById( "monitor-exposed-passwords-link" ); }, "Monitor card exposed passwords block exists"); monitorExposedPasswordsBlock.click(); }); events = await waitForTelemetryEventCount(37); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "mtr_report_link" && e[4] == "exposed_passwords_all_breaches" ); is( events.length, 1, `recorded telemetry for monitor exposed passwords block` ); // Clean up. AboutProtectionsParent.setTestOverride(null); await BrowserTestUtils.removeTab(tab); }); // This tests that telemetry is sent when saveEvents is called. add_task(async function test_save_telemetry() { // Clear all scalar telemetry. Services.telemetry.clearScalars(); await TrackingDBService.saveEvents(JSON.stringify(LOG)); const scalars = Services.telemetry.getSnapshotForScalars( "main", false ).parent; is(scalars["contentblocking.trackers_blocked_count"], 6); // Use the TrackingDBService API to delete the data. await TrackingDBService.clearAll(); }); // Test that telemetry is sent if entrypoint param is included, // and test that it is recorded as default if entrypoint param is not properly included add_task(async function checkTelemetryLoadEventForEntrypoint() { await SpecialPowers.pushPrefEnv({ set: [ ["browser.contentblocking.database.enabled", false], ["browser.contentblocking.report.monitor.enabled", false], ["browser.contentblocking.report.lockwise.enabled", false], ["browser.contentblocking.report.proxy.enabled", false], ["browser.vpn_promo.enabled", false], ], }); await addArbitraryTimeout(); // Clear everything. Services.telemetry.clearEvents(); await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; return !events || !events.length; }); Services.telemetry.setEventRecordingEnabled("security.ui.protections", true); info("Typo in 'entrypoint' should not be recorded"); let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections?entryPoint=newPage", gBrowser, }); let loadEvents = await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; if (events && events.length) { events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "show" && e[4] == "direct" ); if (events.length == 1) { return events; } } return null; }, "recorded telemetry for showing the report contains default 'direct' entrypoint"); is( loadEvents.length, 1, `recorded telemetry for showing the report contains default 'direct' entrypoint` ); await BrowserTestUtils.removeTab(tab); tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections?entrypoint=page", gBrowser, }); loadEvents = await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; if (events && events.length) { events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "show" && e[4] == "page" ); if (events.length == 1) { return events; } } return null; }, "recorded telemetry for showing the report contains correct entrypoint"); is( loadEvents.length, 1, "recorded telemetry for showing the report contains correct entrypoint" ); // Clean up. await BrowserTestUtils.removeTab(tab); }); // This test is skipping due to failures on try, it passes locally. // Test that telemetry is sent from the vpn card add_task(async function checkTelemetryClickEventsVPN() { if (Services.sysinfo.getProperty("name") != "Windows_NT") { ok(true, "User is on an unsupported platform, the vpn card will not show"); return; } await addArbitraryTimeout(); // Clear everything. Services.telemetry.clearEvents(); await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; return !events || !events.length; }); Services.telemetry.setEventRecordingEnabled("security.ui.protections", true); // user is not subscribed to VPN, and is in the us AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us")); await SpecialPowers.pushPrefEnv({ set: [ ["browser.vpn_promo.enabled", true], [ "browser.vpn_promo.disallowed_regions", "ae,by,cn,cu,iq,ir,kp,om,ru,sd,sy,tm,tr,ua", ], ["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"], ["browser.contentblocking.database.enabled", false], ["browser.contentblocking.report.monitor.enabled", false], ["browser.contentblocking.report.lockwise.enabled", false], ["browser.contentblocking.report.proxy.enabled", false], ["browser.contentblocking.report.hide_vpn_banner", true], ["browser.contentblocking.report.vpn-android.url", ""], ["browser.contentblocking.report.vpn-ios.url", ""], ["browser.contentblocking.report.vpn.url", ""], ], }); Services.locale.availableLocales = ["en-US"]; Services.locale.requestedLocales = ["en-US"]; await promiseSetHomeRegion("US"); let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections", gBrowser, }); info("checking for vpn link"); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const getVPNLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("get-vpn-link"); }, "get vpn link exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(getVPNLink), "get vpn link is visible" ); EventUtils.sendMouseEvent( { type: "click", button: 1 }, getVPNLink, content ); }); let events = await waitForTelemetryEventCount(2); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "vpn_card_link" ); is( events.length, 1, `recorded telemetry for vpn_card_link when user is not subscribed` ); // User is subscribed to VPN AboutProtectionsParent.setTestOverride(getVPNOverrides(true, "us")); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const androidVPNLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("vpn-google-playstore-link"); }, "android vpn link exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(androidVPNLink), "android vpn link is visible" ); await ContentTaskUtils.waitForCondition(() => { return content.document .querySelector(".vpn-card") .classList.contains("subscribed"); }, "subscribed class is added to the vpn card"); EventUtils.sendMouseEvent( { type: "click", button: 1 }, androidVPNLink, content ); }); events = await waitForTelemetryEventCount(5); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "vpn_app_link_android" ); is(events.length, 1, `recorded telemetry for vpn_app_link_android link`); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const iosVPNLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("vpn-app-store-link"); }, "ios vpn link exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(iosVPNLink), "ios vpn link is visible" ); await ContentTaskUtils.waitForCondition(() => { return content.document .querySelector(".vpn-card") .classList.contains("subscribed"); }, "subscribed class is added to the vpn card"); EventUtils.sendMouseEvent( { type: "click", button: 1 }, iosVPNLink, content ); }); events = await waitForTelemetryEventCount(6); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "vpn_app_link_ios" ); is(events.length, 1, `recorded telemetry for vpn_app_link_ios link`); // Clean up. await BrowserTestUtils.removeTab(tab); }).skip(); // This test is skipping due to failures on try, it passes locally. // Test that telemetry is sent from the vpn banner add_task(async function checkTelemetryEventsVPNBanner() { if (Services.sysinfo.getProperty("name") != "Windows_NT") { ok(true, "User is on an unsupported platform, the vpn card will not show"); return; } AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us")); await SpecialPowers.pushPrefEnv({ set: [ ["browser.vpn_promo.enabled", true], ["browser.contentblocking.report.vpn_regions", "us,ca,nz,sg,my,gb"], [ "browser.vpn_promo.disallowed_regions", "ae,by,cn,cu,iq,ir,kp,om,ru,sd,sy,tm,tr,ua", ], ["browser.contentblocking.database.enabled", false], ["browser.contentblocking.report.monitor.enabled", false], ["browser.contentblocking.report.lockwise.enabled", false], ["browser.contentblocking.report.proxy.enabled", false], ["browser.contentblocking.report.hide_vpn_banner", false], ["browser.contentblocking.report.vpn-promo.url", ""], ], }); await addArbitraryTimeout(); // The VPN banner only shows if the user is in en* Services.locale.availableLocales = ["en-US"]; Services.locale.requestedLocales = ["en-US"]; // Clear everything. Services.telemetry.clearEvents(); await TestUtils.waitForCondition(() => { let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true ).content; return !events || !events.length; }); Services.telemetry.setEventRecordingEnabled("security.ui.protections", true); // User is not subscribed to VPN AboutProtectionsParent.setTestOverride(getVPNOverrides(false, "us")); let tab = await BrowserTestUtils.openNewForegroundTab({ url: "about:protections", gBrowser, }); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const bannerVPNLink = await ContentTaskUtils.waitForCondition(() => { return content.document.getElementById("vpn-banner-link"); }, "vpn banner link exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(bannerVPNLink), "vpn banner link is visible" ); EventUtils.sendMouseEvent( { type: "click", button: 1 }, bannerVPNLink, content ); }); let events = await waitForTelemetryEventCount(3); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "vpn_banner_link" ); is(events.length, 1, `recorded telemetry for vpn_banner_link`); // VPN Banner flips this pref each time it shows, flip back between each instruction. await SpecialPowers.pushPrefEnv({ set: [["browser.contentblocking.report.hide_vpn_banner", false]], }); await BrowserTestUtils.reloadTab(tab); await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { const bannerExitLink = await ContentTaskUtils.waitForCondition(() => { return content.document.querySelector(".vpn-banner .exit-icon"); }, "vpn banner exit link exists"); await ContentTaskUtils.waitForCondition( () => ContentTaskUtils.is_visible(bannerExitLink), "vpn banner exit link is visible" ); EventUtils.sendMouseEvent( { type: "click", button: 1 }, bannerExitLink, content ); }); events = await waitForTelemetryEventCount(7); events = events.filter( e => e[1] == "security.ui.protections" && e[2] == "click" && e[3] == "vpn_banner_close" ); is(events.length, 1, `recorded telemetry for vpn_banner_close`); // Clean up. await BrowserTestUtils.removeTab(tab); }).skip();