Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
ChromeUtils.defineESModuleGetters(this, {
BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.sys.mjs",
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
SearchSERPTelemetry: "resource:///modules/SearchSERPTelemetry.sys.mjs",
});
const TESTS = [
{
title: "Google search access point",
trackingUrl:
expectedSearchCountEntry: "google:tagged:firefox-b-1-ab",
expectedAdKey: "google:tagged",
adUrls: [
],
nonAdUrls: [
],
},
{
title: "Google search access point follow-on",
trackingUrl:
expectedSearchCountEntry: "google:tagged-follow-on:firefox-b-1-ab",
},
{
title: "Google organic",
trackingUrl:
expectedSearchCountEntry: "google:organic:other",
expectedAdKey: "google:organic",
},
{
title: "Google organic no code",
trackingUrl:
expectedSearchCountEntry: "google:organic:none",
expectedAdKey: "google:organic",
},
{
title: "Google organic UK",
trackingUrl:
expectedSearchCountEntry: "google:organic:none",
},
{
title: "Bing search access point",
expectedSearchCountEntry: "bing:tagged:MOZI",
expectedAdKey: "bing:tagged",
adUrls: [
],
nonAdUrls: [
],
},
{
setUp() {
Services.cookies.removeAll();
Services.cookies.add(
"www.bing.com",
"/",
"SRCHS",
"PC=MOZI",
false,
false,
false,
Date.now() + 1000 * 60 * 60,
{},
Ci.nsICookie.SAMESITE_NONE,
Ci.nsICookie.SCHEME_HTTPS
);
},
tearDown() {
Services.cookies.removeAll();
},
title: "Bing search access point follow-on",
trackingUrl:
expectedSearchCountEntry: "bing:tagged-follow-on:MOZI",
},
{
title: "Bing organic",
expectedSearchCountEntry: "bing:organic:other",
expectedAdKey: "bing:organic",
},
{
title: "Bing organic no code",
trackingUrl:
expectedSearchCountEntry: "bing:organic:none",
expectedAdKey: "bing:organic",
},
{
title: "DuckDuckGo search access point",
expectedSearchCountEntry: "duckduckgo:tagged:ffab",
expectedAdKey: "duckduckgo:tagged",
adUrls: [
],
nonAdUrls: [
],
},
{
title: "DuckDuckGo organic",
expectedSearchCountEntry: "duckduckgo:organic:other",
expectedAdKey: "duckduckgo:organic",
},
{
title: "DuckDuckGo expected organic code",
expectedSearchCountEntry: "duckduckgo:organic:none",
expectedAdKey: "duckduckgo:organic",
},
{
title: "DuckDuckGo expected organic code 2",
expectedSearchCountEntry: "duckduckgo:organic:none",
expectedAdKey: "duckduckgo:organic",
},
{
title: "DuckDuckGo organic no code",
expectedSearchCountEntry: "duckduckgo:organic:none",
expectedAdKey: "duckduckgo:organic",
},
{
title: "Baidu search access point",
expectedSearchCountEntry: "baidu:tagged:monline_7_dg",
expectedAdKey: "baidu:tagged",
},
{
title: "Baidu search access point follow-on",
trackingUrl:
expectedSearchCountEntry: "baidu:tagged-follow-on:monline_7_dg",
},
{
title: "Baidu organic",
trackingUrl:
expectedSearchCountEntry: "baidu:organic:other",
},
{
title: "Baidu organic no code",
trackingUrl:
expectedSearchCountEntry: "baidu:organic:none",
},
{
title: "Ecosia search access point",
expectedSearchCountEntry: "ecosia:tagged:mzl",
expectedAdKey: "ecosia:tagged",
nonAdUrls: [],
},
{
title: "Ecosia organic",
expectedSearchCountEntry: "ecosia:organic:none",
expectedAdKey: "ecosia:organic",
nonAdUrls: [],
},
];
/**
* This function is primarily for testing the Ad URL regexps that are triggered
* when a URL is clicked on. These regexps are also used for the `with_ads`
* probe. However, we test the ad_clicks route as that is easier to hit.
*
* @param {string} serpUrl
* The url to simulate where the page the click came from.
* @param {string} adUrl
* The ad url to simulate being clicked.
* @param {string} [expectedAdKey]
* The expected key to be logged for the scalar. Omit if no scalar should be
* logged.
*/
async function testAdUrlClicked(serpUrl, adUrl, expectedAdKey) {
info(`Testing Ad URL: ${adUrl}`);
let channel = NetUtil.newChannel({
uri: NetUtil.newURI(adUrl),
triggeringPrincipal: Services.scriptSecurityManager.createContentPrincipal(
NetUtil.newURI(serpUrl),
{}
),
loadUsingSystemPrincipal: true,
});
SearchSERPTelemetry._contentHandler.observeActivity(
channel,
Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION,
Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
);
// Since the content handler takes a moment to allow the channel information
// to settle down, wait the same amount of time here.
await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
const scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
if (!expectedAdKey) {
Assert.ok(
!("browser.search.adclicks.unknown" in scalars),
"Should not have recorded an ad click"
);
} else {
TelemetryTestUtils.assertKeyedScalar(
scalars,
"browser.search.adclicks.unknown",
expectedAdKey,
1
);
}
}
do_get_profile();
add_setup(async function () {
await SearchSERPTelemetry.init();
sinon.stub(BrowserSearchTelemetry, "shouldRecordSearchCount").returns(true);
});
add_task(async function test_parsing_search_urls() {
for (const test of TESTS) {
info(`Running ${test.title}`);
if (test.setUp) {
test.setUp();
}
SearchSERPTelemetry.updateTrackingStatus(
{
getTabBrowser: () => {},
// There is no concept of browsing in unit tests, so assume in tests that we
// are not in private browsing mode. We have browser tests that check when
// private browsing is used.
contentPrincipal: {
originAttributes: {
privateBrowsingId: 0,
},
},
},
test.trackingUrl
);
let scalars = TelemetryTestUtils.getProcessScalars("parent", true, true);
TelemetryTestUtils.assertKeyedScalar(
scalars,
"browser.search.content.unknown",
test.expectedSearchCountEntry,
1
);
if ("adUrls" in test) {
for (const adUrl of test.adUrls) {
await testAdUrlClicked(test.trackingUrl, adUrl, test.expectedAdKey);
}
for (const nonAdUrls of test.nonAdUrls) {
await testAdUrlClicked(test.trackingUrl, nonAdUrls);
}
}
if (test.tearDown) {
test.tearDown();
}
}
});