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_protections_report_ui.js | 845 +++++++++++++++++++++ 1 file changed, 845 insertions(+) create mode 100644 browser/components/protections/test/browser/browser_protections_report_ui.js (limited to 'browser/components/protections/test/browser/browser_protections_report_ui.js') diff --git a/browser/components/protections/test/browser/browser_protections_report_ui.js b/browser/components/protections/test/browser/browser_protections_report_ui.js new file mode 100644 index 0000000000..6f8d48d6dd --- /dev/null +++ b/browser/components/protections/test/browser/browser_protections_report_ui.js @@ -0,0 +1,845 @@ +/* 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/. */ + +// Note: This test may cause intermittents if run at exactly midnight. + +const { Sqlite } = ChromeUtils.importESModule( + "resource://gre/modules/Sqlite.sys.mjs" +); +const { AboutProtectionsParent } = ChromeUtils.importESModule( + "resource:///actors/AboutProtectionsParent.sys.mjs" +); + +XPCOMUtils.defineLazyServiceGetter( + this, + "TrackingDBService", + "@mozilla.org/tracking-db-service;1", + "nsITrackingDBService" +); + +XPCOMUtils.defineLazyGetter(this, "DB_PATH", function () { + return PathUtils.join(PathUtils.profileDir, "protections.sqlite"); +}); + +const SQL = { + insertCustomTimeEvent: + "INSERT INTO events (type, count, timestamp)" + + "VALUES (:type, :count, date(:timestamp));", + + selectAll: "SELECT * FROM events", +}; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.contentblocking.database.enabled", true], + ["browser.vpn_promo.enabled", false], + ], + }); +}); + +add_task(async function test_graph_display() { + // This creates the schema. + await TrackingDBService.saveEvents(JSON.stringify({})); + let db = await Sqlite.openConnection({ path: DB_PATH }); + + let date = new Date().toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 1, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.FINGERPRINTERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKING_COOKIES_ID, + count: 4, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.SOCIAL_ID, + count: 1, + timestamp: date, + }); + + date = new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 4, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: 3, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.FINGERPRINTERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.SOCIAL_ID, + count: 1, + timestamp: date, + }); + + date = new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 4, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: 3, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKING_COOKIES_ID, + count: 1, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.SOCIAL_ID, + count: 1, + timestamp: date, + }); + + date = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 3, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.FINGERPRINTERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKING_COOKIES_ID, + count: 1, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.SOCIAL_ID, + count: 1, + timestamp: date, + }); + + date = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.FINGERPRINTERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKING_COOKIES_ID, + count: 1, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.SOCIAL_ID, + count: 1, + timestamp: date, + }); + + date = new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 3, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.CRYPTOMINERS_ID, + count: 3, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.FINGERPRINTERS_ID, + count: 2, + timestamp: date, + }); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKING_COOKIES_ID, + count: 8, + timestamp: date, + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + const DATA_TYPES = [ + "cryptominer", + "fingerprinter", + "tracker", + "cookie", + "social", + ]; + let allBars = null; + await ContentTaskUtils.waitForCondition(() => { + allBars = content.document.querySelectorAll(".graph-bar"); + return allBars.length; + }, "The graph has been built"); + + Assert.equal(allBars.length, 7, "7 bars have been found on the graph"); + + // For accessibility, test if the graph is a table + // and has a correct column count (number of data types + total + day) + Assert.equal( + content.document.getElementById("graph").getAttribute("role"), + "table", + "Graph is an accessible table" + ); + Assert.equal( + content.document.getElementById("graph").getAttribute("aria-colcount"), + DATA_TYPES.length + 2, + "Table has the right number of columns" + ); + Assert.equal( + content.document.getElementById("graph").getAttribute("aria-labelledby"), + "graphLegendDescription", + "Table has an accessible label" + ); + + // today has each type + // yesterday will have no tracking cookies + // 2 days ago will have no fingerprinters + // 3 days ago will have no cryptominers + // 4 days ago will have no trackers + // 5 days ago will have no social (when we add social) + // 6 days ago will be empty + Assert.equal( + allBars[6].querySelectorAll(".inner-bar").length, + DATA_TYPES.length, + "today has all of the data types shown" + ); + Assert.equal( + allBars[6].getAttribute("role"), + "row", + "Today has the correct role" + ); + Assert.equal( + allBars[6].getAttribute("aria-owns"), + "day0 count0 cryptominer0 fingerprinter0 tracker0 cookie0 social0", + "Row has the columns in the right order" + ); + Assert.equal( + allBars[6].querySelector(".tracker-bar").style.height, + "10%", + "trackers take 10%" + ); + Assert.equal( + allBars[6].querySelector(".tracker-bar").parentNode.getAttribute("role"), + "cell", + "Trackers have the correct role" + ); + Assert.equal( + allBars[6].querySelector(".tracker-bar").getAttribute("role"), + "img", + "Tracker bar has the correct image role" + ); + Assert.equal( + allBars[6].querySelector(".tracker-bar").getAttribute("aria-label"), + "1 tracking content (10%)", + "Trackers have the correct accessible text" + ); + Assert.equal( + allBars[6].querySelector(".cryptominer-bar").style.height, + "20%", + "cryptominers take 20%" + ); + Assert.equal( + allBars[6] + .querySelector(".cryptominer-bar") + .parentNode.getAttribute("role"), + "cell", + "Cryptominers have the correct role" + ); + Assert.equal( + allBars[6].querySelector(".cryptominer-bar").getAttribute("role"), + "img", + "Cryptominer bar has the correct image role" + ); + Assert.equal( + allBars[6].querySelector(".cryptominer-bar").getAttribute("aria-label"), + "2 cryptominers (20%)", + "Cryptominers have the correct accessible label" + ); + Assert.equal( + allBars[6].querySelector(".fingerprinter-bar").style.height, + "20%", + "fingerprinters take 20%" + ); + Assert.equal( + allBars[6] + .querySelector(".fingerprinter-bar") + .parentNode.getAttribute("role"), + "cell", + "Fingerprinters have the correct role" + ); + Assert.equal( + allBars[6].querySelector(".fingerprinter-bar").getAttribute("role"), + "img", + "Fingerprinter bar has the correct image role" + ); + Assert.equal( + allBars[6].querySelector(".fingerprinter-bar").getAttribute("aria-label"), + "2 fingerprinters (20%)", + "Fingerprinters have the correct accessible label" + ); + Assert.equal( + allBars[6].querySelector(".cookie-bar").style.height, + "40%", + "cross site tracking cookies take 40%" + ); + Assert.equal( + allBars[6].querySelector(".cookie-bar").parentNode.getAttribute("role"), + "cell", + "cross site tracking cookies have the correct role" + ); + Assert.equal( + allBars[6].querySelector(".cookie-bar").getAttribute("role"), + "img", + "Cross site tracking cookies bar has the correct image role" + ); + Assert.equal( + allBars[6].querySelector(".cookie-bar").getAttribute("aria-label"), + "4 cross-site tracking cookies (40%)", + "cross site tracking cookies have the correct accessible label" + ); + Assert.equal( + allBars[6].querySelector(".social-bar").style.height, + "10%", + "social trackers take 10%" + ); + Assert.equal( + allBars[6].querySelector(".social-bar").parentNode.getAttribute("role"), + "cell", + "social trackers have the correct role" + ); + Assert.equal( + allBars[6].querySelector(".social-bar").getAttribute("role"), + "img", + "social tracker bar has the correct image role" + ); + Assert.equal( + allBars[6].querySelector(".social-bar").getAttribute("aria-label"), + "1 social media tracker (10%)", + "social trackers have the correct accessible text" + ); + + Assert.equal( + allBars[5].querySelectorAll(".inner-bar").length, + DATA_TYPES.length - 1, + "1 day ago is missing one type" + ); + Assert.ok( + !allBars[5].querySelector(".cookie-bar"), + "there is no cross site tracking cookie section 1 day ago." + ); + Assert.equal( + allBars[5].getAttribute("aria-owns"), + "day1 count1 cryptominer1 fingerprinter1 tracker1 social1", + "Row has the columns in the right order" + ); + + Assert.equal( + allBars[4].querySelectorAll(".inner-bar").length, + DATA_TYPES.length - 1, + "2 days ago is missing one type" + ); + Assert.ok( + !allBars[4].querySelector(".fingerprinter-bar"), + "there is no fingerprinter section 1 day ago." + ); + Assert.equal( + allBars[4].getAttribute("aria-owns"), + "day2 count2 cryptominer2 tracker2 cookie2 social2", + "Row has the columns in the right order" + ); + + Assert.equal( + allBars[3].querySelectorAll(".inner-bar").length, + DATA_TYPES.length - 1, + "3 days ago is missing one type" + ); + Assert.ok( + !allBars[3].querySelector(".cryptominer-bar"), + "there is no cryptominer section 1 day ago." + ); + Assert.equal( + allBars[3].getAttribute("aria-owns"), + "day3 count3 fingerprinter3 tracker3 cookie3 social3", + "Row has the columns in the right order" + ); + + Assert.equal( + allBars[2].querySelectorAll(".inner-bar").length, + DATA_TYPES.length - 1, + "4 days ago is missing one type" + ); + Assert.ok( + !allBars[2].querySelector(".tracker-bar"), + "there is no tracker section 1 day ago." + ); + Assert.equal( + allBars[2].getAttribute("aria-owns"), + "day4 count4 cryptominer4 fingerprinter4 cookie4 social4", + "Row has the columns in the right order" + ); + + Assert.equal( + allBars[1].querySelectorAll(".inner-bar").length, + DATA_TYPES.length - 1, + "5 days ago is missing one type" + ); + Assert.ok( + !allBars[1].querySelector(".social-bar"), + "there is no social section 1 day ago." + ); + Assert.equal( + allBars[1].getAttribute("aria-owns"), + "day5 count5 cryptominer5 fingerprinter5 tracker5 cookie5", + "Row has the columns in the right order" + ); + + Assert.equal( + allBars[0].querySelectorAll(".inner-bar").length, + 0, + "6 days ago has no content" + ); + Assert.ok( + allBars[0].classList.contains("empty"), + "6 days ago is an empty bar" + ); + Assert.equal( + allBars[0].getAttribute("aria-owns"), + "day6 ", + "Row has the columns in the right order" + ); + + // Check that each tab has the correct aria-labelledby and aria-describedby + // values. This helps screen readers know what type of tracker the reported + // tab number is referencing. + const socialTab = content.document.getElementById("tab-social"); + Assert.equal( + socialTab.getAttribute("aria-labelledby"), + "socialLabel socialTitle", + "aria-labelledby attribute is socialLabel socialTitle" + ); + Assert.equal( + socialTab.getAttribute("aria-describedby"), + "socialContent", + "aria-describedby attribute is socialContent" + ); + + const cookieTab = content.document.getElementById("tab-cookie"); + Assert.equal( + cookieTab.getAttribute("aria-labelledby"), + "cookieLabel cookieTitle", + "aria-labelledby attribute is cookieLabel cookieTitle" + ); + Assert.equal( + cookieTab.getAttribute("aria-describedby"), + "cookieContent", + "aria-describedby attribute is cookieContent" + ); + + const trackerTab = content.document.getElementById("tab-tracker"); + Assert.equal( + trackerTab.getAttribute("aria-labelledby"), + "trackerLabel trackerTitle", + "aria-labelledby attribute is trackerLabel trackerTitle" + ); + Assert.equal( + trackerTab.getAttribute("aria-describedby"), + "trackerContent", + "aria-describedby attribute is trackerContent" + ); + + const fingerprinterTab = + content.document.getElementById("tab-fingerprinter"); + Assert.equal( + fingerprinterTab.getAttribute("aria-labelledby"), + "fingerprinterLabel fingerprinterTitle", + "aria-labelledby attribute is fingerprinterLabel fingerprinterTitle" + ); + Assert.equal( + fingerprinterTab.getAttribute("aria-describedby"), + "fingerprinterContent", + "aria-describedby attribute is fingerprinterContent" + ); + + const cryptominerTab = content.document.getElementById("tab-cryptominer"); + Assert.equal( + cryptominerTab.getAttribute("aria-labelledby"), + "cryptominerLabel cryptominerTitle", + "aria-labelledby attribute is cryptominerLabel cryptominerTitle" + ); + Assert.equal( + cryptominerTab.getAttribute("aria-describedby"), + "cryptominerContent", + "aria-describedby attribute is cryptominerContent" + ); + }); + + // Use the TrackingDBService API to delete the data. + await TrackingDBService.clearAll(); + // Make sure the data was deleted. + let rows = await db.execute(SQL.selectAll); + is(rows.length, 0, "length is 0"); + await db.close(); + BrowserTestUtils.removeTab(tab); +}); + +// Ensure that each type of tracker is hidden from the graph if there are no recorded +// trackers of that type and the user has chosen to not block that type. +add_task(async function test_etp_custom_settings() { + Services.prefs.setStringPref("browser.contentblocking.category", "strict"); + Services.prefs.setBoolPref( + "privacy.socialtracking.block_cookies.enabled", + true + ); + // hide cookies from the graph + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let legend = content.document.getElementById("legend"); + return ContentTaskUtils.is_visible(legend); + }, "The legend is visible"); + + let label = content.document.getElementById("cookieLabel"); + Assert.ok(ContentTaskUtils.is_hidden(label), "Cookie Label is hidden"); + + label = content.document.getElementById("trackerLabel"); + Assert.ok(ContentTaskUtils.is_visible(label), "Tracker Label is visible"); + label = content.document.getElementById("socialLabel"); + Assert.ok(ContentTaskUtils.is_visible(label), "Social Label is visible"); + label = content.document.getElementById("cryptominerLabel"); + Assert.ok( + ContentTaskUtils.is_visible(label), + "Cryptominer Label is visible" + ); + label = content.document.getElementById("fingerprinterLabel"); + Assert.ok( + ContentTaskUtils.is_visible(label), + "Fingerprinter Label is visible" + ); + }); + BrowserTestUtils.removeTab(tab); + + // hide ad trackers from the graph + Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let legend = content.document.getElementById("legend"); + return ContentTaskUtils.is_visible(legend); + }, "The legend is visible"); + + let label = content.document.querySelector("#trackerLabel"); + Assert.ok(ContentTaskUtils.is_hidden(label), "Tracker Label is hidden"); + + label = content.document.querySelector("#socialLabel"); + Assert.ok(ContentTaskUtils.is_hidden(label), "Social Label is hidden"); + }); + BrowserTestUtils.removeTab(tab); + + // hide social from the graph + Services.prefs.setBoolPref( + "privacy.trackingprotection.socialtracking.enabled", + false + ); + Services.prefs.setBoolPref( + "privacy.socialtracking.block_cookies.enabled", + false + ); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let legend = content.document.getElementById("legend"); + return ContentTaskUtils.is_visible(legend); + }, "The legend is visible"); + + let label = content.document.querySelector("#socialLabel"); + Assert.ok(ContentTaskUtils.is_hidden(label), "Social Label is hidden"); + }); + BrowserTestUtils.removeTab(tab); + + // hide fingerprinting from the graph + Services.prefs.setBoolPref( + "privacy.trackingprotection.fingerprinting.enabled", + false + ); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let legend = content.document.getElementById("legend"); + return ContentTaskUtils.is_visible(legend); + }, "The legend is visible"); + + let label = content.document.querySelector("#fingerprinterLabel"); + Assert.ok( + ContentTaskUtils.is_hidden(label), + "Fingerprinter Label is hidden" + ); + }); + BrowserTestUtils.removeTab(tab); + + // hide cryptomining from the graph + Services.prefs.setBoolPref( + "privacy.trackingprotection.cryptomining.enabled", + false + ); + // Turn fingerprinting on so that all protectionsare not turned off, otherwise we will get a special card. + Services.prefs.setBoolPref( + "privacy.trackingprotection.fingerprinting.enabled", + true + ); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let legend = content.document.getElementById("legend"); + return ContentTaskUtils.is_visible(legend); + }, "The legend is visible"); + + let label = content.document.querySelector("#cryptominerLabel"); + Assert.ok(ContentTaskUtils.is_hidden(label), "Cryptominer Label is hidden"); + }); + Services.prefs.clearUserPref("browser.contentblocking.category"); + Services.prefs.clearUserPref( + "privacy.trackingprotection.fingerprinting.enabled" + ); + Services.prefs.clearUserPref( + "privacy.trackingprotection.cryptomining.enabled" + ); + Services.prefs.clearUserPref("privacy.trackingprotection.enabled"); + Services.prefs.clearUserPref("network.cookie.cookieBehavior"); + Services.prefs.clearUserPref("privacy.socialtracking.block_cookies.enabled"); + + BrowserTestUtils.removeTab(tab); +}); + +// Ensure that the Custom manage Protections card is shown if the user has all protections turned off. +add_task(async function test_etp_custom_protections_off() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.contentblocking.category", "custom"], + ["network.cookie.cookieBehavior", 0], // not blocking + ["privacy.trackingprotection.cryptomining.enabled", false], // not blocking + ["privacy.trackingprotection.fingerprinting.enabled", false], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.socialtracking.enabled", false], + ["privacy.socialtracking.block_cookies.enabled", false], + ], + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + + let aboutPreferencesPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + "about:preferences#privacy" + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let etpCard = content.document.querySelector(".etp-card"); + return etpCard.classList.contains("custom-not-blocking"); + }, "The custom protections warning card is showing"); + + let manageProtectionsButton = + content.document.getElementById("manage-protections"); + Assert.ok( + ContentTaskUtils.is_visible(manageProtectionsButton), + "Button to manage protections is displayed" + ); + }); + + // Custom protection card should show, even if there would otherwise be data on the graph. + let db = await Sqlite.openConnection({ path: DB_PATH }); + let date = new Date().toISOString(); + await db.execute(SQL.insertCustomTimeEvent, { + type: TrackingDBService.TRACKERS_ID, + count: 1, + timestamp: date, + }); + await BrowserTestUtils.reloadTab(tab); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + await ContentTaskUtils.waitForCondition(() => { + let etpCard = content.document.querySelector(".etp-card"); + return etpCard.classList.contains("custom-not-blocking"); + }, "The custom protections warning card is showing"); + + let manageProtectionsButton = + content.document.getElementById("manage-protections"); + Assert.ok( + ContentTaskUtils.is_visible(manageProtectionsButton), + "Button to manage protections is displayed" + ); + + manageProtectionsButton.click(); + }); + let aboutPreferencesTab = await aboutPreferencesPromise; + info("about:preferences#privacy was successfully opened in a new tab"); + gBrowser.removeTab(aboutPreferencesTab); + + Services.prefs.setStringPref("browser.contentblocking.category", "standard"); + // Use the TrackingDBService API to delete the data. + await TrackingDBService.clearAll(); + // Make sure the data was deleted. + let rows = await db.execute(SQL.selectAll); + is(rows.length, 0, "length is 0"); + await db.close(); + BrowserTestUtils.removeTab(tab); +}); + +// Ensure that the ETP mobile promotion card is shown when the pref is on and +// there are no mobile devices connected. +add_task(async function test_etp_mobile_promotion_pref_on() { + AboutProtectionsParent.setTestOverride(mockGetLoginDataWithSyncedDevices()); + await SpecialPowers.pushPrefEnv({ + set: [["browser.contentblocking.report.show_mobile_app", true]], + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + let mobilePromotion = content.document.getElementById("mobile-hanger"); + Assert.ok( + ContentTaskUtils.is_visible(mobilePromotion), + "Mobile promotions card is displayed when pref is on and there are no synced mobile devices" + ); + + // Card should hide after the X is clicked. + mobilePromotion.querySelector(".exit-icon").click(); + Assert.ok( + ContentTaskUtils.is_hidden(mobilePromotion), + "Mobile promotions card is no longer displayed after clicking the X button" + ); + }); + BrowserTestUtils.removeTab(tab); + + // Add a mock mobile device. The promotion should now be hidden. + AboutProtectionsParent.setTestOverride( + mockGetLoginDataWithSyncedDevices(true) + ); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + let mobilePromotion = content.document.getElementById("mobile-hanger"); + Assert.ok( + ContentTaskUtils.is_hidden(mobilePromotion), + "Mobile promotions card is hidden when pref is on if there are synced mobile devices" + ); + }); + + BrowserTestUtils.removeTab(tab); + AboutProtectionsParent.setTestOverride(null); +}); + +// Test that ETP mobile promotion is not shown when the pref is off, +// even if no mobile devices are synced. +add_task(async function test_etp_mobile_promotion_pref_on() { + AboutProtectionsParent.setTestOverride(mockGetLoginDataWithSyncedDevices()); + await SpecialPowers.pushPrefEnv({ + set: [["browser.contentblocking.report.show_mobile_app", false]], + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + let mobilePromotion = content.document.getElementById("mobile-hanger"); + Assert.ok( + ContentTaskUtils.is_hidden(mobilePromotion), + "Mobile promotions card is not displayed when pref is off and there are no synced mobile devices" + ); + }); + + BrowserTestUtils.removeTab(tab); + + AboutProtectionsParent.setTestOverride( + mockGetLoginDataWithSyncedDevices(true) + ); + tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + let mobilePromotion = content.document.getElementById("mobile-hanger"); + Assert.ok( + ContentTaskUtils.is_hidden(mobilePromotion), + "Mobile promotions card is not displayed when pref is off even if there are synced mobile devices" + ); + }); + BrowserTestUtils.removeTab(tab); + AboutProtectionsParent.setTestOverride(null); +}); + +// Test that clicking on the link to settings in the header properly opens the settings page. +add_task(async function test_settings_links() { + let tab = await BrowserTestUtils.openNewForegroundTab({ + url: "about:protections", + gBrowser, + }); + let aboutPreferencesPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + "about:preferences#privacy" + ); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + const protectionSettings = await ContentTaskUtils.waitForCondition(() => { + return content.document.getElementById("protection-settings"); + }, "protection-settings link exists"); + + protectionSettings.click(); + }); + let aboutPreferencesTab = await aboutPreferencesPromise; + info("about:preferences#privacy was successfully opened in a new tab"); + gBrowser.removeTab(aboutPreferencesTab); + gBrowser.removeTab(tab); +}); -- cgit v1.2.3