Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Errors
- This test gets skipped with pattern: tsan OR os == 'linux' && os_version == '18.04' && debug OR os == 'linux' && os_version == '18.04' && asan
- This test failed 2 times in the preceding 30 days. quicksearch this test
- Manifest: toolkit/components/passwordmgr/test/browser/browser.toml
/**
* Test that doorhanger submit telemetry is sent when the user saves/updates.
*/
add_setup(function () {
// This test used to rely on the initial timer of
// The test is perma-fail on Linux asan opt without this.
let originalWaitForCondition = TestUtils.waitForCondition;
TestUtils.waitForCondition = async function (
condition,
msg,
interval = 100,
maxTries = 50
) {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 100));
return originalWaitForCondition(condition, msg, interval, maxTries);
};
registerCleanupFunction(function () {
TestUtils.waitForCondition = originalWaitForCondition;
});
});
const PAGE_USERNAME_SELECTOR = "#form-basic-username";
const PAGE_PASSWORD_SELECTOR = "#form-basic-password";
const TEST_CASES = [
{
description:
"Saving a new login from page values without modification sends a 'no modification' event",
savedLogin: undefined,
userActions: [
{
pageChanges: {
username: "pageUn",
password: "pagePw",
},
doorhangerChanges: [],
},
],
expectedEvents: [
{
type: "save",
ping: {
did_edit_un: "false",
did_select_un: "false",
did_edit_pw: "false",
did_select_pw: "false",
},
},
],
},
/////////////////
{
description: "Saving two logins sends two events",
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
typedUsername: "doorhangerUn",
},
],
},
{
pageChanges: { password: "pagePw2" },
doorhangerChanges: [
{
typedPassword: "doorhangerPw",
},
],
},
],
expectedEvents: [
{
type: "save",
ping: {
did_edit_un: "true",
did_select_un: "false",
did_edit_pw: "false",
did_select_pw: "false",
},
},
{
type: "update",
ping: {
did_edit_un: "false",
did_select_un: "false",
did_edit_pw: "true",
did_select_pw: "false",
},
},
],
},
/////////////////
{
description: "Updating a doorhanger password sends a 'pw updated' event",
savedLogin: {
username: "savedUn",
password: "savedPw",
},
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
typedPassword: "doorhangerPw",
},
],
},
],
expectedEvents: [
{
type: "update",
ping: {
did_edit_un: "false",
did_select_un: "false",
did_edit_pw: "true",
did_select_pw: "false",
},
},
],
},
/////////////////
{
description:
"Saving a new username with an existing password sends a 'un updated' event",
savedLogin: {
username: "savedUn",
password: "savedPw",
},
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
typedUsername: "doorhangerUn",
},
],
},
],
expectedEvents: [
{
type: "update",
ping: {
did_edit_un: "true",
did_select_un: "false",
did_edit_pw: "false",
did_select_pw: "false",
},
},
],
},
///////////////
{
description: "selecting a saved username sends a 'not edited' event",
savedLogin: {
username: "savedUn",
password: "savedPw",
},
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
selectUsername: "savedUn",
},
],
},
],
expectedEvents: [
{
type: "update",
ping: {
did_edit_un: "false",
did_select_un: "true",
did_edit_pw: "false",
did_select_pw: "false",
},
},
],
},
/////////////////
{
description:
"typing a new username then selecting a saved username sends a 'not edited' event",
savedLogin: {
username: "savedUn",
password: "savedPw",
},
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
typedUsername: "doorhangerTypedUn",
},
{
selectUsername: "savedUn",
},
],
},
],
expectedEvents: [
{
type: "update",
ping: {
did_edit_un: "false",
did_select_un: "true",
did_edit_pw: "false",
did_select_pw: "false",
},
},
],
},
/////////////////
{
description:
"selecting a saved username then typing a new username sends an 'edited' event",
savedLogin: {
username: "savedUn",
password: "savedPw",
},
userActions: [
{
pageChanges: { password: "pagePw" },
doorhangerChanges: [
{
selectUsername: "savedUn",
},
{
typedUsername: "doorhangerTypedUn",
},
],
},
],
expectedEvents: [
{
type: "update",
ping: {
did_edit_un: "true",
did_select_un: "false",
did_edit_pw: "false",
did_select_pw: "false",
},
},
],
},
/////////////////
];
for (let testData of TEST_CASES) {
let tmp = {
async [testData.description]() {
info("testing with: " + JSON.stringify(testData));
await test_submit_telemetry(testData);
},
};
add_task(tmp[testData.description]);
}
function _validateTestCase(tc) {
for (let event of tc.expectedEvents) {
Assert.ok(
!(event.ping.did_edit_un && event.ping.did_select_un),
"'did_edit_un' and 'did_select_un' can never be true at the same time"
);
Assert.ok(
!(event.ping.did_edit_pw && event.ping.did_select_pw),
"'did_edit_pw' and 'did_select_pw' can never be true at the same time"
);
}
}
async function test_submit_telemetry(tc) {
if (tc.savedLogin) {
await Services.logins.addLoginAsync(
LoginTestUtils.testData.formLogin({
username: tc.savedLogin.username,
password: tc.savedLogin.password,
})
);
}
let notif;
for (let userAction of tc.userActions) {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url:
"passwordmgr/test/browser/form_basic.html",
},
async function (browser) {
await SimpleTest.promiseFocus(browser.ownerGlobal);
if (userAction.pageChanges) {
info(
`Update form with changes: ${JSON.stringify(
userAction.pageChanges
)}`
);
let changeTo = {};
if (userAction.pageChanges.username) {
changeTo[PAGE_USERNAME_SELECTOR] = userAction.pageChanges.username;
}
if (userAction.pageChanges.password) {
changeTo[PAGE_PASSWORD_SELECTOR] = userAction.pageChanges.password;
}
await changeContentFormValues(browser, changeTo);
}
info("Submitting form");
let formSubmittedPromise = listenForTestNotification("ShowDoorhanger");
await SpecialPowers.spawn(browser, [], async function () {
let doc = this.content.document;
doc.getElementById("form-basic").submit();
});
await formSubmittedPromise;
let saveDoorhanger = waitForDoorhanger(browser, "password-save");
let updateDoorhanger = waitForDoorhanger(browser, "password-change");
notif = await Promise.race([saveDoorhanger, updateDoorhanger]);
if (PopupNotifications.panel.state !== "open") {
await BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
}
if (userAction.doorhangerChanges) {
for (let doorhangerChange of userAction.doorhangerChanges) {
if (
doorhangerChange.typedUsername ||
doorhangerChange.typedPassword
) {
await updateDoorhangerInputValues({
username: doorhangerChange.typedUsername,
password: doorhangerChange.typedPassword,
});
}
if (doorhangerChange.selectUsername) {
await selectDoorhangerUsername(doorhangerChange.selectUsername);
}
if (doorhangerChange.selectPassword) {
await selectDoorhangerPassword(doorhangerChange.selectPassword);
}
}
}
info("Waiting for doorhanger");
await clickDoorhangerButton(notif, REMEMBER_BUTTON);
}
);
}
let expectedEvents = tc.expectedEvents.map(expectedEvent => [
"pwmgr",
"doorhanger_submitted",
expectedEvent.type,
null,
expectedEvent.ping,
]);
await LoginTestUtils.telemetry.waitForEventCount(
expectedEvents.length,
"parent",
"pwmgr",
"doorhanger_submitted"
);
TelemetryTestUtils.assertEvents(
expectedEvents,
{ category: "pwmgr", method: "doorhanger_submitted" },
{ clear: true }
);
// Clean up the database before the next test case is executed.
await cleanupDoorhanger(notif);
Services.logins.removeAllUserFacingLogins();
}