/* 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(); });