Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/**
* Test LoginManagerParent._onPasswordEditedOrGenerated()
*/
"use strict";
const { sinon } = ChromeUtils.importESModule(
);
const { LoginManagerParent } = ChromeUtils.importESModule(
"resource://gre/modules/LoginManagerParent.sys.mjs"
);
const { LoginManagerPrompter } = ChromeUtils.importESModule(
"resource://gre/modules/LoginManagerPrompter.sys.mjs"
);
const { TestUtils } = ChromeUtils.importESModule(
);
const loginTemplate = Object.freeze({
formActionOrigin: "https://www.mozilla.org",
});
let LMP = new LoginManagerParent();
function stubPrompter() {
let fakePromptToSavePassword = sinon.stub();
let fakePromptToChangePassword = sinon.stub();
sinon.stub(LMP, "_getPrompter").callsFake(() => {
return {
promptToSavePassword: fakePromptToSavePassword,
promptToChangePassword: fakePromptToChangePassword,
};
});
LMP._getPrompter().promptToSavePassword();
LMP._getPrompter().promptToChangePassword();
Assert.ok(LMP._getPrompter.calledTwice, "Checking _getPrompter stub");
Assert.ok(
fakePromptToSavePassword.calledOnce,
"Checking fakePromptToSavePassword stub"
);
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking fakePromptToChangePassword stub"
);
function resetPrompterHistory() {
LMP._getPrompter.resetHistory();
fakePromptToSavePassword.resetHistory();
fakePromptToChangePassword.resetHistory();
}
function restorePrompter() {
LMP._getPrompter.restore();
}
resetPrompterHistory();
return {
fakePromptToSavePassword,
fakePromptToChangePassword,
resetPrompterHistory,
restorePrompter,
};
}
async function stubGeneratedPasswordForBrowsingContextId(id) {
Assert.ok(
LoginManagerParent._browsingContextGlobal,
"Check _browsingContextGlobal exists"
);
Assert.ok(
!LoginManagerParent._browsingContextGlobal.get(id),
`BrowsingContext ${id} shouldn't exist yet`
);
info(`Stubbing BrowsingContext.get(${id})`);
let stub = sinon
.stub(LoginManagerParent._browsingContextGlobal, "get")
.withArgs(id)
.callsFake(() => {
return {
currentWindowGlobal: {
documentPrincipal:
Services.scriptSecurityManager.createContentPrincipalFromOrigin(
),
documentURI: Services.io.newURI("https://www.example.com"),
},
get embedderElement() {
info("returning embedderElement");
let browser = MockDocument.createTestDocument(
"chrome://browser/content/browser.xhtml",
<browser></browser>
</box>`,
"application/xml",
true
).querySelector("browser");
MockDocument.mockBrowsingContextProperty(browser, this);
return browser;
},
get top() {
return this;
},
};
});
Assert.ok(
LoginManagerParent._browsingContextGlobal.get(id),
`Checking BrowsingContext.get(${id}) stub`
);
const generatedPassword = await LMP.getGeneratedPassword();
notEqual(generatedPassword, null, "Check password was returned");
equal(
generatedPassword.length,
LoginTestUtils.generation.LENGTH,
"Check password length"
);
equal(
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().size,
1,
"1 added to cache"
);
equal(
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
).value,
generatedPassword,
"Cache key and value"
);
LoginManagerParent._browsingContextGlobal.get.resetHistory();
return {
stub,
generatedPassword,
};
}
function checkEditTelemetryRecorded(expectedCount, msg) {
info("Check that expected telemetry event was recorded");
const snapshot = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
false
);
let resultsCount = 0;
if ("parent" in snapshot) {
const telemetryProps = Object.freeze({
category: "pwmgr",
method: "filled_field_edited",
object: "generatedpassword",
});
const results = snapshot.parent.filter(
([_time, category, method, object]) =>
category === telemetryProps.category &&
method === telemetryProps.method &&
object === telemetryProps.object
);
resultsCount = results.length;
}
equal(
resultsCount,
expectedCount,
"Check count of pwmgr.filled_field_edited for generatedpassword: " + msg
);
}
async function startTestConditions(contextId) {
LMP.useBrowsingContext(contextId);
Assert.ok(
LMP._onPasswordEditedOrGenerated,
"LMP._onPasswordEditedOrGenerated exists"
);
equal(await LMP.getGeneratedPassword(), null, "Null with no BrowsingContext");
equal(
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().size,
0,
"Empty cache to start"
);
equal(
(await Services.logins.getAllLogins()).length,
0,
"Should have no saved logins at the start of the test"
);
}
/*
* Compare login details excluding usernameField and passwordField
*/
function assertLoginProperties(actualLogin, expected) {
equal(actualLogin.origin, expected.origin, "Compare origin");
equal(
actualLogin.formActionOrigin,
expected.formActionOrigin,
"Compare formActionOrigin"
);
equal(actualLogin.httpRealm, expected.httpRealm, "Compare httpRealm");
equal(actualLogin.username, expected.username, "Compare username");
equal(actualLogin.password, expected.password, "Compare password");
}
add_setup(async () => {
// Get a profile for storage.
do_get_profile();
// Force the feature to be enabled.
Services.prefs.setBoolPref("signon.generation.available", true);
Services.prefs.setBoolPref("signon.generation.enabled", true);
await LoginTestUtils.remoteSettings.setupImprovedPasswordRules();
});
add_task(async function test_onPasswordEditedOrGenerated_generatedPassword() {
await startTestConditions(99);
let { generatedPassword } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "addLogin"
);
equal(
(await Services.logins.getAllLogins()).length,
0,
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let [login] = await storageChangedPromised;
let expected = new LoginInfo(
null,
"", // verify we don't include the username when auto-saving a login
generatedPassword
);
Assert.ok(login.equals(expected), "Check added login");
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
info("Edit the password");
const newPassword = generatedPassword + "🔥";
storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let generatedPW =
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited boolean should be true");
equal(generatedPW.value, newPassword, "Cached password should be updated");
// login metadata should be updated
let [dataArray] = await storageChangedPromised;
login = dataArray.queryElementAt(1, Ci.nsILoginInfo);
expected.password = newPassword;
Assert.ok(login.equals(expected), "Check updated login");
equal(
(await Services.logins.getAllLogins()).length,
1,
"Should have 1 saved login still"
);
info(
"Simulate a second edit to check that the telemetry event for the first edit is not recorded twice"
);
const newerPassword = newPassword + "🦊";
storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newerPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited state should remain true");
equal(generatedPW.value, newerPassword, "Cached password should be updated");
[dataArray] = await storageChangedPromised;
login = dataArray.queryElementAt(1, Ci.nsILoginInfo);
expected.password = newerPassword;
Assert.ok(login.equals(expected), "Check updated login");
equal(
(await Services.logins.getAllLogins()).length,
1,
"Should have 1 saved login still"
);
checkEditTelemetryRecorded(1, "with auto-save");
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
add_task(
async function test_onPasswordEditedOrGenerated_editToEmpty_generatedPassword() {
await startTestConditions(99);
let { generatedPassword } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "addLogin"
);
equal(
(await Services.logins.getAllLogins()).length,
0,
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let [login] = await storageChangedPromised;
let expected = new LoginInfo(
null,
"", // verify we don't include the username when auto-saving a login
generatedPassword
);
Assert.ok(login.equals(expected), "Check added login");
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
info("Edit the password to be empty");
const newPassword = "";
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let generatedPW =
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(!generatedPW.edited, "Cached edited boolean should be false");
equal(
generatedPW.value,
generatedPassword,
"Cached password shouldn't be updated"
);
checkEditTelemetryRecorded(0, "Blanking doesn't count as an edit");
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
add_task(async function test_addUsernameBeforeAutoSaveEdit() {
await startTestConditions(99);
let { generatedPassword } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { fakePromptToChangePassword, restorePrompter, resetPrompterHistory } =
stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let fakePopupNotifications = {
getNotification: sinon.stub().returns({ dismissed: true }),
};
sinon.stub(LoginHelper, "getBrowserForPrompt").callsFake(() => {
return {
ownerGlobal: {
PopupNotifications: fakePopupNotifications,
},
};
});
let storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "addLogin"
);
equal(
(await Services.logins.getAllLogins()).length,
0,
"Should have no saved logins at the start of the test"
);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let [login] = await storageChangedPromised;
let expected = new LoginInfo(
null,
"", // verify we don't include the username when auto-saving a login
generatedPassword
);
Assert.ok(login.equals(expected), "Check added login");
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
info("Checking the getNotification stub");
Assert.ok(
!fakePopupNotifications.getNotification.called,
"getNotification didn't get called yet"
);
resetPrompterHistory();
info("Add a username to the auto-saved login in storage");
let loginWithUsername = login.clone();
loginWithUsername.username = "added_username";
LoginManagerPrompter._updateLogin(login, loginWithUsername);
info("Edit the password");
const newPassword = generatedPassword + "🔥";
storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
// will update the doorhanger with changed password
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let generatedPW =
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited boolean should be true");
equal(generatedPW.value, newPassword, "Cached password should be updated");
let [dataArray] = await storageChangedPromised;
login = dataArray.queryElementAt(1, Ci.nsILoginInfo);
loginWithUsername.password = newPassword;
// the password should be updated in storage, but not the username (until the user confirms the doorhanger)
assertLoginProperties(login, loginWithUsername);
Assert.ok(login.matches(loginWithUsername, false), "Check updated login");
equal(
(await Services.logins.getAllLogins()).length,
1,
"Should have 1 saved login still"
);
Assert.ok(
fakePopupNotifications.getNotification.calledOnce,
"getNotification was called"
);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
// The generated password changed, so we expect notifySaved to be true
Assert.ok(
fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword should have a falsey 'notifySaved' argument"
);
resetPrompterHistory();
info(
"Simulate a second edit to check that the telemetry event for the first edit is not recorded twice"
);
const newerPassword = newPassword + "🦊";
storageChangedPromised = TestUtils.topicObserved(
"passwordmgr-storage-changed",
(_, data) => data == "modifyLogin"
);
info("Calling _onPasswordEditedOrGenerated again");
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newerPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
generatedPW = LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited state should remain true");
equal(generatedPW.value, newerPassword, "Cached password should be updated");
[dataArray] = await storageChangedPromised;
login = dataArray.queryElementAt(1, Ci.nsILoginInfo);
loginWithUsername.password = newerPassword;
assertLoginProperties(login, loginWithUsername);
Assert.ok(login.matches(loginWithUsername, false), "Check updated login");
equal(
(await Services.logins.getAllLogins()).length,
1,
"Should have 1 saved login still"
);
checkEditTelemetryRecorded(1, "with auto-save");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
equal(
fakePromptToChangePassword.getCall(0).args[2].password,
newerPassword,
"promptToChangePassword had the updated password"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
LoginManagerParent._browsingContextGlobal.get.restore();
LoginHelper.getBrowserForPrompt.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
add_task(async function test_editUsernameOfFilledSavedLogin() {
await startTestConditions(99);
await stubGeneratedPasswordForBrowsingContextId(99);
let {
fakePromptToChangePassword,
fakePromptToSavePassword,
restorePrompter,
resetPrompterHistory,
} = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
let fakePopupNotifications = {
getNotification: sinon.stub().returns({ dismissed: true }),
};
sinon.stub(LoginHelper, "getBrowserForPrompt").callsFake(() => {
return {
ownerGlobal: {
PopupNotifications: fakePopupNotifications,
},
};
});
let login0Props = Object.assign({}, loginTemplate, {
username: "someusername",
password: "qweqweq",
});
info("Adding initial login: " + JSON.stringify(login0Props));
let savedLogin = await LoginTestUtils.addLogin(login0Props);
let logins = await Services.logins.getAllLogins();
info("Saved initial login: " + JSON.stringify(logins[0]));
equal(logins.length, 1, "Should have 1 saved login at the start of the test");
// first prompt to save a new login
let newUsername = "differentuser";
let newPassword = login0Props.password + "🔥";
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
autoFilledLoginGuid: savedLogin.guid,
newPasswordField: { value: newPassword },
usernameField: { value: newUsername },
triggeredByFillingGenerated: false,
}
);
let expected = new LoginInfo(
login0Props.origin,
login0Props.formActionOrigin,
null,
newUsername,
newPassword
);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
info("Checking the getNotification stub");
Assert.ok(
!fakePopupNotifications.getNotification.called,
"getNotification was not called"
);
Assert.ok(
fakePromptToSavePassword.calledOnce,
"Checking promptToSavePassword was called"
);
Assert.ok(
fakePromptToSavePassword.getCall(0).args[2],
"promptToSavePassword had a truthy 'dismissed' argument"
);
Assert.ok(
!fakePromptToSavePassword.getCall(0).args[3],
"promptToSavePassword had a falsey 'notifySaved' argument"
);
assertLoginProperties(fakePromptToSavePassword.getCall(0).args[1], expected);
resetPrompterHistory();
// then prompt with matching username/password
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
autoFilledLoginGuid: savedLogin.guid,
newPasswordField: { value: login0Props.password },
usernameField: { value: login0Props.username },
triggeredByFillingGenerated: false,
}
);
expected = new LoginInfo(
login0Props.origin,
login0Props.formActionOrigin,
null,
login0Props.username,
login0Props.password
);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
info("Checking the getNotification stub");
Assert.ok(
fakePopupNotifications.getNotification.called,
"getNotification was called"
);
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
!fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a falsey 'notifySaved' argument"
);
assertLoginProperties(
fakePromptToChangePassword.getCall(0).args[2],
expected
);
resetPrompterHistory();
LoginManagerParent._browsingContextGlobal.get.restore();
LoginHelper.getBrowserForPrompt.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
});
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withDisabledLogin() {
await startTestConditions(99);
let { generatedPassword } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
info("Disable login saving for the site");
Services.logins.setLoginSavingEnabled("https://www.example.com", false);
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: generatedPassword },
triggeredByFillingGenerated: true,
}
);
equal(
(await Services.logins.getAllLogins()).length,
0,
"Should have no saved logins since saving is disabled"
);
Assert.ok(
LMP._getPrompter.notCalled,
"Checking _getPrompter wasn't called"
);
// Clean up
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.setLoginSavingEnabled("https://www.example.com", true);
Services.logins.removeAllUserFacingLogins();
}
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedEmptyUsername() {
await startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "",
password: "qweqweq",
});
info("Adding initial login: " + JSON.stringify(login0Props));
let expected = await LoginTestUtils.addLogin(login0Props);
info(
"Saved initial login: " +
JSON.stringify(Services.logins.getAllLogins()[0])
);
let { generatedPassword: password1 } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
}
);
let logins = await Services.logins.getAllLogins();
equal(
logins.length,
1,
"Should just have the previously-saved login with empty username"
);
assertLoginProperties(logins[0], login0Props);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
!fakePromptToChangePassword.getCall(0).args[4],
"promptToChangePassword had a falsey 'notifySaved' argument"
);
info("Edit the password");
const newPassword = password1 + "🔥";
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "someusername" },
triggeredByFillingGenerated: true,
}
);
let generatedPW =
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited boolean should be true");
equal(generatedPW.storageGUID, null, "Should have no storageGUID");
equal(generatedPW.value, newPassword, "Cached password should be updated");
logins = await Services.logins.getAllLogins();
assertLoginProperties(logins[0], login0Props);
Assert.ok(logins[0].equals(expected), "Ensure no changes");
equal(logins.length, 1, "Should have 1 saved login still");
checkEditTelemetryRecorded(1, "Updating cache, not storage (no auto-save)");
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedEmptyUsernameAndUsernameValue() {
// Save as the above task but with a non-empty username field value.
await startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "",
password: "qweqweq",
});
info("Adding initial login: " + JSON.stringify(login0Props));
let expected = await LoginTestUtils.addLogin(login0Props);
info(
"Saved initial login: " +
JSON.stringify(await Services.logins.getAllLogins()[0])
);
let { generatedPassword: password1 } =
await stubGeneratedPasswordForBrowsingContextId(99);
let {
restorePrompter,
fakePromptToChangePassword,
fakePromptToSavePassword,
} = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
usernameField: { value: "non-empty-username" },
triggeredByFillingGenerated: true,
}
);
let logins = await Services.logins.getAllLogins();
equal(
logins.length,
1,
"Should just have the previously-saved login with empty username"
);
assertLoginProperties(logins[0], login0Props);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.notCalled,
"Checking promptToChangePassword wasn't called"
);
Assert.ok(
fakePromptToSavePassword.calledOnce,
"Checking promptToSavePassword was called"
);
Assert.ok(
fakePromptToSavePassword.getCall(0).args[2],
"promptToSavePassword had a truthy 'dismissed' argument"
);
Assert.ok(
!fakePromptToSavePassword.getCall(0).args[3],
"promptToSavePassword had a falsey 'notifySaved' argument"
);
info("Edit the password");
const newPassword = password1 + "🔥";
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: newPassword },
usernameField: { value: "non-empty-username" },
triggeredByFillingGenerated: true,
}
);
Assert.ok(
fakePromptToChangePassword.notCalled,
"Checking promptToChangePassword wasn't called"
);
Assert.ok(
fakePromptToSavePassword.calledTwice,
"Checking promptToSavePassword was called again"
);
Assert.ok(
fakePromptToSavePassword.getCall(1).args[2],
"promptToSavePassword had a truthy 'dismissed' argument"
);
Assert.ok(
!fakePromptToSavePassword.getCall(1).args[3],
"promptToSavePassword had a falsey 'notifySaved' argument"
);
let generatedPW =
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().get(
);
Assert.ok(generatedPW.edited, "Cached edited boolean should be true");
equal(generatedPW.storageGUID, null, "Should have no storageGUID");
equal(generatedPW.value, newPassword, "Cached password should be updated");
logins = await Services.logins.getAllLogins();
assertLoginProperties(logins[0], login0Props);
Assert.ok(logins[0].equals(expected), "Ensure no changes");
equal(logins.length, 1, "Should have 1 saved login still");
checkEditTelemetryRecorded(
1,
"Updating cache, not storage (no auto-save) with username in field"
);
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
Services.telemetry.clearEvents();
}
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withEmptyUsernameDifferentFormActionOrigin() {
await startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "",
password: "qweqweq",
});
await LoginTestUtils.addLogin(login0Props);
let { generatedPassword: password1 } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.elsewhere.com",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
}
);
let savedLogins = await Services.logins.getAllLogins();
equal(
savedLogins.length,
2,
"Should have saved the generated-password login"
);
assertLoginProperties(savedLogins[0], login0Props);
assertLoginProperties(
savedLogins[1],
Object.assign({}, loginTemplate, {
formActionOrigin: "https://www.elsewhere.com",
username: "",
password: password1,
})
);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[2],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
}
);
add_task(
async function test_onPasswordEditedOrGenerated_generatedPassword_withSavedUsername() {
await startTestConditions(99);
let login0Props = Object.assign({}, loginTemplate, {
username: "previoususer",
password: "qweqweq",
});
await LoginTestUtils.addLogin(login0Props);
let { generatedPassword: password1 } =
await stubGeneratedPasswordForBrowsingContextId(99);
let { restorePrompter, fakePromptToChangePassword } = stubPrompter();
let rootBrowser = LMP.getRootBrowser();
await LMP._onPasswordEditedOrGenerated(
rootBrowser,
{
browsingContextId: 99,
formActionOrigin: "https://www.mozilla.org",
newPasswordField: { value: password1 },
triggeredByFillingGenerated: true,
}
);
let savedLogins = await Services.logins.getAllLogins();
equal(
savedLogins.length,
2,
"Should have saved the generated-password login"
);
assertLoginProperties(savedLogins[0], login0Props);
assertLoginProperties(
savedLogins[1],
Object.assign({}, loginTemplate, {
username: "",
password: password1,
})
);
Assert.ok(LMP._getPrompter.calledOnce, "Checking _getPrompter was called");
Assert.ok(
fakePromptToChangePassword.calledOnce,
"Checking promptToChangePassword was called"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[2],
"promptToChangePassword had a truthy 'dismissed' argument"
);
Assert.ok(
fakePromptToChangePassword.getCall(0).args[3],
"promptToChangePassword had a truthy 'notifySaved' argument"
);
LoginManagerParent._browsingContextGlobal.get.restore();
restorePrompter();
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
Services.logins.removeAllUserFacingLogins();
}
);