/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

add_setup(async function () {
  await SpecialPowers.pushPrefEnv({
    set: [["test.wait300msAfterTabSwitch", true]],
  });
});

add_task(async function test_about_glean_redesign_views_hidden_behind_pref() {
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("about.glean.redesign.enabled");
  });

  await BrowserTestUtils.withNewTab("about:glean", async function (browser) {
    ok(!browser.isRemoteBrowser, "Browser should not be remote.");
    await ContentTask.spawn(browser, null, async function () {
      let metrics_table_category_button = content.document.getElementById(
        "category-metrics-table"
      );
      is(metrics_table_category_button, null);
    });
  });

  await BrowserTestUtils.withNewTab("about:glean", async function (browser) {
    ok(!browser.isRemoteBrowser, "Browser should not be remote.");
    await ContentTask.spawn(browser, null, async function () {
      content.document.getElementById("enable-new-features").click();

      let metrics_table_category_button = content.document.getElementById(
        "category-metrics-table"
      );
      Assert.notEqual(
        metrics_table_category_button,
        null,
        "Metrics table category button should not be null"
      );
    });
  });
});

add_task(async function test_about_glean_metrics_table_loads_dynamically() {
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("about.glean.redesign.enabled");
  });
  Services.prefs.setBoolPref("about.glean.redesign.enabled", true);

  await BrowserTestUtils.withNewTab("about:glean", async function (browser) {
    ok(!browser.isRemoteBrowser, "Browser should not be remote.");
    await ContentTask.spawn(browser, null, async function () {
      const { TestUtils } = ChromeUtils.importESModule(
        "resource://testing-common/TestUtils.sys.mjs"
      );
      content.document.getElementById("category-metrics-table").click();

      let tableContainer, tableBody;
      const fetchTableBody = () => {
        tableContainer = content.document.getElementById(
          "metrics-table-instance"
        );
        tableBody = content.document.getElementById("metrics-table-body");
      };
      fetchTableBody();

      let currentChildrenLength = tableBody.children.length;
      const tableChildrenLengthChanged = () => {
        fetchTableBody();
        if (currentChildrenLength != tableBody.children.length) {
          currentChildrenLength = tableBody.children.length;
          return true;
        }
        return false;
      };
      let currentFirstChild =
        tableBody.children[0].attributes["data-d3-row"].value;
      const tableFirstChildChanged = () => {
        fetchTableBody();
        if (
          currentFirstChild !=
          tableBody.children[0].attributes["data-d3-row"].value
        ) {
          currentFirstChild =
            tableBody.children[0].attributes["data-d3-row"].value;
          return true;
        }
        return false;
      };

      Assert.equal(
        currentChildrenLength,
        200,
        "Table should start with 200 elements in it"
      );

      // Scroll and extend the count to 300
      tableContainer.scrollTo({
        top: tableContainer.scrollHeight - 1000,
        behavior: "instant",
      });

      await TestUtils.waitForCondition(
        tableChildrenLengthChanged,
        "Wait for table children length to change",
        100,
        3
      );

      Assert.equal(
        tableBody.children.length,
        300,
        "Table should now have 300 elements in it"
      );

      // Scroll and extend the count to 400
      tableContainer.scrollTo({
        top: tableContainer.scrollHeight - 1000,
        behavior: "instant",
      });

      await TestUtils.waitForCondition(
        tableChildrenLengthChanged,
        "Wait for table children length to change",
        100,
        3
      );

      Assert.equal(
        tableBody.children.length,
        400,
        "Table should now have 400 elements in it"
      );

      // Scroll and extend the count to 500
      tableContainer.scrollTo({
        top: tableContainer.scrollHeight - 1000,
        behavior: "instant",
      });

      await TestUtils.waitForCondition(
        tableChildrenLengthChanged,
        "Wait for table children length to change",
        100,
        3
      );

      Assert.equal(
        tableBody.children.length,
        500,
        "Table should now have 500 elements in it"
      );

      // Scroll offset the metrics by 100
      tableContainer.scrollTo({
        top: tableContainer.scrollHeight - 1000,
        behavior: "instant",
      });

      await TestUtils.waitForCondition(
        tableFirstChildChanged,
        "Wait for the table's first child to change",
        100,
        3
      );

      Assert.equal(
        tableBody.children.length,
        500,
        "Table should still have 500 elements in it"
      );
    });
  });
});

add_task(async function test_about_glean_histogram() {
  Services.fog.testResetFOG();
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("about.glean.redesign.enabled");
  });
  Services.prefs.setBoolPref("about.glean.redesign.enabled", true);

  await BrowserTestUtils.withNewTab("about:glean", async function (browser) {
    ok(!browser.isRemoteBrowser, "Browser should not be remote.");
    await ContentTask.spawn(browser, null, async function () {
      const { TestUtils } = ChromeUtils.importESModule(
        "resource://testing-common/TestUtils.sys.mjs"
      );
      content.document.getElementById("category-metrics-table").click();

      let tableBody;
      const fetchTableBody = () => {
        tableBody = content.document.getElementById("metrics-table-body");
      };
      fetchTableBody();
      let currentFirstChild =
        tableBody.children[0].attributes["data-d3-row"].value;

      const tableFirstChildChanged = () => {
        fetchTableBody();
        if (
          currentFirstChild !=
          tableBody.children[0].attributes["data-d3-row"].value
        ) {
          currentFirstChild =
            tableBody.children[0].attributes["data-d3-row"].value;
          return true;
        }
        return false;
      };

      const input = content.document.getElementById("filter-metrics");
      input.value = "aCustomDist";
      input.dispatchEvent(new Event("input"));

      await TestUtils.waitForCondition(
        tableFirstChildChanged,
        "Wait for the table's first child to change",
        100,
        3
      );

      Glean.testOnlyIpc.aCustomDist.accumulateSamples([0, 0, 1, 1, 1]);

      content.document
        .querySelector(
          "[data-d3-row='testOnlyIpc.aCustomDist'] button[data-l10n-id='about-glean-button-load-value']"
        )
        .click();

      let valueCell;
      const getValueCell = () => {
        valueCell = content.document.querySelector(
          "[data-d3-row='testOnlyIpc.aCustomDist'] [data-d3-cell='value']"
        );
      };
      getValueCell();

      Assert.equal(valueCell.childElementCount, 1);
      Assert.equal(valueCell.firstChild.tagName, "svg");

      //            td        svg        g.boxes    g*n     >rect+text+text
      const boxes = valueCell.firstChild.firstChild.children;

      Assert.equal(boxes.length, 2, "Bar graphs for bucket 0 and 1 shown");
      Assert.equal(boxes[0].children[1].textContent, "2");
      Assert.equal(boxes[0].children[2].textContent, "0");
      Assert.equal(boxes[1].children[1].textContent, "3");
      Assert.equal(boxes[1].children[2].textContent, "1");

      Services.fog.testResetFOG();
      content.document
        .querySelector(
          "[data-d3-row='testOnlyIpc.aCustomDist'] button[data-l10n-id='about-glean-button-load-value']"
        )
        .click();

      getValueCell();

      Assert.equal(valueCell.childElementCount, 1);
      Assert.equal(valueCell.firstChild.tagName, "P");
      Assert.equal(
        valueCell.firstChild.getAttribute("data-l10n-id"),
        "about-glean-no-data-to-display"
      );
    });
  });
});

add_task(async function test_about_glean_event_timeline() {
  Services.fog.testResetFOG();
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("about.glean.redesign.enabled");
  });
  Services.prefs.setBoolPref("about.glean.redesign.enabled", true);

  await BrowserTestUtils.withNewTab("about:glean", async function (browser) {
    ok(!browser.isRemoteBrowser, "Browser should not be remote.");
    await ContentTask.spawn(browser, null, async function () {
      const { TestUtils } = ChromeUtils.importESModule(
        "resource://testing-common/TestUtils.sys.mjs"
      );
      content.document.getElementById("category-metrics-table").click();

      let tableBody;
      const fetchTableBody = () => {
        tableBody = content.document.getElementById("metrics-table-body");
      };
      fetchTableBody();
      let currentFirstChild =
        tableBody.children[0].attributes["data-d3-row"].value;

      const tableFirstChildChanged = () => {
        fetchTableBody();
        if (
          currentFirstChild !=
          tableBody.children[0].attributes["data-d3-row"].value
        ) {
          currentFirstChild =
            tableBody.children[0].attributes["data-d3-row"].value;
          return true;
        }
        return false;
      };

      const input = content.document.getElementById("filter-metrics");
      input.value = "anEvent";
      input.dispatchEvent(new Event("input"));

      await TestUtils.waitForCondition(
        tableFirstChildChanged,
        "Wait for the table's first child to change",
        100,
        3
      );

      let extra = {
        value: "a value for Telemetry",
        extra1: "can set extras",
        extra2: "passing more data",
      };
      Glean.testOnlyIpc.anEvent.record(extra);
      Glean.testOnlyIpc.anEvent.record();
      Glean.testOnlyIpc.anEvent.record();

      content.document
        .querySelector(
          "[data-d3-row='testOnlyIpc.anEvent'] button[data-l10n-id='about-glean-button-load-value']"
        )
        .click();

      let valueCell;
      const getValueCell = () => {
        valueCell = content.document.querySelector(
          "[data-d3-row='testOnlyIpc.anEvent'] [data-d3-cell='value']"
        );
      };
      getValueCell();

      Assert.equal(valueCell.childElementCount, 2);
      Assert.equal(valueCell.firstChild.tagName, "svg");

      let code = content.document.querySelector(
        `[data-d3-row='testOnlyIpc.anEvent'] pre>code`
      ).textContent;
      const codeChanged = () => {
        const newCode = content.document.querySelector(
          `[data-d3-row='testOnlyIpc.anEvent'] pre>code`
        ).textContent;
        if (newCode != code) {
          code = newCode;
          return true;
        }
        return false;
      };

      content.document
        .querySelector(`[data-d3-row='testOnlyIpc.anEvent'] g.event`)
        .focus();

      await TestUtils.waitForCondition(
        codeChanged,
        "Wait for the table row's code textContent to change",
        100,
        3
      );

      getValueCell();
      Assert.equal(valueCell.childElementCount, 2);
      Assert.equal(valueCell.lastChild.tagName, "PRE");
      console.log(valueCell.lastChild.firstChild.textContent);
      Assert.equal(
        valueCell.lastChild.firstChild.textContent.includes(extra.value),
        true
      );
    });
  });
});

add_task(async function test_about_glean_ping_groups_and_none_label() {
  await BrowserTestUtils.withNewTab("about:glean", async browser => {
    await ContentTask.spawn(browser, null, async function () {
      const { TestUtils } = ChromeUtils.importESModule(
        "resource://testing-common/TestUtils.sys.mjs"
      );
      const { Assert } = ChromeUtils.importESModule(
        "resource://testing-common/Assert.sys.mjs"
      );

      // Wait for the select and optgroups to exist and be populated.
      await TestUtils.waitForCondition(() => {
        const doc = content.document;
        const select = doc.getElementById("ping-names");
        const builtin = doc.getElementById("builtin-pings");
        const custom = doc.getElementById("custom-pings");
        return select && builtin && custom && builtin.children.length >= 1;
      }, "Wait for ping select to be populated");

      const doc = content.document;
      const builtin = doc.getElementById("builtin-pings");
      const custom = doc.getElementById("custom-pings");

      // Built-in group should contain metrics/events/baseline and end with the localized 'none' option.
      const builtinOptions = Array.from(builtin.children);
      Assert.strictEqual(
        builtinOptions.length,
        4,
        "Built-in group has 4 options (incl. none)"
      );

      Assert.equal(
        builtinOptions[0].textContent,
        "metrics",
        "Built-ins include 'metrics'"
      );

      Assert.equal(
        builtinOptions[1].textContent,
        "events",
        "Built-ins include 'events'"
      );

      Assert.equal(
        builtinOptions[2].textContent,
        "baseline",
        "Built-ins include 'baseline'"
      );

      // Note - We're checking the value instead of textContent
      // for the last built in option, as textContent may be localized.
      Assert.equal(
        builtinOptions[3].value,
        "(don't submit any ping)",
        "Built-ins include '(don't submit any ping)'"
      );

      // Custom group should be alphabetically sorted by displayed label.
      const customLabels = Array.from(custom.children).map(o => o.textContent);
      if (customLabels.length >= 2) {
        const sorted = customLabels.slice().sort((a, b) => a.localeCompare(b));
        Assert.deepEqual(
          customLabels,
          sorted,
          "Custom ping options are alphabetically sorted"
        );
      }
    });
  });
});

add_task(async function test_about_glean_metrics_table_settings() {
  Services.fog.testResetFOG();
  registerCleanupFunction(() => {
    Services.prefs.clearUserPref("about.glean.redesign.enabled");
  });
  Services.prefs.setBoolPref("about.glean.redesign.enabled", true);

  await BrowserTestUtils.withNewTab("about:glean", async browser => {
    await ContentTask.spawn(browser, null, async function () {
      const { TestUtils } = ChromeUtils.importESModule(
        "resource://testing-common/TestUtils.sys.mjs"
      );
      const { Assert } = ChromeUtils.importESModule(
        "resource://testing-common/Assert.sys.mjs"
      );

      content.document.getElementById("category-metrics-table").click();

      let tableBody;
      const fetchTableBody = () => {
        tableBody = content.document.getElementById("metrics-table-body");
      };
      fetchTableBody();
      let currentFirstChild =
        tableBody.children[0].attributes["data-d3-row"].value;

      const tableFirstChildChanged = () => {
        fetchTableBody();
        if (
          currentFirstChild !=
          tableBody.children[0].attributes["data-d3-row"].value
        ) {
          currentFirstChild =
            tableBody.children[0].attributes["data-d3-row"].value;
          return true;
        }
        return false;
      };

      const input = content.document.getElementById("filter-metrics");
      input.value = "aBool";
      input.dispatchEvent(new Event("input"));

      await TestUtils.waitForCondition(
        tableFirstChildChanged,
        "Wait for the table's first child to change",
        100,
        3
      );

      content.document
        .querySelector(
          "[data-d3-row='testOnlyIpc.aBool'] button[data-l10n-id='about-glean-button-load-value']"
        )
        .click();

      content.document.getElementById("metrics-table-settings-button").click();

      const checkbox = content.document.getElementById(
        "settings-hide-empty-value-rows"
      );
      checkbox.checked = true;
      checkbox.dispatchEvent(new Event("input"));

      const tableNoChildren = () => {
        fetchTableBody();
        if (tableBody.children.length === 0) {
          return true;
        }
        return false;
      };

      await TestUtils.waitForCondition(
        tableNoChildren,
        "Wait for the table's children to be empty",
        100,
        3
      );

      Assert.equal(
        content.document.querySelector("[data-d3-row='testOnlyIpc.aBool']"),
        null,
        "Data row for `testOnlyIpc.aBool` should not exist"
      );
    });
  });
});
