diff options
Diffstat (limited to 'toolkit/components/passwordmgr/test/browser/browser_doorhanger_promptToChangePassword.js')
-rw-r--r-- | toolkit/components/passwordmgr/test/browser/browser_doorhanger_promptToChangePassword.js | 685 |
1 files changed, 685 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/browser/browser_doorhanger_promptToChangePassword.js b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_promptToChangePassword.js new file mode 100644 index 0000000000..84c241e020 --- /dev/null +++ b/toolkit/components/passwordmgr/test/browser/browser_doorhanger_promptToChangePassword.js @@ -0,0 +1,685 @@ +/** + * Test result of different input to the promptToChangePassword doorhanger + */ + +"use strict"; + +// The origin for the test URIs. +const TEST_ORIGIN = "https://example.com"; +const passwordInputSelector = "#form-basic-password"; +const usernameInputSelector = "#form-basic-username"; + +const availLoginsByValue = new Map(); +let savedLoginsByName; +const finalLoginsByGuid = new Map(); +let finalLogins; + +const availLogins = { + emptyXYZ: LoginTestUtils.testData.formLogin({ + username: "", + password: "xyz", + }), + bobXYZ: LoginTestUtils.testData.formLogin({ + username: "bob", + password: "xyz", + }), + bobABC: LoginTestUtils.testData.formLogin({ + username: "bob", + password: "abc", + }), +}; +availLoginsByValue.set(availLogins.emptyXYZ, "emptyXYZ"); +availLoginsByValue.set(availLogins.bobXYZ, "bobXYZ"); +availLoginsByValue.set(availLogins.bobABC, "bobABC"); + +async function showChangePasswordDoorhanger( + browser, + oldLogin, + formLogin, + { notificationType = "password-change", autoSavedLoginGuid = "" } = {} +) { + let windowGlobal = browser.browsingContext.currentWindowGlobal; + let loginManagerActor = windowGlobal.getActor("LoginManager"); + let prompter = loginManagerActor._getPrompter(browser, null); + Assert.ok( + !PopupNotifications.isPanelOpen, + "Check the doorhanger isn't already open" + ); + + let promiseShown = BrowserTestUtils.waitForEvent( + PopupNotifications.panel, + "popupshown" + ); + + prompter.promptToChangePassword( + browser, + oldLogin, + formLogin, + false, // dimissed prompt + false, // notifySaved + autoSavedLoginGuid + ); + await promiseShown; + + let notif = getCaptureDoorhanger(notificationType); + Assert.ok(notif, `${notificationType} notification exists`); + + let { panel } = PopupNotifications; + let notificationElement = panel.childNodes[0]; + await BrowserTestUtils.waitForCondition(() => { + return ( + notificationElement.querySelector("#password-notification-password") + .value == formLogin.password && + notificationElement.querySelector("#password-notification-username") + .value == formLogin.username + ); + }, "Wait for the notification panel to be populated"); + return notif; +} + +async function setupLogins(...logins) { + Services.logins.removeAllUserFacingLogins(); + let savedLogins = {}; + let timesCreated = new Set(); + for (let login of logins) { + let loginName = availLoginsByValue.get(login); + let savedLogin = await LoginTestUtils.addLogin(login); + // we rely on sorting by timeCreated so ensure none are identical + Assert.ok( + !timesCreated.has(savedLogin.timeCreated), + "Each login has a different timeCreated" + ); + timesCreated.add(savedLogin.timeCreated); + savedLogins[loginName || savedLogin.guid] = savedLogin.clone(); + } + return savedLogins; +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["signon.autofillForms", false]], + }); + Assert.ok(!PopupNotifications.isPanelOpen, "No notifications panel open"); +}); + +async function promptToChangePasswordTest(testData) { + info("Starting: " + testData.name); + savedLoginsByName = await setupLogins(...testData.initialSavedLogins); + await SimpleTest.promiseFocus(); + info("got focus"); + + let oldLogin = savedLoginsByName[testData.promptArgs.oldLogin]; + let changeLogin = LoginTestUtils.testData.formLogin( + testData.promptArgs.changeLogin + ); + let options; + if (testData.autoSavedLoginName) { + options = { + autoSavedLoginGuid: savedLoginsByName[testData.autoSavedLoginName].guid, + }; + } + info( + "Waiting for showChangePasswordDoorhanger, username: " + + changeLogin.username + ); + await BrowserTestUtils.withNewTab( + { + gBrowser, + TEST_ORIGIN, + }, + async function (browser) { + await SimpleTest.promiseFocus(browser.ownerGlobal); + let notif = await showChangePasswordDoorhanger( + browser, + oldLogin, + changeLogin, + options + ); + + await updateDoorhangerInputValues(testData.promptTextboxValues); + + let mainActionButton = getDoorhangerButton(notif, CHANGE_BUTTON); + Assert.equal( + mainActionButton.label, + testData.expectedButtonLabel, + "Check button label" + ); + + let { panel } = PopupNotifications; + let promiseHidden = BrowserTestUtils.waitForEvent(panel, "popuphidden"); + let storagePromise; + if (testData.expectedStorageChange) { + storagePromise = TestUtils.topicObserved("passwordmgr-storage-changed"); + } + + info("Clicking mainActionButton"); + mainActionButton.doCommand(); + info("Waiting for promiseHidden"); + await promiseHidden; + info("Waiting for storagePromise"); + await storagePromise; + + // ensure the notification was removed to keep clean state for next run + await cleanupDoorhanger(notif); + + info(testData.resultDescription); + + finalLoginsByGuid.clear(); + finalLogins = Services.logins.getAllLogins(); + finalLogins.sort((a, b) => a.timeCreated > b.timeCreated); + + for (let l of finalLogins) { + info(`saved login: ${l.guid}: ${l.username}/${l.password}`); + finalLoginsByGuid.set(l.guid, l); + } + info("verifyLogins next"); + verifyLogins(testData.expectedResultLogins); + if (testData.resultCheck) { + testData.resultCheck(); + } + } + ); +} + +let tests = [ + { + name: "Add username to sole login", + initialSavedLogins: [availLogins.emptyXYZ], + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "zaphod", + password: "xyz", + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + resultDescription: "The existing login just gets a new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "zaphod", + password: "xyz", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.emptyXYZ.guid, + "Check guid" + ); + }, + }, + { + name: "Change password of the sole login", + initialSavedLogins: [availLogins.bobXYZ], + promptArgs: { + oldLogin: "bobXYZ", + changeLogin: { + username: "bob", + password: "&*$", + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + resultDescription: "The existing login just gets a new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "bob", + password: "&*$", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobXYZ.guid, + "Check guid" + ); + }, + }, + { + name: "Change password of the sole empty-username login", + initialSavedLogins: [availLogins.emptyXYZ], + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "", + password: "&*$", + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + resultDescription: "The existing login just gets a new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "", + password: "&*$", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.emptyXYZ.guid, + "Check guid" + ); + }, + }, + { + name: "Add different username to empty-usernamed login", + initialSavedLogins: [availLogins.emptyXYZ, availLogins.bobABC], + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "alice", + password: "xyz", + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + resultDescription: "The existing login just gets a new username", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "alice", + password: "xyz", + }, + { + username: "bob", + password: "abc", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.emptyXYZ.guid, + "Check guid" + ); + Assert.ok( + finalLogins[0].timeLastUsed > savedLoginsByName.emptyXYZ.timeLastUsed, + "Check timeLastUsed of 0th login" + ); + }, + }, + { + name: "Add username to autosaved login to match an existing usernamed login", + initialSavedLogins: [availLogins.emptyXYZ, availLogins.bobABC], + autoSavedLoginName: "emptyXYZ", + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "bob", + password: availLogins.emptyXYZ.password, + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + resultDescription: + "Empty-username login is removed, other login gets the empty-login's password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "bob", + password: "xyz", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[0].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed changed" + ); + }, + }, + { + name: "Add username to non-autosaved login to match an existing usernamed login", + initialSavedLogins: [availLogins.emptyXYZ, availLogins.bobABC], + autoSavedLoginName: "", + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "bob", + password: availLogins.emptyXYZ.password, + }, + }, + promptTextboxValues: {}, + expectedButtonLabel: "Update", + // We can't end up with duplicates (bob:xyz and bob:ABC) so the following seems reasonable. + // We could delete the emptyXYZ but we would want to intelligently merge metadata. + resultDescription: + "Multiple login matches but user indicated they want bob:xyz in the prompt so modify bob to give that", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "", + password: "xyz", + }, + { + username: "bob", + password: "xyz", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.emptyXYZ.guid, + "Check guid" + ); + Assert.equal( + finalLogins[0].timeLastUsed, + savedLoginsByName.emptyXYZ.timeLastUsed, + "Check timeLastUsed didn't change" + ); + Assert.equal( + finalLogins[0].timePasswordChanged, + savedLoginsByName.emptyXYZ.timePasswordChanged, + "Check timePasswordChanged didn't change" + ); + + Assert.equal( + finalLogins[1].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[1].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed did change" + ); + Assert.ok( + finalLogins[1].timePasswordChanged > + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged did change" + ); + }, + }, + { + name: "Username & password changes to an auto-saved login apply to matching usernamed-login", + // when we update an auto-saved login - changing both username & password, is + // the matching login updated and empty-username login removed? + initialSavedLogins: [availLogins.emptyXYZ, availLogins.bobABC], + autoSavedLoginName: "emptyXYZ", + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "bob", + password: "xyz", + }, + }, + promptTextboxValues: { + // type a new password in the doorhanger + password: "newpassword", + }, + expectedButtonLabel: "Update", + resultDescription: + "The empty-username login is removed, other login gets the new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "bob", + password: "newpassword", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[0].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed did change" + ); + }, + }, + { + name: "Username & password changes to a non-auto-saved login matching usernamed-login", + // when we update a non-auto-saved login - changing both username & password, is + // the matching login updated and empty-username login unchanged? + initialSavedLogins: [availLogins.emptyXYZ, availLogins.bobABC], + autoSavedLoginName: "", // no auto-saved logins for this session + promptArgs: { + oldLogin: "emptyXYZ", + changeLogin: { + username: "bob", + password: "xyz", + }, + }, + promptTextboxValues: { + // type a new password in the doorhanger + password: "newpassword", + }, + expectedButtonLabel: "Update", + resultDescription: + "The empty-username login is not changed, other login gets the new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "", + password: "xyz", + }, + { + username: "bob", + password: "newpassword", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.emptyXYZ.guid, + "Check guid" + ); + Assert.equal( + finalLogins[0].timeLastUsed, + savedLoginsByName.emptyXYZ.timeLastUsed, + "Check timeLastUsed didn't change" + ); + Assert.equal( + finalLogins[0].timePasswordChanged, + savedLoginsByName.emptyXYZ.timePasswordChanged, + "Check timePasswordChanged didn't change" + ); + Assert.equal( + finalLogins[1].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[1].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed did change" + ); + Assert.ok( + finalLogins[1].timePasswordChanged > + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged did change" + ); + }, + }, + { + name: "Remove the username and change password of autosaved login", + initialSavedLogins: [availLogins.bobABC], + autoSavedLoginName: "bobABC", + promptArgs: { + oldLogin: "bobABC", + changeLogin: { + username: "bob", + password: "abc!", // trigger change prompt with a password change + }, + }, + promptTextboxValues: { + username: "", + }, + expectedButtonLabel: "Update", + resultDescription: + "The auto-saved login is updated with new empty-username login and new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "", + password: "abc!", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[0].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed did change" + ); + Assert.ok( + finalLogins[0].timePasswordChanged > + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged did change" + ); + }, + }, + { + name: "Remove the username and change password of non-autosaved login", + initialSavedLogins: [availLogins.bobABC], + // no autosaved guid + promptArgs: { + oldLogin: "bobABC", + changeLogin: { + username: "bob", + password: "abc!", // trigger change prompt with a password change + }, + }, + promptTextboxValues: { + username: "", + }, + expectedButtonLabel: "Save", + resultDescription: + "A new empty-username login is created with the new password", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "bob", + password: "abc", + }, + { + username: "", + password: "abc!", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.equal( + finalLogins[0].timeLastUsed, + savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed didn't change" + ); + Assert.equal( + finalLogins[0].timePasswordChanged, + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged didn't change" + ); + }, + }, + { + name: "Remove username from the auto-saved sole login", + initialSavedLogins: [availLogins.bobABC], + autoSavedLoginName: "bobABC", + promptArgs: { + oldLogin: "bobABC", + changeLogin: { + username: "bob", + password: "abc!", // trigger change prompt with a password change + }, + }, + promptTextboxValues: { + username: "", + password: "abc", // put password back to what it was + }, + expectedButtonLabel: "Update", + resultDescription: "The existing login is updated", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "", + password: "abc", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.ok( + finalLogins[0].timeLastUsed > savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed did change" + ); + todo_is( + finalLogins[0].timePasswordChanged, + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged didn't change" + ); + }, + }, + { + name: "Remove username from the non-auto-saved sole login", + initialSavedLogins: [availLogins.bobABC], + // no autoSavedLoginGuid + promptArgs: { + oldLogin: "bobABC", + changeLogin: { + username: "bob", + password: "abc!", // trigger change prompt with a password change + }, + }, + promptTextboxValues: { + username: "", + password: "abc", // put password back to what it was + }, + expectedButtonLabel: "Save", + resultDescription: "A new empty-username login is created", + expectedStorageChange: true, + expectedResultLogins: [ + { + username: "bob", + password: "abc", + }, + { + username: "", + password: "abc", + }, + ], + resultCheck() { + Assert.equal( + finalLogins[0].guid, + savedLoginsByName.bobABC.guid, + "Check guid" + ); + Assert.equal( + finalLogins[0].timeLastUsed, + savedLoginsByName.bobABC.timeLastUsed, + "Check timeLastUsed didn't change" + ); + Assert.equal( + finalLogins[0].timePasswordChanged, + savedLoginsByName.bobABC.timePasswordChanged, + "Check timePasswordChanged didn't change" + ); + }, + }, +]; + +for (let testData of tests) { + let tmp = { + async [testData.name]() { + await promptToChangePasswordTest(testData); + }, + }; + add_task(tmp[testData.name]); +} |