Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/**
* 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 = await 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");
await 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]);
}