Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test telemetry on autofill and autocomplete on autocomplete=new-password fields</title>
<!-- This test assumes that telemetry events are not cleared after the `setup` task. -->
<script src="/tests/SimpleTest/GleanTest.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="pwmgr_common.js"></script>
<script src="../../../satchel/test/satchel_common.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content"></div>
<pre id="test">
Login Manager test: telemetry events during autofill with password generation on `autocomplete=new-password` fields.
<template id="form1-template">
<form id="form1" action="https://autofill">
<input type="text" name="uname">
<input type="password" name="p">
<button type="submit" name="submit">Submit</button>
</form>
</template>
<template id="form2-template">
<form id="form2" action="https://autofill">
<input type="text" name="uname">
<input type="password" name="password" autocomplete="new-password">
<button type="submit" name="submit">Submit</button>
</form>
</template>
<template id="form3-template">
<form id="form3" action="https://autofill">
<input type="text" name="username">
<label>New password<input type="password" name="password"></label>
<button type="submit" name="submit">Submit</button>
</form>
</template>
<script class="testbody" type="text/javascript">
const { TestUtils } = SpecialPowers.ChromeUtils.importESModule(
);
const formTemplates = {
form1: document.getElementById("form1-template"),
form2: document.getElementById("form2-template"),
form3: document.getElementById("form3-template"),
};
const dateAndTimeFormatter = new SpecialPowers.Services.intl.DateTimeFormat(undefined, {
dateStyle: "medium",
});
async function showACPopup(formNumber, expectedACLabels) {
const autocompleteItems = await popupByArrowDown();
checkAutoCompleteResults(autocompleteItems, expectedACLabels,
window.location.host, "Check all rows are correct");
}
function clearGeneratedPasswords() {
const { LoginManagerParent } = ChromeUtils.importESModule(
"resource://gre/modules/LoginManagerParent.sys.mjs"
);
if (LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin()) {
LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear();
}
}
add_setup(async () => {
const useEventsCount = await GleanTest.pwmgr.autocompleteFieldGeneratedpassword.testGetValue()?.length ?? 0;
is(useEventsCount, 0, "Expect 0 use events");
const showEventsCount = await GleanTest.pwmgr.autocompleteShownGeneratedpassword.testGetValue()?.length ?? 0;
is(showEventsCount, 0, "Expect 0 show events");
const acEventsCount = await GleanTest.formAutocomplete.showLogins.testGetValue()?.length ?? 0;
is(acEventsCount, 0, "Expect 0 autocomplete events");
});
add_named_task("autofill autocomplete username no generation", async () => {
await GleanTest.testResetFOG();
await setPreferencesForTask(
["signon.generation.available", false],
["signon.generation.enabled", false],
);
await setStoredLoginsDuringTask([location.origin, "https://autofill", null, "user1", "pass1"]);
const form = setContentForTask(formTemplates.form2);
const autofillResult = await formAutofillResult(form.id);
is(autofillResult, "password_autocomplete_new_password", "form has not been filled due to password_autocomplete_new_password");
form.uname.focus();
let acEvents;
await TestUtils.waitForCondition(async () => {
acEvents = await GleanTest.formAutocomplete.showLogins.testGetValue();
return acEvents?.length == 1;
}, "Wait for 1 autocomplete event.");
is("0", acEvents[0].extra.hadPrevious);
is("1", acEvents[0].extra.login);
is("1", acEvents[0].extra.loginsFooter);
});
add_named_task("filling out two forms one after the other", async () => {
await GleanTest.testResetFOG();
await setPreferencesForTask(
["signon.generation.available", true],
["signon.generation.enabled", true],
);
const formsToTest = [
{
num: 2,
template: formTemplates.form2
},
{
num: 3,
template: formTemplates.form3
}
];
// Bug 1616356 and Bug 1548878: Recorded once per origin
let expectedPWGenTelemetryEvents = 0;
// Bug 2070466: Recorded once per input element, on the first opening of
// its autocomplete popup
let expectedACShownTelemetryEvents = 0;
for (const { num: formNumber, template } of formsToTest) {
runInParent(clearGeneratedPasswords);
await setStoredLoginsAsync([location.origin, "https://autofill", null, "user1", "pass1"]);
const form = setContentForTask(template);
await promiseFormsProcessedInSameProcess();
form.reset();
form.password.focus();
await showACPopup(formNumber, [
"user1",
"Use a Securely Generated Password",
]);
expectedPWGenTelemetryEvents++;
expectedACShownTelemetryEvents++;
await TestUtils.waitForCondition(async () => {
return (await GleanTest.pwmgr.autocompleteShownGeneratedpassword.testGetValue())?.length == expectedPWGenTelemetryEvents;
}, `Wait for ${expectedPWGenTelemetryEvents} generated password events.`);
let acEvents;
await TestUtils.waitForCondition(async () => {
acEvents = await GleanTest.formAutocomplete.showLogins.testGetValue();
return acEvents?.length == expectedACShownTelemetryEvents;
}, `Wait for ${expectedACShownTelemetryEvents} autocomplete events.`);
is("1", acEvents.at(-1).extra.generatedPasswo);
is("0", acEvents.at(-1).extra.hadPrevious);
is("1", acEvents.at(-1).extra.login);
is("1", acEvents.at(-1).extra.loginsFooter);
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
// Can't use promiseFormsProcessedInSameProcess() when autocomplete fills the field directly.
await SimpleTest.promiseWaitForCondition(() => form.password.value == "pass1", "Check pw filled");
// No autocomplete results should appear for non-empty pw fields.
await noPopupByArrowDown();
info("Removing all logins to test auto-saving of generated passwords");
await LoginManager.removeAllUserFacingLoginsAsync();
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("This time select the generated password");
await showACPopup(formNumber, [
"Use a Securely Generated Password",
]);
await TestUtils.waitForCondition(async () => {
return (await GleanTest.pwmgr.autocompleteShownGeneratedpassword.testGetValue())?.length == expectedPWGenTelemetryEvents;
}, `Wait for ${expectedPWGenTelemetryEvents} generated password events.`);
synthesizeKey("KEY_ArrowDown");
let storageAddPromise = promiseStorageChanged(["addLogin"]);
synthesizeKey("KEY_Enter");
info("waiting for the password field to be filled with the generated password");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check generated pw filled");
info("Wait for generated password to be added to storage");
await storageAddPromise;
let logins = await LoginManager.getAllLogins();
let timePasswordChanged = logins[logins.length - 1].timePasswordChanged;
let time = dateAndTimeFormatter.format(new Date(timePasswordChanged));
const LABEL_NO_USERNAME = "No username (" + time + ")";
info("Check field is masked upon blurring");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
// Remove selection for OS where the whole value is selected upon focus.
synthesizeKey("KEY_ArrowRight");
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("Blur the empty field to trigger a 'change' event");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
info("Type a single character after blanking");
synthesizeKey("@");
info("Blur the single-character field to trigger a 'change' event");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
synthesizeKey("KEY_Backspace"); // Blank the field again
await showACPopup(formNumber, [
LABEL_NO_USERNAME,
"Use a Securely Generated Password",
]);
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check generated pw filled");
await TestUtils.waitForCondition(async () => {
return (await GleanTest.pwmgr.autocompleteFieldGeneratedpassword.testGetValue())?.length == expectedPWGenTelemetryEvents;
}, `Wait for ${expectedPWGenTelemetryEvents} generated password events.`);
info("filling the saved login to ensure the field is masked again");
while (form.password.value) {
synthesizeKey("KEY_Backspace");
}
info("Blur the field to trigger a 'change' event again");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus again
// Remove selection for OS where the whole value is selected upon focus.
synthesizeKey("KEY_ArrowRight");
await showACPopup(formNumber, [
LABEL_NO_USERNAME,
"Use a Securely Generated Password",
]);
synthesizeKey("KEY_ArrowDown");
synthesizeKey("KEY_Enter");
await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check saved generated pw filled");
synthesizeKey("KEY_Tab"); // blur
synthesizeKey("KEY_Tab", { shiftKey: true }); // focus
}
await TestUtils.waitForCondition(async () => {
return (await GleanTest.formAutocomplete.showLogins.testGetValue())?.length == expectedACShownTelemetryEvents;
}, `Wait for ${expectedACShownTelemetryEvents} autocomplete events.`);
});
</script>
</pre>
</body>
</html>