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

"use strict";

/**
 * Test autocomplete based on filtering flags and requests
 */
const REQUESTS = [
  {
    url: "sjs_content-type-test-server.sjs?fmt=html&res=undefined&text=Sample",
  },
  {
    url:
      "sjs_content-type-test-server.sjs?fmt=html&res=undefined&text=Sample" +
      "&cookies=1",
  },
  { url: "sjs_content-type-test-server.sjs?fmt=css&text=sample" },
  { url: "sjs_content-type-test-server.sjs?fmt=js&text=sample" },
  { url: "sjs_content-type-test-server.sjs?fmt=font" },
  { url: "sjs_content-type-test-server.sjs?fmt=image" },
  { url: "sjs_content-type-test-server.sjs?fmt=audio" },
  { url: "sjs_content-type-test-server.sjs?fmt=video" },
  { url: "sjs_content-type-test-server.sjs?fmt=gzip" },
  { url: "sjs_status-codes-test-server.sjs?sts=304" },
];

function testAutocompleteContents(expected, document) {
  expected.forEach(function (item, i) {
    is(
      document.querySelector(
        `.devtools-autocomplete-listbox .autocomplete-item:nth-child(${i + 1})`
      ).textContent,
      item,
      `${expected[i]} found`
    );
  });
}

add_task(async function () {
  const { monitor } = await initNetMonitor(FILTERING_URL, { requestCount: 1 });
  const { document, store, windowRequire } = monitor.panelWin;
  const Actions = windowRequire("devtools/client/netmonitor/src/actions/index");

  store.dispatch(Actions.batchEnable(false));

  info("Starting test... ");

  // Let the requests load completely before the autocomplete tests begin
  // as autocomplete values also rely on the network requests.
  const waitNetwork = waitForNetworkEvents(monitor, REQUESTS.length);
  await performRequestsInContent(REQUESTS);
  await waitNetwork;

  EventUtils.synthesizeMouseAtCenter(
    document.querySelector(".devtools-filterinput"),
    {},
    document.defaultView
  );
  // Empty Mouse click should keep autocomplete hidden
  ok(
    !document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup still hidden"
  );

  document.querySelector(".devtools-filterinput").focus();

  // Typing numbers that corresponds to status codes should invoke an autocomplete
  EventUtils.sendString("2");
  ok(
    document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup Created"
  );
  testAutocompleteContents(
    [
      "status-code:200",
      "status-code:201",
      "status-code:202",
      "status-code:203",
      "status-code:204",
      "status-code:205",
      "status-code:206",
    ],
    document
  );
  EventUtils.synthesizeKey("KEY_Enter");
  is(
    document.querySelector(".devtools-filterinput").value,
    "status-code:200",
    "Value correctly set after Enter"
  );
  ok(
    !document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup hidden after keyboard Enter key"
  );

  // Space separated tokens
  // The last token where autocomplete is available shall generate the popup
  EventUtils.sendString(" s");
  ok(
    document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup Created"
  );
  testAutocompleteContents(
    [
      "scheme:",
      "set-cookie-domain:",
      "set-cookie-name:",
      "set-cookie-value:",
      "size:",
      "status-code:",
    ],
    document
  );

  EventUtils.sendString("c");
  testAutocompleteContents(["scheme:"], document);
  EventUtils.synthesizeKey("KEY_Tab");
  // Tab selection should hide autocomplete
  ok(
    document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup alive with content values"
  );
  testAutocompleteContents(["scheme:http"], document);

  EventUtils.synthesizeKey("KEY_Enter");
  is(
    document.querySelector(".devtools-filterinput").value,
    "status-code:200 scheme:http",
    "Value correctly set after Enter"
  );
  ok(
    !document.querySelector(".devtools-autocomplete-popup"),
    "Autocomplete Popup hidden after keyboard Enter key"
  );

  // Space separated tokens
  // The last token where autocomplete is available shall generate the popup
  EventUtils.sendString(" pro");
  testAutocompleteContents(["protocol:"], document);

  // The new value of the text box should be previousTokens + latest value selected
  // First return selects "protocol:"
  EventUtils.synthesizeKey("KEY_Enter");
  // Second return selects "protocol:HTTP/1.1"
  EventUtils.synthesizeKey("KEY_Enter");
  is(
    document.querySelector(".devtools-filterinput").value,
    "status-code:200 scheme:http protocol:HTTP/1.1",
    "Tokenized click generates correct value in input box"
  );

  // Explicitly type in `flag:` renders autocomplete with values
  EventUtils.sendString(" status-code:");
  testAutocompleteContents(["status-code:200", "status-code:304"], document);

  // Typing the exact value closes autocomplete
  EventUtils.sendString("304");
  ok(
    !document.querySelector(".devtools-autocomplete-popup"),
    "Typing the exact value closes autocomplete"
  );

  // Check if mime-type has been correctly parsed out and values also get autocomplete
  EventUtils.sendString(" mime-type:text");
  testAutocompleteContents(
    ["mime-type:text/css", "mime-type:text/html", "mime-type:text/plain"],
    document
  );

  // The negative filter flags
  EventUtils.sendString(" -");
  testAutocompleteContents(
    [
      "-cause:",
      "-domain:",
      "-has-response-header:",
      "-initiator:",
      "-is:",
      "-larger-than:",
      "-method:",
      "-mime-type:",
      "-priority:",
      "-protocol:",
      "-regexp:",
      "-remote-ip:",
      "-scheme:",
      "-set-cookie-domain:",
      "-set-cookie-name:",
      "-set-cookie-value:",
      "-size:",
      "-status-code:",
      "-transferred-larger-than:",
      "-transferred:",
    ],
    document
  );

  // Autocomplete for negative filtering
  EventUtils.sendString("is:");
  testAutocompleteContents(
    ["-is:cached", "-is:from-cache", "-is:running"],
    document
  );

  await teardown(monitor);
});
