From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../tests/browser/browser_createLogin.js | 535 +++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 browser/components/aboutlogins/tests/browser/browser_createLogin.js (limited to 'browser/components/aboutlogins/tests/browser/browser_createLogin.js') diff --git a/browser/components/aboutlogins/tests/browser/browser_createLogin.js b/browser/components/aboutlogins/tests/browser/browser_createLogin.js new file mode 100644 index 0000000000..a876320ecf --- /dev/null +++ b/browser/components/aboutlogins/tests/browser/browser_createLogin.js @@ -0,0 +1,535 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_setup(async function () { + let aboutLoginsTab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: "about:logins", + }); + registerCleanupFunction(() => { + BrowserTestUtils.removeTab(aboutLoginsTab); + Services.logins.removeAllUserFacingLogins(); + }); +}); + +add_task(async function test_create_login() { + let browser = gBrowser.selectedBrowser; + await SpecialPowers.spawn(browser, [], async () => { + let loginList = Cu.waiveXrays(content.document.querySelector("login-list")); + Assert.ok( + !loginList._selectedGuid, + "should not be a selected guid by default" + ); + Assert.ok( + content.document.documentElement.classList.contains("no-logins"), + "Should initially be in no logins view" + ); + Assert.ok( + loginList.classList.contains("no-logins"), + "login-list should initially be in no logins view" + ); + Assert.equal( + loginList._loginGuidsSortedOrder.length, + 0, + "login list should be empty" + ); + }); + + let testCases = [ + ["ftp://ftp.example.com/", "ftp://ftp.example.com"], + ["https://example.com/foo", "https://example.com"], + ["http://example.com/", "http://example.com"], + [ + "https://testuser1:testpass1@bugzilla.mozilla.org/show_bug.cgi?id=1556934", + "https://bugzilla.mozilla.org", + ], + ["https://www.example.com/bar", "https://www.example.com"], + ]; + + for (let i = 0; i < testCases.length; i++) { + let originTuple = testCases[i]; + info("Testcase " + i); + let storageChangedPromised = TestUtils.topicObserved( + "passwordmgr-storage-changed", + (_, data) => data == "addLogin" + ); + + await SpecialPowers.spawn( + browser, + [[originTuple, i]], + async ([aOriginTuple, index]) => { + let loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + let createButton = loginList._createLoginButton; + Assert.ok( + ContentTaskUtils.is_hidden(loginList._blankLoginListItem), + "the blank login list item should be hidden initially" + ); + Assert.ok( + !createButton.disabled, + "Create button should not be disabled initially" + ); + + let loginItem = Cu.waiveXrays( + content.document.querySelector("login-item") + ); + let usernameInput = loginItem.shadowRoot.querySelector( + "input[name='username']" + ); + usernameInput.placeholder = "dummy placeholder"; + + createButton.click(); + + Assert.ok( + ContentTaskUtils.is_visible(loginList._blankLoginListItem), + "the blank login list item should be visible after clicking on the create button" + ); + Assert.ok( + createButton.disabled, + "Create button should be disabled after being clicked" + ); + + let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button"); + Assert.ok( + ContentTaskUtils.is_visible(cancelButton), + "cancel button should be visible in create mode with no logins saved" + ); + + let originInput = loginItem.shadowRoot.querySelector( + "input[name='origin']" + ); + let passwordInput = loginItem.shadowRoot.querySelector( + "input[name='password']" + ); + + Assert.equal( + content.document.l10n.getAttributes(usernameInput).id, + null, + "there should be no placeholder id on the username input in edit mode" + ); + Assert.equal( + usernameInput.placeholder, + "", + "there should be no placeholder on the username input in edit mode" + ); + originInput.value = aOriginTuple[0]; + usernameInput.value = "testuser1"; + passwordInput.value = "testpass1"; + + let saveChangesButton = loginItem.shadowRoot.querySelector( + ".save-changes-button" + ); + saveChangesButton.click(); + } + ); + + info("waiting for login to get added to storage"); + await storageChangedPromised; + info("login added to storage"); + + let canTestOSKeyStoreLogin = OSKeyStoreTestUtils.canTestOSKeyStoreLogin(); + if (canTestOSKeyStoreLogin) { + storageChangedPromised = TestUtils.topicObserved( + "passwordmgr-storage-changed", + (_, data) => data == "modifyLogin" + ); + } + await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => { + await ContentTaskUtils.waitForCondition(() => { + return !content.document.documentElement.classList.contains( + "no-logins" + ); + }, "waiting for no-logins view to exit"); + Assert.ok( + !content.document.documentElement.classList.contains("no-logins"), + "Should no longer be in no logins view" + ); + let loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + Assert.ok( + !loginList.classList.contains("no-logins"), + "login-list should no longer be in no logins view" + ); + Assert.ok( + ContentTaskUtils.is_hidden(loginList._blankLoginListItem), + "the blank login list item should be hidden after adding new login" + ); + Assert.ok( + !loginList._createLoginButton.disabled, + "Create button shouldn't be disabled after exiting create login view" + ); + + let loginGuid = await ContentTaskUtils.waitForCondition(() => { + return loginList._loginGuidsSortedOrder.find( + guid => loginList._logins[guid].login.origin == aOriginTuple[1] + ); + }, "Waiting for login to be displayed"); + Assert.ok(loginGuid, "Expected login found in login-list"); + + let loginItem = Cu.waiveXrays( + content.document.querySelector("login-item") + ); + Assert.equal(loginItem._login.guid, loginGuid, "login-item should match"); + + let { login, listItem } = loginList._logins[loginGuid]; + Assert.ok( + listItem.classList.contains("selected"), + "list item should be selected" + ); + Assert.equal( + login.origin, + aOriginTuple[1], + "Stored login should only include the origin of the URL provided during creation" + ); + Assert.equal( + login.username, + "testuser1", + "Stored login should have username provided during creation" + ); + Assert.equal( + login.password, + "testpass1", + "Stored login should have password provided during creation" + ); + + let usernameInput = loginItem.shadowRoot.querySelector( + "input[name='username']" + ); + await ContentTaskUtils.waitForCondition( + () => usernameInput.placeholder, + "waiting for placeholder to get set" + ); + Assert.ok( + usernameInput.placeholder, + "there should be a placeholder on the username input when not in edit mode" + ); + }); + + if (!canTestOSKeyStoreLogin) { + continue; + } + + let reauthObserved = forceAuthTimeoutAndWaitForOSKeyStoreLogin({ + loginResult: true, + }); + await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => { + let loginItem = Cu.waiveXrays( + content.document.querySelector("login-item") + ); + let editButton = loginItem.shadowRoot.querySelector(".edit-button"); + info("clicking on edit button"); + editButton.click(); + }); + info("waiting for oskeystore auth"); + await reauthObserved; + + await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => { + let loginItem = Cu.waiveXrays( + content.document.querySelector("login-item") + ); + await ContentTaskUtils.waitForCondition( + () => loginItem.dataset.editing, + "waiting for 'edit' mode" + ); + info("in edit mode"); + + let usernameInput = loginItem.shadowRoot.querySelector( + "input[name='username']" + ); + let passwordInput = loginItem.shadowRoot.querySelector( + "input[type='password']" + ); + passwordInput.focus(); + passwordInput = loginItem.shadowRoot.querySelector( + "input[name='password']" + ); + usernameInput.value = "testuser2"; + passwordInput.value = "testpass2"; + + let saveChangesButton = loginItem.shadowRoot.querySelector( + ".save-changes-button" + ); + info("clicking save changes button"); + saveChangesButton.click(); + }); + + info("waiting for login to get modified in storage"); + await storageChangedPromised; + info("login modified in storage"); + + await SpecialPowers.spawn(browser, [originTuple], async aOriginTuple => { + let loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + let login; + await ContentTaskUtils.waitForCondition(() => { + login = Object.values(loginList._logins).find( + obj => obj.login.origin == aOriginTuple[1] + ).login; + info(`${login.origin} / ${login.username} / ${login.password}`); + return ( + login.origin == aOriginTuple[1] && + login.username == "testuser2" && + login.password == "testpass2" + ); + }, "waiting for the login to get updated"); + Assert.equal( + login.origin, + aOriginTuple[1], + "Stored login should only include the origin of the URL provided during creation" + ); + Assert.equal( + login.username, + "testuser2", + "Stored login should have modified username" + ); + Assert.equal( + login.password, + "testpass2", + "Stored login should have modified password" + ); + }); + } + + await SpecialPowers.spawn( + browser, + [testCases.length], + async testCasesLength => { + let loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + Assert.equal( + loginList._loginGuidsSortedOrder.length, + 5, + "login list should have a login per testcase" + ); + } + ); +}); + +add_task(async function test_cancel_create_login() { + let browser = gBrowser.selectedBrowser; + await SpecialPowers.spawn(browser, [], async () => { + let loginList = Cu.waiveXrays(content.document.querySelector("login-list")); + Assert.ok( + loginList._selectedGuid, + "there should be a selected guid before create mode" + ); + Assert.ok( + ContentTaskUtils.is_hidden(loginList._blankLoginListItem), + "the blank login list item should be hidden before create mode" + ); + + let createButton = content.document + .querySelector("login-list") + .shadowRoot.querySelector(".create-login-button"); + createButton.click(); + + Assert.ok( + !loginList._selectedGuid, + "there should be no selected guid when in create mode" + ); + Assert.ok( + ContentTaskUtils.is_visible(loginList._blankLoginListItem), + "the blank login list item should be visible in create mode" + ); + + let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); + let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button"); + cancelButton.click(); + + Assert.ok( + loginList._selectedGuid, + "there should be a selected guid after canceling create mode" + ); + Assert.ok( + ContentTaskUtils.is_hidden(loginList._blankLoginListItem), + "the blank login list item should be hidden after canceling create mode" + ); + }); +}); + +add_task( + async function test_cancel_create_login_with_filter_showing_one_login() { + const browser = gBrowser.selectedBrowser; + await SpecialPowers.spawn(browser, [], async () => { + const loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + + const loginFilter = Cu.waiveXrays( + loginList.shadowRoot.querySelector("login-filter") + ); + loginFilter.value = "bugzilla.mozilla.org"; + Assert.equal( + loginList._list.querySelectorAll( + ".login-list-item[data-guid]:not([hidden])" + ).length, + 1, + "filter should have one login showing" + ); + let visibleLoginGuid = loginList.shadowRoot.querySelectorAll( + ".login-list-item[data-guid]:not([hidden])" + )[0].dataset.guid; + + let createButton = loginList._createLoginButton; + createButton.click(); + + let loginItem = Cu.waiveXrays( + content.document.querySelector("login-item") + ); + let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button"); + Assert.ok( + ContentTaskUtils.is_visible(cancelButton), + "cancel button should be visible in create mode with one login showing" + ); + cancelButton.click(); + + Assert.equal( + loginFilter.value, + "bugzilla.mozilla.org", + "login-filter should not be cleared if there was a login in the list" + ); + Assert.equal( + loginList.shadowRoot.querySelectorAll( + ".login-list-item[data-guid]:not([hidden])" + )[0].dataset.guid, + visibleLoginGuid, + "the same login should still be visible" + ); + }); + } +); + +add_task(async function test_cancel_create_login_with_logins_filtered_out() { + const browser = gBrowser.selectedBrowser; + await SpecialPowers.spawn(browser, [], async () => { + const loginList = Cu.waiveXrays( + content.document.querySelector("login-list") + ); + const loginFilter = Cu.waiveXrays( + loginList.shadowRoot.querySelector("login-filter") + ); + loginFilter.value = "XXX-no-logins-should-match-this-XXX"; + await Promise.resolve(); + Assert.equal( + loginList._list.querySelectorAll( + ".login-list-item[data-guid]:not([hidden])" + ).length, + 0, + "filter should have no logins showing" + ); + + let createButton = loginList._createLoginButton; + createButton.click(); + + let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); + let cancelButton = loginItem.shadowRoot.querySelector(".cancel-button"); + Assert.ok( + ContentTaskUtils.is_visible(cancelButton), + "cancel button should be visible in create mode with no logins showing" + ); + cancelButton.click(); + await Promise.resolve(); + + Assert.equal( + loginFilter.value, + "", + "login-filter should be cleared if there were no logins in the list" + ); + let visibleLoginItems = loginList.shadowRoot.querySelectorAll( + ".login-list-item[data-guid]:not([hidden])" + ); + Assert.equal( + visibleLoginItems.length, + 5, + "all logins should be visible with blank filter" + ); + Assert.equal( + loginList._selectedGuid, + visibleLoginItems[0].dataset.guid, + "the first item in the list should be selected" + ); + }); +}); + +add_task(async function test_create_duplicate_login() { + if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) { + return; + } + + let browser = gBrowser.selectedBrowser; + EXPECTED_ERROR_MESSAGE = "This login already exists."; + await SpecialPowers.spawn(browser, [], async () => { + let loginList = Cu.waiveXrays(content.document.querySelector("login-list")); + let createButton = loginList._createLoginButton; + createButton.click(); + + let loginItem = Cu.waiveXrays(content.document.querySelector("login-item")); + let originInput = loginItem.shadowRoot.querySelector( + "input[name='origin']" + ); + let usernameInput = loginItem.shadowRoot.querySelector( + "input[name='username']" + ); + let passwordInput = loginItem.shadowRoot.querySelector( + "input[name='password']" + ); + const EXISTING_USERNAME = "testuser2"; + const EXISTING_ORIGIN = "https://example.com"; + originInput.value = EXISTING_ORIGIN; + usernameInput.value = EXISTING_USERNAME; + passwordInput.value = "different password value"; + + let saveChangesButton = loginItem.shadowRoot.querySelector( + ".save-changes-button" + ); + saveChangesButton.click(); + + await ContentTaskUtils.waitForCondition( + () => !loginItem._errorMessage.hidden, + "waiting until the error message is visible" + ); + let duplicatedGuid = Object.values(loginList._logins).find( + v => + v.login.origin == EXISTING_ORIGIN && + v.login.username == EXISTING_USERNAME + ).login.guid; + Assert.equal( + loginItem._errorMessageLink.dataset.errorGuid, + duplicatedGuid, + "Error message has GUID of existing duplicated login set on it" + ); + + let confirmationDialog = Cu.waiveXrays( + content.document.querySelector("confirmation-dialog") + ); + Assert.ok( + confirmationDialog.hidden, + "the discard-changes dialog should be hidden before clicking the error-message-text" + ); + loginItem._errorMessageLink.querySelector("a").click(); + Assert.ok( + !confirmationDialog.hidden, + "the discard-changes dialog should be visible" + ); + let discardChangesButton = + confirmationDialog.shadowRoot.querySelector(".confirm-button"); + discardChangesButton.click(); + + await ContentTaskUtils.waitForCondition( + () => + Object.keys(loginItem._login).length > 1 && + loginItem._login.guid == duplicatedGuid, + "waiting until the existing duplicated login is selected" + ); + Assert.equal( + loginList._selectedGuid, + duplicatedGuid, + "the duplicated login should be selected in the list" + ); + }); + EXPECTED_ERROR_MESSAGE = null; +}); -- cgit v1.2.3