<!DOCTYPE HTML>
<html>
<!--
Test the login-item component
-->
<head>
  <meta charset="utf-8">
  <title>Test the login-item component</title>
  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/login-item.mjs"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.mjs"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/login-timeline.mjs"></script>
  <script type="module" src="chrome://browser/content/aboutlogins/components/login-alert.mjs"></script>
  <script src="aboutlogins_common.js"></script>

  <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
</head>
<body>
  <p id="display">
  </p>
<div id="content" style="display: none">
  <iframe id="templateFrame" src="chrome://browser/content/aboutlogins/aboutLogins.html"
          sandbox="allow-same-origin"></iframe>
</div>
<pre id="test">
</pre>
<script type="module">

import { CONCEALED_PASSWORD_TEXT } from "chrome://browser/content/aboutlogins/aboutLoginsUtils.mjs";

/** Test the login-item component **/

let gLoginItem, gConfirmationDialog;
const TEST_LOGIN_1 = {
  guid: "123456789",
  origin: "https://example.com",
  username: "user1",
  password: "pass1",
  timeCreated: "1000",
  timePasswordChanged: "2000",
  timeLastUsed: "4000",
};

const TEST_LOGIN_2 = {
  guid: "987654321",
  origin: "https://example.com",
  username: "user2",
  password: "pass2",
  timeCreated: "2000",
  timePasswordChanged: "4000",
  timeLastUsed: "8000",
};

const TEST_BREACH = {
  Name: "Test-Breach",
  breachAlertURL: "https://monitor.firefox.com/breach-details/Test-Breach",
};

const TEST_BREACHES_MAP = new Map();
TEST_BREACHES_MAP.set(TEST_LOGIN_1.guid, TEST_BREACH);

const TEST_VULNERABLE_MAP = new Map();
TEST_VULNERABLE_MAP.set(TEST_LOGIN_2.guid, true);

const getLoginTimeline = loginItem =>
  loginItem.shadowRoot.querySelector("login-timeline");

const verifyTimelineActions = (actions, expectedActions) => {
  is(
    actions.length,
    expectedActions.length,
    `Number timeline actions length is correct. Actual: ${actions.length}. Expected: ${expectedActions.length}`
  );

  actions.forEach((point, index) => {
    let actionId = document.l10n.getAttributes(point).id;
    let expectedAction = expectedActions[index];

    is(
      actionId,
      expectedAction,
      `Rendered action is correct. Actual: ${actionId}. Expected: ${expectedAction}`
    );
  });
};

add_setup(async () => {
  let templateFrame = document.getElementById("templateFrame");
  let displayEl = document.getElementById("display");
  await importDependencies(templateFrame, displayEl);

  gLoginItem = document.createElement("login-item");
  displayEl.appendChild(gLoginItem);

  gConfirmationDialog = document.createElement("confirmation-dialog");
  gConfirmationDialog.hidden = true;
  displayEl.appendChild(gConfirmationDialog);
});

add_task(async function test_empty_item() {
  ok(gLoginItem, "loginItem exists");
  is(gLoginItem.shadowRoot.querySelector("a[name='origin']").getAttribute("href"), "", "origin should be blank");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be blank");
  is(gLoginItem._passwordInput.value, "", "password should be blank");
  ok(!gLoginItem._passwordInput.isConnected, "Real password input should be disconnected");
  is(gLoginItem._passwordDisplayInput.value, "", "password display should be blank");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display input should be visible")
  ok(isHidden(getLoginTimeline(gLoginItem)), "Timeline should be hidden");
});

add_task(async function test_set_login() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  await asyncElementRendered();

  ok(!gLoginItem.dataset.editing, "loginItem should not be in 'edit' mode");
  ok(!gLoginItem.dataset.isNewLogin, "loginItem should not be in 'isNewLogin' mode");
  ok(isHidden(gLoginItem._originInput), "Origin input should be hidden when not in edit mode");
  ok(!isHidden(gLoginItem._originDisplayInput), "Origin display link should be visible when not in edit mode");
  let originLink = gLoginItem.shadowRoot.querySelector("a[name='origin']");
  is(originLink.getAttribute("href"), TEST_LOGIN_1.origin, "origin should be populated");
  let usernameInput = gLoginItem.shadowRoot.querySelector("input[name='username']");
  is(usernameInput.value, TEST_LOGIN_1.username, "username should be populated");
  is(document.l10n.getAttributes(usernameInput).id, "about-logins-login-item-username", "username field should have default placeholder when not editing");

  let passwordInput = gLoginItem._passwordInput;
  is(passwordInput.value, TEST_LOGIN_1.password, "password should be populated");
  ok(!passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");
  ok(!gLoginItem._passwordInput.isConnected, "Real password input should be disconnected");
  let passwordDisplayInput = gLoginItem._passwordDisplayInput;
  is(passwordDisplayInput.value, CONCEALED_PASSWORD_TEXT, "password display should be populated");
  ok(!isHidden(passwordDisplayInput), "Password display input should be visible");

  let timeline = getLoginTimeline(gLoginItem);
  ok(!isHidden(timeline), "Timeline should be visible");
  let actions = timeline.shadowRoot.querySelectorAll(".action");
  verifyTimelineActions(actions, [
    "login-item-timeline-action-created",
    "login-item-timeline-action-updated",
    "login-item-timeline-action-used",
  ]);

  let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
  ok(copyButtons.every(button => !isHidden(button)), "The copy buttons should be visible when viewing a login");

  let loginNoUsername = Object.assign({}, TEST_LOGIN_1, {username: ""});
  gLoginItem.setLogin(loginNoUsername);
  ok(!gLoginItem.dataset.editing, "loginItem should not be in 'edit' mode");
  is(document.l10n.getAttributes(usernameInput).id, "about-logins-login-item-username", "username field should have default placeholder when username is not present and not editing");
  let copyUsernameButton = gLoginItem.shadowRoot.querySelector(".copy-username-button");
  ok(copyUsernameButton.disabled, "The copy-username-button should be disabled if there is no username");

  usernameInput.placeholder = "dummy placeholder";
  gLoginItem.shadowRoot.querySelector(".edit-button").click();
  await asyncElementRendered();
  is(
    document.l10n.getAttributes(usernameInput).id,
    null,
    "there should be no placeholder id on the username input in edit mode"
  );
  is(usernameInput.placeholder, "", "there should be no placeholder on the username input in edit mode");
});

add_task(async function test_update_breaches() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  gLoginItem.setBreaches(TEST_BREACHES_MAP);
  await asyncElementRendered();

  let breachAlert = gLoginItem.shadowRoot.querySelector("login-breach-alert");
  ok(!isHidden(breachAlert), "Breach alert should be visible");
  is(breachAlert.hostname, TEST_LOGIN_1.origin, "Link in the text should point to the login origin");
  let vulernableAlert = gLoginItem.shadowRoot.querySelector("login-vulnerable-password-alert");
  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
});

add_task(async function test_breach_alert_is_correctly_hidden() {
  gLoginItem.setLogin(TEST_LOGIN_2);
  gLoginItem.setBreaches(TEST_BREACHES_MAP);
  await asyncElementRendered();

  let breachAlert = gLoginItem.shadowRoot.querySelector("login-breach-alert");
  ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
  let vulernableAlert = gLoginItem.shadowRoot.querySelector("login-vulnerable-password-alert");
  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
});

add_task(async function test_update_vulnerable() {
  gLoginItem.setLogin(TEST_LOGIN_2);
  gLoginItem.setVulnerableLogins(TEST_VULNERABLE_MAP);
  await asyncElementRendered();

  let breachAlert = gLoginItem.shadowRoot.querySelector("login-breach-alert");
  ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
  let vulernableAlert = gLoginItem.shadowRoot.querySelector("login-vulnerable-password-alert");
  ok(!isHidden(vulernableAlert), "Vulnerable alert should be visible");
  is(vulernableAlert.shadowRoot.querySelector(".alert-link").href, TEST_LOGIN_2.origin + "/", "Link in the text should point to the login origin");
});

add_task(async function test_vulnerable_alert_is_correctly_hidden() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  gLoginItem.setVulnerableLogins(TEST_VULNERABLE_MAP);
  gLoginItem.setBreaches(new Map());
  await asyncElementRendered();

  let breachAlert = gLoginItem.shadowRoot.querySelector("login-breach-alert");
  ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach.");
  let vulernableAlert = gLoginItem.shadowRoot.querySelector("login-vulnerable-password-alert");
  ok(isHidden(vulernableAlert), "Vulnerable alert should not be visible on a non-vulnerable login.");
});

add_task(async function test_edit_login() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  let usernameInput = gLoginItem.shadowRoot.querySelector("input[name='username']");
  usernameInput.placeholder = "dummy placeholder";
  gLoginItem.shadowRoot.querySelector(".edit-button").click();
  await asyncElementRendered();
  await asyncElementRendered();

  ok(gLoginItem.dataset.editing, "loginItem should be in 'edit' mode");
  ok(isHidden(gLoginItem.shadowRoot.querySelector(".edit-button")), "edit button should be hidden in 'edit' mode");
  ok(!gLoginItem.dataset.isNewLogin, "loginItem should not be in 'isNewLogin' mode");
  let deleteButton = gLoginItem.shadowRoot.querySelector(".delete-button");
  ok(!deleteButton.disabled, "Delete button should be enabled when editing a login");
  ok(isHidden(gLoginItem._originInput), "Origin input should be hidden in edit mode");
  ok(!isHidden(gLoginItem._originDisplayInput), "Origin display link should be visible in edit mode");
  is(gLoginItem.shadowRoot.querySelector("a[name='origin']").getAttribute("href"), TEST_LOGIN_1.origin, "origin should be populated");
  is(usernameInput.value, TEST_LOGIN_1.username, "username should be populated");
  is(usernameInput, document.activeElement?.shadowRoot?.activeElement, "username is focused");
  is(usernameInput.selectionStart, 0, "username value is selected from start");
  is(usernameInput.selectionEnd, usernameInput.value.length, "username value is selected to the end");
  is(
    document.l10n.getAttributes(usernameInput).id,
    null,
    "there should be no placeholder id on the username input in edit mode"
  );
  is(usernameInput.placeholder, "", "there should be no placeholder on the username input in edit mode");
  is(gLoginItem._passwordInput.value, TEST_LOGIN_1.password, "password should be populated");
  is(gLoginItem._passwordDisplayInput.value, CONCEALED_PASSWORD_TEXT, "password display should be populated");

  let timeline = getLoginTimeline(gLoginItem);
  ok(!isHidden(timeline), "Timeline should be visible");

  let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
  ok(copyButtons.every(button => isHidden(button)), "The copy buttons should be hidden when editing a login");

  usernameInput.value = "newUsername";
  gLoginItem._passwordInput.value = "newPassword";

  let updateEventDispatched = false;
  document.addEventListener("AboutLoginsUpdateLogin", event => {
    is(event.detail.guid, TEST_LOGIN_1.guid, "event should include guid");
    is(event.detail.origin, TEST_LOGIN_1.origin, "event should include origin");
    is(event.detail.username, "newUsername", "event should include new username");
    is(event.detail.password, "newPassword", "event should include new password");
    updateEventDispatched = true;
  }, {once: true});
  gLoginItem.shadowRoot.querySelector(".save-changes-button").click();
  ok(updateEventDispatched, "Clicking the .save-changes-button should dispatch the AboutLoginsUpdateLogin event");
});

add_task(async function test_edit_login_cancel() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  gLoginItem.shadowRoot.querySelector(".edit-button").click();
  await asyncElementRendered();

  ok(gLoginItem.dataset.editing, "loginItem should be in 'edit' mode");
  is(!!gLoginItem.dataset.isNewLogin, false,
     "loginItem should not be in 'isNewLogin' mode");

  gLoginItem.shadowRoot.querySelector(".cancel-button").click();
  gConfirmationDialog.shadowRoot.querySelector(".confirm-button").click();

  await SimpleTest.promiseWaitForCondition(
    () => gConfirmationDialog.hidden,
    "waiting for confirmation dialog to hide"
  );

  ok(!gLoginItem.dataset.editing, "loginItem should not be in 'edit' mode");
  ok(!gLoginItem.dataset.isNewLogin, "loginItem should not be in 'isNewLogin' mode");
});

add_task(async function test_reveal_password_change_selected_login() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  let revealCheckbox = gLoginItem.shadowRoot.querySelector(".reveal-password-checkbox");
  let passwordInput = gLoginItem._passwordInput;

  ok(!revealCheckbox.checked, "reveal-checkbox should not be checked by default");
  is(passwordInput.type, "password", "Password should be masked by default");
  revealCheckbox.click();
  ok(revealCheckbox.checked, "reveal-checkbox should be checked after clicking");
  await SimpleTest.promiseWaitForCondition(() => passwordInput.type == "text",
    "waiting for password input type to change after checking for primary password");
  is(passwordInput.type, "text", "Password should be unmasked when checkbox is clicked");
  ok(!isHidden(passwordInput), "Password input should be visible");

  let editButton = gLoginItem.shadowRoot.querySelector(".edit-button");
  editButton.click();
  await asyncElementRendered();
  ok(!isHidden(passwordInput), "Password input should still be visible");
  ok(revealCheckbox.checked, "reveal-checkbox should remain checked when entering 'edit' mode");
  gLoginItem.shadowRoot.querySelector(".cancel-button").click();
  ok(!revealCheckbox.checked, "reveal-checkbox should be unchecked after canceling 'edit' mode");
  revealCheckbox.click();
  ok(isHidden(passwordInput), "Password input should be hidden");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display should be visible");
  gLoginItem.setLogin(TEST_LOGIN_2);
  ok(!revealCheckbox.checked, "reveal-checkbox should be unchecked when changing logins");
  is(passwordInput.type, "password", "Password should be masked by default when switching logins");
  ok(isHidden(passwordInput), "Password input should be hidden");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display should be visible");
});

add_task(async function test_set_login_empty() {
  gLoginItem.setLogin({});
  await asyncElementRendered();

  ok(gLoginItem.dataset.editing, "loginItem should be in 'edit' mode");
  ok(isHidden(gLoginItem.shadowRoot.querySelector(".edit-button")), "edit button should be hidden in 'edit' mode");
  ok(gLoginItem.dataset.isNewLogin, "loginItem should be in 'isNewLogin' mode");
  let deleteButton = gLoginItem.shadowRoot.querySelector(".delete-button");
  ok(deleteButton.disabled, "Delete button should be disabled when creating a login");
  ok(!isHidden(gLoginItem._originInput), "Origin input should be visible in new login edit mode");
  ok(isHidden(gLoginItem._originDisplayInput), "Origin display should be hidden in new login edit mode");
  is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, "", "origin should be empty");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be empty");
  is(gLoginItem._passwordInput.value, "", "password should be empty");
  ok(!isHidden(gLoginItem._passwordInput), "Real password input should be visible in edit mode");
  ok(isHidden(gLoginItem._passwordDisplayInput), "Password display should be hidden in edit mode");
  ok(!gLoginItem._passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");

  let timeline = getLoginTimeline(gLoginItem);
  ok(isHidden(timeline), "Timeline should be visible");

  let copyButtons = [...gLoginItem.shadowRoot.querySelectorAll(".copy-button")];
  ok(copyButtons.every(button => isHidden(button)), "The copy buttons should be hidden when creating a login");

  let createEventDispatched = false;
  document.addEventListener("AboutLoginsCreateLogin", event => {
    createEventDispatched = true;
  }, {once: true});
  gLoginItem.shadowRoot.querySelector(".save-changes-button").click();
  ok(!createEventDispatched, "Clicking the .save-changes-button shouldn't dispatch the event when fields are invalid");
  let originInput = gLoginItem.shadowRoot.querySelector("input[name='origin']");
  ok(originInput.matches(":invalid"), "origin value is required");
  is(originInput.value, "", "origin input should be blank at start");

  for (let originTuple of [
    ["ftp://ftp.example.com/", "ftp://ftp.example.com/"],
    ["https://example.com/", "https://example.com/"],
    ["http://example.com/", "http://example.com/"],
    ["www.example.com/bar", "https://www.example.com/bar"],
    ["example.com/foo", "https://example.com/foo"],
  ]) {
    originInput.value = originTuple[0];
    sendKey("TAB");
    is(originInput.value, originTuple[1],
      "origin input should have https:// prefix when not provided by user");
    // Return focus back to the origin input
    synthesizeKey("VK_TAB", { shiftKey: true });
  }

  gLoginItem.shadowRoot.querySelector("input[name='username']").value = "user1";
  gLoginItem._passwordInput.value = "pass1";

  document.addEventListener("AboutLoginsCreateLogin", event => {
    is(event.detail.guid, undefined, "event should not include guid");
    is(event.detail.origin, "https://example.com/foo", "event should include origin");
    is(event.detail.username, "user1", "event should include new username");
    is(event.detail.password, "pass1", "event should include new password");
    createEventDispatched = true;
  }, {once: true});
  gLoginItem.shadowRoot.querySelector(".save-changes-button").click();
  ok(createEventDispatched, "Clicking the .save-changes-button should dispatch the AboutLoginsCreateLogin event");
});

add_task(async function test_different_login_modified() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
  gLoginItem.loginModified(otherLogin);
  await asyncElementRendered();

  is(gLoginItem.shadowRoot.querySelector("a[name='origin']").getAttribute("href"), TEST_LOGIN_1.origin, "origin should be unchanged");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
  is(gLoginItem._passwordInput.value, TEST_LOGIN_1.password, "password should be unchanged");
  ok(!gLoginItem._passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");
  ok(!gLoginItem._passwordInput.isConnected, "Real password input should be disconnected in masked non-edit mode");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display should be visible in masked non-edit mode");
});

add_task(async function test_different_login_removed() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  let otherLogin = Object.assign({}, TEST_LOGIN_1, {username: "fakeuser", guid: "fakeguid"});
  gLoginItem.loginRemoved(otherLogin);
  await asyncElementRendered();

  is(gLoginItem.shadowRoot.querySelector("a[name='origin']").getAttribute("href"), TEST_LOGIN_1.origin, "origin should be unchanged");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, TEST_LOGIN_1.username, "username should be unchanged");
  is(gLoginItem._passwordInput.value, TEST_LOGIN_1.password, "password should be unchanged");
  ok(!gLoginItem._passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");
  ok(!gLoginItem._passwordInput.isConnected, "Real password input should be disconnected in masked non-edit mode");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display should be visible in masked non-edit mode");
});

add_task(async function test_login_modified() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  let modifiedLogin = Object.assign({}, TEST_LOGIN_1, {username: "updateduser"});
  gLoginItem.loginModified(modifiedLogin);
  await asyncElementRendered();

  is(gLoginItem.shadowRoot.querySelector("a[name='origin']").getAttribute("href"), modifiedLogin.origin, "origin should be updated");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, modifiedLogin.username, "username should be updated");
  is(gLoginItem._passwordInput.value, modifiedLogin.password, "password should be updated");
  ok(!gLoginItem._passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");
  ok(!gLoginItem._passwordInput.isConnected, "Real password input should be disconnected in masked non-edit mode");
  ok(!isHidden(gLoginItem._passwordDisplayInput), "Password display should be visible in masked non-edit mode");
});

add_task(async function test_login_removed() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  gLoginItem.loginRemoved(TEST_LOGIN_1);
  await asyncElementRendered();

  is(gLoginItem.shadowRoot.querySelector("input[name='origin']").value, "", "origin should be cleared");
  is(gLoginItem.shadowRoot.querySelector("input[name='username']").value, "", "username should be cleared");
  is(gLoginItem._passwordInput.value, "", "password should be cleared");
  ok(!gLoginItem._passwordInput.hasAttribute("value"), "Password shouldn't be exposed in @value");

  let timeline = getLoginTimeline(gLoginItem);
  ok(isHidden(timeline), "Timeline should be visible");
});

add_task(async function test_login_long_username_scrollLeft_reset() {
  let loginLongUsername = Object.assign({}, TEST_LOGIN_1, {username: "user2longnamelongnamelongnamelongnamelongname"});
  gLoginItem.setLogin(loginLongUsername);
  gLoginItem.shadowRoot.querySelector(".edit-button").click();
  await asyncElementRendered();
  await asyncElementRendered();
  let usernameInput = gLoginItem.shadowRoot.querySelector("input[name='username']");
  usernameInput.scrollLeft = usernameInput.scrollLeftMax;
  gLoginItem.shadowRoot.querySelector(".cancel-button").click();
  is(usernameInput.scrollLeft, 0, "username input should be scrolled horizontally to the beginning");
});

add_task(async function test_copy_button_state() {
  gLoginItem.setLogin(TEST_LOGIN_1);
  await asyncElementRendered();

  let copyUsernameButton = gLoginItem.shadowRoot.querySelector(".copy-username-button");
  ok(!copyUsernameButton.disabled, "The copy-username-button should be enabled");

  let copyPasswordButton = gLoginItem.shadowRoot.querySelector(".copy-password-button");
  ok(!copyPasswordButton.disabled, "The copy-passwoed-button should be enabled");

  copyUsernameButton.click();
  ok(copyUsernameButton.disabled, "The copy-username-button should be disabled when it is clicked");
  ok(!copyPasswordButton.disabled, "The copy-passwoed-button should be enabled when the copy-username-button is clicked");

  copyPasswordButton.click();
  await SimpleTest.promiseWaitForCondition(() => copyPasswordButton.disabled,
  "waiting for copy-password-button to become disabled after checking for primary password");

  ok(copyPasswordButton.disabled, "The copy-passwoed-button should be disabled when it is clicked");
  ok(!copyUsernameButton.disabled, "The copy-username-button should be enabled when the copy-password-button is clicked");

  let loginNoUsername = Object.assign({}, TEST_LOGIN_2, {username: ""});
  gLoginItem.setLogin(loginNoUsername);

  ok(copyUsernameButton.disabled, "The copy-username-button should be disabled when the username is empty");
  ok(!copyPasswordButton.disabled, "The copy-passwoed-button should be enabled");

  copyPasswordButton.click();
  await SimpleTest.promiseWaitForCondition(() => copyPasswordButton.disabled,
  "waiting for copy-password-button to become disabled after checking for primary password");

  ok(copyPasswordButton.disabled, "The copy-passwoed-button should be disabled when it is clicked");
  ok(copyUsernameButton.disabled, "The copy-username-button should still be disabled after clicking the password button when the username is empty");
});

</script>

</body>
</html>
