/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at http://mozilla.org/MPL/2.0/. */

let tabmail = document.getElementById("tabmail");
registerCleanupFunction(() => {
  tabmail.closeOtherTabs(tabmail.tabInfo[0]);
});
let browser;
let searchBar;

const waitForRender = () => {
  return new Promise(resolve => {
    window.requestAnimationFrame(resolve);
  });
};

/* These are shadow-root safe variants of the methods in BrowserTestUtils. */

/**
 * Checks if a DOM element is hidden.
 *
 * @param {Element} element
 *        The element which is to be checked.
 *
 * @return {boolean}
 */
function is_hidden(element) {
  var style = element.ownerGlobal.getComputedStyle(element);
  if (style.display == "none") {
    return true;
  }
  if (style.visibility != "visible") {
    return true;
  }
  if (style.display == "-moz-popup") {
    return ["hiding", "closed"].includes(element.state);
  }

  // Hiding a parent element will hide all its children
  if (element.parentNode != element.ownerDocument && element.parentElement) {
    return is_hidden(element.parentElement);
  }

  return false;
}

/**
 * Checks if a DOM element is visible.
 *
 * @param {Element} element
 *        The element which is to be checked.
 *
 * @return {boolean}
 */
function is_visible(element) {
  var style = element.ownerGlobal.getComputedStyle(element);
  if (style.display == "none") {
    return false;
  }
  if (style.visibility != "visible") {
    return false;
  }
  if (style.display == "-moz-popup" && element.state != "open") {
    return false;
  }

  // Hiding a parent element will hide all its children
  if (element.parentNode != element.ownerDocument && element.parentElement) {
    return is_visible(element.parentElement);
  }

  return true;
}

add_setup(async function () {
  let tab = tabmail.openTab("contentTab", {
    url: "chrome://mochitests/content/browser/comm/mail/components/unifiedtoolbar/test/browser/files/searchBar.xhtml",
  });

  await BrowserTestUtils.browserLoaded(tab.browser);
  tab.browser.focus();
  browser = tab.browser;
  searchBar = tab.browser.contentWindow.document.querySelector("search-bar");
});

add_task(async function test_initialState() {
  const input = searchBar.shadowRoot.querySelector("input");
  is(
    input.getAttribute("aria-label"),
    searchBar.getAttribute("label"),
    "Label forwarded to aria-label on input"
  );
});

add_task(async function test_labelUpdate() {
  const input = searchBar.shadowRoot.querySelector("input");
  searchBar.setAttribute("label", "foo");
  await waitForRender();
  is(
    input.getAttribute("aria-label"),
    "foo",
    "Updated label applied to content"
  );
});

add_task(async function test_focus() {
  const input = searchBar.shadowRoot.querySelector("input");
  searchBar.focus();
  is(
    searchBar.shadowRoot.activeElement,
    input,
    "Input is focused when search bar is focused"
  );
});

add_task(async function test_autocompleteEvent() {
  const typeAndWaitForAutocomplete = async key => {
    const eventPromise = BrowserTestUtils.waitForEvent(
      searchBar,
      "autocomplete"
    );
    await BrowserTestUtils.synthesizeKey(key, {}, browser);
    return eventPromise;
  };
  searchBar.focus();
  let event = await typeAndWaitForAutocomplete("T");
  is(event.detail, "T", "Autocomplete for T");

  event = await typeAndWaitForAutocomplete("e");
  is(event.detail, "Te", "Autocomplete for e");

  event = await typeAndWaitForAutocomplete("KEY_Backspace");
  is(event.detail, "T", "Autocomplete for backspace");

  await BrowserTestUtils.synthesizeKey("KEY_Backspace", {}, browser);
});

add_task(async function test_searchEventFromEnter() {
  const input = searchBar.shadowRoot.querySelector("input");
  input.value = "Lorem ipsum";
  searchBar.focus();

  const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search");
  await BrowserTestUtils.synthesizeKey("KEY_Enter", {}, browser);
  const event = await eventPromise;

  is(event.detail, "Lorem ipsum", "Event contains search query");
  await waitForRender();
  is(input.value, "", "Input was cleared");
});

add_task(async function test_searchEventFromButton() {
  const input = searchBar.shadowRoot.querySelector("input");
  input.value = "Lorem ipsum";

  const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search");
  searchBar.shadowRoot.querySelector("button").click();
  const event = await eventPromise;

  is(event.detail, "Lorem ipsum", "Event contains search query");
  await waitForRender();
  is(input.value, "", "Input was cleared");
});

add_task(async function test_searchEventPreventDefault() {
  const input = searchBar.shadowRoot.querySelector("input");
  input.value = "Lorem ipsum";

  searchBar.addEventListener(
    "search",
    event => {
      event.preventDefault();
    },
    {
      once: true,
      passive: false,
    }
  );

  const eventPromise = BrowserTestUtils.waitForEvent(searchBar, "search");
  searchBar.shadowRoot.querySelector("button").click();
  await eventPromise;
  await waitForRender();

  is(input.value, "Lorem ipsum");

  input.value = "";
});

add_task(async function test_placeholderVisibility() {
  const placeholder = searchBar.shadowRoot.querySelector("div");
  const input = searchBar.shadowRoot.querySelector("input");

  input.value = "";
  await waitForRender();
  ok(is_visible(placeholder), "Placeholder is visible initially");

  input.value = "some input";
  await waitForRender();
  ok(is_hidden(placeholder), "Placeholder is hidden after text is entered");

  input.value = "";
  await waitForRender();
  ok(
    is_visible(placeholder),
    "Placeholder is visible again after input is cleared"
  );
});

add_task(async function test_placeholderFallbackToLabel() {
  const placeholder = searchBar.querySelector("span");
  placeholder.remove();

  const shadowedPlaceholder = searchBar.shadowRoot.querySelector("div");
  const label = searchBar.getAttribute("label");

  is(
    shadowedPlaceholder.textContent,
    label,
    "Falls back to label if no placeholder slot contents provided"
  );

  searchBar.setAttribute("label", "Foo bar");
  is(
    shadowedPlaceholder.textContent,
    "Foo bar",
    "Placeholder contents get updated with label attribute"
  );

  searchBar.prepend(placeholder);
  searchBar.setAttribute("label", label);
});

add_task(async function test_reset() {
  const input = searchBar.shadowRoot.querySelector("input");
  const placeholder = searchBar.shadowRoot.querySelector("div");
  input.value = "Lorem ipsum";

  searchBar.reset();

  is(input.value, "", "Input empty after reset");
  await waitForRender();
  ok(is_visible(placeholder), "Placeholder visible");
});

add_task(async function test_disabled() {
  const input = searchBar.shadowRoot.querySelector("input");
  const button = searchBar.shadowRoot.querySelector("button");

  ok(!input.disabled, "Input enabled");
  ok(!button.disabled, "Button enabled");

  searchBar.setAttribute("disabled", true);

  ok(input.disabled, "Disabled propagated to input");
  ok(button.disabled, "Disabled propagated to button");

  searchBar.removeAttribute("disabled");

  ok(!input.disabled, "Input enabled again");
  ok(!button.disabled, "Button enabled again");
});
