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