diff options
Diffstat (limited to 'toolkit/components/passwordmgr/test/browser/browser_doorhanger_password_edits.js')
-rw-r--r-- | toolkit/components/passwordmgr/test/browser/browser_doorhanger_password_edits.js | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_password_edits.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_password_edits.js new file mode 100644 index 0000000000..414208eaa5 --- /dev/null +++ b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_password_edits.js @@ -0,0 +1,220 @@ +/** + * Test changing the password inside the doorhanger notification for passwords. + * + * We check the following cases: + * - Editing the password of a new login. + * - Editing the password of an existing login. + * - Changing both username and password to an existing login. + * - Changing the username to an existing login. + * - Editing username to an empty one and a new password. + * + * If both the username and password matches an already existing login, we should not + * update it's password, but only it's usage timestamp and count. + */ +add_task(async function test_edit_password() { + let testCases = [ + { + description: "No saved logins, update password in doorhanger", + usernameInPage: "username", + passwordInPage: "password", + passwordChangedTo: "newPassword", + timesUsed: 1, + }, + { + description: "Login is saved, update password in doorhanger", + usernameInPage: "username", + usernameInPageExists: true, + passwordInPage: "password", + passwordInStorage: "oldPassword", + passwordChangedTo: "newPassword", + timesUsed: 2, + }, + { + description: + "Change username in doorhanger to match saved login, update password in doorhanger", + usernameInPage: "username", + usernameChangedTo: "newUsername", + usernameChangedToExists: true, + passwordInPage: "password", + passwordChangedTo: "newPassword", + timesUsed: 2, + }, + { + description: + "Change username in doorhanger to match saved login, dont update password in doorhanger", + usernameInPage: "username", + usernameChangedTo: "newUsername", + usernameChangedToExists: true, + passwordInPage: "password", + passwordChangedTo: "password", + timesUsed: 2, + checkPasswordNotUpdated: true, + }, + { + description: + "Change username and password in doorhanger to match saved empty-username login", + usernameInPage: "newUsername", + usernameChangedTo: "", + usernameChangedToExists: true, + passwordInPage: "password", + passwordChangedTo: "newPassword", + timesUsed: 2, + }, + ]; + + for (let testCase of testCases) { + info("Test case: " + JSON.stringify(testCase)); + // Clean state before the test case is executed. + await LoginTestUtils.clearData(); + await cleanupDoorhanger(); + await cleanupPasswordNotifications(); + + // Create the pre-existing logins when needed. + if (testCase.usernameInPageExists) { + await Services.logins.addLoginAsync( + LoginTestUtils.testData.formLogin({ + origin: "https://example.com", + formActionOrigin: "https://example.com", + username: testCase.usernameInPage, + password: testCase.passwordInStorage, + }) + ); + } + + if (testCase.usernameChangedToExists) { + await Services.logins.addLoginAsync( + LoginTestUtils.testData.formLogin({ + origin: "https://example.com", + formActionOrigin: "https://example.com", + username: testCase.usernameChangedTo, + password: testCase.passwordChangedTo, + }) + ); + } + + let formFilledPromise = listenForTestNotification("FormProcessed"); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: + "https://example.com/browser/toolkit/components/" + + "passwordmgr/test/browser/form_basic.html", + }, + async function (browser) { + await formFilledPromise; + + // Set the form to a known state so we can expect a single PasswordEditedOrGenerated message + await initForm(browser, { + "#form-basic-username": testCase.usernameInPage, + "#form-basic-password": "", + }); + + let passwordEditedPromise = listenForTestNotification( + "PasswordEditedOrGenerated" + ); + info("Editing the form"); + await changeContentFormValues(browser, { + "#form-basic-password": testCase.passwordInPage, + }); + info("Waiting for passwordEditedPromise"); + await passwordEditedPromise; + + // reset doorhanger/notifications, we're only interested in the submit outcome + await cleanupDoorhanger(); + await cleanupPasswordNotifications(); + // reset message cache, we're only interested in the submit outcome + await clearMessageCache(browser); + + // Submit the form in the content page with the credentials from the test + // case. This will cause the doorhanger notification to be displayed. + info("Submitting the form"); + let formSubmittedPromise = listenForTestNotification("ShowDoorhanger"); + let promiseShown = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown", + event => event.target == PopupNotifications.panel + ); + await SpecialPowers.spawn(browser, [], function () { + content.document.getElementById("form-basic").submit(); + }); + await formSubmittedPromise; + + let notif = await waitForDoorhanger(browser, "any"); + Assert.ok(!notif.dismissed, "Doorhanger is not dismissed"); + await promiseShown; + + // Modify the username & password in the dialog if requested. + await updateDoorhangerInputValues({ + username: testCase.usernameChangedTo, + password: testCase.passwordChangedTo, + }); + + // We expect a modifyLogin notification if the final username used by the + // dialog exists in the logins database, otherwise an addLogin one. + let expectModifyLogin = + typeof testCase.usernameChangedTo !== "undefined" + ? testCase.usernameChangedToExists + : testCase.usernameInPageExists; + + // Simulate the action on the notification to request the login to be + // saved, and wait for the data to be updated or saved based on the type + // of operation we expect. + let expectedNotification = expectModifyLogin + ? "modifyLogin" + : "addLogin"; + let promiseLogin = TestUtils.topicObserved( + "passwordmgr-storage-changed", + (_, data) => data == expectedNotification + ); + + let promiseHidden = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popuphidden" + ); + clickDoorhangerButton(notif, CHANGE_BUTTON); + await promiseHidden; + info("Waiting for storage changed"); + let [result] = await promiseLogin; + + // Check that the values in the database match the expected values. + let login = expectModifyLogin + ? result + .QueryInterface(Ci.nsIArray) + .queryElementAt(1, Ci.nsILoginInfo) + : result.QueryInterface(Ci.nsILoginInfo); + let meta = login.QueryInterface(Ci.nsILoginMetaInfo); + + let expectedLogin = { + username: + "usernameChangedTo" in testCase + ? testCase.usernameChangedTo + : testCase.usernameInPage, + password: + "passwordChangedTo" in testCase + ? testCase.passwordChangedTo + : testCase.passwordInPage, + timesUsed: testCase.timesUsed, + }; + // Check that the password was not updated if the user is empty + if (testCase.checkPasswordNotUpdated) { + expectedLogin.usedSince = meta.timeCreated; + expectedLogin.timeCreated = meta.timePasswordChanged; + } + await verifyLogins([expectedLogin]); + } + ); + } +}); + +async function initForm(browser, formDefaults = {}) { + await ContentTask.spawn( + browser, + formDefaults, + async function (selectorValues) { + for (let [sel, value] of Object.entries(selectorValues)) { + content.document.querySelector(sel).value = value; + } + } + ); +} |