summaryrefslogtreecommitdiffstats
path: root/browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js')
-rw-r--r--browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js329
1 files changed, 329 insertions, 0 deletions
diff --git a/browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js b/browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js
new file mode 100644
index 0000000000..5f2afcf6fc
--- /dev/null
+++ b/browser/components/search/test/browser/telemetry/browser_search_telemetry_remote_settings_sync.js
@@ -0,0 +1,329 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * When Remote Settings receives an update to search-telemetry-v2, we should
+ * trigger an update within SearchSERPTelemetry and SearchSERPTelemetryChild
+ * without requiring a user to restart their browser.
+ */
+
+requestLongerTimeout(5);
+
+ChromeUtils.defineESModuleGetters(this, {
+ ADLINK_CHECK_TIMEOUT_MS: "resource:///modules/SearchSERPTelemetry.sys.mjs",
+ RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
+ SEARCH_TELEMETRY_SHARED: "resource:///modules/SearchSERPTelemetry.sys.mjs",
+ SearchSERPCategorization: "resource:///modules/SearchSERPTelemetry.sys.mjs",
+ SearchSERPDomainToCategoriesMap:
+ "resource:///modules/SearchSERPTelemetry.sys.mjs",
+ SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
+ TELEMETRY_SETTINGS_KEY: "resource:///modules/SearchSERPTelemetry.sys.mjs",
+});
+
+const TEST_PROVIDER_INFO = [
+ {
+ telemetryId: "example",
+ searchPageRegexp:
+ /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/telemetry\/searchTelemetry/,
+ queryParamNames: ["s"],
+ codeParamName: "abc",
+ taggedCodes: ["ff"],
+ adServerAttributes: ["mozAttr"],
+ nonAdsLinkRegexps: [],
+ extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
+ components: [
+ {
+ type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ default: true,
+ },
+ ],
+ },
+];
+
+const TEST_PROVIDER_BROKEN_VARIANT = [
+ {
+ ...TEST_PROVIDER_INFO[0],
+ queryParamNames: ["foo"],
+ },
+];
+
+const RECORDS = {
+ current: TEST_PROVIDER_INFO,
+ created: [],
+ updated: TEST_PROVIDER_INFO,
+ deleted: [],
+};
+
+const BROKEN_VARIANT_RECORDS = {
+ current: TEST_PROVIDER_BROKEN_VARIANT,
+ created: [],
+ updated: TEST_PROVIDER_BROKEN_VARIANT,
+ deleted: [],
+};
+
+const client = RemoteSettings(TELEMETRY_SETTINGS_KEY);
+const db = client.db;
+let record = TEST_PROVIDER_INFO[0];
+
+async function updateClientWithRecords(records) {
+ let promise = TestUtils.topicObserved("search-telemetry-v2-synced");
+
+ await client.emit("sync", { data: records });
+
+ info("Wait for SearchSERPTelemetry to update.");
+ await promise;
+}
+
+add_setup(async function () {
+ // Initialize the test with a variant of telemetry that won't trigger an
+ // impression due to an odd query param name.
+ SearchSERPTelemetry.overrideSearchTelemetryForTests(
+ TEST_PROVIDER_BROKEN_VARIANT
+ );
+ await waitForIdle();
+ // Enable local telemetry recording for the duration of the tests.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.search.serpEventTelemetry.enabled", true],
+ // Set the IPC count to a small number so that we only have to open
+ // one additional tab to reuse the same process.
+ ["dom.ipc.processCount.webIsolated", 1],
+ ],
+ });
+
+ // Shorten the time it takes to examine pages for ads.
+ Services.ppmm.sharedData.set(SEARCH_TELEMETRY_SHARED.LOAD_TIMEOUT, 500);
+ Services.ppmm.sharedData.flush();
+
+ registerCleanupFunction(async () => {
+ SearchSERPTelemetry.overrideSearchTelemetryForTests();
+ resetTelemetry();
+ await db.clear();
+ await SpecialPowers.popPrefEnv();
+ Services.ppmm.sharedData.set(
+ SEARCH_TELEMETRY_SHARED.LOAD_TIMEOUT,
+ ADLINK_CHECK_TIMEOUT_MS
+ );
+ Services.ppmm.sharedData.flush();
+ });
+});
+
+add_task(async function update_telemetry_tab_already_open() {
+ info("Load SERP in a new tab.");
+ let url = getSERPUrl("searchTelemetryAd.html");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+
+ info("Wait a brief amount of time for a possible SERP impression.");
+ await waitForIdle();
+
+ info("Assert no impressions are found.");
+ assertSERPTelemetry([]);
+
+ info("Update search-telemetry-v2 with a matching queryParamName.");
+ await updateClientWithRecords(RECORDS);
+
+ info("Reload page.");
+ gBrowser.reload();
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await waitForPageWithAdImpressions();
+
+ assertSERPTelemetry([
+ {
+ impression: {
+ provider: "example",
+ tagged: "true",
+ partner_code: "ff",
+ source: "reload",
+ is_shopping_page: "false",
+ is_private: "false",
+ shopping_tab_displayed: "false",
+ },
+ adImpressions: [
+ {
+ component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ ads_loaded: "2",
+ ads_visible: "2",
+ ads_hidden: "0",
+ },
+ ],
+ },
+ ]);
+
+ // Change search-telemetry-v2 back to the broken variant so that the next
+ // test can check updating the collection while no tabs are open results
+ // in a SERP check.
+ info("Update search-telemetry-v2 with non-matching queryParamName.");
+ await updateClientWithRecords(BROKEN_VARIANT_RECORDS);
+
+ info("Remove tab and reset telemetry.");
+ await BrowserTestUtils.removeTab(tab);
+ resetTelemetry();
+});
+
+add_task(async function update_telemetry_tab_closed() {
+ info("Load SERP in a new tab.");
+ let url = getSERPUrl("searchTelemetryAd.html");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+
+ info("Wait a brief amount of time for a possible SERP impression.");
+ await waitForIdle();
+
+ info("Assert no impressions are found.");
+ assertSERPTelemetry([]);
+
+ info("Remove tab.");
+ await BrowserTestUtils.removeTab(tab);
+
+ info("Update search-telemetry-v2 with a matching queryParamName.");
+ await updateClientWithRecords(RECORDS);
+
+ info("Load SERP in a new tab.");
+ tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ await waitForPageWithAdImpressions();
+ assertSERPTelemetry([
+ {
+ impression: {
+ provider: "example",
+ tagged: "true",
+ partner_code: "ff",
+ source: "unknown",
+ is_shopping_page: "false",
+ is_private: "false",
+ shopping_tab_displayed: "false",
+ },
+ adImpressions: [
+ {
+ component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ ads_loaded: "2",
+ ads_visible: "2",
+ ads_hidden: "0",
+ },
+ ],
+ },
+ ]);
+
+ info("Update search-telemetry-v2 with non-matching queryParamName.");
+ await updateClientWithRecords(BROKEN_VARIANT_RECORDS);
+
+ info("Remove tab and reset telemetry.");
+ await BrowserTestUtils.removeTab(tab);
+ resetTelemetry();
+});
+
+add_task(async function update_telemetry_multiple_tabs() {
+ info("Load SERP in a new tab.");
+ let url = getSERPUrl("searchTelemetryAd.html");
+
+ let tabs = [];
+ for (let index = 0; index < 5; ++index) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ tabs.push(tab);
+ }
+
+ info("Wait a brief amount of time for a possible SERP impression.");
+ await waitForIdle();
+
+ info("Assert no impressions are found.");
+ assertSERPTelemetry([]);
+
+ info("Update search-telemetry-v2 with a matching queryParamName.");
+ await updateClientWithRecords(RECORDS);
+
+ for (let tab of tabs) {
+ await BrowserTestUtils.switchTab(gBrowser, tab);
+ gBrowser.reload();
+ await waitForPageWithAdImpressions();
+
+ assertSERPTelemetry([
+ {
+ impression: {
+ provider: "example",
+ tagged: "true",
+ partner_code: "ff",
+ source: "reload",
+ is_shopping_page: "false",
+ is_private: "false",
+ shopping_tab_displayed: "false",
+ },
+ adImpressions: [
+ {
+ component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ ads_loaded: "2",
+ ads_visible: "2",
+ ads_hidden: "0",
+ },
+ ],
+ },
+ ]);
+ await BrowserTestUtils.removeTab(tab);
+ resetTelemetry();
+ }
+
+ info("Update search-telemetry-v2 with non-matching queryParamName.");
+ await updateClientWithRecords(BROKEN_VARIANT_RECORDS);
+});
+
+add_task(async function update_telemetry_multiple_processes_and_tabs() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ // Set the IPC count to a higher number to allow for multiple processes
+ // for the same domain to be available.
+ ["dom.ipc.processCount.webIsolated", 4],
+ ],
+ });
+
+ info("Load SERP in a new tab.");
+ let url = getSERPUrl("searchTelemetryAd.html");
+
+ let tabs = [];
+ for (let index = 0; index < 8; ++index) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ tabs.push(tab);
+ }
+
+ info("Wait a brief amount of time for a possible SERP impression.");
+ await waitForIdle();
+
+ info("Assert no impressions are found.");
+ assertSERPTelemetry([]);
+
+ info("Update search-telemetry-v2 with a matching queryParamName.");
+ await updateClientWithRecords(RECORDS);
+
+ for (let tab of tabs) {
+ await BrowserTestUtils.switchTab(gBrowser, tab);
+ gBrowser.reload();
+ await waitForPageWithAdImpressions();
+
+ assertSERPTelemetry([
+ {
+ impression: {
+ provider: "example",
+ tagged: "true",
+ partner_code: "ff",
+ source: "reload",
+ is_shopping_page: "false",
+ is_private: "false",
+ shopping_tab_displayed: "false",
+ },
+ adImpressions: [
+ {
+ component: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ ads_loaded: "2",
+ ads_visible: "2",
+ ads_hidden: "0",
+ },
+ ],
+ },
+ ]);
+
+ await BrowserTestUtils.removeTab(tab);
+ resetTelemetry();
+ }
+
+ info("Update search-telemetry-v2 with non-matching queryParamName.");
+ await updateClientWithRecords(BROKEN_VARIANT_RECORDS);
+ await SpecialPowers.popPrefEnv();
+});