Bug 1653083 - Add doorhanger telemetry for Credit Card A/B testing. r=abr

Differential Revision: https://phabricator.services.mozilla.com/D83723
This commit is contained in:
Zibi Braniecki 2020-07-27 22:43:26 +00:00
parent ab461dd2a4
commit 48beff740e
3 changed files with 254 additions and 33 deletions

View File

@ -706,12 +706,29 @@ class FormAutofillParent extends JSWindowActorParent {
creditCard.record["cc-number-decrypted"];
let name = creditCard.record["cc-name"];
const description = await CreditCard.getLabel({ name, number });
const telemetryObject = creditCard.guid
? "update_doorhanger"
: "capture_doorhanger";
Services.telemetry.recordEvent(
"creditcard",
"show",
telemetryObject,
creditCard.flowId
);
const state = await FormAutofillDoorhanger.show(
browser,
creditCard.guid ? "updateCreditCard" : "addCreditCard",
description
);
if (state == "cancel") {
Services.telemetry.recordEvent(
"creditcard",
"cancel",
telemetryObject,
creditCard.flowId
);
return;
}
@ -720,6 +737,12 @@ class FormAutofillParent extends JSWindowActorParent {
"extensions.formautofill.creditCards.enabled",
false
);
Services.telemetry.recordEvent(
"creditcard",
"disable",
telemetryObject,
creditCard.flowId
);
return;
}
@ -731,6 +754,12 @@ class FormAutofillParent extends JSWindowActorParent {
let changedGUIDs = [];
if (creditCard.guid) {
if (state == "update") {
Services.telemetry.recordEvent(
"creditcard",
"update",
telemetryObject,
creditCard.flowId
);
await gFormAutofillStorage.creditCards.update(
creditCard.guid,
creditCard.record,
@ -738,6 +767,12 @@ class FormAutofillParent extends JSWindowActorParent {
);
changedGUIDs.push(creditCard.guid);
} else if ("create") {
Services.telemetry.recordEvent(
"creditcard",
"save",
telemetryObject,
creditCard.flowId
);
changedGUIDs.push(
await gFormAutofillStorage.creditCards.add(creditCard.record)
);
@ -749,6 +784,12 @@ class FormAutofillParent extends JSWindowActorParent {
))
);
if (!changedGUIDs.length) {
Services.telemetry.recordEvent(
"creditcard",
"save",
telemetryObject,
creditCard.flowId
);
changedGUIDs.push(
await gFormAutofillStorage.creditCards.add(creditCard.record)
);

View File

@ -5,12 +5,12 @@ const { TelemetryTestUtils } = ChromeUtils.import(
"resource://testing-common/TelemetryTestUtils.jsm"
);
async function assertTelemetry(expected_content = [], expected_parent = []) {
async function assertTelemetry(expected_content, expected_parent) {
let snapshots;
info(
`Waiting for ${expected_content.length} content events and ` +
`${expected_parent.length} parent events`
`Waiting for ${expected_content?.length ?? 0} content events and ` +
`${expected_parent?.length ?? 0} parent events`
);
await TestUtils.waitForCondition(
@ -20,8 +20,8 @@ async function assertTelemetry(expected_content = [], expected_parent = []) {
false
);
return (
(snapshots.parent?.length ?? 0) >= expected_parent.length &&
(snapshots.content?.length ?? 0) >= expected_content.length
(snapshots.parent?.length ?? 0) >= (expected_parent?.length ?? 0) &&
(snapshots.content?.length ?? 0) >= (expected_content?.length ?? 0)
);
},
"Wait for telemetry to be collected",
@ -31,12 +31,12 @@ async function assertTelemetry(expected_content = [], expected_parent = []) {
info(JSON.stringify(snapshots, null, 2));
if (expected_content.length) {
if (expected_content !== undefined) {
expected_content = expected_content.map(([category, method, object]) => {
return { category, method, object };
});
let clear = !expected_parent.length;
let clear = expected_parent === undefined;
TelemetryTestUtils.assertEvents(
expected_content,
@ -47,7 +47,7 @@ async function assertTelemetry(expected_content = [], expected_parent = []) {
);
}
if (expected_parent.length) {
if (expected_parent !== undefined) {
expected_parent = expected_parent.map(([category, method, object]) => {
return { category, method, object };
});
@ -87,55 +87,216 @@ add_task(async function test_popup_opened() {
]);
});
add_task(async function test_submit_creditCard_saved() {
add_task(async function test_submit_creditCard_new() {
async function test_per_command(command, idx, expectChanged) {
await SpecialPowers.pushPrefEnv({
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_URL },
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await SpecialPowers.spawn(browser, [], async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
name.setUserInput("User 1");
form.querySelector("#cc-number").setUserInput("5038146897157463");
form.querySelector("#cc-exp-month").setUserInput("12");
form.querySelector("#cc-exp-year").setUserInput("2017");
form.querySelector("#cc-type").value = "mastercard";
// Wait 1000ms before submission to make sure the input value applied
await new Promise(resolve => content.setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(command, idx);
if (expectChanged) {
await onChanged;
}
}
);
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
SpecialPowers.clearUserPref(ENABLED_AUTOFILL_CREDITCARDS_PREF);
await removeAllRecords();
}
Services.telemetry.clearEvents();
Services.telemetry.setEventRecordingEnabled("creditcard", true);
let expected_content = [
["creditcard", "detected", "cc_form"],
["creditcard", "submitted", "cc_form"],
];
await test_per_command(MAIN_BUTTON, undefined, true);
await assertTelemetry(expected_content, [
["creditcard", "show", "capture_doorhanger"],
["creditcard", "save", "capture_doorhanger"],
]);
await test_per_command(SECONDARY_BUTTON, undefined, false);
await assertTelemetry(expected_content, [
["creditcard", "show", "capture_doorhanger"],
["creditcard", "cancel", "capture_doorhanger"],
]);
await test_per_command(MENU_BUTTON, 0, false);
await assertTelemetry(expected_content, [
["creditcard", "show", "capture_doorhanger"],
["creditcard", "disable", "capture_doorhanger"],
]);
});
add_task(async function test_submit_creditCard_autofill() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(
OSKeyStoreTestUtils.canTestOSKeyStoreLogin(),
"Cannot test OS key store login on official builds."
);
return;
}
Services.telemetry.clearEvents();
Services.telemetry.setEventRecordingEnabled("creditcard", true);
await SpecialPowers.pushPrefEnv({
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
});
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
let onUsed = TestUtils.topicObserved(
"formautofill-storage-changed",
(subject, data) => data == "notifyUsed"
);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_URL },
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await openPopupOn(browser, "form #cc-name");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await osKeyStoreLoginShown;
await SpecialPowers.spawn(browser, [], async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
return name.value == "John Doe";
}, "Credit card detail never fills");
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
name.setUserInput("User 1");
form.querySelector("#cc-number").setUserInput("5038146897157463");
form.querySelector("#cc-exp-month").setUserInput("12");
form.querySelector("#cc-exp-year").setUserInput("2017");
form.querySelector("#cc-type").value = "mastercard";
// Wait 1000ms before submission to make sure the input value applied
await new Promise(resolve => content.setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await onChanged;
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);
await onUsed;
is(
SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF),
2,
"User has seen the doorhanger"
);
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
await removeAllRecords();
await assertTelemetry([
await assertTelemetry(
[
["creditcard", "detected", "cc_form"],
["creditcard", "popup_shown", "cc_form"],
["creditcard", "filled", "cc_form"],
["creditcard", "submitted", "cc_form"],
],
[]
);
});
add_task(async function test_submit_creditCard_update() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(
OSKeyStoreTestUtils.canTestOSKeyStoreLogin(),
"Cannot test OS key store login on official builds."
);
return;
}
async function test_per_command(command, idx, expectChanged) {
await SpecialPowers.pushPrefEnv({
set: [[CREDITCARDS_USED_STATUS_PREF, 0]],
});
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: CREDITCARD_FORM_URL },
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(
PopupNotifications.panel,
"popupshown"
);
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await openPopupOn(browser, "form #cc-name");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await osKeyStoreLoginShown;
await SpecialPowers.spawn(browser, [], async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
return name.value == "John Doe";
}, "Credit card detail never fills");
let form = content.document.getElementById("form");
let year = form.querySelector("#cc-exp-year");
year.setUserInput("2019");
// Wait 1000ms before submission to make sure the input value applied
await new Promise(resolve => content.setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(command, idx);
if (expectChanged) {
await onChanged;
}
}
);
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
await removeAllRecords();
}
Services.telemetry.clearEvents();
Services.telemetry.setEventRecordingEnabled("creditcard", true);
let expected_content = [
["creditcard", "detected", "cc_form"],
["creditcard", "popup_shown", "cc_form"],
["creditcard", "filled", "cc_form"],
["creditcard", "filled_modified", "cc_form"],
["creditcard", "submitted", "cc_form"],
];
await test_per_command(MAIN_BUTTON, undefined, true);
await assertTelemetry(expected_content, [
["creditcard", "show", "update_doorhanger"],
["creditcard", "update", "update_doorhanger"],
]);
await test_per_command(SECONDARY_BUTTON, undefined, true);
await assertTelemetry(expected_content, [
["creditcard", "show", "update_doorhanger"],
["creditcard", "save", "update_doorhanger"],
]);
});

View File

@ -350,6 +350,25 @@ form_autocomplete:
release_channel_collection: opt-out
creditcard:
doorhanger:
description: >-
User interactions for the browser credit card autofill doorhanger.
objects:
- "capture_doorhanger"
- "update_doorhanger"
methods:
- "show"
- "save"
- "update"
- "cancel"
- "disable"
bug_numbers: [1653073, 1653083]
notification_emails: ["jmathies@mozilla.com", "chsiang@mozilla.com"]
expiry_version: "93"
products:
- "firefox"
record_in_processes: ["main"]
release_channel_collection: opt-out
cc_form:
description: >-
User interactions for credit card autofill forms