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 --- ...er_search_telemetry_engagement_multiple_tabs.js | 225 +++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 browser/components/search/test/browser/browser_search_telemetry_engagement_multiple_tabs.js (limited to 'browser/components/search/test/browser/browser_search_telemetry_engagement_multiple_tabs.js') diff --git a/browser/components/search/test/browser/browser_search_telemetry_engagement_multiple_tabs.js b/browser/components/search/test/browser/browser_search_telemetry_engagement_multiple_tabs.js new file mode 100644 index 0000000000..8010434b88 --- /dev/null +++ b/browser/components/search/test/browser/browser_search_telemetry_engagement_multiple_tabs.js @@ -0,0 +1,225 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * This test ensures that recorded telemetry is consistent even with multiple + * tabs opened and closed. + */ + +"use strict"; + +const { SearchSERPTelemetry, SearchSERPTelemetryUtils } = + ChromeUtils.importESModule("resource:///modules/SearchSERPTelemetry.sys.mjs"); + +const TEST_PROVIDER_INFO = [ + { + telemetryId: "example", + searchPageRegexp: + /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_/, + queryParamName: "s", + codeParamName: "abc", + taggedCodes: ["ff"], + adServerAttributes: ["mozAttr"], + nonAdsLinkRegexps: [ + /^https:\/\/example.org\/browser\/browser\/components\/search\/test\/browser\/searchTelemetryAd_nonAdsLink_redirect/, + ], + extraAdServersRegexps: [/^https:\/\/example\.com\/ad/], + components: [ + { + type: SearchSERPTelemetryUtils.COMPONENTS.INCONTENT_SEARCHBOX, + included: { + parent: { + selector: "form", + }, + children: [ + { + // This isn't contained in any of the HTML examples but the + // presence of the entry ensures that if it is not found during + // a topDown examination, the next element in the array is + // inspected and found. + selector: "textarea", + }, + { + selector: "input", + }, + ], + related: { + selector: "div", + }, + }, + topDown: true, + }, + { + type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK, + default: true, + }, + ], + }, +]; + +// Deliberately make the web isolated process count as small as possible +// so that we don't have to create a ton of tabs to reuse a process. +const MAX_IPC = 1; +const TABS_TO_OPEN = 2; + +function getSERPUrl(page, term) { + let url = + getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.org" + ) + page; + return `${url}?s=${term}`; +} + +async function waitForIdle() { + for (let i = 0; i < 10; i++) { + await new Promise(resolve => Services.tm.idleDispatchToMainThread(resolve)); + } +} + +add_setup(async function () { + SearchSERPTelemetry.overrideSearchTelemetryForTests(TEST_PROVIDER_INFO); + await waitForIdle(); + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.search.log", true], + ["browser.search.serpEventTelemetry.enabled", true], + ["dom.ipc.processCount.webIsolated", MAX_IPC], + ], + }); + + registerCleanupFunction(async () => { + SearchSERPTelemetry.overrideSearchTelemetryForTests(); + resetTelemetry(); + }); +}); + +async function do_test(tab, impressionId, switchTab) { + if (switchTab) { + await BrowserTestUtils.switchTab(gBrowser, tab); + } + await BrowserTestUtils.synthesizeMouseAtCenter( + "input", + {}, + tab.linkedBrowser + ); + + await BrowserTestUtils.synthesizeMouseAtCenter( + "a#images", + {}, + tab.linkedBrowser + ); + + await BrowserTestUtils.browserLoaded(tab.linkedBrowser, true); + + await Services.fog.testFlushAllChildren(); + let engagements = Glean.serp.engagement.testGetValue() ?? []; + Assert.equal(engagements.length, 2, "Should have two events recorded."); + + Assert.deepEqual( + engagements[0].extra, + { + action: SearchSERPTelemetryUtils.ACTIONS.CLICKED, + target: SearchSERPTelemetryUtils.COMPONENTS.INCONTENT_SEARCHBOX, + impression_id: impressionId, + }, + "Search box engagement event should match." + ); + Assert.deepEqual( + engagements[1].extra, + { + action: SearchSERPTelemetryUtils.ACTIONS.CLICKED, + target: SearchSERPTelemetryUtils.COMPONENTS.NON_ADS_LINK, + impression_id: impressionId, + }, + "Non ads page engagement event should match." + ); + resetTelemetry(); +} + +// This test deliberately opens a lot of tabs to ensure SERPs share the +// same process. It interacts with the page to ensure the engagement +// has the correct recording, especially the impression id which can be out of +// sync if data in the child process isn't cached properly. +add_task(async function test_multiple_tabs_forward() { + resetTelemetry(); + + let tabs = []; + let pid; + + // Open multiple tabs. + for (let index = 0; index < TABS_TO_OPEN; ++index) { + let url = getSERPUrl( + "searchTelemetryAd_searchbox_with_content.html", + `hello+world+${index}` + ); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + await waitForPageWithAdImpressions(); + tabs.push(tab); + let currentPid = E10SUtils.getBrowserPids(tab.linkedBrowser).at(0); + if (pid == null) { + pid = currentPid; + } else { + Assert.ok(pid == currentPid, "The process ID should be the same."); + } + } + + // Extract the impression IDs. + await Services.fog.testFlushAllChildren(); + let recordedImpressions = Glean.serp.impression.testGetValue() ?? []; + let impressionIds = recordedImpressions.map( + impression => impression.extra.impression_id + ); + + // Reset telemetry because we're not concerned about inspecting every + // impression event. + resetTelemetry(); + + for (let index = 0; index < TABS_TO_OPEN; ++index) { + let tab = tabs[index]; + let impressionId = impressionIds[index]; + await do_test(tab, impressionId, true); + BrowserTestUtils.removeTab(tab); + } +}); + +add_task(async function test_multiple_tabs_backward() { + resetTelemetry(); + + let tabs = []; + let pid; + + for (let index = 0; index < TABS_TO_OPEN; ++index) { + let url = getSERPUrl( + "searchTelemetryAd_searchbox_with_content.html", + `hello+world+${index}` + ); + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + await waitForPageWithAdImpressions(); + tabs.push(tab); + let currentPid = E10SUtils.getBrowserPids(tab.linkedBrowser).at(0); + if (pid == null) { + pid = currentPid; + } else { + Assert.ok(pid == currentPid, "The process ID should be the same."); + } + } + + // Extract the impression IDs. + await Services.fog.testFlushAllChildren(); + let recordedImpressions = Glean.serp.impression.testGetValue() ?? []; + let impressionIds = recordedImpressions.map( + impression => impression.extra.impression_id + ); + + // Reset telemetry because we're not concerned about inspecting every + // impression event. + resetTelemetry(); + + for (let index = TABS_TO_OPEN - 1; index >= 0; --index) { + let tab = tabs[index]; + let impressionId = impressionIds[index]; + await do_test(tab, impressionId, false); + BrowserTestUtils.removeTab(tab); + } +}); -- cgit v1.2.3