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

add_setup(async function () {
  let storageChangedPromised = TestUtils.topicObserved(
    "passwordmgr-storage-changed",
    (_, data) => data == "addLogin"
  );
  TEST_LOGIN1 = await Services.logins.addLoginAsync(TEST_LOGIN1);
  await storageChangedPromised;
  storageChangedPromised = TestUtils.topicObserved(
    "passwordmgr-storage-changed",
    (_, data) => data == "addLogin"
  );
  TEST_LOGIN2 = await Services.logins.addLoginAsync(TEST_LOGIN2);
  await storageChangedPromised;
  let tabOpenedPromise = BrowserTestUtils.waitForNewTab(
    gBrowser,
    url =>
      url.includes(
        `about:logins?filter=${encodeURIComponent(TEST_LOGIN1.origin)}`
      ),
    true
  );
  LoginHelper.openPasswordManager(window, {
    filterString: TEST_LOGIN1.origin,
    entryPoint: "preferences",
  });
  await tabOpenedPromise;
  registerCleanupFunction(() => {
    BrowserTestUtils.removeTab(gBrowser.selectedTab);
    Services.logins.removeAllUserFacingLogins();
  });
});

add_task(async function test_query_parameter_filter() {
  let browser = gBrowser.selectedBrowser;
  let vanillaLogins = [
    LoginHelper.loginToVanillaObject(TEST_LOGIN1),
    LoginHelper.loginToVanillaObject(TEST_LOGIN2),
  ];
  await SpecialPowers.spawn(browser, [vanillaLogins], async logins => {
    const loginList = Cu.waiveXrays(
      content.document.querySelector("login-list")
    );
    await ContentTaskUtils.waitForCondition(() => {
      return loginList._loginGuidsSortedOrder.length == 2;
    }, "Waiting for logins to be cached");

    await ContentTaskUtils.waitForCondition(() => {
      const selectedLoginItem = Cu.waiveXrays(
        loginList.shadowRoot.querySelector("li[aria-selected='true']")
      );
      return selectedLoginItem.dataset.guid === logins[0].guid;
    }, "Waiting for TEST_LOGIN1 to be selected for the login-item view");

    const loginItem = Cu.waiveXrays(
      content.document.querySelector("login-item")
    );

    Assert.ok(
      ContentTaskUtils.is_visible(loginItem),
      "login-item should be visible when a login is selected"
    );
    const loginIntro = content.document.querySelector("login-intro");
    Assert.ok(
      ContentTaskUtils.is_hidden(loginIntro),
      "login-intro should be hidden when a login is selected"
    );

    const loginFilter = loginList.shadowRoot.querySelector("login-filter");

    const xRayLoginFilter = Cu.waiveXrays(loginFilter);
    Assert.equal(
      xRayLoginFilter.value,
      logins[0].origin,
      "The filter should be prepopulated"
    );
    Assert.equal(
      loginList.shadowRoot.activeElement,
      loginFilter,
      "login-filter should be focused"
    );
    Assert.equal(
      loginFilter.shadowRoot.activeElement,
      loginFilter.shadowRoot.querySelector(".filter"),
      "the actual input inside of login-filter should be focused"
    );

    let hiddenLoginListItems = loginList.shadowRoot.querySelectorAll(
      ".login-list-item[hidden]"
    );
    let visibleLoginListItems = loginList.shadowRoot.querySelectorAll(
      ".login-list-item:not([hidden])"
    );
    Assert.equal(
      visibleLoginListItems.length,
      1,
      "The one login should be visible"
    );
    Assert.equal(
      visibleLoginListItems[0].dataset.guid,
      logins[0].guid,
      "TEST_LOGIN1 should be visible"
    );
    Assert.equal(
      hiddenLoginListItems.length,
      2,
      "One saved login and one blank login should be hidden"
    );
    Assert.equal(
      hiddenLoginListItems[0].id,
      "new-login-list-item",
      "#new-login-list-item should be hidden"
    );
    Assert.equal(
      hiddenLoginListItems[1].dataset.guid,
      logins[1].guid,
      "TEST_LOGIN2 should be hidden"
    );
  });
});

add_task(async function test_query_parameter_filter_no_logins_for_site() {
  BrowserTestUtils.removeTab(gBrowser.selectedTab);
  const HOSTNAME_WITH_NO_LOGINS = "xxx-no-logins-for-site-xxx";
  let tabOpenedPromise = BrowserTestUtils.waitForNewTab(
    gBrowser,
    url =>
      url.includes(
        `about:logins?filter=${encodeURIComponent(HOSTNAME_WITH_NO_LOGINS)}`
      ),
    true
  );
  LoginHelper.openPasswordManager(window, {
    filterString: HOSTNAME_WITH_NO_LOGINS,
    entryPoint: "preferences",
  });
  await tabOpenedPromise;

  let browser = gBrowser.selectedBrowser;
  await SpecialPowers.spawn(browser, [], async () => {
    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
    await ContentTaskUtils.waitForCondition(() => {
      return loginList._loginGuidsSortedOrder.length == 2;
    }, "Waiting for logins to be cached");
    Assert.equal(
      loginList._loginGuidsSortedOrder.length,
      2,
      "login list should have two logins stored"
    );

    Assert.ok(
      ContentTaskUtils.is_hidden(loginList._list),
      "the login list should be hidden when there is a search with no results"
    );
    let intro = loginList.shadowRoot.querySelector(".intro");
    Assert.ok(
      ContentTaskUtils.is_hidden(intro),
      "the intro should be hidden when there is a search with no results"
    );
    let emptySearchMessage = loginList.shadowRoot.querySelector(
      ".empty-search-message"
    );
    Assert.ok(
      ContentTaskUtils.is_visible(emptySearchMessage),
      "the empty search message should be visible when there is a search with no results"
    );

    let visibleLoginListItems = loginList.shadowRoot.querySelectorAll(
      ".login-list-item:not([hidden])"
    );
    Assert.equal(visibleLoginListItems.length, 0, "No login should be visible");

    Assert.ok(
      !loginList._createLoginButton.disabled,
      "create button should be enabled"
    );

    let loginItem = content.document.querySelector("login-item");
    Assert.ok(!loginItem.dataset.isNewLogin, "should not be in create mode");
    Assert.ok(!loginItem.dataset.editing, "should not be in edit mode");
    Assert.ok(
      ContentTaskUtils.is_hidden(loginItem),
      "login-item should be hidden when a login is not selected and we're not in create mode"
    );
    let loginIntro = content.document.querySelector("login-intro");
    Assert.ok(
      ContentTaskUtils.is_hidden(loginIntro),
      "login-intro should be hidden when a login is not selected and we're not in create mode"
    );

    loginList._createLoginButton.click();

    Assert.ok(loginItem.dataset.isNewLogin, "should be in create mode");
    Assert.ok(loginItem.dataset.editing, "should be in edit mode");
    Assert.ok(
      ContentTaskUtils.is_visible(loginItem),
      "login-item should be visible in create mode"
    );
    Assert.ok(
      ContentTaskUtils.is_hidden(loginIntro),
      "login-intro should be hidden in create mode"
    );
  });
});

add_task(async function test_query_parameter_filter_no_login_until_backspace() {
  BrowserTestUtils.removeTab(gBrowser.selectedTab);
  let tabOpenedPromise = BrowserTestUtils.waitForNewTab(
    gBrowser,
    "about:logins?filter=" + encodeURIComponent(TEST_LOGIN1.origin) + "x",
    true
  );
  LoginHelper.openPasswordManager(window, {
    filterString: TEST_LOGIN1.origin + "x",
    entryPoint: "preferences",
  });
  await tabOpenedPromise;

  let browser = gBrowser.selectedBrowser;
  await SpecialPowers.spawn(browser, [], async () => {
    let loginList = Cu.waiveXrays(content.document.querySelector("login-list"));
    await ContentTaskUtils.waitForCondition(() => {
      return loginList._loginGuidsSortedOrder.length == 2;
    }, "Waiting for logins to be cached");
    Assert.equal(
      loginList._loginGuidsSortedOrder.length,
      2,
      "login list should have two logins stored"
    );

    Assert.ok(
      ContentTaskUtils.is_hidden(loginList._list),
      "the login list should be hidden when there is a search with no results"
    );

    // Backspace the trailing 'x' to get matching logins
    const EventUtils = ContentTaskUtils.getEventUtils(content);
    EventUtils.sendChar("KEY_Backspace", content);

    let intro = loginList.shadowRoot.querySelector(".intro");
    Assert.ok(
      ContentTaskUtils.is_hidden(intro),
      "the intro should be hidden when there is no selection"
    );
    let emptySearchMessage = loginList.shadowRoot.querySelector(
      ".empty-search-message"
    );
    Assert.ok(
      ContentTaskUtils.is_hidden(emptySearchMessage),
      "the empty search message should be hidden when there is matching logins"
    );

    let visibleLoginListItems = loginList.shadowRoot.querySelectorAll(
      ".login-list-item:not([hidden])"
    );
    Assert.equal(
      visibleLoginListItems.length,
      1,
      "One login should be visible after backspacing"
    );

    Assert.ok(
      !loginList._createLoginButton.disabled,
      "create button should be enabled"
    );

    let loginItem = content.document.querySelector("login-item");
    Assert.ok(!loginItem.dataset.isNewLogin, "should not be in create mode");
    Assert.ok(!loginItem.dataset.editing, "should not be in edit mode");
    Assert.ok(
      ContentTaskUtils.is_hidden(loginItem),
      "login-item should be hidden when a login is not selected and we're not in create mode"
    );
    let loginIntro = content.document.querySelector("login-intro");
    Assert.ok(
      ContentTaskUtils.is_hidden(loginIntro),
      "login-intro should be hidden when a login is not selected and we're not in create mode"
    );

    loginList._createLoginButton.click();

    Assert.ok(loginItem.dataset.isNewLogin, "should be in create mode");
    Assert.ok(loginItem.dataset.editing, "should be in edit mode");
    Assert.ok(
      ContentTaskUtils.is_visible(loginItem),
      "login-item should be visible in create mode"
    );
    Assert.ok(
      ContentTaskUtils.is_hidden(loginIntro),
      "login-intro should be hidden in create mode"
    );
  });
});
