/* 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/. */

// Tests reverse search features.

"use strict";

const TEST_URI = `data:text/html,<!DOCTYPE html><meta charset=utf8>Test reverse search`;
const isMacOS = AppConstants.platform === "macosx";

add_task(async function () {
  const hud = await openNewTabAndConsole(TEST_URI);

  const jstermHistory = [
    `document`,
    `Dog = "Snoopy"`,
    `document
       .querySelectorAll("*")
       .forEach(()=>{})`,
    `document`,
    `"a" + "😎"`,
  ];

  // We have to wait for the same message twice in order to wait for the evaluation line
  // as well as the result line
  const onLastMessage = Promise.all([
    waitForMessageByType(hud, `"a" + "😎"`, ".command"),
    waitForMessageByType(hud, `"a😎"`, ".result"),
  ]);
  for (const input of jstermHistory) {
    execute(hud, input);
  }
  await onLastMessage;

  const initialValue = "initialValue";
  setInputValue(hud, initialValue);

  info("Check that the reverse search toolbar as the expected initial state");
  let reverseSearchElement = await openReverseSearch(hud);
  ok(
    reverseSearchElement,
    "Reverse search is displayed with a keyboard shortcut"
  );
  ok(
    !getReverseSearchInfoElement(hud),
    "The result info element is not displayed by default"
  );
  ok(
    !reverseSearchElement.querySelector(".search-result-button-prev") &&
      !reverseSearchElement.querySelector(".search-result-button-next"),
    "The results navigation buttons are not displayed by default"
  );
  is(
    getInputValue(hud),
    initialValue,
    "The jsterm value is not changed when opening reverse search"
  );
  is(isReverseSearchInputFocused(hud), true, "reverse search input is focused");

  EventUtils.sendString("d");
  let infoElement = await waitFor(() => getReverseSearchInfoElement(hud));
  is(
    infoElement.textContent,
    "3 of 3 results",
    "The reverse info has the expected text " +
      "— duplicated results (`document`) are coalesced"
  );

  const previousButton = reverseSearchElement.querySelector(
    ".search-result-button-prev"
  );
  const nextButton = reverseSearchElement.querySelector(
    ".search-result-button-next"
  );
  ok(previousButton, "Previous navigation button is now displayed");
  is(
    previousButton.title,
    `Previous result (${isMacOS ? "Ctrl + R" : "F9"})`,
    "Previous navigation button has expected title"
  );

  ok(nextButton, "Next navigation button is now displayed");
  is(
    nextButton.title,
    `Next result (${isMacOS ? "Ctrl + S" : "Shift + F9"})`,
    "Next navigation button has expected title"
  );
  is(getInputValue(hud), "document", "JsTerm has the expected input");
  is(
    hud.jsterm.autocompletePopup.isOpen,
    false,
    "Setting the input value did not trigger the autocompletion"
  );
  is(isReverseSearchInputFocused(hud), true, "reverse search input is focused");

  let onJsTermValueChanged = hud.jsterm.once("set-input-value");
  EventUtils.sendString("og");
  await onJsTermValueChanged;
  is(getInputValue(hud), `Dog = "Snoopy"`, "JsTerm input was updated");
  is(
    infoElement.textContent,
    "1 result",
    "The reverse info has the expected text"
  );
  ok(
    !reverseSearchElement.querySelector(".search-result-button-prev") &&
      !reverseSearchElement.querySelector(".search-result-button-next"),
    "The results navigation buttons are not displayed when there's only one result"
  );

  info("Check that the UI and results are updated when typing in the input");
  onJsTermValueChanged = hud.jsterm.once("set-input-value");
  EventUtils.sendString("g");
  await waitFor(() => reverseSearchElement.classList.contains("no-result"));
  is(
    getInputValue(hud),
    `Dog = "Snoopy"`,
    "JsTerm input was not updated since there's no results"
  );
  is(
    infoElement.textContent,
    "No results",
    "The reverse info has the expected text"
  );
  ok(
    !reverseSearchElement.querySelector(".search-result-button-prev") &&
      !reverseSearchElement.querySelector(".search-result-button-next"),
    "The results navigation buttons are not displayed when there's no result"
  );

  info("Check that Backspace updates the UI");
  EventUtils.synthesizeKey("KEY_Backspace");
  await waitFor(() => !reverseSearchElement.classList.contains("no-result"));
  is(
    infoElement.textContent,
    "1 result",
    "The reverse info has the expected text"
  );
  is(getInputValue(hud), `Dog = "Snoopy"`, "JsTerm kept its value");

  info("Check that Escape does not affect the jsterm value");
  EventUtils.synthesizeKey("KEY_Escape");
  await waitFor(() => !getReverseSearchElement(hud));
  is(
    getInputValue(hud),
    `Dog = "Snoopy"`,
    "Closing the input did not changed the JsTerm value"
  );
  is(isInputFocused(hud), true, "input is focused");

  info("Check that the search works with emojis");
  reverseSearchElement = await openReverseSearch(hud);
  onJsTermValueChanged = hud.jsterm.once("set-input-value");
  EventUtils.sendString("😎");
  infoElement = await waitFor(() => getReverseSearchInfoElement(hud));
  is(
    infoElement.textContent,
    "1 result",
    "The reverse info has the expected text"
  );

  info("Check that Enter evaluates the JsTerm and closes the UI");
  // We have to wait for the same message twice in order to wait for the evaluation line
  // as well as the result line
  const onMessage = Promise.all([
    waitForMessageByType(hud, `"a" + "😎"`, ".command"),
    waitForMessageByType(hud, `"a😎"`, ".result"),
  ]);
  const onReverseSearchClose = waitFor(() => !getReverseSearchElement(hud));
  EventUtils.synthesizeKey("KEY_Enter");
  await Promise.all([onMessage, onReverseSearchClose]);
  ok(
    true,
    "Enter evaluates what's in the JsTerm and closes the reverse search UI"
  );
});
