Bug 1833975: Introduce MockRemoteSettings to sync RemoteSettings data r=adw
[gecko.git] / browser / components / urlbar / tests / engagementTelemetry / browser / head.js
blobc46d3253196f2c2b99d4a159ebbe91cfd2dc2a29
1 /* Any copyright is dedicated to the Public Domain.
2  * http://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 Services.scriptloader.loadSubScript(
7   "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/head-common.js",
8   this
9 );
11 ChromeUtils.defineESModuleGetters(this, {
12   QuickSuggest: "resource:///modules/QuickSuggest.sys.mjs",
13 });
15 const lazy = {};
17 XPCOMUtils.defineLazyGetter(lazy, "QuickSuggestTestUtils", () => {
18   const { QuickSuggestTestUtils: module } = ChromeUtils.importESModule(
19     "resource://testing-common/QuickSuggestTestUtils.sys.mjs"
20   );
21   module.init(this);
22   return module;
23 });
25 XPCOMUtils.defineLazyGetter(this, "MerinoTestUtils", () => {
26   const { MerinoTestUtils: module } = ChromeUtils.importESModule(
27     "resource://testing-common/MerinoTestUtils.sys.mjs"
28   );
29   module.init(this);
30   return module;
31 });
33 ChromeUtils.defineESModuleGetters(lazy, {
34   UrlbarTestUtils: "resource://testing-common/UrlbarTestUtils.sys.mjs",
35   sinon: "resource://testing-common/Sinon.sys.mjs",
36 });
38 async function addTopSites(url) {
39   for (let i = 0; i < 5; i++) {
40     await PlacesTestUtils.addVisits(url);
41   }
42   await updateTopSites(sites => {
43     return sites && sites[0] && sites[0].url == url;
44   });
47 function assertAbandonmentTelemetry(expectedExtraList) {
48   _assertGleanTelemetry("abandonment", expectedExtraList);
51 function assertEngagementTelemetry(expectedExtraList) {
52   _assertGleanTelemetry("engagement", expectedExtraList);
55 function assertImpressionTelemetry(expectedExtraList) {
56   _assertGleanTelemetry("impression", expectedExtraList);
59 function assertExposureTelemetry(expectedExtraList) {
60   _assertGleanTelemetry("exposure", expectedExtraList);
63 function _assertGleanTelemetry(telemetryName, expectedExtraList) {
64   const telemetries = Glean.urlbar[telemetryName].testGetValue() ?? [];
65   Assert.equal(
66     telemetries.length,
67     expectedExtraList.length,
68     "Telemetry event length matches expected event length."
69   );
71   for (let i = 0; i < telemetries.length; i++) {
72     const telemetry = telemetries[i];
73     Assert.equal(telemetry.category, "urlbar");
74     Assert.equal(telemetry.name, telemetryName);
76     const expectedExtra = expectedExtraList[i];
77     for (const key of Object.keys(expectedExtra)) {
78       Assert.equal(
79         telemetry.extra[key],
80         expectedExtra[key],
81         `${key} is correct`
82       );
83     }
84   }
87 async function ensureQuickSuggestInit({
88   merinoSuggestions = undefined,
89   config = undefined,
90 } = {}) {
91   return lazy.QuickSuggestTestUtils.ensureQuickSuggestInit({
92     config,
93     merinoSuggestions,
94     remoteSettingsResults: [
95       {
96         type: "data",
97         attachment: [
98           {
99             id: 1,
100             url: "https://example.com/sponsored",
101             title: "Sponsored suggestion",
102             keywords: ["sponsored"],
103             click_url: "https://example.com/click",
104             impression_url: "https://example.com/impression",
105             advertiser: "TestAdvertiser",
106             iab_category: "22 - Shopping",
107           },
108           {
109             id: 2,
110             url: `https://example.com/nonsponsored`,
111             title: "Non-sponsored suggestion",
112             keywords: ["nonsponsored"],
113             click_url: "https://example.com/click",
114             impression_url: "https://example.com/impression",
115             advertiser: "TestAdvertiser",
116             iab_category: "5 - Education",
117           },
118         ],
119       },
120     ],
121   });
124 async function doBlur() {
125   await UrlbarTestUtils.promisePopupClose(window, () => {
126     gURLBar.blur();
127   });
130 async function doClick() {
131   const selected = UrlbarTestUtils.getSelectedRow(window);
132   const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
133   EventUtils.synthesizeMouseAtCenter(selected, {});
134   await onLoad;
137 async function doClickSubButton(selector) {
138   const selected = UrlbarTestUtils.getSelectedElement(window);
139   const button = selected.closest(".urlbarView-row").querySelector(selector);
140   EventUtils.synthesizeMouseAtCenter(button, {});
143 async function doDropAndGo(data) {
144   const onLoad = BrowserTestUtils.browserLoaded(browser);
145   EventUtils.synthesizeDrop(
146     document.getElementById("back-button"),
147     gURLBar.inputField,
148     [[{ type: "text/plain", data }]],
149     "copy",
150     window
151   );
152   await onLoad;
155 async function doEnter(modifier = {}) {
156   const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
157   EventUtils.synthesizeKey("KEY_Enter", modifier);
158   await onLoad;
161 async function doPaste(data) {
162   await SimpleTest.promiseClipboardChange(data, () => {
163     clipboardHelper.copyString(data);
164   });
166   gURLBar.focus();
167   gURLBar.select();
168   document.commandDispatcher
169     .getControllerForCommand("cmd_paste")
170     .doCommand("cmd_paste");
171   await UrlbarTestUtils.promiseSearchComplete(window);
174 async function doPasteAndGo(data) {
175   await SimpleTest.promiseClipboardChange(data, () => {
176     clipboardHelper.copyString(data);
177   });
178   const inputBox = gURLBar.querySelector("moz-input-box");
179   const contextMenu = inputBox.menupopup;
180   const onPopup = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
181   EventUtils.synthesizeMouseAtCenter(gURLBar.inputField, {
182     type: "contextmenu",
183     button: 2,
184   });
185   await onPopup;
186   const onLoad = BrowserTestUtils.browserLoaded(browser);
187   const menuitem = inputBox.getMenuItem("paste-and-go");
188   contextMenu.activateItem(menuitem);
189   await onLoad;
192 async function doTest(testFn) {
193   await Services.fog.testFlushAllChildren();
194   Services.fog.testResetFOG();
195   // Enable recording telemetry for abandonment, engagement and impression.
196   Services.fog.setMetricsFeatureConfig(
197     JSON.stringify({
198       "urlbar.abandonment": true,
199       "urlbar.engagement": true,
200       "urlbar.impression": true,
201     })
202   );
204   gURLBar.controller.engagementEvent.reset();
205   await PlacesUtils.history.clear();
206   await PlacesUtils.bookmarks.eraseEverything();
207   await PlacesTestUtils.clearHistoryVisits();
208   await PlacesTestUtils.clearInputHistory();
209   await UrlbarTestUtils.formHistory.clear(window);
210   await QuickSuggest.blockedSuggestions.clear();
211   await QuickSuggest.blockedSuggestions._test_readyPromise;
212   await updateTopSites(() => true);
214   try {
215     await BrowserTestUtils.withNewTab(gBrowser, testFn);
216   } finally {
217     Services.fog.setMetricsFeatureConfig("{}");
218   }
221 async function initGroupTest() {
222   /* import-globals-from head-groups.js */
223   Services.scriptloader.loadSubScript(
224     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-groups.js",
225     this
226   );
227   await setup();
230 async function initInteractionTest() {
231   /* import-globals-from head-interaction.js */
232   Services.scriptloader.loadSubScript(
233     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-interaction.js",
234     this
235   );
236   await setup();
239 async function initNCharsAndNWordsTest() {
240   /* import-globals-from head-n_chars_n_words.js */
241   Services.scriptloader.loadSubScript(
242     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-n_chars_n_words.js",
243     this
244   );
245   await setup();
248 async function initSapTest() {
249   /* import-globals-from head-sap.js */
250   Services.scriptloader.loadSubScript(
251     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-sap.js",
252     this
253   );
254   await setup();
257 async function initSearchModeTest() {
258   /* import-globals-from head-search_mode.js */
259   Services.scriptloader.loadSubScript(
260     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-search_mode.js",
261     this
262   );
263   await setup();
266 async function initExposureTest() {
267   /* import-globals-from head-exposure.js */
268   Services.scriptloader.loadSubScript(
269     "chrome://mochitests/content/browser/browser/components/urlbar/tests/engagementTelemetry/browser/head-exposure.js",
270     this
271   );
272   await setup();
275 function loadOmniboxAddon({ keyword }) {
276   return ExtensionTestUtils.loadExtension({
277     manifest: {
278       permissions: ["tabs"],
279       omnibox: {
280         keyword,
281       },
282     },
283     background() {
284       /* global browser */
285       browser.omnibox.setDefaultSuggestion({
286         description: "doit",
287       });
288       browser.omnibox.onInputEntered.addListener(() => {
289         browser.tabs.update({ url: "https://example.com/" });
290       });
291       browser.omnibox.onInputChanged.addListener((text, suggest) => {
292         suggest([]);
293       });
294     },
295   });
298 async function loadRemoteTab(url) {
299   await SpecialPowers.pushPrefEnv({
300     set: [
301       ["browser.urlbar.suggest.searches", false],
302       ["browser.urlbar.maxHistoricalSearchSuggestions", 0],
303       ["browser.urlbar.autoFill", false],
304       ["services.sync.username", "fake"],
305       ["services.sync.syncedTabs.showRemoteTabs", true],
306     ],
307   });
309   const REMOTE_TAB = {
310     id: "test",
311     type: "client",
312     lastModified: 1492201200,
313     name: "test",
314     clientType: "desktop",
315     tabs: [
316       {
317         type: "tab",
318         title: "tesrt",
319         url,
320         icon: UrlbarUtils.ICON.DEFAULT,
321         client: "test",
322         lastUsed: Math.floor(Date.now() / 1000),
323       },
324     ],
325   };
327   const sandbox = lazy.sinon.createSandbox();
328   // eslint-disable-next-line no-undef
329   const syncedTabs = SyncedTabs;
330   const originalSyncedTabsInternal = syncedTabs._internal;
331   syncedTabs._internal = {
332     isConfiguredToSyncTabs: true,
333     hasSyncedThisSession: true,
334     getTabClients() {
335       return Promise.resolve([]);
336     },
337     syncTabs() {
338       return Promise.resolve();
339     },
340   };
341   const weaveXPCService = Cc["@mozilla.org/weave/service;1"].getService(
342     Ci.nsISupports
343   ).wrappedJSObject;
344   const oldWeaveServiceReady = weaveXPCService.ready;
345   weaveXPCService.ready = true;
346   sandbox
347     .stub(syncedTabs._internal, "getTabClients")
348     .callsFake(() => Promise.resolve(Cu.cloneInto([REMOTE_TAB], {})));
350   return {
351     async unload() {
352       sandbox.restore();
353       weaveXPCService.ready = oldWeaveServiceReady;
354       syncedTabs._internal = originalSyncedTabsInternal;
355       // Reset internal cache in UrlbarProviderRemoteTabs.
356       Services.obs.notifyObservers(null, "weave:engine:sync:finish", "tabs");
357       await SpecialPowers.popPrefEnv();
358     },
359   };
362 async function openPopup(input) {
363   await UrlbarTestUtils.promisePopupOpen(window, async () => {
364     await UrlbarTestUtils.inputIntoURLBar(window, input);
365   });
366   await UrlbarTestUtils.promiseSearchComplete(window);
369 async function selectRowByURL(url) {
370   for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
371     const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
372     if (detail.url === url) {
373       UrlbarTestUtils.setSelectedRowIndex(window, i);
374       return;
375     }
376   }
379 async function selectRowByProvider(provider) {
380   for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
381     const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
382     if (detail.result.providerName === provider) {
383       UrlbarTestUtils.setSelectedRowIndex(window, i);
384       break;
385     }
386   }
389 async function selectRowByType(type) {
390   for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
391     const detail = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
392     if (detail.result.payload.type === type) {
393       UrlbarTestUtils.setSelectedRowIndex(window, i);
394       return;
395     }
396   }
399 async function setup() {
400   await SpecialPowers.pushPrefEnv({
401     set: [
402       ["browser.urlbar.searchEngagementTelemetry.enabled", true],
403       ["browser.urlbar.quickactions.enabled", true],
404       ["browser.urlbar.quickactions.minimumSearchString", 0],
405       ["browser.urlbar.suggest.quickactions", true],
406       ["browser.urlbar.shortcuts.quickactions", true],
407       [
408         "browser.urlbar.searchEngagementTelemetry.pauseImpressionIntervalMs",
409         100,
410       ],
411     ],
412   });
414   const engine = await SearchTestUtils.promiseNewSearchEngine({
415     url: "chrome://mochitests/content/browser/browser/components/urlbar/tests/browser/searchSuggestionEngine.xml",
416   });
417   const originalDefaultEngine = await Services.search.getDefault();
418   await Services.search.setDefault(
419     engine,
420     Ci.nsISearchService.CHANGE_REASON_UNKNOWN
421   );
422   await Services.search.moveEngine(engine, 0);
424   registerCleanupFunction(async function () {
425     await SpecialPowers.popPrefEnv();
426     await Services.search.setDefault(
427       originalDefaultEngine,
428       Ci.nsISearchService.CHANGE_REASON_UNKNOWN
429     );
430   });
433 async function setupNimbus(variables) {
434   return lazy.UrlbarTestUtils.initNimbusFeature(variables);
437 async function showResultByArrowDown() {
438   gURLBar.value = "";
439   gURLBar.select();
440   await UrlbarTestUtils.promisePopupOpen(window, () => {
441     EventUtils.synthesizeKey("KEY_ArrowDown");
442   });
443   await UrlbarTestUtils.promiseSearchComplete(window);
446 async function waitForPauseImpression() {
447   await new Promise(r =>
448     setTimeout(
449       r,
450       UrlbarPrefs.get("searchEngagementTelemetry.pauseImpressionIntervalMs")
451     )
452   );
453   await Services.fog.testFlushAllChildren();