diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html')
-rw-r--r-- | toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html b/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html new file mode 100644 index 0000000000..4bbba09483 --- /dev/null +++ b/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_generation_telemetry.html @@ -0,0 +1,309 @@ +<!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/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( + "resource://testing-common/TestUtils.sys.mjs" + ); + + 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", + }); + + const TelemetryFilterPropsUsed = Object.freeze({ + category: "pwmgr", + method: "autocomplete_field", + object: "generatedpassword", + }); + + const TelemetryFilterPropsShown = Object.freeze({ + category: "pwmgr", + method: "autocomplete_shown", + object: "generatedpassword", + }); + + async function waitForTelemetryEventsCondition(cond, options = {}, + errorMsg = "waitForTelemetryEventsCondition timed out", maxTries = 200) { + return TestUtils.waitForCondition(async () => { + let events = await getTelemetryEvents(options); + let result; + try { + result = cond(events); + info("waitForTelemetryEventsCondition, result: " + result); + } catch (e) { + info("waitForTelemetryEventsCondition caught exception, got events: " + JSON.stringify(events)); + ok(false, `${e}\n${e.stack}`); + } + return result ? events : null; + }, errorMsg, maxTries); + } + + async function showACPopup(formNumber, expectedACLabels) { + const autocompleteItems = await popupByArrowDown(); + checkAutoCompleteResults(autocompleteItems, expectedACLabels, + window.location.host, "Check all rows are correct"); + } + + async function checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents) { + info(`showed generated password option, check there are now ${expectedPWGenTelemetryEvents} generatedpassword telemetry events`); + await waitForTelemetryEventsCondition(events => { + return events.length == expectedPWGenTelemetryEvents; + }, { process: "parent", filterProps: TelemetryFilterPropsShown }, `Wait for there to be ${expectedPWGenTelemetryEvents} shown telemetry events`); + } + + async function checkTelemetryEventsPWGenUsed(expectedPWGenTelemetryEvents) { + info("filled generated password again, ensure we don't record another generatedpassword autocomplete telemetry event"); + let telemetryEvents; + try { + telemetryEvents = await waitForTelemetryEventsCondition(events => events.length == expectedPWGenTelemetryEvents + 1, + { process: "parent", filterProps: TelemetryFilterPropsUsed }, + `Wait for there to be ${expectedPWGenTelemetryEvents + 1} used events`, 50); + } catch (ex) {} + ok(!telemetryEvents, `Expected to timeout waiting for there to be ${expectedPWGenTelemetryEvents + 1} events`); + } + + function clearGeneratedPasswords() { + const { LoginManagerParent } = ChromeUtils.importESModule( + "resource://gre/modules/LoginManagerParent.sys.mjs" + ); + if (LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin()) { + LoginManagerParent.getGeneratedPasswordsByPrincipalOrigin().clear(); + } + } + + add_setup(async () => { + let useEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsUsed, clear: true }); + is(useEvents.length, 0, "Expect 0 use events"); + let showEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsShown, clear: true }); + is(showEvents.length, 0, "Expect 0 show events"); + let acEvents = await getTelemetryEvents({ process: "parent", filterProps: TelemetryFilterPropsAC, clear: true }); + is(acEvents.length, 0, "Expect 0 autocomplete events"); + }); + + add_named_task("autofill autocomplete username no generation", async () => { + 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(); + + const [acEvent] = await waitForTelemetryEventsCondition(events => { + return events.length == 1; + }, { process: "parent", filterProps: TelemetryFilterPropsAC, clear: true }, `Wait for there to be 1 autocomplete telemetry event`); + checkACTelemetryEvent(acEvent, form.uname, { + "hadPrevious": "0", + "login": "1", + "loginsFooter": "1" + }); + }); + + add_named_task("filling out two forms one after the other", async () => { + 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 1619498: Recorded once every time the autocomplete popup is shown + 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 checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents); + let acEvents = await waitForTelemetryEventsCondition(events => { + return events.length == expectedACShownTelemetryEvents; + }, { process: "parent", filterProps: TelemetryFilterPropsAC }, `Wait for there to be ${expectedACShownTelemetryEvents} autocomplete telemetry event(s)`); + checkACTelemetryEvent(acEvents[expectedACShownTelemetryEvents - 1], form.password, { + "generatedPasswo": "1", + "hadPrevious": "0", + "login": "1", + "loginsFooter": "1" + }); + + 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.removeAllUserFacingLogins(); + + while (form.password.value) { + synthesizeKey("KEY_Backspace"); + } + + info("This time select the generated password"); + await showACPopup(formNumber, [ + "Use a Securely Generated Password", + ]); + expectedACShownTelemetryEvents++; + + await checkTelemetryEventsPWGenShown(expectedPWGenTelemetryEvents); + acEvents = await waitForTelemetryEventsCondition(events => { + return events.length == expectedACShownTelemetryEvents; + }, { process: "parent", filterProps: TelemetryFilterPropsAC }, `Wait for there to be ${expectedACShownTelemetryEvents} autocomplete telemetry event(s)`); + checkACTelemetryEvent(acEvents[expectedACShownTelemetryEvents - 1], form.password, { + "generatedPasswo": "1", + "hadPrevious": "0", + "loginsFooter": "1" + }); + + 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", + ]); + expectedACShownTelemetryEvents++; + + synthesizeKey("KEY_ArrowDown"); + synthesizeKey("KEY_ArrowDown"); + synthesizeKey("KEY_Enter"); + await SimpleTest.promiseWaitForCondition(() => !!form.password.value, "Check generated pw filled"); + + await checkTelemetryEventsPWGenUsed(expectedPWGenTelemetryEvents); + + 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", + ]); + expectedACShownTelemetryEvents++; + + 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 + } + }); +</script> +</pre> +</body> +</html> |