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

EXPECTED_BREACH = {
  AddedDate: "2018-12-20T23:56:26Z",
  BreachDate: "2018-12-16",
  Domain: "breached.example.com",
  Name: "Breached",
  PwnCount: 1643100,
  DataClasses: ["Email addresses", "Usernames", "Passwords", "IP addresses"],
  _status: "synced",
  id: "047940fe-d2fd-4314-b636-b4a952ee0043",
  last_modified: "1541615610052",
  schema: "1541615609018",
};

let VULNERABLE_TEST_LOGIN2 = new nsLoginInfo(
  "https://2.example.com",
  "https://2.example.com",
  null,
  "user2",
  "pass3",
  "username",
  "password"
);

add_setup(async function () {
  TEST_LOGIN1 = await addLogin(TEST_LOGIN1);
  VULNERABLE_TEST_LOGIN2 = await addLogin(VULNERABLE_TEST_LOGIN2);
  TEST_LOGIN3 = await addLogin(TEST_LOGIN3);
  await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    url: "about:logins",
  });
  await SpecialPowers.pushPrefEnv({
    set: [["toolkit.osKeyStore.unofficialBuildOnlyLogin", ""]],
  });
  registerCleanupFunction(() => {
    BrowserTestUtils.removeTab(gBrowser.selectedTab);
    Services.logins.removeAllUserFacingLogins();
  });
});

add_task(async function test_added_login_shows_breach_warning() {
  let browser = gBrowser.selectedBrowser;
  await SpecialPowers.spawn(
    browser,
    [[TEST_LOGIN1.guid, VULNERABLE_TEST_LOGIN2.guid, TEST_LOGIN3.guid]],
    async ([regularLoginGuid, vulnerableLoginGuid, breachedLoginGuid]) => {
      let loginList = Cu.waiveXrays(
        content.document.querySelector("login-list")
      );
      await ContentTaskUtils.waitForCondition(
        () => loginList.shadowRoot.querySelectorAll("login-list-item").length,
        "Waiting for login-list to get populated"
      );
      let { listItem: regularListItem } = loginList._logins[regularLoginGuid];
      let { listItem: vulnerableListItem } =
        loginList._logins[vulnerableLoginGuid];
      let { listItem: breachedListItem } = loginList._logins[breachedLoginGuid];
      await ContentTaskUtils.waitForCondition(() => {
        return (
          !regularListItem.matches(".breached.vulnerable") &&
          vulnerableListItem.matches(".vulnerable") &&
          breachedListItem.matches(".breached")
        );
      }, `waiting for the list items to get their classes updated: ${regularListItem.className} / ${vulnerableListItem.className} / ${breachedListItem.className}`);
      Assert.ok(
        !regularListItem.classList.contains("breached") &&
          !regularListItem.classList.contains("vulnerable"),
        "regular login should not be marked breached or vulnerable: " +
          regularLoginGuid.className
      );
      Assert.ok(
        !vulnerableListItem.classList.contains("breached") &&
          vulnerableListItem.classList.contains("vulnerable"),
        "vulnerable login should be marked vulnerable: " +
          vulnerableListItem.className
      );
      Assert.ok(
        breachedListItem.classList.contains("breached") &&
          !breachedListItem.classList.contains("vulnerable"),
        "breached login should be marked breached: " +
          breachedListItem.className
      );

      breachedListItem.click();
      let loginItem = Cu.waiveXrays(
        content.document.querySelector("login-item")
      );
      await ContentTaskUtils.waitForCondition(() => {
        return loginItem._login && loginItem._login.guid == breachedLoginGuid;
      }, "waiting for breached login to get selected");
      Assert.ok(
        !ContentTaskUtils.isHidden(
          loginItem.shadowRoot.querySelector("login-breach-alert")
        ),
        "the breach alert should be visible"
      );
    }
  );

  if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
    info(
      "leaving test early since the remaining part of the test requires 'edit' mode which requires 'oskeystore' login"
    );
    return;
  }

  let reauthObserved = Promise.resolve();
  if (OSKeyStore.canReauth()) {
    reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({
      loginResult: true,
    });
  }

  // Change the password on the breached login and check that the
  // login is no longer marked as breached. The vulnerable login
  // should still be marked as vulnerable afterwards.
  await SpecialPowers.spawn(browser, [], () => {
    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
    loginItem.shadowRoot.querySelector("edit-button").click();
  });
  await reauthObserved;
  await SpecialPowers.spawn(
    browser,
    [[VULNERABLE_TEST_LOGIN2.guid, TEST_LOGIN3.guid]],
    async ([vulnerableLoginGuid, breachedLoginGuid]) => {
      let loginList = Cu.waiveXrays(
        content.document.querySelector("login-list")
      );
      let loginItem = Cu.waiveXrays(
        content.document.querySelector("login-item")
      );
      await ContentTaskUtils.waitForCondition(
        () => loginItem.dataset.editing == "true",
        "waiting for login-item to enter edit mode"
      );

      // The password display field is in the DOM when password input is unfocused.
      // To get the password input field, ensure it receives focus.
      let passwordInput = loginItem.shadowRoot.querySelector(
        "input[type='password']"
      );
      passwordInput.focus();
      passwordInput = loginItem.shadowRoot.querySelector(
        "input[name='password']"
      );

      const CHANGED_PASSWORD_VALUE = "changedPassword";
      passwordInput.value = CHANGED_PASSWORD_VALUE;
      let saveChangesButton = loginItem.shadowRoot.querySelector(
        ".save-changes-button"
      );
      saveChangesButton.click();

      await ContentTaskUtils.waitForCondition(() => {
        return (
          loginList._logins[breachedLoginGuid].login.password ==
          CHANGED_PASSWORD_VALUE
        );
      }, "waiting for stored login to get updated");

      Assert.ok(
        ContentTaskUtils.isHidden(
          loginItem.shadowRoot.querySelector("login-breach-alert")
        ),
        "the breach alert should be hidden now"
      );

      let { listItem: breachedListItem } = loginList._logins[breachedLoginGuid];
      let { listItem: vulnerableListItem } =
        loginList._logins[vulnerableLoginGuid];
      Assert.ok(
        !breachedListItem.classList.contains("breached") &&
          !breachedListItem.classList.contains("vulnerable"),
        "the originally breached login should no longer be marked as breached"
      );
      Assert.ok(
        !vulnerableListItem.classList.contains("breached") &&
          vulnerableListItem.classList.contains("vulnerable"),
        "vulnerable login should still be marked vulnerable: " +
          vulnerableListItem.className
      );

      // Change the password on the vulnerable login and check that the
      // login is no longer marked as vulnerable.
      vulnerableListItem.click();
      await ContentTaskUtils.waitForCondition(() => {
        return loginItem._login && loginItem._login.guid == vulnerableLoginGuid;
      }, "waiting for vulnerable login to get selected");
      Assert.ok(
        !ContentTaskUtils.isHidden(
          loginItem.shadowRoot.querySelector("login-vulnerable-password-alert")
        ),
        "the vulnerable alert should be visible"
      );
      loginItem.shadowRoot.querySelector("edit-button").click();
      await ContentTaskUtils.waitForCondition(
        () => loginItem.dataset.editing == "true",
        "waiting for login-item to enter edit mode"
      );

      passwordInput.value = CHANGED_PASSWORD_VALUE;
      saveChangesButton.click();

      await ContentTaskUtils.waitForCondition(() => {
        return (
          loginList._logins[vulnerableLoginGuid].login.password ==
          CHANGED_PASSWORD_VALUE
        );
      }, "waiting for stored login to get updated");

      Assert.ok(
        ContentTaskUtils.isHidden(
          loginItem.shadowRoot.querySelector("login-vulnerable-password-alert")
        ),
        "the vulnerable alert should be hidden now"
      );
      Assert.equal(
        vulnerableListItem.notificationIcon,
        "",
        ".alert-icon for the vulnerable list item should have its source removed"
      );
      vulnerableListItem = loginList._logins[vulnerableLoginGuid].listItem;
      Assert.ok(
        !vulnerableListItem.classList.contains("breached") &&
          !vulnerableListItem.classList.contains("vulnerable"),
        "vulnerable login should no longer be marked vulnerable: " +
          vulnerableListItem.className
      );
    }
  );
});
