summaryrefslogtreecommitdiffstats
path: root/browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js')
-rw-r--r--browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js239
1 files changed, 239 insertions, 0 deletions
diff --git a/browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js b/browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js
new file mode 100644
index 0000000000..26ebea5a56
--- /dev/null
+++ b/browser/components/search/test/browser/browser_search_telemetry_engagement_cached_serp.js
@@ -0,0 +1,239 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * These tests check when a SERP retrieves data from the BFCache as SERPs
+ * typically set their response headers with Cache-Control as private.
+ */
+
+"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.com/],
+ extraAdServersRegexps: [/^https:\/\/example\.com\/ad/],
+ components: [
+ {
+ type: SearchSERPTelemetryUtils.COMPONENTS.INCONTENT_SEARCHBOX,
+ included: {
+ parent: {
+ selector: "form",
+ },
+ children: [
+ {
+ selector: "input",
+ },
+ ],
+ related: {
+ selector: "div",
+ },
+ },
+ topDown: true,
+ },
+ {
+ type: SearchSERPTelemetryUtils.COMPONENTS.AD_LINK,
+ default: true,
+ },
+ ],
+ },
+];
+
+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],
+ ],
+ });
+
+ registerCleanupFunction(async () => {
+ SearchSERPTelemetry.overrideSearchTelemetryForTests();
+ resetTelemetry();
+ });
+});
+
+async function goBack(tab, callback = async () => {}) {
+ info("Go back.");
+ let pageShowPromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow"
+ );
+ tab.linkedBrowser.goBack();
+ await pageShowPromise;
+ await callback();
+}
+
+async function goForward(tab, callback = async () => {}) {
+ info("Go forward.");
+ let pageShowPromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "pageshow"
+ );
+ tab.linkedBrowser.goForward();
+ await pageShowPromise;
+ await callback();
+}
+
+// This test loads a cached SERP and checks returning to it and interacting
+// with elements on the page don't count the events more than once.
+// This is a proxy for ensuring we remove event listeners.
+add_task(async function test_cached_serp() {
+ resetTelemetry();
+ let url = getSERPUrl("searchTelemetryAd_searchbox.html");
+ info("Load search page.");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ await waitForPageWithAdImpressions();
+
+ for (let index = 0; index < 3; ++index) {
+ info("Load non-search page.");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, true);
+ BrowserTestUtils.loadURIString(
+ tab.linkedBrowser,
+ "https://www.example.com"
+ );
+ await loadPromise;
+ await goBack(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+ }
+
+ info("Click on searchbox.");
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "input",
+ {},
+ tab.linkedBrowser
+ );
+
+ await Services.fog.testFlushAllChildren();
+ let engagements = Glean.serp.engagement.testGetValue() ?? [];
+ Assert.equal(
+ engagements.length,
+ 1,
+ "There should be 1 engagement event recorded."
+ );
+ BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_back_and_forward_serp_to_serp() {
+ await SpecialPowers.pushPrefEnv({
+ // This has to be disabled or else using back and forward in the test won't
+ // trigger responses in the network listener in SearchSERPTelemetry. The
+ // page will still load from a BFCache.
+ set: [["fission.bfcacheInParent", false]],
+ });
+ resetTelemetry();
+
+ let url = getSERPUrl("searchTelemetryAd_searchbox.html");
+ info("Load search page.");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ await waitForPageWithAdImpressions();
+
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false);
+ info("Click on a suggested search term.");
+ BrowserTestUtils.synthesizeMouseAtCenter("#suggest", {}, tab.linkedBrowser);
+ await loadPromise;
+ await waitForPageWithAdImpressions();
+
+ for (let index = 0; index < 3; ++index) {
+ info("Return to first search page.");
+ await goBack(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+ info("Return to second search page.");
+ await goForward(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+ }
+
+ await Services.fog.testFlushAllChildren();
+ let engagements = Glean.serp.engagement.testGetValue() ?? [];
+ let abandonments = Glean.serp.abandonment.testGetValue() ?? [];
+ Assert.equal(engagements.length, 1, "There should be 1 engagement.");
+ Assert.equal(abandonments.length, 6, "There should be 6 abandonments.");
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_back_and_forward_content_to_serp_to_serp() {
+ await SpecialPowers.pushPrefEnv({
+ // This has to be disabled or else using back and forward in the test won't
+ // trigger responses in the network listener in SearchSERPTelemetry. The
+ // page will still load from a BFCache.
+ set: [["fission.bfcacheInParent", false]],
+ });
+ resetTelemetry();
+
+ info("Load non-search page.");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "https://www.example.com/"
+ );
+
+ info("Load search page.");
+ let url = getSERPUrl("searchTelemetryAd_searchbox.html");
+ let loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, true);
+ BrowserTestUtils.loadURIString(tab.linkedBrowser, url);
+ await loadPromise;
+ await waitForPageWithAdImpressions();
+
+ loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false);
+ info("Click on a suggested search term.");
+ BrowserTestUtils.synthesizeMouseAtCenter("#suggest", {}, tab.linkedBrowser);
+ await loadPromise;
+ await waitForPageWithAdImpressions();
+
+ info("Return to first search page.");
+ await goBack(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+
+ info("Return to non-search page.");
+ await goBack(tab);
+
+ info("Return to first search page.");
+ await goForward(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+
+ info("Return to second search page.");
+ await goForward(tab, async () => {
+ await waitForPageWithAdImpressions();
+ });
+
+ await Services.fog.testFlushAllChildren();
+ let engagements = Glean.serp.engagement.testGetValue() ?? [];
+ let abandonments = Glean.serp.abandonment.testGetValue() ?? [];
+ Assert.equal(engagements.length, 1, "There should be 1 engagement.");
+ Assert.equal(abandonments.length, 3, "There should be 3 abandonments.");
+
+ BrowserTestUtils.removeTab(tab);
+ await SpecialPowers.popPrefEnv();
+});