/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; const TIMING_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS"; const TIMING_HISTOGRAM_KEYED = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS_BY_ADDONID"; const RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT"; const RESULT_HISTOGRAM_KEYED = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT_BY_ADDONID"; const EXTENSION_ID1 = "@test-extension1"; const EXTENSION_ID2 = "@test-extension2"; // Keep this in sync with the order in Histograms.json for // WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT const CATEGORIES = ["popupShown", "clearAfterHover", "clearAfterMousedown"]; const GLEAN_RESULT_LABELS = [...CATEGORIES, "__other__"]; function assertGleanPreloadResultLabelCounter(expectedLabelsValue) { for (const label of GLEAN_RESULT_LABELS) { const expectedLabelValue = expectedLabelsValue[label]; Assert.deepEqual( Glean.extensionsCounters.browserActionPreloadResult[label].testGetValue(), expectedLabelValue, `Expect Glean browserActionPreloadResult metric label ${label} to be ${ expectedLabelValue > 0 ? expectedLabelValue : "empty" }` ); } } function assertGleanPreloadResultLabelCounterEmpty() { // All empty labels passed to the other helpers to make it // assert that all labels are empty. assertGleanPreloadResultLabelCounter({}); } /** * Takes a Telemetry histogram snapshot and makes sure * that the index for that value (as defined by CATEGORIES) * has a count of 1, and that it's the only value that * has been incremented. * * @param {object} snapshot * The Telemetry histogram snapshot to examine. * @param {string} category * The category in CATEGORIES whose index we expect to have * been set to 1. */ function assertOnlyOneTypeSet(snapshot, category) { let categoryIndex = CATEGORIES.indexOf(category); Assert.equal( snapshot.values[categoryIndex], 1, `Should have seen the ${category} count increment.` ); // Use Array.prototype.reduce to sum up all of the // snapshot.count entries Assert.equal( Object.values(snapshot.values).reduce((a, b) => a + b, 0), 1, "Should only be 1 collected value." ); } add_task(async function testBrowserActionTelemetryTiming() { let extensionOptions = { manifest: { browser_action: { default_popup: "popup.html", default_area: "navbar", browser_style: true, }, }, files: { "popup.html": `
`, }, }; let extension1 = ExtensionTestUtils.loadExtension({ ...extensionOptions, manifest: { ...extensionOptions.manifest, browser_specific_settings: { gecko: { id: EXTENSION_ID1 }, }, }, }); let extension2 = ExtensionTestUtils.loadExtension({ ...extensionOptions, manifest: { ...extensionOptions.manifest, browser_specific_settings: { gecko: { id: EXTENSION_ID2 }, }, }, }); let histogram = Services.telemetry.getHistogramById(TIMING_HISTOGRAM); let histogramKeyed = Services.telemetry.getKeyedHistogramById( TIMING_HISTOGRAM_KEYED ); histogram.clear(); histogramKeyed.clear(); Services.fog.testResetFOG(); is( histogram.snapshot().sum, 0, `No data recorded for histogram: ${TIMING_HISTOGRAM}.` ); is( Object.keys(histogramKeyed).length, 0, `No data recorded for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); Assert.deepEqual( Glean.extensionsTiming.browserActionPopupOpen.testGetValue(), undefined, "No data recorded for glean metric extensionsTiming.browserActionPopupOpen" ); await extension1.startup(); await extension2.startup(); is( histogram.snapshot().sum, 0, `No data recorded for histogram after startup: ${TIMING_HISTOGRAM}.` ); is( Object.keys(histogramKeyed).length, 0, `No data recorded for histogram after startup: ${TIMING_HISTOGRAM_KEYED}.` ); Assert.deepEqual( Glean.extensionsTiming.browserActionPopupOpen.testGetValue(), undefined, "No data recorded for glean metric extensionsTiming.browserActionPopupOpen" ); clickBrowserAction(extension1); await awaitExtensionPanel(extension1); let sumOld = histogram.snapshot().sum; Assert.greater( sumOld, 0, `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM}.` ); let oldKeyedSnapshot = histogramKeyed.snapshot(); Assert.deepEqual( Object.keys(oldKeyedSnapshot), [EXTENSION_ID1], `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); Assert.greater( oldKeyedSnapshot[EXTENSION_ID1].sum, 0, `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); let gleanSumOld = Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; Assert.greater( gleanSumOld, 0, "Data recorded for first extension on glean metric extensionsTiming.browserActionPopupOpen" ); await closeBrowserAction(extension1); clickBrowserAction(extension2); await awaitExtensionPanel(extension2); let sumNew = histogram.snapshot().sum; Assert.greater( sumNew, sumOld, `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM}.` ); sumOld = sumNew; let gleanSumNew = Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; Assert.greater( gleanSumNew, gleanSumOld, "Data recorded for second extension on glean metric extensionsTiming.browserActionPopupOpen" ); gleanSumOld = gleanSumNew; let newKeyedSnapshot = histogramKeyed.snapshot(); Assert.deepEqual( Object.keys(newKeyedSnapshot).sort(), [EXTENSION_ID1, EXTENSION_ID2], `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); Assert.greater( newKeyedSnapshot[EXTENSION_ID2].sum, 0, `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); is( newKeyedSnapshot[EXTENSION_ID1].sum, oldKeyedSnapshot[EXTENSION_ID1].sum, `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); oldKeyedSnapshot = newKeyedSnapshot; await closeBrowserAction(extension2); clickBrowserAction(extension2); await awaitExtensionPanel(extension2); sumNew = histogram.snapshot().sum; Assert.greater( sumNew, sumOld, `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM}.` ); sumOld = sumNew; gleanSumNew = Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; Assert.greater( gleanSumNew, gleanSumOld, "Data recorded for second popup opening on glean metric extensionsTiming.browserActionPopupOpen" ); gleanSumOld = gleanSumNew; newKeyedSnapshot = histogramKeyed.snapshot(); Assert.greater( newKeyedSnapshot[EXTENSION_ID2].sum, oldKeyedSnapshot[EXTENSION_ID2].sum, `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); is( newKeyedSnapshot[EXTENSION_ID1].sum, oldKeyedSnapshot[EXTENSION_ID1].sum, `Data recorded for first extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); oldKeyedSnapshot = newKeyedSnapshot; await closeBrowserAction(extension2); clickBrowserAction(extension1); await awaitExtensionPanel(extension1); sumNew = histogram.snapshot().sum; Assert.greater( sumNew, sumOld, `Data recorded for third opening of popup for histogram: ${TIMING_HISTOGRAM}.` ); gleanSumNew = Glean.extensionsTiming.browserActionPopupOpen.testGetValue()?.sum; Assert.greater( gleanSumNew, gleanSumOld, "Data recorded for third popup opening on glean metric extensionsTiming.browserActionPopupOpen" ); newKeyedSnapshot = histogramKeyed.snapshot(); Assert.greater( newKeyedSnapshot[EXTENSION_ID1].sum, oldKeyedSnapshot[EXTENSION_ID1].sum, `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); is( newKeyedSnapshot[EXTENSION_ID2].sum, oldKeyedSnapshot[EXTENSION_ID2].sum, `Data recorded for second extension should not change for histogram: ${TIMING_HISTOGRAM_KEYED}.` ); await closeBrowserAction(extension1); await extension1.unload(); await extension2.unload(); }); add_task(async function testBrowserActionTelemetryResults() { let extensionOptions = { manifest: { browser_specific_settings: { gecko: { id: EXTENSION_ID1 }, }, browser_action: { default_popup: "popup.html", default_area: "navbar", browser_style: true, }, }, files: { "popup.html": `
`, }, }; let extension = ExtensionTestUtils.loadExtension(extensionOptions); let histogram = Services.telemetry.getHistogramById(RESULT_HISTOGRAM); let histogramKeyed = Services.telemetry.getKeyedHistogramById( RESULT_HISTOGRAM_KEYED ); histogram.clear(); histogramKeyed.clear(); Services.fog.testResetFOG(); is( histogram.snapshot().sum, 0, `No data recorded for histogram: ${RESULT_HISTOGRAM}.` ); is( Object.keys(histogramKeyed).length, 0, `No data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` ); assertGleanPreloadResultLabelCounterEmpty(); await extension.startup(); // Make sure the mouse isn't hovering over the browserAction widget to start. EventUtils.synthesizeMouseAtCenter( gURLBar.textbox, { type: "mouseover" }, window ); let widget = getBrowserActionWidget(extension).forWindow(window); // Hover the mouse over the browserAction widget and then move it away. EventUtils.synthesizeMouseAtCenter( widget.node, { type: "mouseover", button: 0 }, window ); EventUtils.synthesizeMouseAtCenter( widget.node, { type: "mouseout", button: 0 }, window ); EventUtils.synthesizeMouseAtCenter( document.documentElement, { type: "mousemove" }, window ); assertOnlyOneTypeSet(histogram.snapshot(), "clearAfterHover"); assertGleanPreloadResultLabelCounter({ clearAfterHover: 1 }); let keyedSnapshot = histogramKeyed.snapshot(); Assert.deepEqual( Object.keys(keyedSnapshot), [EXTENSION_ID1], `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` ); assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "clearAfterHover"); histogram.clear(); histogramKeyed.clear(); Services.fog.testResetFOG(); // TODO: Create a test for cancel after mousedown. // This is tricky because calling mouseout after mousedown causes a // "Hover" event to be added to the queue in ext-browserAction.js. clickBrowserAction(extension); await awaitExtensionPanel(extension); assertOnlyOneTypeSet(histogram.snapshot(), "popupShown"); assertGleanPreloadResultLabelCounter({ popupShown: 1 }); keyedSnapshot = histogramKeyed.snapshot(); Assert.deepEqual( Object.keys(keyedSnapshot), [EXTENSION_ID1], `Data recorded for histogram: ${RESULT_HISTOGRAM_KEYED}.` ); assertOnlyOneTypeSet(keyedSnapshot[EXTENSION_ID1], "popupShown"); await closeBrowserAction(extension); await extension.unload(); });