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

function waitForLoginCountToReach(browser, loginCount) {
  return SpecialPowers.spawn(
    browser,
    [loginCount],
    async expectedLoginCount => {
      let loginList = Cu.waiveXrays(
        content.document.querySelector("login-list")
      );
      await ContentTaskUtils.waitForCondition(() => {
        return loginList._loginGuidsSortedOrder.length == expectedLoginCount;
      });
      return loginList._loginGuidsSortedOrder.length;
    }
  );
}

add_setup(async function () {
  await addLogin(TEST_LOGIN1);
  registerCleanupFunction(() => {
    Services.logins.removeAllUserFacingLogins();
    LoginTestUtils.primaryPassword.disable();
  });
});

add_task(async function test() {
  // Confirm that the mocking of the OS auth dialog isn't enabled so the
  // test will timeout if a real OS auth dialog is shown. We don't show
  // the OS auth dialog when Primary Password is enabled.
  Assert.equal(
    Services.prefs.getStringPref(
      "toolkit.osKeyStore.unofficialBuildOnlyLogin",
      ""
    ),
    "",
    "Pref should be set to default value of empty string to start the test"
  );
  LoginTestUtils.primaryPassword.enable();

  let mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("cancel");
  await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    url: "about:logins",
  });
  await mpDialogShown;

  let browser = gBrowser.selectedBrowser;
  let logins = await waitForLoginCountToReach(browser, 0);
  Assert.equal(
    logins,
    0,
    "No logins should be displayed when MP is set and unauthenticated"
  );

  let notification;
  await TestUtils.waitForCondition(
    () =>
      (notification = gBrowser
        .getNotificationBox()
        .getNotificationWithValue("primary-password-login-required")),
    "waiting for primary-password-login-required notification"
  );

  Assert.ok(
    notification,
    "primary-password-login-required notification should be visible"
  );

  let buttons = notification.buttonContainer.querySelectorAll(
    ".notification-button"
  );
  Assert.equal(buttons.length, 1, "Should have one button.");

  let refreshPromise = BrowserTestUtils.browserLoaded(browser);
  // Sign in with the Primary Password this time the dialog is shown
  mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("authenticate");
  // Click the button to reload the page.
  buttons[0].click();
  await refreshPromise;
  info("Page reloaded");

  await mpDialogShown;
  info("Primary Password dialog shown and authenticated");

  logins = await waitForLoginCountToReach(browser, 1);
  Assert.equal(
    logins,
    1,
    "Logins should be displayed when MP is set and authenticated"
  );

  // Show MP dialog when Copy Password button clicked
  mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("cancel");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let copyButton = loginItem.shadowRoot.querySelector(
      ".copy-password-button"
    );
    copyButton.click();
  });
  await mpDialogShown;
  info("Primary Password dialog shown and canceled");
  mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("authenticate");
  info("Clicking copy password button again");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let copyButton = loginItem.shadowRoot.querySelector(
      ".copy-password-button"
    );
    copyButton.click();
  });
  await mpDialogShown;
  info("Primary Password dialog shown and authenticated");
  await SpecialPowers.spawn(browser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let copyButton = loginItem.shadowRoot.querySelector(
      ".copy-password-button"
    );
    await ContentTaskUtils.waitForCondition(() => {
      return copyButton.disabled;
    }, "Waiting for copy button to be disabled");
    info("Password was copied to clipboard");
  });

  // Show MP dialog when Reveal Password checkbox is checked if not on a new login
  mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("cancel");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let revealCheckbox = loginItem.shadowRoot.querySelector(
      ".reveal-password-checkbox"
    );
    revealCheckbox.click();
  });
  await mpDialogShown;
  info("Primary Password dialog shown and canceled");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let revealCheckbox = loginItem.shadowRoot.querySelector(
      ".reveal-password-checkbox"
    );
    Assert.ok(
      !revealCheckbox.checked,
      "reveal checkbox should be unchecked if MP dialog canceled"
    );
  });
  mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("authenticate");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let revealCheckbox = loginItem.shadowRoot.querySelector(
      ".reveal-password-checkbox"
    );
    revealCheckbox.click();
  });
  await mpDialogShown;
  info("Primary Password dialog shown and authenticated");
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    let revealCheckbox = loginItem.shadowRoot.querySelector(
      ".reveal-password-checkbox"
    );
    Assert.ok(
      revealCheckbox.checked,
      "reveal checkbox should be checked if MP dialog authenticated"
    );
  });

  info("Test toggling the password visibility on a new login");
  await SpecialPowers.spawn(browser, [], async function createNewToggle() {
    let createButton = content.document
      .querySelector("login-list")
      .shadowRoot.querySelector(".create-login-button");
    createButton.click();

    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
    let passwordField = loginItem.shadowRoot.querySelector(
      "input[name='password']"
    );
    let revealCheckbox = loginItem.shadowRoot.querySelector(
      ".reveal-password-checkbox"
    );
    Assert.ok(ContentTaskUtils.is_visible(revealCheckbox), "Toggle visible");
    Assert.ok(!revealCheckbox.checked, "Not revealed initially");
    Assert.equal(passwordField.type, "password", "type is password");
    revealCheckbox.click();

    await ContentTaskUtils.waitForCondition(() => {
      return passwordField.type == "text";
    }, "Waiting for type='text'");
    Assert.ok(revealCheckbox.checked, "Not revealed after click");

    let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button");
    cancelButton.click();
  });

  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    const loginList = Cu.waiveXrays(
      content.document.querySelector("login-list")
    );

    const loginFilter = Cu.waiveXrays(
      loginList.shadowRoot.querySelector("login-filter")
    );
    loginFilter.value = "pass1";
    Assert.equal(
      loginList._list.querySelectorAll(
        ".login-list-item[data-guid]:not([hidden])"
      ).length,
      1,
      "login-list should show corresponding result when primary password is enabled"
    );
    loginFilter.value = "";
    Assert.equal(
      loginList._list.querySelectorAll(
        ".login-list-item[data-guid]:not([hidden])"
      ).length,
      1,
      "login-list should show all results since the filter is empty"
    );
  });
  LoginTestUtils.primaryPassword.disable();
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    Cu.waiveXrays(content).AboutLoginsUtils.primaryPasswordEnabled = false;
    const loginList = Cu.waiveXrays(
      content.document.querySelector("login-list")
    );
    const loginFilter = Cu.waiveXrays(
      loginList.shadowRoot.querySelector("login-filter")
    );
    loginFilter.value = "pass1";
    Assert.equal(
      loginList._list.querySelectorAll(
        ".login-list-item[data-guid]:not([hidden])"
      ).length,
      1,
      "login-list should show login with matching password since MP is disabled"
    );
  });

  BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

add_task(async function test_login_item_after_successful_auth() {
  // Confirm that the mocking of the OS auth dialog isn't enabled so the
  // test will timeout if a real OS auth dialog is shown. We don't show
  // the OS auth dialog when Primary Password is enabled.
  Assert.equal(
    Services.prefs.getStringPref(
      "toolkit.osKeyStore.unofficialBuildOnlyLogin",
      ""
    ),
    "",
    "Pref should be set to default value of empty string to start the test"
  );
  LoginTestUtils.primaryPassword.enable();

  let mpDialogShown = forceAuthTimeoutAndWaitForMPDialog("authenticate");
  await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    url: "about:logins",
  });
  await mpDialogShown;

  let browser = gBrowser.selectedBrowser;
  let logins = await waitForLoginCountToReach(browser, 1);
  Assert.equal(
    logins,
    1,
    "Logins should be displayed when MP is set and authenticated"
  );
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
    let loginItem = content.document.querySelector("login-item");
    Assert.ok(
      !loginItem.classList.contains("no-logins"),
      "Login item should have content after MP is authenticated"
    );
  });

  LoginTestUtils.primaryPassword.disable();
  BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
