/* -*- 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",
];

/**
 * 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",
        "browser_style": true,
      },
    },

    files: {
      "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></html>`,
    },
  };
  let extension1 = ExtensionTestUtils.loadExtension({
    ...extensionOptions,
    manifest: {
      ...extensionOptions.manifest,
      "applications": {
        "gecko": {"id": EXTENSION_ID1},
      },
    },
  });
  let extension2 = ExtensionTestUtils.loadExtension({
    ...extensionOptions,
    manifest: {
      ...extensionOptions.manifest,
      "applications": {
        "gecko": {"id": EXTENSION_ID2},
      },
    },
  });

  let histogram = Services.telemetry.getHistogramById(TIMING_HISTOGRAM);
  let histogramKeyed = Services.telemetry.getKeyedHistogramById(TIMING_HISTOGRAM_KEYED);

  histogram.clear();
  histogramKeyed.clear();

  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}.`);

  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}.`);

  clickBrowserAction(extension1);
  await awaitExtensionPanel(extension1);
  let sumOld = histogram.snapshot().sum;
  ok(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}.`);
  ok(oldKeyedSnapshot[EXTENSION_ID1].sum > 0,
     `Data recorded for first extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`);

  await closeBrowserAction(extension1);

  clickBrowserAction(extension2);
  await awaitExtensionPanel(extension2);
  let sumNew = histogram.snapshot().sum;
  ok(sumNew > sumOld,
     `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM}.`);
  sumOld = sumNew;

  let newKeyedSnapshot = histogramKeyed.snapshot();
  Assert.deepEqual(Object.keys((newKeyedSnapshot)).sort(), [EXTENSION_ID1, EXTENSION_ID2],
                   `Data recorded for second extension for histogram: ${TIMING_HISTOGRAM_KEYED}.`);
  ok(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;
  ok(sumNew > sumOld,
     `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM}.`);
  sumOld = sumNew;

  newKeyedSnapshot = histogramKeyed.snapshot();
  ok(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;
  ok(sumNew > sumOld,
     `Data recorded for second opening of popup for histogram: ${TIMING_HISTOGRAM}.`);

  newKeyedSnapshot = histogramKeyed.snapshot();
  ok(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: {
      "applications": {
        "gecko": {"id": EXTENSION_ID1},
      },
      "browser_action": {
        "default_popup": "popup.html",
        "browser_style": true,
      },
    },

    files: {
      "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><div></div></body></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();

  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}.`);

  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");

  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();

  // 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");

  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();
});
