<!DOCTYPE HTML>
<html>
<!--
Test the login-filter component
-->
<head>
  <meta charset="utf-8">
  <title>Test the login-filter component</title>
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/login-filter.mjs"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/login-list.mjs"></script>
  <script src="aboutlogins_common.js"></script>

  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
  <p id="display">
  </p>
<div id="content" style="display: none">
  <iframe id="templateFrame" src="chrome://browser/content/aboutlogins/aboutLogins.html"
          sandbox="allow-same-origin"></iframe>
</div>
<pre id="test">
</pre>
<script>
/** Test the login-filter component **/

let gLoginFilter;
let gLoginList;
add_setup(async () => {
  let templateFrame = document.getElementById("templateFrame");
  let displayEl = document.getElementById("display");
  importDependencies(templateFrame, displayEl);

  gLoginFilter = document.createElement("login-filter");
  displayEl.appendChild(gLoginFilter);

  gLoginList = document.createElement("login-list");
  displayEl.appendChild(gLoginList);
});

add_task(async function test_empty_filter() {
  ok(gLoginFilter, "loginFilter exists");
  is(gLoginFilter.shadowRoot.querySelector("input").value, "", "Initially empty");
});

add_task(async function test_input_events() {
  let filterEvent = null;
  window.addEventListener("AboutLoginsFilterLogins", event => filterEvent = event);
  let input = SpecialPowers.wrap(gLoginFilter.shadowRoot.querySelector("input"));
  input.setUserInput("test");
  ok(filterEvent, "Filter event received");
  is(filterEvent.detail, "test", "Event includes input value");
});

add_task(async function test_list_filtered() {
  const LOGINS = [{
    guid: "123456789",
    origin: "https://example.com",
    username: "user1",
    password: "pass1",
  }, {
    guid: "987654321",
    origin: "https://example.com",
    username: "user2",
    password: "pass2",
  }];
  gLoginList.setLogins(LOGINS);

  let tests = [
    ["", 2],
    [LOGINS[0].username, 1],
    [LOGINS[0].username + "-notfound", 0],
    [LOGINS[0].username.substr(2, 3), 1],
    ["", 2],
    // The password is also used for search when MP is disabled.
    [LOGINS[0].password, 1],
    [LOGINS[0].password + "-notfound", 0],
    [LOGINS[0].password.substr(2, 3), 1],
    ["", 2],
    [LOGINS[0].origin, 2],
    [LOGINS[0].origin + "-notfound", 0],
    [LOGINS[0].origin.substr(2, 3), 2],
    ["", 2],
    // The guid is not used for search.
    [LOGINS[0].guid, 0],
    [LOGINS[0].guid + "-notfound", 0],
    [LOGINS[0].guid.substr(0, 2), 0],
    ["", 2],
  ];

  let loginFilterInput = gLoginFilter.shadowRoot.querySelector("input");
  loginFilterInput.focus();

  for (let i = 0; i < tests.length; i++) {
    info("Testcase: " + i);

    let testObj = {
      testCase: i,
      query: tests[i][0],
      resultExpectedCount: tests[i][1],
    };

    let filterLength = loginFilterInput.value.length;
    while (filterLength-- > 0) {
      sendKey("BACK_SPACE");
    }
    sendString(testObj.query);

    await SimpleTest.promiseWaitForCondition(() => {
      let countElement = gLoginList.shadowRoot.querySelector(".count");
      return countElement.hasAttribute("data-l10n-args") &&
             JSON.parse(countElement.getAttribute("data-l10n-args")).count == testObj.resultExpectedCount;
    }, `Waiting for the search result count to update to ${testObj.resultExpectedCount} (tc#${testObj.testCase})`);
  }
});

  add_task(async function test_keys_in_filter() {
    const LOGINS = [{
      guid: "123456789",
      origin: "https://example.com",
      username: "user1",
      password: "pass1",
    }, {
      guid: "987654321",
      origin: "https://example.com",
      username: "user2",
      password: "pass2",
    }, {
      guid: "333333333",
      origin: "https://example.com",
      username: "user3",
      password: "pass3",
    }];
    gLoginList.setLogins(LOGINS);

    const ol = gLoginList.shadowRoot.querySelector("ol");
    const loginFilterInput = gLoginFilter.shadowRoot.querySelector("input");
    loginFilterInput.focus();

    // Up/down keys must select previous/next item in the list
    function pressKeyAndExpectSelection(key, selectedIndex) {
      sendKey(key);
      ok(ol.querySelectorAll(".login-list-item[data-guid]:not([hidden])")[selectedIndex]?.classList?.contains("keyboard-selected"),
        `item ${selectedIndex} should be marked as keyboard-selected`);
      is(ol.querySelector(".selected").dataset.guid, LOGINS[selectedIndex].guid, `item ${selectedIndex} must be selected`);
    }

    pressKeyAndExpectSelection("DOWN", 1);
    pressKeyAndExpectSelection("DOWN", 2);

    // ENTER key in search box must click on selected item in the list
    sendKey("RETURN");
    is(ol.querySelector(".selected").dataset.guid, LOGINS[2].guid, "item 2 must still be selected");

    pressKeyAndExpectSelection("DOWN", 2);
    pressKeyAndExpectSelection("UP", 1);
    pressKeyAndExpectSelection("UP", 0);
    pressKeyAndExpectSelection("UP", 0);

    // ESC must clear search box
    async function expectItemCount(count) {
      await SimpleTest.promiseWaitForCondition(() =>
        JSON.parse(gLoginList.shadowRoot.querySelector(".count").getAttribute("data-l10n-args"))?.count == count,
        `Waiting for the search result count to update to ${count}`);
    }

    sendString("unique string");
    await expectItemCount(0);
    sendKey("Escape");
    ok(!loginFilterInput.value, "ESC must clear filter input");
    await expectItemCount(LOGINS.length);
  });
</script>

</body>
</html>
