Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test gets skipped with pattern: os == 'android'
- This test failed 36 times in the preceding 30 days. quicksearch this test
- Manifest: browser/components/newtab/test/xpcshell/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
const { actionCreators: ac, actionTypes: at } = ChromeUtils.importESModule(
"resource://activity-stream/common/Actions.mjs"
);
const { MESSAGE_TYPE_HASH: msg } = ChromeUtils.importESModule(
"resource:///modules/asrouter/ActorConstants.mjs"
);
const { updateAppInfo } = ChromeUtils.importESModule(
);
const { TelemetryFeed, USER_PREFS_ENCODING } = ChromeUtils.importESModule(
"resource://activity-stream/lib/TelemetryFeed.sys.mjs"
);
ChromeUtils.defineESModuleGetters(this, {
AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
AboutWelcomeTelemetry:
"resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs",
ExperimentAPI: "resource://nimbus/ExperimentAPI.sys.mjs",
ExtensionSettingsStore:
"resource://gre/modules/ExtensionSettingsStore.sys.mjs",
HomePage: "resource:///modules/HomePage.sys.mjs",
JsonSchemaValidator:
"resource://gre/modules/components-utils/JsonSchemaValidator.sys.mjs",
TelemetryController: "resource://gre/modules/TelemetryController.sys.mjs",
TelemetrySession: "resource://gre/modules/TelemetrySession.sys.mjs",
UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
UTEventReporting: "resource://activity-stream/lib/UTEventReporting.sys.mjs",
});
const FAKE_UUID = "{foo-123-foo}";
const PREF_IMPRESSION_ID = "browser.newtabpage.activity-stream.impressionId";
const PREF_TELEMETRY = "browser.newtabpage.activity-stream.telemetry";
const PREF_EVENT_TELEMETRY =
"browser.newtabpage.activity-stream.telemetry.ut.events";
let ASRouterEventPingSchemaPromise;
let BasePingSchemaPromise;
let SessionPingSchemaPromise;
let UserEventPingSchemaPromise;
function assertPingMatchesSchema(pingKind, ping, schema) {
// Unlike the validator from JsonSchema.sys.mjs, JsonSchemaValidator
// lets us opt-in to having "undefined" properties, which are then
// ignored. This is fine because the ping is sent as a JSON string
// over an XHR, and undefined properties are culled as part of the
// JSON encoding process.
let result = JsonSchemaValidator.validate(ping, schema, {
allowExplicitUndefinedProperties: true,
});
if (!result.valid) {
info(`${pingKind} failed to validate against the schema: ${result.error}`);
}
Assert.ok(result.valid, `${pingKind} is valid against the schema.`);
}
async function assertSessionPingValid(ping) {
let schema = await SessionPingSchemaPromise;
assertPingMatchesSchema("SessionPing", ping, schema);
}
async function assertBasePingValid(ping) {
let schema = await BasePingSchemaPromise;
assertPingMatchesSchema("BasePing", ping, schema);
}
async function assertUserEventPingValid(ping) {
let schema = await UserEventPingSchemaPromise;
assertPingMatchesSchema("UserEventPing", ping, schema);
}
async function assertASRouterEventPingValid(ping) {
let schema = await ASRouterEventPingSchemaPromise;
assertPingMatchesSchema("ASRouterEventPing", ping, schema);
}
add_setup(async function setup() {
ASRouterEventPingSchemaPromise = IOUtils.readJSON(
do_get_file("../schemas/asrouter_event_ping.schema.json").path
);
BasePingSchemaPromise = IOUtils.readJSON(
do_get_file("../schemas/base_ping.schema.json").path
);
SessionPingSchemaPromise = IOUtils.readJSON(
do_get_file("../schemas/session_ping.schema.json").path
);
UserEventPingSchemaPromise = IOUtils.readJSON(
do_get_file("../schemas/user_event_ping.schema.json").path
);
do_get_profile();
// FOG needs to be initialized in order for data to flow.
Services.fog.initializeFOG();
await TelemetryController.testReset();
updateAppInfo({
name: "XPCShell",
ID: "xpcshell@tests.mozilla.org",
version: "122",
platformVersion: "122",
});
Services.prefs.setCharPref(
"browser.contextual-services.contextId",
FAKE_UUID
);
});
add_task(async function test_construction() {
let testInstance = new TelemetryFeed();
Assert.ok(
testInstance,
"Should have been able to create an instance of TelemetryFeed."
);
Assert.ok(
testInstance.utEvents instanceof UTEventReporting,
"Should add .utEvents, a UTEventReporting instance."
);
Assert.ok(
testInstance._impressionId,
"Should create impression id if none exists"
);
});
add_task(async function test_load_impressionId() {
info(
"Constructing a TelemetryFeed should use a saved impression ID if one exists."
);
const FAKE_IMPRESSION_ID = "{some-fake-impression-ID}";
const IMPRESSION_PREF = "browser.newtabpage.activity-stream.impressionId";
Services.prefs.setCharPref(IMPRESSION_PREF, FAKE_IMPRESSION_ID);
Assert.equal(new TelemetryFeed()._impressionId, FAKE_IMPRESSION_ID);
Services.prefs.clearUserPref(IMPRESSION_PREF);
});
add_task(async function test_init() {
info(
"init should make this.browserOpenNewtabStart() observe browser-open-newtab-start"
);
let sandbox = sinon.createSandbox();
sandbox
.stub(TelemetryFeed.prototype, "getOrCreateImpressionId")
.returns(FAKE_UUID);
let instance = new TelemetryFeed();
sandbox.stub(instance, "browserOpenNewtabStart");
instance.init();
Services.obs.notifyObservers(null, "browser-open-newtab-start");
Assert.ok(
instance.browserOpenNewtabStart.calledOnce,
"browserOpenNewtabStart called once."
);
info("init should create impression id if none exists");
Assert.equal(instance._impressionId, FAKE_UUID);
instance.uninit();
sandbox.restore();
});
add_task(async function test_saved_impression_id() {
const FAKE_IMPRESSION_ID = "fakeImpressionId";
Services.prefs.setCharPref(PREF_IMPRESSION_ID, FAKE_IMPRESSION_ID);
Assert.equal(new TelemetryFeed()._impressionId, FAKE_IMPRESSION_ID);
Services.prefs.clearUserPref(PREF_IMPRESSION_ID);
});
add_task(async function test_telemetry_prefs() {
info("Telemetry pref changes from false to true");
Services.prefs.setBoolPref(PREF_TELEMETRY, false);
let instance = new TelemetryFeed();
Assert.ok(!instance.telemetryEnabled, "Telemetry disabled");
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
Assert.ok(instance.telemetryEnabled, "Telemetry enabled");
info("Event telemetry pref changes from false to true");
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, false);
Assert.ok(!instance.eventTelemetryEnabled, "Event telemetry disabled");
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
Assert.ok(instance.eventTelemetryEnabled, "Event telemetry enabled");
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
Services.prefs.clearUserPref(PREF_TELEMETRY);
});
add_task(async function test_deletionRequest_scalars() {
info("TelemetryFeed.init should set two scalars for deletion-request");
Services.telemetry.clearScalars();
let instance = new TelemetryFeed();
instance.init();
let snapshot = Services.telemetry.getSnapshotForScalars(
"deletion-request",
false
).parent;
TelemetryTestUtils.assertScalar(
snapshot,
"deletion.request.impression_id",
instance._impressionId
);
TelemetryTestUtils.assertScalar(
snapshot,
"deletion.request.context_id",
FAKE_UUID
);
instance.uninit();
});
add_task(async function test_metrics_on_initialization() {
info("TelemetryFeed.init should record initial metrics from newtab prefs");
Services.fog.testResetFOG();
const ENABLED_SETTING = true;
const TOP_SITES_ROWS = 3;
const BLOCKED_SPONSORS = ["mozilla"];
Services.prefs.setBoolPref(
"browser.newtabpage.activity-stream.feeds.topsites",
ENABLED_SETTING
);
Services.prefs.setIntPref(
"browser.newtabpage.activity-stream.topSitesRows",
TOP_SITES_ROWS
);
Services.prefs.setCharPref(
"browser.topsites.blockedSponsors",
JSON.stringify(BLOCKED_SPONSORS)
);
let instance = new TelemetryFeed();
instance.init();
Assert.equal(Glean.topsites.enabled.testGetValue(), ENABLED_SETTING);
Assert.equal(Glean.topsites.rows.testGetValue(), TOP_SITES_ROWS);
Assert.deepEqual(
Glean.newtab.blockedSponsors.testGetValue(),
BLOCKED_SPONSORS
);
instance.uninit();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.feeds.topsites"
);
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.topSitesRows"
);
Services.prefs.clearUserPref("browser.topsites.blockedSponsors");
});
add_task(async function test_metrics_with_bad_json() {
info(
"TelemetryFeed.init should not record blocked sponsor metrics when " +
"bad json string is passed"
);
Services.fog.testResetFOG();
Services.prefs.setCharPref("browser.topsites.blockedSponsors", "BAD[JSON]");
let instance = new TelemetryFeed();
instance.init();
Assert.equal(Glean.newtab.blockedSponsors.testGetValue(), null);
instance.uninit();
Services.prefs.clearUserPref("browser.topsites.blockedSponsors");
});
add_task(async function test_metrics_on_pref_changes() {
info("TelemetryFeed.init should record new metrics for newtab pref changes");
const INITIAL_TOP_SITES_ROWS = 3;
const INITIAL_BLOCKED_SPONSORS = [];
Services.fog.testResetFOG();
Services.prefs.setIntPref(
"browser.newtabpage.activity-stream.topSitesRows",
INITIAL_TOP_SITES_ROWS
);
Services.prefs.setCharPref(
"browser.topsites.blockedSponsors",
JSON.stringify(INITIAL_BLOCKED_SPONSORS)
);
let instance = new TelemetryFeed();
instance.init();
Assert.equal(Glean.topsites.rows.testGetValue(), INITIAL_TOP_SITES_ROWS);
Assert.deepEqual(
Glean.newtab.blockedSponsors.testGetValue(),
INITIAL_BLOCKED_SPONSORS
);
const NEXT_TOP_SITES_ROWS = 2;
const NEXT_BLOCKED_SPONSORS = ["mozilla"];
Services.prefs.setIntPref(
"browser.newtabpage.activity-stream.topSitesRows",
NEXT_TOP_SITES_ROWS
);
Services.prefs.setStringPref(
"browser.topsites.blockedSponsors",
JSON.stringify(NEXT_BLOCKED_SPONSORS)
);
Assert.equal(Glean.topsites.rows.testGetValue(), NEXT_TOP_SITES_ROWS);
Assert.deepEqual(
Glean.newtab.blockedSponsors.testGetValue(),
NEXT_BLOCKED_SPONSORS
);
instance.uninit();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.topSitesRows"
);
Services.prefs.clearUserPref("browser.topsites.blockedSponsors");
});
add_task(async function test_events_on_pref_changes() {
info("TelemetryFeed.init should record events for some newtab pref changes");
// We only record events for browser.newtabpage.activity-stream.feeds.topsites and
// browser.newtabpage.activity-stream.showSponsoredTopSites being changed.
const INITIAL_TOPSITES_ENABLED = false;
const INITIAL_SHOW_SPONSORED_TOP_SITES = true;
Services.fog.testResetFOG();
Services.prefs.setBoolPref(
"browser.newtabpage.activity-stream.feeds.topsites",
INITIAL_TOPSITES_ENABLED
);
Services.prefs.setBoolPref(
"browser.newtabpage.activity-stream.showSponsoredTopSites",
INITIAL_SHOW_SPONSORED_TOP_SITES
);
let instance = new TelemetryFeed();
instance.init();
const NEXT_TOPSITES_ENABLED = true;
const NEXT_SHOW_SPONSORED_TOP_SITES = false;
Services.prefs.setBoolPref(
"browser.newtabpage.activity-stream.feeds.topsites",
NEXT_TOPSITES_ENABLED
);
Services.prefs.setBoolPref(
"browser.newtabpage.activity-stream.showSponsoredTopSites",
NEXT_SHOW_SPONSORED_TOP_SITES
);
let prefChangeEvents = Glean.topsites.prefChanged.testGetValue();
Assert.deepEqual(prefChangeEvents[0].extra, {
pref_name: "browser.newtabpage.activity-stream.feeds.topsites",
new_value: String(NEXT_TOPSITES_ENABLED),
});
Assert.deepEqual(prefChangeEvents[1].extra, {
pref_name: "browser.newtabpage.activity-stream.showSponsoredTopSites",
new_value: String(NEXT_SHOW_SPONSORED_TOP_SITES),
});
instance.uninit();
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.feeds.topsites"
);
Services.prefs.clearUserPref(
"browser.newtabpage.activity-stream.showSponsoredTopSites"
);
});
add_task(async function test_browserOpenNewtabStart() {
info(
"TelemetryFeed.browserOpenNewtabStart should call " +
"ChromeUtils.addProfilerMarker with browser-open-newtab-start"
);
let instance = new TelemetryFeed();
let entries = 10000;
let interval = 1;
let threads = ["GeckoMain"];
let features = [];
await Services.profiler.StartProfiler(entries, interval, features, threads);
instance.browserOpenNewtabStart();
let profileArrayBuffer =
await Services.profiler.getProfileDataAsArrayBuffer();
await Services.profiler.StopProfiler();
let profileUint8Array = new Uint8Array(profileArrayBuffer);
let textDecoder = new TextDecoder("utf-8", { fatal: true });
let profileString = textDecoder.decode(profileUint8Array);
let profile = JSON.parse(profileString);
Assert.ok(profile.threads);
Assert.equal(profile.threads.length, 1);
let foundMarker = profile.threads[0].markers.data.find(marker => {
return marker[5]?.name === "browser-open-newtab-start";
});
Assert.ok(foundMarker, "Found the browser-open-newtab-start marker");
});
add_task(async function test_addSession_and_get_session() {
info("TelemetryFeed.addSession should add a session and return it");
let instance = new TelemetryFeed();
let session = instance.addSession("foo");
Assert.equal(instance.sessions.get("foo"), session);
info("TelemetryFeed.addSession should set a session_id");
Assert.ok(session.session_id, "Should have a session_id set");
});
add_task(async function test_addSession_url_param() {
info("TelemetryFeed.addSession should set the page if a url param is given");
let instance = new TelemetryFeed();
let session = instance.addSession("foo", "about:monkeys");
Assert.equal(session.page, "about:monkeys");
info(
"TelemetryFeed.assSession should set the page prop to 'unknown' " +
"if no URL param given"
);
session = instance.addSession("test2");
Assert.equal(session.page, "unknown");
});
add_task(async function test_addSession_perf_properties() {
info(
"TelemetryFeed.addSession should set the perf type when lacking " +
"timestamp"
);
let instance = new TelemetryFeed();
let session = instance.addSession("foo");
Assert.equal(session.perf.load_trigger_type, "unexpected");
info(
"TelemetryFeed.addSession should set load_trigger_type to " +
"first_window_opened on the first about:home seen"
);
session = instance.addSession("test2", "about:home");
Assert.equal(session.perf.load_trigger_type, "first_window_opened");
info(
"TelemetryFeed.addSession should set load_trigger_ts to the " +
"value of the process start timestamp"
);
Assert.equal(
session.perf.load_trigger_ts,
Services.startup.getStartupInfo().process.getTime(),
"Should have set a timestamp to be the process start time"
);
info(
"TelemetryFeed.addSession should NOT set load_trigger_type to " +
"first_window_opened on the second about:home seen"
);
let session2 = instance.addSession("test2", "about:home");
Assert.notEqual(session2.perf.load_trigger_type, "first_window_opened");
});
add_task(async function test_addSession_valid_ping_on_first_abouthome() {
info(
"TelemetryFeed.addSession should create a valid session ping " +
"on the first about:home seen"
);
let instance = new TelemetryFeed();
// Add a session
const PORT_ID = "foo";
let session = instance.addSession(PORT_ID, "about:home");
// Create a ping referencing the session
let ping = instance.createSessionEndEvent(session);
await assertSessionPingValid(ping);
});
add_task(async function test_addSession_valid_ping_data_late_by_ms() {
info(
"TelemetryFeed.addSession should create a valid session ping " +
"with the data_late_by_ms perf"
);
let instance = new TelemetryFeed();
// Add a session
const PORT_ID = "foo";
let session = instance.addSession(PORT_ID, "about:home");
const TOPSITES_LATE_BY_MS = 10;
const HIGHLIGHTS_LATE_BY_MS = 20;
instance.saveSessionPerfData("foo", {
topsites_data_late_by_ms: TOPSITES_LATE_BY_MS,
});
instance.saveSessionPerfData("foo", {
highlights_data_late_by_ms: HIGHLIGHTS_LATE_BY_MS,
});
// Create a ping referencing the session
let ping = instance.createSessionEndEvent(session);
await assertSessionPingValid(ping);
Assert.equal(session.perf.topsites_data_late_by_ms, TOPSITES_LATE_BY_MS);
Assert.equal(session.perf.highlights_data_late_by_ms, HIGHLIGHTS_LATE_BY_MS);
});
add_task(async function test_addSession_valid_ping_topsites_stats_perf() {
info(
"TelemetryFeed.addSession should create a valid session ping " +
"with the topsites stats perf"
);
let instance = new TelemetryFeed();
// Add a session
const PORT_ID = "foo";
let session = instance.addSession(PORT_ID, "about:home");
const SCREENSHOT_WITH_ICON = 2;
const TOPSITES_PINNED = 3;
const TOPSITES_SEARCH_SHORTCUTS = 2;
instance.saveSessionPerfData("foo", {
topsites_icon_stats: {
custom_screenshot: 0,
screenshot_with_icon: SCREENSHOT_WITH_ICON,
screenshot: 1,
tippytop: 2,
rich_icon: 1,
no_image: 0,
},
topsites_pinned: TOPSITES_PINNED,
topsites_search_shortcuts: TOPSITES_SEARCH_SHORTCUTS,
});
// Create a ping referencing the session
let ping = instance.createSessionEndEvent(session);
await assertSessionPingValid(ping);
Assert.equal(
instance.sessions.get("foo").perf.topsites_icon_stats.screenshot_with_icon,
SCREENSHOT_WITH_ICON
);
Assert.equal(
instance.sessions.get("foo").perf.topsites_pinned,
TOPSITES_PINNED
);
Assert.equal(
instance.sessions.get("foo").perf.topsites_search_shortcuts,
TOPSITES_SEARCH_SHORTCUTS
);
});
add_task(async function test_endSession_no_throw_on_bad_session() {
info(
"TelemetryFeed.endSession should not throw if there is no " +
"session for a given port ID"
);
let instance = new TelemetryFeed();
try {
instance.endSession("doesn't exist");
Assert.ok(true, "Did not throw.");
} catch (e) {
Assert.ok(false, "Should not have thrown.");
}
});
add_task(async function test_endSession_session_duration() {
info(
"TelemetryFeed.endSession should add a session_duration integer " +
"if there is a visibility_event_rcvd_ts"
);
let instance = new TelemetryFeed();
let session = instance.addSession("foo");
session.perf.visibility_event_rcvd_ts = 444.4732;
instance.endSession("foo");
Assert.ok(
Number.isInteger(session.session_duration),
"session_duration should be an integer"
);
});
add_task(async function test_endSession_no_ping_on_no_visibility_event() {
info(
"TelemetryFeed.endSession shouldn't send session ping if there's " +
"no visibility_event_rcvd_ts"
);
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
let instance = new TelemetryFeed();
instance.addSession("foo");
Services.telemetry.clearEvents();
instance.endSession("foo");
TelemetryTestUtils.assertNumberOfEvents(0);
info("TelemetryFeed.endSession should remove the session from .sessions");
Assert.ok(!instance.sessions.has("foo"));
Services.prefs.clearUserPref(PREF_TELEMETRY);
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
});
add_task(async function test_endSession_send_ping() {
info(
"TelemetryFeed.endSession should call createSessionSendEvent with the " +
"session if visibilty_event_rcvd_ts was set"
);
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
let instance = new TelemetryFeed();
let sandbox = sinon.createSandbox();
sandbox.stub(instance, "createSessionEndEvent");
sandbox.stub(instance.utEvents, "sendSessionEndEvent");
let session = instance.addSession("foo");
session.perf.visibility_event_rcvd_ts = 444.4732;
instance.endSession("foo");
Assert.ok(instance.createSessionEndEvent.calledWith(session));
let sessionEndEvent = instance.createSessionEndEvent.firstCall.returnValue;
Assert.ok(instance.utEvents.sendSessionEndEvent.calledWith(sessionEndEvent));
info("TelemetryFeed.endSession should remove the session from .sessions");
Assert.ok(!instance.sessions.has("foo"));
Services.prefs.clearUserPref(PREF_TELEMETRY);
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
sandbox.restore();
});
add_task(async function test_createPing_valid_base_if_no_portID() {
info(
"TelemetryFeed.createPing should create a valid base ping " +
"without a session if no portID is supplied"
);
let instance = new TelemetryFeed();
let ping = await instance.createPing();
await assertBasePingValid(ping);
Assert.ok(!ping.session_id);
Assert.ok(!ping.page);
});
add_task(async function test_createPing_valid_base_if_portID() {
info(
"TelemetryFeed.createPing should create a valid base ping " +
"with session info if a portID is supplied"
);
// Add a session
const PORT_ID = "foo";
let instance = new TelemetryFeed();
instance.addSession(PORT_ID, "about:home");
let sessionID = instance.sessions.get(PORT_ID).session_id;
// Create a ping referencing the session
let ping = await instance.createPing(PORT_ID);
await assertBasePingValid(ping);
// Make sure we added the right session-related stuff to the ping
Assert.equal(ping.session_id, sessionID);
Assert.equal(ping.page, "about:home");
});
add_task(async function test_createPing_no_session_yet_portID() {
info(
"TelemetryFeed.createPing should create an 'unexpected' base ping " +
"if no session yet portID is supplied"
);
let instance = new TelemetryFeed();
let ping = await instance.createPing("foo");
await assertBasePingValid(ping);
Assert.equal(ping.page, "unknown");
Assert.equal(
instance.sessions.get("foo").perf.load_trigger_type,
"unexpected"
);
});
add_task(async function test_createPing_includes_userPrefs() {
info("TelemetryFeed.createPing should create a base ping with user_prefs");
let expectedUserPrefs = 0;
for (let pref of Object.keys(USER_PREFS_ENCODING)) {
Services.prefs.setBoolPref(
`browser.newtabpage.activity-stream.${pref}`,
true
);
expectedUserPrefs |= USER_PREFS_ENCODING[pref];
}
let instance = new TelemetryFeed();
let ping = await instance.createPing("foo");
await assertBasePingValid(ping);
Assert.equal(ping.user_prefs, expectedUserPrefs);
for (const pref of Object.keys(USER_PREFS_ENCODING)) {
Services.prefs.clearUserPref(`browser.newtabpage.activity-stream.${pref}`);
}
});
add_task(async function test_createUserEvent_is_valid() {
info(
"TelemetryFeed.createUserEvent should create a valid user event ping " +
"with the right session_id"
);
const PORT_ID = "foo";
let instance = new TelemetryFeed();
let data = { source: "TOP_SITES", event: "CLICK" };
let action = ac.AlsoToMain(ac.UserEvent(data), PORT_ID);
let session = instance.addSession(PORT_ID);
let ping = await instance.createUserEvent(action);
// Is it valid?
await assertUserEventPingValid(ping);
// Does it have the right session_id?
Assert.equal(ping.session_id, session.session_id);
});
add_task(async function test_createSessionEndEvent_is_valid() {
info(
"TelemetryFeed.createSessionEndEvent should create a valid session ping"
);
const FAKE_DURATION = 12345;
let instance = new TelemetryFeed();
let ping = await instance.createSessionEndEvent({
session_id: FAKE_UUID,
page: "about:newtab",
session_duration: FAKE_DURATION,
perf: {
load_trigger_ts: 10,
load_trigger_type: "menu_plus_or_keyboard",
visibility_event_rcvd_ts: 20,
is_preloaded: true,
},
});
// Is it valid?
await assertSessionPingValid(ping);
Assert.equal(ping.session_id, FAKE_UUID);
Assert.equal(ping.page, "about:newtab");
Assert.equal(ping.session_duration, FAKE_DURATION);
});
add_task(async function test_createSessionEndEvent_with_unexpected_is_valid() {
info(
"TelemetryFeed.createSessionEndEvent should create a valid 'unexpected' " +
"session ping"
);
const FAKE_DURATION = 12345;
const FAKE_TRIGGER_TYPE = "unexpected";
let instance = new TelemetryFeed();
let ping = await instance.createSessionEndEvent({
session_id: FAKE_UUID,
page: "about:newtab",
session_duration: FAKE_DURATION,
perf: {
load_trigger_type: FAKE_TRIGGER_TYPE,
is_preloaded: true,
},
});
// Is it valid?
await assertSessionPingValid(ping);
Assert.equal(ping.session_id, FAKE_UUID);
Assert.equal(ping.page, "about:newtab");
Assert.equal(ping.session_duration, FAKE_DURATION);
Assert.equal(ping.perf.load_trigger_type, FAKE_TRIGGER_TYPE);
});
add_task(async function test_applyCFRPolicy_prerelease() {
info(
"TelemetryFeed.applyCFRPolicy should use client_id and message_id " +
"in prerelease"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("nightly");
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "IMPRESSION",
message_id: "cfr_message_01",
bucket_id: "cfr_bucket_01",
};
let { ping, pingType } = await instance.applyCFRPolicy(data);
Assert.equal(pingType, "cfr");
Assert.equal(ping.impression_id, undefined);
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.bucket_id, "cfr_bucket_01");
Assert.equal(ping.message_id, "cfr_message_01");
sandbox.restore();
});
add_task(async function test_applyCFRPolicy_release() {
info(
"TelemetryFeed.applyCFRPolicy should use impression_id and bucket_id " +
"in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox
.stub(TelemetryFeed.prototype, "getOrCreateImpressionId")
.returns(FAKE_UUID);
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "IMPRESSION",
message_id: "cfr_message_01",
bucket_id: "cfr_bucket_01",
};
let { ping, pingType } = await instance.applyCFRPolicy(data);
Assert.equal(pingType, "cfr");
Assert.equal(ping.impression_id, FAKE_UUID);
Assert.equal(ping.client_id, undefined);
Assert.equal(ping.bucket_id, "cfr_bucket_01");
Assert.equal(ping.message_id, "n/a");
sandbox.restore();
});
add_task(async function test_applyCFRPolicy_experiment_release() {
info(
"TelemetryFeed.applyCFRPolicy should use impression_id and bucket_id " +
"in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
slug: "SOME-CFR-EXP",
});
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "IMPRESSION",
message_id: "cfr_message_01",
bucket_id: "cfr_bucket_01",
};
let { ping, pingType } = await instance.applyCFRPolicy(data);
Assert.equal(pingType, "cfr");
Assert.equal(ping.impression_id, undefined);
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.bucket_id, "cfr_bucket_01");
Assert.equal(ping.message_id, "cfr_message_01");
sandbox.restore();
});
add_task(async function test_applyCFRPolicy_release_private_browsing() {
info(
"TelemetryFeed.applyCFRPolicy should use impression_id and bucket_id " +
"in Private Browsing in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox
.stub(TelemetryFeed.prototype, "getOrCreateImpressionId")
.returns(FAKE_UUID);
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "IMPRESSION",
is_private: true,
message_id: "cfr_message_01",
bucket_id: "cfr_bucket_01",
};
let { ping, pingType } = await instance.applyCFRPolicy(data);
Assert.equal(pingType, "cfr");
Assert.equal(ping.impression_id, FAKE_UUID);
Assert.equal(ping.client_id, undefined);
Assert.equal(ping.bucket_id, "cfr_bucket_01");
Assert.equal(ping.message_id, "n/a");
sandbox.restore();
});
add_task(
async function test_applyCFRPolicy_release_experiment_private_browsing() {
info(
"TelemetryFeed.applyCFRPolicy should use client_id and message_id in the " +
"experiment cohort in Private Browsing in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
slug: "SOME-CFR-EXP",
});
let instance = new TelemetryFeed();
let data = {
action: "cfr_user_event",
event: "IMPRESSION",
is_private: true,
message_id: "cfr_message_01",
bucket_id: "cfr_bucket_01",
};
let { ping, pingType } = await instance.applyCFRPolicy(data);
Assert.equal(pingType, "cfr");
Assert.equal(ping.impression_id, undefined);
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.bucket_id, "cfr_bucket_01");
Assert.equal(ping.message_id, "cfr_message_01");
sandbox.restore();
}
);
add_task(async function test_applyToolbarBadgePolicy() {
info(
"TelemetryFeed.applyToolbarBadgePolicy should set client_id and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applyToolbarBadgePolicy({});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "toolbar-badge");
});
add_task(async function test_applyInfoBarPolicy() {
info(
"TelemetryFeed.applyInfoBarPolicy should set client_id and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applyInfoBarPolicy({});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "infobar");
});
add_task(async function test_applyToastNotificationPolicy() {
info(
"TelemetryFeed.applyToastNotificationPolicy should set client_id " +
"and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applyToastNotificationPolicy({});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "toast_notification");
});
add_task(async function test_applySpotlightPolicy() {
info(
"TelemetryFeed.applySpotlightPolicy should set client_id " +
"and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applySpotlightPolicy({
action: "foo",
});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "spotlight");
Assert.equal(ping.action, undefined);
});
add_task(async function test_applyMomentsPolicy_prerelease() {
info(
"TelemetryFeed.applyMomentsPolicy should use client_id and " +
"message_id in prerelease"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("nightly");
let instance = new TelemetryFeed();
let data = {
action: "moments_user_event",
event: "IMPRESSION",
message_id: "moments_message_01",
bucket_id: "moments_bucket_01",
};
let { ping, pingType } = await instance.applyMomentsPolicy(data);
Assert.equal(pingType, "moments");
Assert.equal(ping.impression_id, undefined);
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.bucket_id, "moments_bucket_01");
Assert.equal(ping.message_id, "moments_message_01");
sandbox.restore();
});
add_task(async function test_applyMomentsPolicy_release() {
info(
"TelemetryFeed.applyMomentsPolicy should use impression_id and " +
"bucket_id in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox
.stub(TelemetryFeed.prototype, "getOrCreateImpressionId")
.returns(FAKE_UUID);
let instance = new TelemetryFeed();
let data = {
action: "moments_user_event",
event: "IMPRESSION",
message_id: "moments_message_01",
bucket_id: "moments_bucket_01",
};
let { ping, pingType } = await instance.applyMomentsPolicy(data);
Assert.equal(pingType, "moments");
Assert.equal(ping.impression_id, FAKE_UUID);
Assert.equal(ping.client_id, undefined);
Assert.equal(ping.bucket_id, "moments_bucket_01");
Assert.equal(ping.message_id, "n/a");
sandbox.restore();
});
add_task(async function test_applyMomentsPolicy_experiment_release() {
info(
"TelemetryFeed.applyMomentsPolicy client_id and message_id in " +
"the experiment cohort in release"
);
let sandbox = sinon.createSandbox();
sandbox.stub(UpdateUtils, "getUpdateChannel").returns("release");
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
slug: "SOME-CFR-EXP",
});
let instance = new TelemetryFeed();
let data = {
action: "moments_user_event",
event: "IMPRESSION",
message_id: "moments_message_01",
bucket_id: "moments_bucket_01",
};
let { ping, pingType } = await instance.applyMomentsPolicy(data);
Assert.equal(pingType, "moments");
Assert.equal(ping.impression_id, undefined);
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.bucket_id, "moments_bucket_01");
Assert.equal(ping.message_id, "moments_message_01");
sandbox.restore();
});
add_task(async function test_applyOnboardingPolicy_includes_client_id() {
info("TelemetryFeed.applyOnboardingPolicy should include client_id");
let instance = new TelemetryFeed();
let data = {
action: "onboarding_user_event",
event: "CLICK_BUTTION",
message_id: "onboarding_message_01",
};
let { ping, pingType } = await instance.applyOnboardingPolicy(data);
Assert.equal(pingType, "onboarding");
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(ping.message_id, "onboarding_message_01");
Assert.equal(
ping.browser_session_id,
TelemetrySession.getMetadata("").sessionId
);
});
add_task(async function test_applyOnboardingPolicy_with_session() {
info(
"TelemetryFeed.applyOnboardingPolicy should include page to " +
"event_context if there is a session"
);
let instance = new TelemetryFeed();
let data = {
action: "onboarding_user_event",
event: "CLICK_BUTTION",
message_id: "onboarding_message_01",
};
let session = { page: "about:welcome" };
let { ping, pingType } = await instance.applyOnboardingPolicy(data, session);
Assert.equal(pingType, "onboarding");
Assert.equal(ping.event_context, JSON.stringify({ page: "about:welcome" }));
Assert.equal(ping.message_id, "onboarding_message_01");
});
add_task(async function test_applyOnboardingPolicy_only_allowed_pages() {
info(
"TelemetryFeed.applyOnboardingPolicy should not set page if it is " +
"not in ONBOARDING_ALLOWED_PAGE_VALUES"
);
let instance = new TelemetryFeed();
let data = {
action: "onboarding_user_event",
event: "CLICK_BUTTION",
message_id: "onboarding_message_01",
};
let session = { page: "foo" };
let { ping, pingType } = await instance.applyOnboardingPolicy(data, session);
Assert.equal(pingType, "onboarding");
Assert.equal(ping.event_context, JSON.stringify({}));
Assert.equal(ping.message_id, "onboarding_message_01");
});
add_task(
async function test_applyOnboardingPolicy_append_page_to_event_context() {
info(
"TelemetryFeed.applyOnboardingPolicy should append page to event_context " +
"if it is not empty"
);
let instance = new TelemetryFeed();
let data = {
action: "onboarding_user_event",
event: "CLICK_BUTTION",
message_id: "onboarding_message_01",
event_context: JSON.stringify({ foo: "bar" }),
};
let session = { page: "about:welcome" };
let { ping, pingType } = await instance.applyOnboardingPolicy(
data,
session
);
Assert.equal(pingType, "onboarding");
Assert.equal(
ping.event_context,
JSON.stringify({ foo: "bar", page: "about:welcome" })
);
Assert.equal(ping.message_id, "onboarding_message_01");
}
);
add_task(
async function test_applyOnboardingPolicy_append_page_to_event_context() {
info(
"TelemetryFeed.applyOnboardingPolicy should append page to event_context " +
"if it is not a JSON serialized string"
);
let instance = new TelemetryFeed();
let data = {
action: "onboarding_user_event",
event: "CLICK_BUTTION",
message_id: "onboarding_message_01",
event_context: "foo",
};
let session = { page: "about:welcome" };
let { ping, pingType } = await instance.applyOnboardingPolicy(
data,
session
);
Assert.equal(pingType, "onboarding");
Assert.equal(
ping.event_context,
JSON.stringify({ value: "foo", page: "about:welcome" })
);
Assert.equal(ping.message_id, "onboarding_message_01");
}
);
add_task(async function test_applyMenuMessagePolicy() {
info(
"TelemetryFeed.applyMenuMessagePolicy should set client_id and set pingType"
);
let instance = new TelemetryFeed();
let { ping, pingType } = await instance.applyMenuMessagePolicy({});
Assert.equal(
ping.client_id,
Services.prefs.getCharPref("toolkit.telemetry.cachedClientID")
);
Assert.equal(pingType, "menu");
});
add_task(async function test_applyUndesiredEventPolicy() {
info(
"TelemetryFeed.applyUndesiredEventPolicy should exclude client_id " +
"and use impression_id"
);
let sandbox = sinon.createSandbox();
sandbox
.stub(TelemetryFeed.prototype, "getOrCreateImpressionId")
.returns(FAKE_UUID);
let instance = new TelemetryFeed();
let data = {
action: "asrouter_undesired_event",
event: "RS_MISSING_DATA",
};
let { ping, pingType } = await instance.applyUndesiredEventPolicy(data);
Assert.equal(pingType, "undesired-events");
Assert.equal(ping.client_id, undefined);
Assert.equal(ping.impression_id, FAKE_UUID);
sandbox.restore();
});
add_task(async function test_createASRouterEvent_valid_ping() {
info(
"TelemetryFeed.createASRouterEvent should create a valid " +
"ASRouterEventPing ping"
);
let instance = new TelemetryFeed();
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "cfr_user_event",
event: "CLICK",
message_id: "cfr_message_01",
},
};
let { ping } = await instance.createASRouterEvent(action);
await assertASRouterEventPingValid(ping);
Assert.equal(ping.event, "CLICK");
});
add_task(async function test_createASRouterEvent_call_correctPolicy() {
let testCallCorrectPolicy = async (expectedPolicyFnName, data) => {
info(
`TelemetryFeed.createASRouterEvent should call ${expectedPolicyFnName} ` +
`on action ${data.action} and event ${data.event}`
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, expectedPolicyFnName);
let action = { type: msg.AS_ROUTER_TELEMETRY_USER_EVENT, data };
await instance.createASRouterEvent(action);
Assert.ok(
instance[expectedPolicyFnName].calledOnce,
`TelemetryFeed.${expectedPolicyFnName} called`
);
sandbox.restore();
};
testCallCorrectPolicy("applyCFRPolicy", {
action: "cfr_user_event",
event: "IMPRESSION",
message_id: "cfr_message_01",
});
testCallCorrectPolicy("applyOnboardingPolicy", {
action: "onboarding_user_event",
event: "CLICK_BUTTON",
message_id: "onboarding_message_01",
});
testCallCorrectPolicy("applyToolbarBadgePolicy", {
action: "badge_user_event",
event: "IMPRESSION",
message_id: "badge_message_01",
});
testCallCorrectPolicy("applyMomentsPolicy", {
action: "moments_user_event",
event: "CLICK_BUTTON",
message_id: "moments_message_01",
});
testCallCorrectPolicy("applySpotlightPolicy", {
action: "spotlight_user_event",
event: "CLICK",
message_id: "SPOTLIGHT_MESSAGE_93",
});
testCallCorrectPolicy("applyToastNotificationPolicy", {
action: "toast_notification_user_event",
event: "IMPRESSION",
message_id: "TEST_TOAST_NOTIFICATION1",
});
testCallCorrectPolicy("applyUndesiredEventPolicy", {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
});
});
add_task(async function test_createASRouterEvent_stringify_event_context() {
info(
"TelemetryFeed.createASRouterEvent should stringify event_context if " +
"it is an Object"
);
let instance = new TelemetryFeed();
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: { foo: "bar" },
},
};
let { ping } = await instance.createASRouterEvent(action);
Assert.equal(ping.event_context, JSON.stringify({ foo: "bar" }));
});
add_task(async function test_createASRouterEvent_not_stringify_event_context() {
info(
"TelemetryFeed.createASRouterEvent should not stringify event_context " +
"if it is a String"
);
let instance = new TelemetryFeed();
let action = {
type: msg.AS_ROUTER_TELEMETRY_USER_EVENT,
data: {
action: "asrouter_undesired_event",
event: "UNDESIRED_EVENT",
event_context: "foo",
},
};
let { ping } = await instance.createASRouterEvent(action);
Assert.equal(ping.event_context, "foo");
});
add_task(async function test_sendUTEvent_call_right_function() {
info("TelemetryFeed.sendUTEvent should call the UT event function passed in");
let sandbox = sinon.createSandbox();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
let event = {};
let instance = new TelemetryFeed();
sandbox.stub(instance.utEvents, "sendUserEvent");
instance.addSession("foo");
await instance.sendUTEvent(event, instance.utEvents.sendUserEvent);
Assert.ok(instance.utEvents.sendUserEvent.calledWith(event));
Services.prefs.clearUserPref(PREF_TELEMETRY);
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
sandbox.restore();
});
add_task(async function test_setLoadTriggerInfo() {
info(
"TelemetryFeed.setLoadTriggerInfo should call saveSessionPerfData " +
"w/load_trigger_{ts,type} data"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "saveSessionPerfData");
instance.browserOpenNewtabStart();
instance.addSession("port123");
instance.setLoadTriggerInfo("port123");
Assert.ok(
instance.saveSessionPerfData.calledWith(
"port123",
sinon.match({
load_trigger_type: "menu_plus_or_keyboard",
load_trigger_ts: sinon.match.number,
})
),
"TelemetryFeed.saveSessionPerfData was called with the right arguments"
);
sandbox.restore();
});
add_task(async function test_setLoadTriggerInfo_no_saveSessionPerfData() {
info(
"TelemetryFeed.setLoadTriggerInfo should not call saveSessionPerfData " +
"when getting mark throws"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "saveSessionPerfData");
instance.addSession("port123");
instance.setLoadTriggerInfo("port123");
Assert.ok(
instance.saveSessionPerfData.notCalled,
"TelemetryFeed.saveSessionPerfData was not called"
);
sandbox.restore();
});
add_task(async function test_saveSessionPerfData_updates_session_with_data() {
info(
"TelemetryFeed.saveSessionPerfData should update the given session " +
"with the given data"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
instance.addSession("port123");
Assert.equal(instance.sessions.get("port123").fake_ts, undefined);
let data = { fake_ts: 456, other_fake_ts: 789 };
instance.saveSessionPerfData("port123", data);
let sessionPerfData = instance.sessions.get("port123").perf;
Assert.equal(sessionPerfData.fake_ts, 456);
Assert.equal(sessionPerfData.other_fake_ts, 789);
sandbox.restore();
});
add_task(async function test_saveSessionPerfData_calls_setLoadTriggerInfo() {
info(
"TelemetryFeed.saveSessionPerfData should call setLoadTriggerInfo if " +
"data has visibility_event_rcvd_ts"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "setLoadTriggerInfo");
instance.addSession("port123");
let data = { visibility_event_rcvd_ts: 444455 };
instance.saveSessionPerfData("port123", data);
Assert.ok(
instance.setLoadTriggerInfo.calledOnce,
"TelemetryFeed.setLoadTriggerInfo was called once"
);
Assert.ok(instance.setLoadTriggerInfo.calledWithExactly("port123"));
Assert.equal(
instance.sessions.get("port123").perf.visibility_event_rcvd_ts,
444455
);
sandbox.restore();
});
add_task(
async function test_saveSessionPerfData_does_not_call_setLoadTriggerInfo() {
info(
"TelemetryFeed.saveSessionPerfData shouldn't call setLoadTriggerInfo if " +
"data has no visibility_event_rcvd_ts"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "setLoadTriggerInfo");
instance.addSession("port123");
instance.saveSessionPerfData("port123", { monkeys_ts: 444455 });
Assert.ok(
instance.setLoadTriggerInfo.notCalled,
"TelemetryFeed.setLoadTriggerInfo was not called"
);
sandbox.restore();
}
);
add_task(
async function test_saveSessionPerfData_does_not_call_setLoadTriggerInfo_about_home() {
info(
"TelemetryFeed.saveSessionPerfData should not call setLoadTriggerInfo when " +
"url is about:home"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "setLoadTriggerInfo");
instance.addSession("port123", "about:home");
let data = { visibility_event_rcvd_ts: 444455 };
instance.saveSessionPerfData("port123", data);
Assert.ok(
instance.setLoadTriggerInfo.notCalled,
"TelemetryFeed.setLoadTriggerInfo was not called"
);
sandbox.restore();
}
);
add_task(
async function test_saveSessionPerfData_calls_maybeRecordTopsitesPainted() {
info(
"TelemetryFeed.saveSessionPerfData should call maybeRecordTopsitesPainted " +
"when url is about:home and topsites_first_painted_ts is given"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
const TOPSITES_FIRST_PAINTED_TS = 44455;
let data = { topsites_first_painted_ts: TOPSITES_FIRST_PAINTED_TS };
sandbox.stub(AboutNewTab, "maybeRecordTopsitesPainted");
instance.addSession("port123", "about:home");
instance.saveSessionPerfData("port123", data);
Assert.ok(
AboutNewTab.maybeRecordTopsitesPainted.calledOnce,
"AboutNewTab.maybeRecordTopsitesPainted called once"
);
Assert.ok(
AboutNewTab.maybeRecordTopsitesPainted.calledWith(
TOPSITES_FIRST_PAINTED_TS
)
);
sandbox.restore();
}
);
add_task(
async function test_saveSessionPerfData_records_Glean_newtab_opened_event() {
info(
"TelemetryFeed.saveSessionPerfData should record a Glean newtab.opened event " +
"with the correct visit_id when visibility event received"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "decafc0ffee";
const PAGE = "about:newtab";
let session = { page: PAGE, perf: {}, session_id: SESSION_ID };
let data = { visibility_event_rcvd_ts: 444455 };
sandbox.stub(instance.sessions, "get").returns(session);
instance.saveSessionPerfData("port123", data);
let newtabOpenedEvents = Glean.newtab.opened.testGetValue();
Assert.deepEqual(newtabOpenedEvents[0].extra, {
newtab_visit_id: SESSION_ID,
source: PAGE,
});
sandbox.restore();
}
);
add_task(async function test_uninit_deregisters_observer() {
info(
"TelemetryFeed.uninit should make this.browserOpenNewtabStart() stop " +
"observing browser-open-newtab-start"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let countObservers = () => {
return [...Services.obs.enumerateObservers("browser-open-newtab-start")]
.length;
};
const ORIGINAL_COUNT = countObservers();
instance.init();
Assert.equal(countObservers(), ORIGINAL_COUNT + 1, "Observer was added");
instance.uninit();
Assert.equal(countObservers(), ORIGINAL_COUNT, "Observer was removed");
sandbox.restore();
});
add_task(async function test_onAction_basic_actions() {
let browser = Services.appShell
.createWindowlessBrowser(false)
.document.createElement("browser");
let testOnAction = (setupFn, action, checkFn) => {
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
setupFn(sandbox, instance);
instance.onAction(action);
checkFn(instance);
sandbox.restore();
};
info("TelemetryFeed.onAction should call .init() on an INIT action");
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "init");
sandbox.stub(instance, "sendPageTakeoverData");
},
{ type: at.INIT },
instance => {
Assert.ok(instance.init.calledOnce, "TelemetryFeed.init called once");
Assert.ok(
instance.sendPageTakeoverData.calledOnce,
"TelemetryFeed.sendPageTakeoverData called once"
);
}
);
info("TelemetryFeed.onAction should call .uninit() on an UNINIT action");
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "uninit");
},
{ type: at.UNINIT },
instance => {
Assert.ok(instance.uninit.calledOnce, "TelemetryFeed.uninit called once");
}
);
info(
"TelemetryFeed.onAction should call .handleNewTabInit on a " +
"NEW_TAB_INIT action"
);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "handleNewTabInit");
},
ac.AlsoToMain({
type: at.NEW_TAB_INIT,
data: { url: "about:newtab", browser },
}),
instance => {
Assert.ok(
instance.handleNewTabInit.calledOnce,
"TelemetryFeed.handleNewTabInit called once"
);
}
);
info(
"TelemetryFeed.onAction should call .addSession() on a " +
"NEW_TAB_INIT action"
);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "addSession").returns({ perf: {} });
sandbox.stub(instance, "setLoadTriggerInfo");
},
ac.AlsoToMain(
{
type: at.NEW_TAB_INIT,
data: { url: "about:monkeys", browser },
},
"port123"
),
instance => {
Assert.ok(
instance.addSession.calledOnce,
"TelemetryFeed.addSession called once"
);
Assert.ok(instance.addSession.calledWith("port123", "about:monkeys"));
}
);
info(
"TelemetryFeed.onAction should call .endSession() on a " +
"NEW_TAB_UNLOAD action"
);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "endSession");
},
ac.AlsoToMain({ type: at.NEW_TAB_UNLOAD }, "port123"),
instance => {
Assert.ok(
instance.endSession.calledOnce,
"TelemetryFeed.endSession called once"
);
Assert.ok(instance.endSession.calledWith("port123"));
}
);
info(
"TelemetryFeed.onAction should call .saveSessionPerfData " +
"on SAVE_SESSION_PERF_DATA"
);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "saveSessionPerfData");
},
ac.AlsoToMain(
{ type: at.SAVE_SESSION_PERF_DATA, data: { some_ts: 10 } },
"port123"
),
instance => {
Assert.ok(
instance.saveSessionPerfData.calledOnce,
"TelemetryFeed.saveSessionPerfData called once"
);
Assert.ok(
instance.saveSessionPerfData.calledWith("port123", { some_ts: 10 })
);
}
);
info(
"TelemetryFeed.onAction should send an event on a TELEMETRY_USER_EVENT " +
"action"
);
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "createUserEvent");
sandbox.stub(instance.utEvents, "sendUserEvent");
},
{ type: at.TELEMETRY_USER_EVENT },
instance => {
Assert.ok(
instance.createUserEvent.calledOnce,
"TelemetryFeed.createUserEvent called once"
);
Assert.ok(
instance.createUserEvent.calledWith({ type: at.TELEMETRY_USER_EVENT })
);
Assert.ok(
instance.utEvents.sendUserEvent.calledOnce,
"TelemetryFeed.utEvents.sendUserEvent called once"
);
Assert.ok(
instance.utEvents.sendUserEvent.calledWith(
instance.createUserEvent.returnValue
)
);
}
);
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
Services.prefs.clearUserPref(PREF_TELEMETRY);
info(
"TelemetryFeed.onAction should send an event on a " +
"DISCOVERY_STREAM_USER_EVENT action"
);
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
testOnAction(
(sandbox, instance) => {
sandbox.stub(instance, "createUserEvent");
sandbox.stub(instance.utEvents, "sendUserEvent");
},
{ type: at.DISCOVERY_STREAM_USER_EVENT },
instance => {
Assert.ok(
instance.createUserEvent.calledOnce,
"TelemetryFeed.createUserEvent called once"
);
Assert.ok(
instance.createUserEvent.calledWith({
type: at.DISCOVERY_STREAM_USER_EVENT,
data: {
value: {
pocket_logged_in_status: Glean.pocket.isSignedIn.testGetValue(),
},
},
})
);
Assert.ok(
instance.utEvents.sendUserEvent.calledOnce,
"TelemetryFeed.utEvents.sendUserEvent called once"
);
Assert.ok(
instance.utEvents.sendUserEvent.calledWith(
instance.createUserEvent.returnValue
)
);
}
);
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
Services.prefs.clearUserPref(PREF_TELEMETRY);
});
add_task(async function test_onAction_calls_handleASRouterUserEvent() {
let actions = [
msg.AS_ROUTER_TELEMETRY_USER_EVENT,
msg.TOOLBAR_BADGE_TELEMETRY,
msg.TOOLBAR_PANEL_TELEMETRY,
msg.MOMENTS_PAGE_TELEMETRY,
msg.DOORHANGER_TELEMETRY,
];
Services.prefs.setBoolPref(PREF_EVENT_TELEMETRY, true);
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
actions.forEach(type => {
info(`Testing ${type} action`);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
const eventHandler = sandbox.spy(instance, "handleASRouterUserEvent");
const action = {
type,
data: { event: "CLICK" },
};
instance.onAction(action);
Assert.ok(eventHandler.calledWith(action));
sandbox.restore();
});
Services.prefs.clearUserPref(PREF_EVENT_TELEMETRY);
Services.prefs.clearUserPref(PREF_TELEMETRY);
});
add_task(
async function test_onAction_calls_handleDiscoveryStreamImpressionStats_ds() {
info(
"TelemetryFeed.onAction should call " +
".handleDiscoveryStreamImpressionStats on a " +
"DISCOVERY_STREAM_IMPRESSION_STATS action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let session = {};
sandbox.stub(instance.sessions, "get").returns(session);
let data = { source: "foo", tiles: [{ id: 1 }] };
let action = { type: at.DISCOVERY_STREAM_IMPRESSION_STATS, data };
sandbox.spy(instance, "handleDiscoveryStreamImpressionStats");
instance.onAction(ac.AlsoToMain(action, "port123"));
Assert.ok(
instance.handleDiscoveryStreamImpressionStats.calledWith("port123", data)
);
sandbox.restore();
}
);
add_task(
async function test_onAction_calls_handleTopSitesSponsoredImpressionStats() {
info(
"TelemetryFeed.onAction should call " +
".handleTopSitesSponsoredImpressionStats on a " +
"TOP_SITES_SPONSORED_IMPRESSION_STATS action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let session = {};
sandbox.stub(instance.sessions, "get").returns(session);
let data = { type: "impression", tile_id: 42, position: 1 };
let action = { type: at.TOP_SITES_SPONSORED_IMPRESSION_STATS, data };
sandbox.spy(instance, "handleTopSitesSponsoredImpressionStats");
instance.onAction(ac.AlsoToMain(action));
Assert.ok(
instance.handleTopSitesSponsoredImpressionStats.calledOnce,
"TelemetryFeed.handleTopSitesSponsoredImpressionStats called once"
);
Assert.deepEqual(
instance.handleTopSitesSponsoredImpressionStats.firstCall.args[0].data,
data
);
sandbox.restore();
}
);
add_task(async function test_onAction_calls_handleAboutSponsoredTopSites() {
info(
"TelemetryFeed.onAction should call " +
".handleAboutSponsoredTopSites on a " +
"ABOUT_SPONSORED_TOP_SITES action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let data = { position: 0, advertiser_name: "moo", tile_id: 42 };
let action = { type: at.ABOUT_SPONSORED_TOP_SITES, data };
sandbox.spy(instance, "handleAboutSponsoredTopSites");
instance.onAction(ac.AlsoToMain(action));
Assert.ok(
instance.handleAboutSponsoredTopSites.calledOnce,
"TelemetryFeed.handleAboutSponsoredTopSites called once"
);
sandbox.restore();
});
add_task(async function test_onAction_calls_handleBlockUrl() {
info(
"TelemetryFeed.onAction should call #handleBlockUrl on a BLOCK_URL action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let data = { position: 0, advertiser_name: "moo", tile_id: 42 };
let action = { type: at.BLOCK_URL, data };
sandbox.spy(instance, "handleBlockUrl");
instance.onAction(ac.AlsoToMain(action));
Assert.ok(
instance.handleBlockUrl.calledOnce,
"TelemetryFeed.handleBlockUrl called once"
);
sandbox.restore();
});
add_task(
async function test_onAction_calls_handleTopSitesOrganicImpressionStats() {
info(
"TelemetryFeed.onAction should call .handleTopSitesOrganicImpressionStats " +
"on a TOP_SITES_ORGANIC_IMPRESSION_STATS action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let session = {};
sandbox.stub(instance.sessions, "get").returns(session);
let data = { type: "impression", position: 1 };
let action = { type: at.TOP_SITES_ORGANIC_IMPRESSION_STATS, data };
sandbox.spy(instance, "handleTopSitesOrganicImpressionStats");
instance.onAction(ac.AlsoToMain(action));
Assert.ok(
instance.handleTopSitesOrganicImpressionStats.calledOnce,
"TelemetryFeed.handleTopSitesOrganicImpressionStats called once"
);
Assert.deepEqual(
instance.handleTopSitesOrganicImpressionStats.firstCall.args[0].data,
data
);
sandbox.restore();
}
);
add_task(async function test_handleNewTabInit_sets_preloaded_session() {
info(
"TelemetryFeed.handleNewTabInit should set the session as preloaded " +
"if the browser is preloaded"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let session = { perf: {} };
let preloadedBrowser = {
getAttribute() {
return "preloaded";
},
};
sandbox.stub(instance, "addSession").returns(session);
instance.onAction(
ac.AlsoToMain({
type: at.NEW_TAB_INIT,
data: { url: "about:newtab", browser: preloadedBrowser },
})
);
Assert.ok(session.perf.is_preloaded, "is_preloaded property was set");
sandbox.restore();
});
add_task(async function test_handleNewTabInit_sets_nonpreloaded_session() {
info(
"TelemetryFeed.handleNewTabInit should set the session as non-preloaded " +
"if the browser is non-preloaded"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
let session = { perf: {} };
let preloadedBrowser = {
getAttribute() {
return "";
},
};
sandbox.stub(instance, "addSession").returns(session);
instance.onAction(
ac.AlsoToMain({
type: at.NEW_TAB_INIT,
data: { url: "about:newtab", browser: preloadedBrowser },
})
);
Assert.ok(!session.perf.is_preloaded, "is_preloaded property is not true");
sandbox.restore();
});
add_task(
async function test_SendASRouterUndesiredEvent_calls_handleASRouterUserEvent() {
info(
"TelemetryFeed.SendASRouterUndesiredEvent should call " +
"handleASRouterUserEvent"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(instance, "handleASRouterUserEvent");
instance.SendASRouterUndesiredEvent({ foo: "bar" });
Assert.ok(
instance.handleASRouterUserEvent.calledOnce,
"TelemetryFeed.handleASRouterUserEvent was called once"
);
let [payload] = instance.handleASRouterUserEvent.firstCall.args;
Assert.equal(payload.data.action, "asrouter_undesired_event");
Assert.equal(payload.data.foo, "bar");
sandbox.restore();
}
);
add_task(async function test_sendPageTakeoverData_homepage_category() {
info(
"TelemetryFeed.sendPageTakeoverData should call " +
"handleASRouterUserEvent"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
instance._classifySite = () => Promise.resolve("other");
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.homepageCategory.testGetValue(), "other");
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_newtab_category_custom() {
info(
"TelemetryFeed.sendPageTakeoverData should send correct newtab " +
"category for about:newtab set to custom URL"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
sandbox.stub(AboutNewTab, "newTabURLOverridden").get(() => true);
sandbox
.stub(AboutNewTab, "newTabURL")
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
instance._classifySite = () => Promise.resolve("other");
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.newtabCategory.testGetValue(), "other");
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_newtab_category_custom() {
info(
"TelemetryFeed.sendPageTakeoverData should not set home|newtab " +
"category if neither about:{home,newtab} are set to custom URL"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
instance._classifySite = () => Promise.resolve("other");
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.newtabCategory.testGetValue(), "enabled");
Assert.equal(Glean.newtab.homepageCategory.testGetValue(), "enabled");
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_newtab_category_extension() {
info(
"TelemetryFeed.sendPageTakeoverData should set correct home|newtab " +
"category when changed by extension"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ID = "{abc-foo-bar}";
sandbox.stub(ExtensionSettingsStore, "getSetting").returns({ id: ID });
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
instance._classifySite = () => Promise.resolve("other");
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.newtabCategory.testGetValue(), "extension");
Assert.equal(Glean.newtab.homepageCategory.testGetValue(), "extension");
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_newtab_disabled() {
info(
"TelemetryFeed.sendPageTakeoverData instruments when newtab is disabled"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
Services.prefs.setBoolPref("browser.newtabpage.enabled", false);
instance._classifySite = () => Promise.resolve("other");
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.newtabCategory.testGetValue(), "disabled");
Services.prefs.clearUserPref(PREF_TELEMETRY);
Services.prefs.clearUserPref("browser.newtabpage.enabled");
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_homepage_disabled() {
info(
"TelemetryFeed.sendPageTakeoverData instruments when homepage is disabled"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
sandbox.stub(HomePage, "overridden").get(() => true);
await instance.sendPageTakeoverData();
Assert.equal(Glean.newtab.homepageCategory.testGetValue(), "disabled");
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(async function test_sendPageTakeoverData_newtab_ping() {
info("TelemetryFeed.sendPageTakeoverData should send a 'newtab' ping");
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
let pingSubmitted = new Promise(resolve => {
GleanPings.newtab.testBeforeNextSubmit(reason => {
Assert.equal(reason, "component_init");
resolve();
});
});
await instance.sendPageTakeoverData();
await pingSubmitted;
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
});
add_task(
async function test_handleDiscoveryStreamImpressionStats_should_throw() {
info(
"TelemetryFeed.handleDiscoveryStreamImpressionStats should throw " +
"for a missing session"
);
let instance = new TelemetryFeed();
try {
instance.handleDiscoveryStreamImpressionStats("a_missing_port", {});
Assert.ok(false, "Should not have reached here.");
} catch (e) {
Assert.ok(true, "Should have thrown for a missing session.");
}
}
);
add_task(
async function test_handleDiscoveryStreamImpressionStats_instrument_pocket_impressions() {
info(
"TelemetryFeed.handleDiscoveryStreamImpressionStats should throw " +
"for a missing session"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "1337cafe";
const POS_1 = 1;
const POS_2 = 4;
const SHIM = "Y29uc2lkZXIgeW91ciBjdXJpb3NpdHkgcmV3YXJkZWQ=";
const FETCH_TIMESTAMP = new Date("March 22, 2024 10:15:20");
const NEWTAB_CREATION_TIMESTAMP = new Date("March 23, 2024 11:10:30");
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let pingSubmitted = new Promise(resolve => {
GleanPings.spoc.testBeforeNextSubmit(reason => {
Assert.equal(reason, "impression");
let pocketImpressions = Glean.pocket.impression.testGetValue();
Assert.equal(pocketImpressions.length, 2);
Assert.deepEqual(pocketImpressions[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: String(POS_1),
recommendation_id: "decaf-c0ff33",
tile_id: String(1),
});
Assert.deepEqual(pocketImpressions[1].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(true),
position: String(POS_2),
tile_id: String(2),
});
Assert.equal(Glean.pocket.shim.testGetValue(), SHIM);
Assert.deepEqual(
Glean.pocket.fetchTimestamp.testGetValue(),
FETCH_TIMESTAMP
);
Assert.deepEqual(
Glean.pocket.newtabCreationTimestamp.testGetValue(),
NEWTAB_CREATION_TIMESTAMP
);
resolve();
});
});
instance.handleDiscoveryStreamImpressionStats("_", {
source: "foo",
tiles: [
{
id: 1,
pos: POS_1,
type: "organic",
recommendation_id: "decaf-c0ff33",
},
{
id: 2,
pos: POS_2,
type: "spoc",
recommendation_id: undefined,
shim: SHIM,
fetchTimestamp: FETCH_TIMESTAMP.valueOf(),
},
],
window_inner_width: 1000,
window_inner_height: 900,
firstVisibleTimestamp: NEWTAB_CREATION_TIMESTAMP.valueOf(),
});
await pingSubmitted;
sandbox.restore();
}
);
add_task(
async function test_handleASRouterUserEvent_calls_submitGleanPingForPing() {
info(
"TelemetryFeed.handleASRouterUserEvent should call " +
"submitGleanPingForPing on known pingTypes when telemetry is enabled"
);
let data = {
action: "onboarding_user_event",
event: "IMPRESSION",
message_id: "12345",
};
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
sandbox.spy(AboutWelcomeTelemetry.prototype, "submitGleanPingForPing");
await instance.handleASRouterUserEvent({ data });
Assert.ok(
AboutWelcomeTelemetry.prototype.submitGleanPingForPing.calledOnce,
"AboutWelcomeTelemetry.submitGleanPingForPing called once"
);
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
}
);
add_task(
async function test_handleASRouterUserEvent_no_submit_unknown_pingTypes() {
info(
"TelemetryFeed.handleASRouterUserEvent not submit pings on unknown pingTypes"
);
let data = {
action: "unknown_event",
event: "IMPRESSION",
message_id: "12345",
};
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
Services.prefs.setBoolPref(PREF_TELEMETRY, true);
sandbox.spy(AboutWelcomeTelemetry.prototype, "submitGleanPingForPing");
await instance.handleASRouterUserEvent({ data });
Assert.ok(
AboutWelcomeTelemetry.prototype.submitGleanPingForPing.notCalled,
"AboutWelcomeTelemetry.submitGleanPingForPing not called"
);
Services.prefs.clearUserPref(PREF_TELEMETRY);
sandbox.restore();
}
);
add_task(
async function test_isInCFRCohort_return_false_for_no_CFR_experiment() {
info(
"TelemetryFeed.isInCFRCohort should return false if there " +
"is no CFR experiment registered"
);
let instance = new TelemetryFeed();
Assert.ok(
!instance.isInCFRCohort,
"Should not be in CFR cohort by default"
);
}
);
add_task(
async function test_isInCFRCohort_return_true_for_registered_CFR_experiment() {
info(
"TelemetryFeed.isInCFRCohort should return true if there " +
"is a CFR experiment registered"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
sandbox.stub(ExperimentAPI, "getExperimentMetaData").returns({
slug: "SOME-CFR-EXP",
});
Assert.ok(instance.isInCFRCohort, "Should be in a CFR cohort");
Assert.equal(
ExperimentAPI.getExperimentMetaData.firstCall.args[0].featureId,
"cfr"
);
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesSponsoredImpressionStats_add_keyed_scalar() {
info(
"TelemetryFeed.handleTopSitesSponsoredImpressionStats should add to " +
"keyed scalar on an impression event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.telemetry.clearScalars();
let data = {
type: "impression",
tile_id: 42,
source: "newtab",
position: 0,
};
await instance.handleTopSitesSponsoredImpressionStats({ data });
TelemetryTestUtils.assertKeyedScalar(
TelemetryTestUtils.getProcessScalars("parent", true, true),
"contextual.services.topsites.impression",
"newtab_1",
1
);
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesSponsoredImpressionStats_add_keyed_scalar_click() {
info(
"TelemetryFeed.handleTopSitesSponsoredImpressionStats should add to " +
"keyed scalar on a click event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.telemetry.clearScalars();
let data = {
type: "click",
tile_id: 42,
source: "newtab",
position: 0,
};
await instance.handleTopSitesSponsoredImpressionStats({ data });
TelemetryTestUtils.assertKeyedScalar(
TelemetryTestUtils.getProcessScalars("parent", true, true),
"contextual.services.topsites.click",
"newtab_1",
1
);
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesSponsoredImpressionStats_record_glean_impression() {
info(
"TelemetryFeed.handleTopSitesSponsoredImpressionStats should record a " +
"Glean topsites.impression event on an impression event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
type: "impression",
tile_id: 42,
source: "newtab",
position: 1,
advertiser: "adnoid ads",
};
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
await instance.handleTopSitesSponsoredImpressionStats({ data });
let impressions = Glean.topsites.impression.testGetValue();
Assert.equal(impressions.length, 1, "Should have recorded 1 impression");
Assert.deepEqual(impressions[0].extra, {
advertiser_name: "adnoid ads",
tile_id: data.tile_id,
newtab_visit_id: SESSION_ID,
is_sponsored: String(true),
position: String(1),
});
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesSponsoredImpressionStats_record_glean_click() {
info(
"TelemetryFeed.handleTopSitesSponsoredImpressionStats should record " +
"a Glean topsites.click event on a click event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
type: "click",
advertiser: "test advertiser",
tile_id: 42,
source: "newtab",
position: 0,
};
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
await instance.handleTopSitesSponsoredImpressionStats({ data });
let clicks = Glean.topsites.click.testGetValue();
Assert.equal(clicks.length, 1, "Should have recorded 1 click");
Assert.deepEqual(clicks[0].extra, {
advertiser_name: "test advertiser",
tile_id: data.tile_id,
newtab_visit_id: SESSION_ID,
is_sponsored: String(true),
position: String(0),
});
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesSponsoredImpressionStats_no_submit_unknown_pingType() {
info(
"TelemetryFeed.handleTopSitesSponsoredImpressionStats should not " +
"submit on unknown pingTypes"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = { type: "unknown_type" };
await instance.handleTopSitesSponsoredImpressionStats({ data });
let impressions = Glean.topsites.impression.testGetValue();
Assert.ok(!impressions, "Should not have recorded any impressions");
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesOrganicImpressionStats_record_glean_topsites_impression() {
info(
"TelemetryFeed.handleTopSitesOrganicImpressionStats should record a " +
"Glean topsites.impression event on an impression event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
type: "impression",
source: "newtab",
position: 0,
};
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
await instance.handleTopSitesOrganicImpressionStats({ data });
let impressions = Glean.topsites.impression.testGetValue();
Assert.equal(impressions.length, 1, "Recorded 1 impression");
Assert.deepEqual(impressions[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: String(0),
});
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesOrganicImpressionStats_record_glean_topsites_click() {
info(
"TelemetryFeed.handleTopSitesOrganicImpressionStats should record a " +
"Glean topsites.click event on a click event"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
type: "click",
source: "newtab",
position: 0,
};
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
await instance.handleTopSitesOrganicImpressionStats({ data });
let clicks = Glean.topsites.click.testGetValue();
Assert.equal(clicks.length, 1, "Recorded 1 click");
Assert.deepEqual(clicks[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: String(0),
});
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesOrganicImpressionStats_no_recording() {
info(
"TelemetryFeed.handleTopSitesOrganicImpressionStats should not " +
"record events on an unknown session"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
sandbox.stub(instance.sessions, "get").returns(false);
await instance.handleTopSitesOrganicImpressionStats({});
Assert.ok(!Glean.topsites.click.testGetValue(), "Click was not recorded");
Assert.ok(
!Glean.topsites.impression.testGetValue(),
"Impression was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleTopSitesOrganicImpressionStats_no_recording_with_session() {
info(
"TelemetryFeed.handleTopSitesOrganicImpressionStats should not record " +
"events on an unknown impressionStats action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
await instance.handleTopSitesOrganicImpressionStats({ type: "unknown" });
Assert.ok(!Glean.topsites.click.testGetValue(), "Click was not recorded");
Assert.ok(
!Glean.topsites.impression.testGetValue(),
"Impression was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_no_recording_with_session() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent correctly handles " +
"action with no `data`"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let action = ac.DiscoveryStreamUserEvent();
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
Assert.ok(
!Glean.pocket.topicClick.testGetValue(),
"Pocket topicClick was not recorded"
);
Assert.ok(
!Glean.pocket.click.testGetValue(),
"Pocket click was not recorded"
);
Assert.ok(
!Glean.pocket.save.testGetValue(),
"Pocket save was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_click_with_no_value() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent correctly handles " +
"CLICK data with no value"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "POPULAR_TOPICS",
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let topicClicks = Glean.pocket.topicClick.testGetValue();
Assert.equal(topicClicks.length, 1, "Recorded 1 click");
Assert.deepEqual(topicClicks[0].extra, {
newtab_visit_id: SESSION_ID,
});
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_non_popular_click_with_no_value() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent correctly handles " +
"non-POPULAR_TOPICS CLICK data with no value"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "not-POPULAR_TOPICS",
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
Assert.ok(
!Glean.pocket.topicClick.testGetValue(),
"Pocket topicClick was not recorded"
);
Assert.ok(
!Glean.pocket.click.testGetValue(),
"Pocket click was not recorded"
);
Assert.ok(
!Glean.pocket.save.testGetValue(),
"Pocket save was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_non_popular_click() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent correctly handles " +
"CLICK data with non-POPULAR_TOPICS source"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const TOPIC = "atopic";
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "not-POPULAR_TOPICS",
value: {
card_type: "topics_widget",
topic: TOPIC,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let topicClicks = Glean.pocket.topicClick.testGetValue();
Assert.equal(topicClicks.length, 1, "Recorded 1 click");
Assert.deepEqual(topicClicks[0].extra, {
newtab_visit_id: SESSION_ID,
topic: TOPIC,
});
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_without_card_type() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent doesn't instrument " +
"a CLICK without a card_type"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "not-POPULAR_TOPICS",
value: {
card_type: "not spoc, organic, or topics_widget",
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
Assert.ok(
!Glean.pocket.topicClick.testGetValue(),
"Pocket topicClick was not recorded"
);
Assert.ok(
!Glean.pocket.click.testGetValue(),
"Pocket click was not recorded"
);
Assert.ok(
!Glean.pocket.save.testGetValue(),
"Pocket save was not recorded"
);
sandbox.restore();
}
);
add_task(async function test_handleDiscoveryStreamUserEvent_popular_click() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a popular " +
"topic click"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const TOPIC = "entertainment";
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "POPULAR_TOPICS",
value: {
card_type: "topics_widget",
topic: TOPIC,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let topicClicks = Glean.pocket.topicClick.testGetValue();
Assert.equal(topicClicks.length, 1, "Recorded 1 click");
Assert.deepEqual(topicClicks[0].extra, {
newtab_visit_id: SESSION_ID,
topic: TOPIC,
});
sandbox.restore();
});
add_task(async function test_handleDiscoveryStreamUserEvent_tooltip_click() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a " +
"tooltip click"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const feature = "SPONSORED_CONTENT_INFO";
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
source: "FEATURE_HIGHLIGHT",
value: {
feature,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let tooltipClicks = Glean.newtab.tooltipClick.testGetValue();
Assert.equal(tooltipClicks.length, 1, "Recorded 1 click");
Assert.deepEqual(tooltipClicks[0].extra, {
newtab_visit_id: SESSION_ID,
feature,
});
sandbox.restore();
});
add_task(
async function test_handleDiscoveryStreamUserEvent_organic_top_stories_click() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments an organic " +
"top stories click"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
action_position: ACTION_POSITION,
value: {
card_type: "organic",
recommendation_id: "decaf-c0ff33",
tile_id: 314623757745896,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let clicks = Glean.pocket.click.testGetValue();
Assert.equal(clicks.length, 1, "Recorded 1 click");
Assert.deepEqual(clicks[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: ACTION_POSITION,
recommendation_id: "decaf-c0ff33",
tile_id: String(314623757745896),
});
Assert.ok(
!Glean.pocket.shim.testGetValue(),
"Pocket shim was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_sponsored_top_stories_click() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a sponsored " +
"top stories click"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
const SHIM = "Y29uc2lkZXIgeW91ciBjdXJpb3NpdHkgcmV3YXJkZWQ=";
const FETCH_TIMESTAMP = new Date("March 22, 2024 10:15:20");
const NEWTAB_CREATION_TIMESTAMP = new Date("March 23, 2024 11:10:30");
let action = ac.DiscoveryStreamUserEvent({
event: "CLICK",
action_position: ACTION_POSITION,
value: {
card_type: "spoc",
recommendation_id: undefined,
tile_id: 448685088,
shim: SHIM,
fetchTimestamp: FETCH_TIMESTAMP.valueOf(),
firstVisibleTimestamp: NEWTAB_CREATION_TIMESTAMP.valueOf(),
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let pingSubmitted = new Promise(resolve => {
GleanPings.spoc.testBeforeNextSubmit(reason => {
Assert.equal(reason, "click");
Assert.deepEqual(
Glean.pocket.fetchTimestamp.testGetValue(),
FETCH_TIMESTAMP
);
Assert.deepEqual(
Glean.pocket.newtabCreationTimestamp.testGetValue(),
NEWTAB_CREATION_TIMESTAMP
);
resolve();
});
});
instance.handleDiscoveryStreamUserEvent(action);
let clicks = Glean.pocket.click.testGetValue();
Assert.equal(clicks.length, 1, "Recorded 1 click");
Assert.deepEqual(clicks[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(true),
position: ACTION_POSITION,
tile_id: String(448685088),
});
await pingSubmitted;
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_organic_top_stories_save() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a save of an " +
"organic top story"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
let action = ac.DiscoveryStreamUserEvent({
event: "SAVE_TO_POCKET",
action_position: ACTION_POSITION,
value: {
card_type: "organic",
recommendation_id: "decaf-c0ff33",
tile_id: 314623757745896,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let saves = Glean.pocket.save.testGetValue();
Assert.equal(saves.length, 1, "Recorded 1 save");
Assert.deepEqual(saves[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: ACTION_POSITION,
recommendation_id: "decaf-c0ff33",
tile_id: String(314623757745896),
});
Assert.ok(
!Glean.pocket.shim.testGetValue(),
"Pocket shim was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_sponsored_top_stories_save() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a save of a " +
"sponsored top story"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
const SHIM = "Y29uc2lkZXIgeW91ciBjdXJpb3NpdHkgcmV3YXJkZWQ=";
const FETCH_TIMESTAMP = new Date("March 22, 2024 10:15:20");
const NEWTAB_CREATION_TIMESTAMP = new Date("March 23, 2024 11:10:30");
let action = ac.DiscoveryStreamUserEvent({
event: "SAVE_TO_POCKET",
action_position: ACTION_POSITION,
value: {
card_type: "spoc",
recommendation_id: undefined,
tile_id: 448685088,
shim: SHIM,
fetchTimestamp: FETCH_TIMESTAMP.valueOf(),
newtabCreationTimestamp: NEWTAB_CREATION_TIMESTAMP.valueOf(),
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let pingSubmitted = new Promise(resolve => {
GleanPings.spoc.testBeforeNextSubmit(reason => {
Assert.equal(reason, "save");
Assert.equal(
Glean.pocket.shim.testGetValue(),
SHIM,
"Pocket shim was recorded"
);
Assert.deepEqual(
Glean.pocket.fetchTimestamp.testGetValue(),
FETCH_TIMESTAMP
);
Assert.deepEqual(
Glean.pocket.newtabCreationTimestamp.testGetValue(),
NEWTAB_CREATION_TIMESTAMP
);
resolve();
});
});
instance.handleDiscoveryStreamUserEvent(action);
let saves = Glean.pocket.save.testGetValue();
Assert.equal(saves.length, 1, "Recorded 1 save");
Assert.deepEqual(saves[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(true),
position: ACTION_POSITION,
tile_id: String(448685088),
});
await pingSubmitted;
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_sponsored_top_stories_save_no_value() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a save of a " +
"sponsored top story, without `value`"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
let action = ac.DiscoveryStreamUserEvent({
event: "SAVE_TO_POCKET",
action_position: ACTION_POSITION,
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let saves = Glean.pocket.save.testGetValue();
Assert.equal(saves.length, 1, "Recorded 1 save");
Assert.deepEqual(saves[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: ACTION_POSITION,
});
Assert.ok(
!Glean.pocket.shim.testGetValue(),
"Pocket shim was not recorded"
);
sandbox.restore();
}
);
add_task(
async function test_handleDiscoveryStreamUserEvent_thumbs_down_event() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a thumbs down" +
" event of an organic story"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
let action = ac.DiscoveryStreamUserEvent({
event: "POCKET_THUMBS_DOWN",
action_position: ACTION_POSITION,
value: {
card_type: "organic",
recommendation_id: "decaf-c0ff33",
tile_id: 314623757745896,
thumbs_down: true,
thumbs_up: false,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let thumbVotes = Glean.pocket.thumbVotingInteraction.testGetValue();
Assert.equal(thumbVotes.length, 1, "Recorded 1 thumbs down");
Assert.deepEqual(thumbVotes[0].extra, {
newtab_visit_id: SESSION_ID,
recommendation_id: "decaf-c0ff33",
tile_id: String(314623757745896),
thumbs_down: String(true),
thumbs_up: String(false),
});
sandbox.restore();
}
);
add_task(async function test_handleDiscoveryStreamUserEvent_thumbs_up_event() {
info(
"TelemetryFeed.handleDiscoveryStreamUserEvent instruments a thumbs up" +
" event of an organic story"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const ACTION_POSITION = 42;
let action = ac.DiscoveryStreamUserEvent({
event: "POCKET_THUMBS_DOWN",
action_position: ACTION_POSITION,
value: {
card_type: "organic",
recommendation_id: "decaf-c0ff33",
tile_id: 314623757745896,
thumbs_down: false,
thumbs_up: true,
},
});
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleDiscoveryStreamUserEvent(action);
let thumbVotes = Glean.pocket.thumbVotingInteraction.testGetValue();
Assert.equal(thumbVotes.length, 1, "Recorded 1 thumbs down");
Assert.deepEqual(thumbVotes[0].extra, {
newtab_visit_id: SESSION_ID,
recommendation_id: "decaf-c0ff33",
tile_id: String(314623757745896),
thumbs_down: String(false),
thumbs_up: String(true),
});
sandbox.restore();
});
add_task(
async function test_handleAboutSponsoredTopSites_record_showPrivacyClick() {
info(
"TelemetryFeed.handleAboutSponsoredTopSites should record a Glean " +
"topsites.showPrivacyClick event on action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
position: 42,
advertiser_name: "mozilla",
tile_id: 4567,
};
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
instance.handleAboutSponsoredTopSites({ data });
let clicks = Glean.topsites.showPrivacyClick.testGetValue();
Assert.equal(clicks.length, 1, "Recorded 1 click");
Assert.deepEqual(clicks[0].extra, {
advertiser_name: data.advertiser_name,
tile_id: String(data.tile_id),
newtab_visit_id: SESSION_ID,
position: String(data.position),
});
sandbox.restore();
}
);
add_task(
async function test_handleAboutSponsoredTopSites_no_record_showPrivacyClick() {
info(
"TelemetryFeed.handleAboutSponsoredTopSites should not record a Glean " +
"topsites.showPrivacyClick event if there's no session"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
let data = {
position: 42,
advertiser_name: "mozilla",
tile_id: 4567,
};
sandbox.stub(instance.sessions, "get").returns(null);
instance.handleAboutSponsoredTopSites({ data });
let clicks = Glean.topsites.showPrivacyClick.testGetValue();
Assert.ok(!clicks, "Did not record any clicks");
sandbox.restore();
}
);
add_task(async function test_handleBlockUrl_no_record_dismisses() {
info(
"TelemetryFeed.handleBlockUrl shouldn't record events for pocket " +
"cards' dismisses"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let data = [
{
// Shouldn't record anything for this one
is_pocket_card: true,
position: 43,
tile_id: undefined,
},
];
await instance.handleBlockUrl({ data });
Assert.ok(
!Glean.topsites.dismiss.testGetValue(),
"Should not record a dismiss for Pocket cards"
);
sandbox.restore();
});
add_task(async function test_handleBlockUrl_record_dismiss_on_action() {
info(
"TelemetryFeed.handleBlockUrl should record a topsites.dismiss event " +
"on action"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let data = [
{
is_pocket_card: false,
position: 42,
advertiser_name: "mozilla",
tile_id: 4567,
isSponsoredTopSite: 1, // for some reason this is an int.
},
];
await instance.handleBlockUrl({ data });
let dismisses = Glean.topsites.dismiss.testGetValue();
Assert.equal(dismisses.length, 1, "Should have recorded 1 dismiss");
Assert.deepEqual(dismisses[0].extra, {
advertiser_name: data[0].advertiser_name,
tile_id: String(data[0].tile_id),
newtab_visit_id: SESSION_ID,
is_sponsored: String(!!data[0].isSponsoredTopSite),
position: String(data[0].position),
});
sandbox.restore();
});
add_task(
async function test_handleBlockUrl_record_dismiss_on_nonsponsored_action() {
info(
"TelemetryFeed.handleBlockUrl should record a Glean topsites.dismiss " +
"event on action on non-sponsored topsite"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
const SESSION_ID = "decafc0ffee";
sandbox.stub(instance.sessions, "get").returns({ session_id: SESSION_ID });
let data = [
{
is_pocket_card: false,
position: 42,
tile_id: undefined,
},
];
await instance.handleBlockUrl({ data });
let dismisses = Glean.topsites.dismiss.testGetValue();
Assert.equal(dismisses.length, 1, "Should have recorded 1 dismiss");
Assert.deepEqual(dismisses[0].extra, {
newtab_visit_id: SESSION_ID,
is_sponsored: String(false),
position: String(data[0].position),
});
sandbox.restore();
}
);
add_task(async function test_handleBlockUrl_no_record_dismiss_on_no_session() {
info(
"TelemetryFeed.handleBlockUrl should not record a Glean " +
"topsites.dismiss event if there's no session"
);
let sandbox = sinon.createSandbox();
let instance = new TelemetryFeed();
Services.fog.testResetFOG();
sandbox.stub(instance.sessions, "get").returns(null);
let data = {};
await instance.handleBlockUrl({ data });
Assert.ok(
!Glean.topsites.dismiss.testGetValue(),
"Should not have recorded a dismiss"
);
sandbox.restore();
});