browser_ext_browserAction_telemetry.js (11352B)
1 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ 2 /* vim: set sts=2 sw=2 et tw=80: */ 3 "use strict"; 4 5 const TIMING_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS"; 6 const TIMING_HISTOGRAM_KEYED = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS_BY_ADDONID"; 7 const RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT"; 8 const RESULT_HISTOGRAM_KEYED = 9 "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT_BY_ADDONID"; 10 11 const EXTENSION_ID1 = "@test-extension1"; 12 const EXTENSION_ID2 = "@test-extension2"; 13 14 // Keep this in sync with the order in Histograms.json for 15 // WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT 16 const CATEGORIES = ["popupShown", "clearAfterHover", "clearAfterMousedown"]; 17 const GLEAN_RESULT_LABELS = [...CATEGORIES, "__other__"]; 18 19 function assertGleanPreloadResultLabelCounter(expectedLabelsValue) { 20 for (const label of GLEAN_RESULT_LABELS) { 21 const expectedLabelValue = expectedLabelsValue[label]; 22 Assert.deepEqual( 23 Glean.extensionsCounters.browserActionPreloadResult[label].testGetValue(), 24 expectedLabelValue, 25 `Expect Glean browserActionPreloadResult metric label ${label} to be ${ 26 expectedLabelValue > 0 ? expectedLabelValue : "empty" 27 }` 28 ); 29 } 30 } 31 32 function assertGleanPreloadResultLabelCounterEmpty() { 33 // All empty labels passed to the other helpers to make it 34 // assert that all labels are empty. 35 assertGleanPreloadResultLabelCounter({}); 36 } 37 38 /** 39 * Takes a Telemetry histogram snapshot and makes sure 40 * that the index for that value (as defined by CATEGORIES) 41 * has a count of 1, and that it's the only value that 42 * has been incremented. 43 * 44 * @param {object} snapshot 45 * The Telemetry histogram snapshot to examine. 46 * @param {string} category 47 * The category in CATEGORIES whose index we expect to have 48 * been set to 1. 49 */ 50 function assertOnlyOneTypeSet(snapshot, category) { 51 let categoryIndex = CATEGORIES.indexOf(category); 52 Assert.equal( 53 snapshot.values[categoryIndex], 54 1, 55 `Should have seen the ${category} count increment.` 56 ); 57 // Use Array.prototype.reduce to sum up all of the 58 // snapshot.count entries 59 Assert.equal( 60 Object.values(snapshot.values).reduce((a, b) => a + b, 0), 61 1, 62 "Should only be 1 collected value." 63 ); 64 } 65 66 add_task(async function testBrowserActionTelemetryTiming() { 67 let extensionOptions = { 68 manifest: { 69 browser_action: { 70 default_popup: "popup.html", 71 default_area: "navbar", 72 browser_style: true, 73 }, 74 }, 75 76 files: { 77 "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></html>`, 78 }, 79 }; 80 let extension1 = ExtensionTestUtils.loadExtension({ 81 ...extensionOptions, 82 manifest: { 83 ...extensionOptions.manifest, 84 browser_specific_settings: { 85 gecko: { id: EXTENSION_ID1 }, 86 }, 87 }, 88 }); 89 let extension2 = ExtensionTestUtils.loadExtension({ 90 ...extensionOptions, 91 manifest: { 92 ...extensionOptions.manifest, 93 browser_specific_settings: { 94 gecko: { id: EXTENSION_ID2 }, 95 }, 96 }, 97 }); 98 99 let histogram = Services.telemetry.getHistogramById(TIMING_HISTOGRAM); 100 let histogramKeyed = Services.telemetry.getKeyedHistogramById( 101 TIMING_HISTOGRAM_KEYED 102 ); 103 104 histogram.clear(); 105 histogramKeyed.clear(); 106 Services.fog.testResetFOG(); 107 108 is( 109 histogram.snapshot().sum, 110 0, 111 `No data recorded for histogram: ${TIMING_HISTOGRAM}.` 112 ); 113 is( 114 Object.keys(histogramKeyed).length, 115 0, 116 `No data recorded for histogram: ${TIMING_HISTOGRAM_KEYED}.` 117 ); 118 Assert.deepEqual( 119 Glean.extensionsTiming.browserActionPopupOpen.testGetValue(), 120 undefined, 121 "No data recorded for glean metric extensionsTiming.browserActionPopupOpen" 122 ); 123 124 await extension1.startup(); 125 await extension2.startup(); 126 127 is( 128 histogram.snapshot().sum, 129 0, 130 `No data recorded for histogram after startup: ${TIMING_HISTOGRAM}.` 131 ); 132 is( 133 Object.keys(histogramKeyed).length, 134 0, 135 `No data recorded for histogram after startup: ${TIMING_HISTOGRAM_KEYED}.` 136 ); 137 Assert.deepEqual( 138 Glean.extensionsTiming.browserActionPopupOpen.testGetValue(), 139 undefined, 140 "No data recorded for glean metric extensionsTiming.browserActionPopupOpen" 141 ); 142 143 clickBrowserAction(extension1); 144 await awaitExtensionPanel(extension1); 145 let sumOld = histogram.snapshot().sum; 146 Assert.greater( 147 sumOld, 148 0, 149 `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM}.` 150 ); 151 152 let oldKeyedSnapshot = histogramKeyed.snapshot(); 153 Assert.deepEqual( 154 Object.keys(oldKeyedSnapshot), 155 [EXTENSION_ID1], 156 `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` 157 ); 158 Assert.greater( 159 oldKeyedSnapshot[EXTENSION_ID1].sum, 160 0, 161 `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` 162 ); 163 164 let gleanSumOld = 165 Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; 166 Assert.greater( 167 gleanSumOld, 168 0, 169 "Data recorded for first extension on glean metric extensionsTiming.browserActionPopupOpen" 170 ); 171 172 await closeBrowserAction(extension1); 173 174 clickBrowserAction(extension2); 175 await awaitExtensionPanel(extension2); 176 let sumNew = histogram.snapshot().sum; 177 Assert.greater( 178 sumNew, 179 sumOld, 180 `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM}.` 181 ); 182 sumOld = sumNew; 183 184 let gleanSumNew = 185 Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; 186 Assert.greater( 187 gleanSumNew, 188 gleanSumOld, 189 "Data recorded for second extension on glean metric extensionsTiming.browserActionPopupOpen" 190 ); 191 gleanSumOld = gleanSumNew; 192 193 let newKeyedSnapshot = histogramKeyed.snapshot(); 194 Assert.deepEqual( 195 Object.keys(newKeyedSnapshot).sort(), 196 [EXTENSION_ID1, EXTENSION_ID2], 197 `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` 198 ); 199 Assert.greater( 200 newKeyedSnapshot[EXTENSION_ID2].sum, 201 0, 202 `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` 203 ); 204 is( 205 newKeyedSnapshot[EXTENSION_ID1].sum, 206 oldKeyedSnapshot[EXTENSION_ID1].sum, 207 `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` 208 ); 209 oldKeyedSnapshot = newKeyedSnapshot; 210 211 await closeBrowserAction(extension2); 212 213 clickBrowserAction(extension2); 214 await awaitExtensionPanel(extension2); 215 sumNew = histogram.snapshot().sum; 216 Assert.greater( 217 sumNew, 218 sumOld, 219 `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM}.` 220 ); 221 sumOld = sumNew; 222 223 gleanSumNew = 224 Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; 225 Assert.greater( 226 gleanSumNew, 227 gleanSumOld, 228 "Data recorded for second popup opening on glean metric extensionsTiming.browserActionPopupOpen" 229 ); 230 gleanSumOld = gleanSumNew; 231 232 newKeyedSnapshot = histogramKeyed.snapshot(); 233 Assert.greater( 234 newKeyedSnapshot[EXTENSION_ID2].sum, 235 oldKeyedSnapshot[EXTENSION_ID2].sum, 236 `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.` 237 ); 238 is( 239 newKeyedSnapshot[EXTENSION_ID1].sum, 240 oldKeyedSnapshot[EXTENSION_ID1].sum, 241 `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` 242 ); 243 oldKeyedSnapshot = newKeyedSnapshot; 244 245 await closeBrowserAction(extension2); 246 247 clickBrowserAction(extension1); 248 await awaitExtensionPanel(extension1); 249 sumNew = histogram.snapshot().sum; 250 Assert.greater( 251 sumNew, 252 sumOld, 253 `Data recorded for third opening of popup for histogram: ${TIMING_HISTOGRAM}.` 254 ); 255 256 gleanSumNew = 257 Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; 258 Assert.greater( 259 gleanSumNew, 260 gleanSumOld, 261 "Data recorded for third popup opening on glean metric extensionsTiming.browserActionPopupOpen" 262 ); 263 264 newKeyedSnapshot = histogramKeyed.snapshot(); 265 Assert.greater( 266 newKeyedSnapshot[EXTENSION_ID1].sum, 267 oldKeyedSnapshot[EXTENSION_ID1].sum, 268 `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.` 269 ); 270 is( 271 newKeyedSnapshot[EXTENSION_ID2].sum, 272 oldKeyedSnapshot[EXTENSION_ID2].sum, 273 `Data recorded for second extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` 274 ); 275 276 await closeBrowserAction(extension1); 277 278 await extension1.unload(); 279 await extension2.unload(); 280 }); 281 282 add_task(async function testBrowserActionTelemetryResults() { 283 let extensionOptions = { 284 manifest: { 285 browser_specific_settings: { 286 gecko: { id: EXTENSION_ID1 }, 287 }, 288 browser_action: { 289 default_popup: "popup.html", 290 default_area: "navbar", 291 browser_style: true, 292 }, 293 }, 294 295 files: { 296 "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></html>`, 297 }, 298 }; 299 let extension = ExtensionTestUtils.loadExtension(extensionOptions); 300 301 let histogram = Services.telemetry.getHistogramById(RESULT_HISTOGRAM); 302 let histogramKeyed = Services.telemetry.getKeyedHistogramById( 303 RESULT_HISTOGRAM_KEYED 304 ); 305 306 histogram.clear(); 307 histogramKeyed.clear(); 308 Services.fog.testResetFOG(); 309 310 is( 311 histogram.snapshot().sum, 312 0, 313 `No data recorded for histogram: ${RESULT_HISTOGRAM}.` 314 ); 315 is( 316 Object.keys(histogramKeyed).length, 317 0, 318 `No data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` 319 ); 320 assertGleanPreloadResultLabelCounterEmpty(); 321 322 await extension.startup(); 323 324 // Make sure the mouse isn't hovering over the browserAction widget to start. 325 EventUtils.synthesizeMouseAtCenter(gURLBar, { type: "mouseover" }, window); 326 327 let widget = getBrowserActionWidget(extension).forWindow(window); 328 329 // Hover the mouse over the browserAction widget and then move it away. 330 EventUtils.synthesizeMouseAtCenter( 331 widget.node, 332 { type: "mouseover", button: 0 }, 333 window 334 ); 335 EventUtils.synthesizeMouseAtCenter( 336 widget.node, 337 { type: "mouseout", button: 0 }, 338 window 339 ); 340 EventUtils.synthesizeMouseAtCenter( 341 document.documentElement, 342 { type: "mousemove" }, 343 window 344 ); 345 346 assertOnlyOneTypeSet(histogram.snapshot(), "clearAfterHover"); 347 assertGleanPreloadResultLabelCounter({ clearAfterHover: 1 }); 348 349 let keyedSnapshot = histogramKeyed.snapshot(); 350 Assert.deepEqual( 351 Object.keys(keyedSnapshot), 352 [EXTENSION_ID1], 353 `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` 354 ); 355 assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "clearAfterHover"); 356 357 histogram.clear(); 358 histogramKeyed.clear(); 359 Services.fog.testResetFOG(); 360 361 // TODO: Create a test for cancel after mousedown. 362 // This is tricky because calling mouseout after mousedown causes a 363 // "Hover" event to be added to the queue in ext-browserAction.js. 364 365 clickBrowserAction(extension); 366 await awaitExtensionPanel(extension); 367 368 assertOnlyOneTypeSet(histogram.snapshot(), "popupShown"); 369 assertGleanPreloadResultLabelCounter({ popupShown: 1 }); 370 371 keyedSnapshot = histogramKeyed.snapshot(); 372 Assert.deepEqual( 373 Object.keys(keyedSnapshot), 374 [EXTENSION_ID1], 375 `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` 376 ); 377 assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "popupShown"); 378 379 await closeBrowserAction(extension); 380 381 await extension.unload(); 382 });