diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/aboutlogins/tests/chrome/test_login_item.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/aboutlogins/tests/chrome/test_login_item.html')
-rw-r--r-- | browser/components/aboutlogins/tests/chrome/test_login_item.html | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/browser/components/aboutlogins/tests/chrome/test_login_item.html b/browser/components/aboutlogins/tests/chrome/test_login_item.html new file mode 100644 index 0000000000..a7946a0618 --- /dev/null +++ b/browser/components/aboutlogins/tests/chrome/test_login_item.html @@ -0,0 +1,481 @@ +<!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 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(".breach-alert"); + ok(!isHidden(breachAlert), "Breach alert should be visible"); + is(breachAlert.querySelector(".alert-link").href, TEST_LOGIN_1.origin + "/", "Link in the text should point to the login origin"); + let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-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(".breach-alert"); + ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach."); + let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-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(".breach-alert"); + ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach."); + let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-alert"); + ok(!isHidden(vulernableAlert), "Vulnerable alert should be visible"); + is(vulernableAlert.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(".breach-alert"); + ok(isHidden(breachAlert), "Breach alert should not be visible on login without an associated breach."); + let vulernableAlert = gLoginItem.shadowRoot.querySelector(".vulnerable-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> |