Bug 1687679: Fix CC expiration month not autofilling correctly on HTML inputs. r=dimi

Fixes the case where credit card autofill incorrectly fills the expiration month due to input elements and placeholders on those input elements.
Enables unit tests for form autofill.
Additionally adds tests to ensure the previous behavior of autofilling expiration month using select elements.

Added workarounds in tests so that we don't change the reality of the mocked CC storage.

Differential Revision: https://phabricator.services.mozilla.com/D119659
This commit is contained in:
Tim Giles 2021-08-16 14:44:20 +00:00
parent 0aaae4a863
commit 8d041ebf24
8 changed files with 187 additions and 40 deletions

View File

@ -38,7 +38,11 @@ BROWSER_CHROME_MANIFESTS += [
"test/browser/focus-leak/browser.ini",
]
XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
XPCSHELL_TESTS_MANIFESTS += [
"test/unit/heuristics/third_party/xpcshell.ini",
"test/unit/heuristics/xpcshell.ini",
"test/unit/xpcshell.ini",
]
MOCHITEST_MANIFESTS += [
"test/mochitest/creditCard/mochitest.ini",

View File

@ -131,9 +131,8 @@ function checkFieldValue(elem, expectedValue) {
}
async function triggerAutofillAndCheckProfile(profile) {
const adaptedProfile = _getAdaptedProfile(profile);
let adaptedProfile = _getAdaptedProfile(profile);
const promises = [];
for (const [fieldName, value] of Object.entries(adaptedProfile)) {
info(`triggerAutofillAndCheckProfile: ${fieldName}`);
const element = document.getElementById(fieldName);

View File

@ -0,0 +1,31 @@
[DEFAULT]
firefox-appdir = browser
head = ../../head.js
support-files =
../../../fixtures/**
[test_BestBuy.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_CDW.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_CostCo.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_HomeDepot.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Macys.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_NewEgg.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_OfficeDepot.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_QVC.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Sears.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Staples.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Walmart.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_Lush.js]
skip-if = (os == "linux") && ccov # bug 1614100

View File

@ -0,0 +1,16 @@
[DEFAULT]
firefox-appdir = browser
head = ../head.js
support-files =
../../fixtures/**
[test_basic.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_cc_exp.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_de_fields.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_known_strings.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_multiple_section.js]
skip-if = (os == "linux") && ccov # bug 1614100

View File

@ -263,8 +263,8 @@ const TESTCASES = [
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "06",
"cc-exp-year": "25",
"cc-exp-month": 6,
"cc-exp-year": 25,
},
expectedResult: {
"street-addr": "",
@ -272,12 +272,62 @@ const TESTCASES = [
country: "",
email: "",
tel: "",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "6",
"cc-exp-year": "25",
},
},
{
description:
"Fill credit card fields in a form with a placeholder on expiration month input field",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
<input id="cc-name" autocomplete="cc-name">
<input id="cc-exp-month" autocomplete="cc-exp-month" placeholder="MM">
<input id="cc-exp-year" autocomplete="cc-exp-year">
</form>
`,
focusedInputId: "cc-number",
profileData: {
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": 6,
"cc-exp-year": 25,
},
expectedResult: {
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "06",
"cc-exp-year": "25",
},
},
{
description:
"Fill credit card fields in a form without a placeholder on expiration month input field",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
<input id="cc-name" autocomplete="cc-name">
<input id="cc-exp-month" autocomplete="cc-exp-month">
<input id="cc-exp-year" autocomplete="cc-exp-year">
</form>
`,
focusedInputId: "cc-number",
profileData: {
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": 6,
"cc-exp-year": 25,
},
expectedResult: {
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "6",
"cc-exp-year": "25",
},
},
];
const TESTCASES_INPUT_UNCHANGED = [
@ -482,6 +532,31 @@ const TESTCASES_FILL_SELECT = [
country: "XX",
},
},
{
description:
"Fill credit card expiration month field in a form with select field",
document: `<form>
<input id="cc-number" autocomplete="cc-number">
<input id="cc-name" autocomplete="cc-name">
<select id="cc-exp-month" autocomplete="cc-exp-month">
<option value="">MM</option>
<option value="6">06</option>
</select></form>`,
focusedInputId: "cc-number",
profileData: {
guid: "123",
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": 6,
"cc-exp-year": 25,
},
expectedResult: {
"cc-number": "4111111111111111",
"cc-name": "test name",
"cc-exp-month": "6",
"cc-exp-year": "2025",
},
},
];
function do_test(testcases, testFn) {

View File

@ -38,6 +38,10 @@ const DEFAULT_CREDITCARD_RECORD = {
"cc-exp": "2025-01",
};
const getCCExpMonthFormatted = () => {
return DEFAULT_CREDITCARD_RECORD["cc-exp-month"].toString().padStart(2, "0");
};
const TESTCASES = [
{
description: "Address form with street-address",
@ -1064,6 +1068,19 @@ const TESTCASES = [
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
},
{
description: "Use placeholder to adjust cc-exp-month field [mm].",
document: `<form>
<input autocomplete="cc-number">
<input autocomplete="cc-exp-month" placeholder="MM">
</form>`,
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [
Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
"cc-exp-month-formatted": getCCExpMonthFormatted(),
}),
],
},
{
description: "Test maxlength=2 on numeric fields.",
document: `<form>
@ -1088,6 +1105,8 @@ const TESTCASES = [
profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
},
// Bug 1687679: The default value of an expiration month, when filled in an input element,
// is a two character length string. Because of this, testing a maxlength of 1 is invalid.
{
description: "Test maxlength=1 on numeric fields.",
document: `<form>
@ -1099,6 +1118,7 @@ const TESTCASES = [
expectedResult: [
Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
"cc-exp-year": 5,
"cc-exp-month": 1,
}),
],
},

View File

@ -4,40 +4,6 @@ head = head.js
support-files =
../fixtures/**
[heuristics/test_basic.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/test_cc_exp.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/test_de_fields.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/test_known_strings.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/test_multiple_section.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_BestBuy.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_CDW.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_CostCo.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_HomeDepot.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_Macys.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_NewEgg.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_OfficeDepot.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_QVC.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_Sears.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_Staples.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_Walmart.js]
skip-if = (os == "linux") && ccov # bug 1614100
[heuristics/third_party/test_Lush.js]
skip-if = (os == "linux") && ccov # bug 1614100
[test_activeStatus.js]
[test_addressDataLoader.js]
[test_addressRecords.js]

View File

@ -359,7 +359,14 @@ class FormAutofillSection {
}
element.previewValue = "";
let value = profile[fieldDetail.fieldName];
// Bug 1687679: Since profile appears to be presentation ready data, we need to utilize the "x-formatted" field
// that is generated when presentation ready data doesn't fit into the autofilling element.
// For example, autofilling expiration month into an input element will not work as expected if
// the month is less than 10, since the input is expected a zero-padded string.
// See Bug 1722941 for follow up.
let value =
profile[`${fieldDetail.fieldName}-formatted`] ||
profile[fieldDetail.fieldName];
if (ChromeUtils.getClassName(element) === "HTMLInputElement" && value) {
// For the focused input element, it will be filled with a valid value
@ -1055,6 +1062,34 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
}
}
creditCardExpMonthTransformer(profile) {
if (!profile["cc-exp-month"]) {
return;
}
let detail = this.getFieldDetailByName("cc-exp-month");
if (!detail) {
return;
}
let element = detail.elementWeakRef.get();
// If the expiration month element is an input,
// then we examine any placeholder to see if we should format the expiration month
// as a zero padded string in order to autofill correctly.
if (element.tagName === "INPUT") {
let placeholder = element.placeholder;
// Checks for 'MM' placeholder and converts the month to a two digit string.
let result = /(?<!.)mm(?!.)/i.test(placeholder);
if (result) {
profile["cc-exp-month-formatted"] = profile["cc-exp-month"]
.toString()
.padStart(2, "0");
}
}
}
async _decrypt(cipherText, reauth) {
// Get the window for the form field.
let window;
@ -1086,6 +1121,7 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
applyTransformers(profile) {
this.matchSelectOptions(profile);
this.creditCardExpDateTransformer(profile);
this.creditCardExpMonthTransformer(profile);
this.adaptFieldMaxLength(profile);
}