зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1688607 - Enable autofill and capturing when there are multiple credit card number fields. r=sgalich
Enable the feature only in Nightly. Add functionality for previewing and autofilling into multiple cc-number fields. Fix test_Lush.js case. Add logic to handle credit card saving/update when multiple cc-number fields are present. Add heuristic test for Lufthansa site. Add test for filling cc-number when there are multiple cc-number fields in a form. Add test for collecting multiple cc-number fields via collectFormFields. Add multiple cc-number fields case to onFormSubmitted test. Add multiple cc-number fields with maxlength=4 case to markAsAutofillField. Add implementation of the preview and fill test cases for multiple cc-number fields. Differential Revision: https://phabricator.services.mozilla.com/D127987
This commit is contained in:
Родитель
709bdad4ad
Коммит
6fe1099c7b
23
browser/extensions/formautofill/test/fixtures/third_party/Lufthansa/Checkout_Payment.html
поставляемый
Normal file
23
browser/extensions/formautofill/test/fixtures/third_party/Lufthansa/Checkout_Payment.html
поставляемый
Normal file
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -18,5 +18,7 @@ scheme=https
|
|||
scheme=https
|
||||
[test_creditcard_autocomplete_off.html]
|
||||
scheme=https
|
||||
[test_preview_highlight_with_multiple_cc_number_fields.html]
|
||||
scheme=https
|
||||
[test_preview_highlight_with_site_prefill.html]
|
||||
scheme=https
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test form autofill - preview and highlight with multiple cc number fields</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="../formautofill_common.js"></script>
|
||||
<script type="text/javascript" src="../../../../../../toolkit/components/satchel/test/satchel_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Form autofill test: preview and highlight multiple cc number fields
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const MOCK_STORAGE = [{
|
||||
"cc-name": "Test Name",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}, {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
}];
|
||||
|
||||
const MOCK_STORAGE_PREVIEW = [{
|
||||
"cc-name": "Test Name",
|
||||
"cc-number": "************1045",
|
||||
"cc-exp-month": "4",
|
||||
"cc-exp-year": "2017",
|
||||
}, {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "************7870",
|
||||
"cc-exp-month": "12",
|
||||
"cc-exp-year": "2022",
|
||||
}];
|
||||
|
||||
|
||||
/*
|
||||
This function is similar to checkFormFieldsStyle in formautofill_common.js, but deals with the case
|
||||
when one value is spread across multiple fields.
|
||||
|
||||
This function is needed because of the multiple cc-number filling behavior introduced in Bug 1688607.
|
||||
Since the cc-number is stored as a whole value in the profile,
|
||||
there has to be specific handling in the test to assert the correct fillable value.
|
||||
Otherwise, we would try to grab a value out of profile["cc-number1"] which doesn't exist.
|
||||
*/
|
||||
async function checkMultipleCCNumberFormStyle(profile, isPreviewing = true) {
|
||||
const elements = document.querySelectorAll("input, select");
|
||||
for (const element of elements) {
|
||||
let fillableValue;
|
||||
if (element.id.includes("cc-number") && isPreviewing) {
|
||||
fillableValue = profile["cc-number"].slice(-8);
|
||||
} else if (element.id.includes("cc-number")) {
|
||||
fillableValue = profile["cc-number"];
|
||||
} else {
|
||||
fillableValue = profile[element.id];
|
||||
}
|
||||
let previewValue = (isPreviewing && fillableValue) || "";
|
||||
await checkFieldHighlighted(element, !!fillableValue);
|
||||
await checkFieldPreview(element, previewValue);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This function sets up 'change' event listeners so that we can safely
|
||||
assert an element's value after autofilling has occurred.
|
||||
This is essentially a barebones copy of triggerAutofillAndCheckProfile
|
||||
that exists in formautofill_common.js.
|
||||
|
||||
We can't use triggerAutofillAndCheckProfile because "cc-number1" through "cc-number4"
|
||||
do not exist in the profile.
|
||||
Again, we store the whole cc-number in the profile, not its subsections.
|
||||
So if we tried to grab the element by ID using "cc-number", this element would not exist in the doc,
|
||||
causing triggerAutofillAndCheckProfile to throw an exception.
|
||||
*/
|
||||
async function setupListeners(elements, profile) {
|
||||
for (const element of elements) {
|
||||
let id = element.id;
|
||||
element.addEventListener("change", () => {
|
||||
let filledValue;
|
||||
if (id == "cc-number1") {
|
||||
filledValue = profile["cc-number"].slice(0, 4);
|
||||
} else if (id == "cc-number2") {
|
||||
filledValue = profile["cc-number"].slice(4, 8);
|
||||
} else if (id == "cc-number3") {
|
||||
filledValue = profile["cc-number"].slice(8, 12);
|
||||
} else if (id == "cc-number4") {
|
||||
filledValue = profile["cc-number"].slice(12, 16);
|
||||
} else {
|
||||
filledValue = profile[element.id];
|
||||
}
|
||||
checkFieldValue(element, filledValue);
|
||||
}, {once: true})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
initPopupListener();
|
||||
|
||||
add_task(async function setup_storage() {
|
||||
await addCreditCard(MOCK_STORAGE[0]);
|
||||
await addCreditCard(MOCK_STORAGE[1]);
|
||||
});
|
||||
|
||||
add_task(async function check_preview() {
|
||||
let canTest = await canTestOSKeyStoreLogin();
|
||||
if (!canTest) {
|
||||
todo(canTest, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
let popup = expectPopup();
|
||||
const focusedInput = await setInput("#cc-name", "");
|
||||
await popup;
|
||||
for (let i = 0; i < MOCK_STORAGE_PREVIEW.length; i++) {
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await notifySelectedIndex(i);
|
||||
await checkMultipleCCNumberFormStyle(MOCK_STORAGE_PREVIEW[i]);
|
||||
}
|
||||
|
||||
focusedInput.blur();
|
||||
});
|
||||
|
||||
add_task(async function check_filled_highlight() {
|
||||
let canTest = await canTestOSKeyStoreLogin();
|
||||
if (!canTest) {
|
||||
todo(canTest, "Cannot test OS key store login on official builds.");
|
||||
return;
|
||||
}
|
||||
await triggerPopupAndHoverItem("#cc-name", 0);
|
||||
let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
|
||||
// filled 1st credit card option
|
||||
synthesizeKey("KEY_Enter");
|
||||
await osKeyStoreLoginShown;
|
||||
let elements = document.querySelectorAll("input, select");
|
||||
let profile = MOCK_STORAGE[0];
|
||||
await setupListeners(elements, profile);
|
||||
await checkMultipleCCNumberFormStyle(profile, false);
|
||||
});
|
||||
</script>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
<form id="form1">
|
||||
<p>This is a basic credit card form.</p>
|
||||
<p>card number subsection 1: <input id="cc-number1" maxlength="4"></p>
|
||||
<p>card number subsection 2: <input id="cc-number2" maxlength="4"></p>
|
||||
<p>card number subsection 3: <input id="cc-number3" maxlength="4"></p>
|
||||
<p>card number subsection 4: <input id="cc-number4" maxlength="4"></p>
|
||||
<p>cardholder name: <input id="cc-name" autocomplete="cc-name"></p>
|
||||
<p>expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></p>
|
||||
<p>expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></p>
|
||||
</form>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
64
browser/extensions/formautofill/test/unit/heuristics/third_party/test_Lufthansa.js
поставляемый
Normal file
64
browser/extensions/formautofill/test/unit/heuristics/third_party/test_Lufthansa.js
поставляемый
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* global runHeuristicsTest */
|
||||
|
||||
"use strict";
|
||||
|
||||
runHeuristicsTest(
|
||||
[
|
||||
{
|
||||
fixturePath: "Checkout_Payment.html",
|
||||
expectedResult: [
|
||||
[
|
||||
[
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-type",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
transform: "fullCCNumber => fullCCNumber.slice(0, 4)",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
transform: "fullCCNumber => fullCCNumber.slice(4, 8)",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
transform: "fullCCNumber => fullCCNumber.slice(8, 12)",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
transform: "fullCCNumber => fullCCNumber.slice(12, 16)",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-month",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-year",
|
||||
},
|
||||
],
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
"../../../fixtures/third_party/Lufthansa/"
|
||||
);
|
|
@ -14,6 +14,7 @@ skip-if = (os == "linux") && ccov # bug 1614100
|
|||
skip-if = (os == "linux") && ccov # bug 1614100
|
||||
[test_HomeDepot.js]
|
||||
skip-if = (os == "linux") && ccov # bug 1614100
|
||||
[test_Lufthansa.js]
|
||||
[test_Macys.js]
|
||||
skip-if = (os == "linux") && ccov # bug 1614100
|
||||
[test_NewEgg.js]
|
||||
|
|
|
@ -386,6 +386,34 @@ const TESTCASES = [
|
|||
"cc-exp-year": "25",
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Fill credit card number fields in a form with multiple cc-number inputs",
|
||||
document: `<form>
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-number4" maxlength="4">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
</form>`,
|
||||
focusedInputId: "cc-number1",
|
||||
profileData: {
|
||||
guid: "123",
|
||||
"cc-number": "371449635398431",
|
||||
"cc-exp-month": 6,
|
||||
"cc-exp-year": 25,
|
||||
},
|
||||
expectedResult: {
|
||||
guid: "123",
|
||||
"cc-number1": "3714",
|
||||
"cc-number2": "4963",
|
||||
"cc-number3": "5398",
|
||||
"cc-number4": "431",
|
||||
"cc-exp-month": "6",
|
||||
"cc-exp-year": "25",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const TESTCASES_INPUT_UNCHANGED = [
|
||||
|
|
|
@ -608,6 +608,83 @@ const TESTCASES = [
|
|||
],
|
||||
ids: ["cc-number", "cc-exp-month", "cc-exp-year"],
|
||||
},
|
||||
{
|
||||
description: "A valid credit card form with multiple cc-number fields",
|
||||
document: `<form>
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-number4" maxlength="4">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
</form>`,
|
||||
sections: [
|
||||
[
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-number",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-month",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-year",
|
||||
},
|
||||
],
|
||||
],
|
||||
validFieldDetails: [
|
||||
{ section: "", addressType: "", contactType: "", fieldName: "cc-number" },
|
||||
{ section: "", addressType: "", contactType: "", fieldName: "cc-number" },
|
||||
{ section: "", addressType: "", contactType: "", fieldName: "cc-number" },
|
||||
{ section: "", addressType: "", contactType: "", fieldName: "cc-number" },
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-month",
|
||||
},
|
||||
{
|
||||
section: "",
|
||||
addressType: "",
|
||||
contactType: "",
|
||||
fieldName: "cc-exp-year",
|
||||
},
|
||||
],
|
||||
ids: [
|
||||
"cc-number1",
|
||||
"cc-number2",
|
||||
"cc-number3",
|
||||
"cc-number4",
|
||||
"cc-exp-month",
|
||||
"cc-exp-year",
|
||||
],
|
||||
},
|
||||
{
|
||||
description: "Three sets of adjacent phone number fields",
|
||||
document: `<form>
|
||||
|
|
|
@ -61,6 +61,29 @@ const TESTCASES = [
|
|||
targetElementId: "cc-number",
|
||||
expectedResult: ["cc-number", "cc-name", "cc-exp-month", "cc-exp-year"],
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Form containing multiple cc-number fields without autocomplete attributes.",
|
||||
document: `<form>
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-number4" maxlength="4">
|
||||
<input id="cc-name">
|
||||
<input id="cc-exp-month">
|
||||
<input id="cc-exp-year">
|
||||
</form>`,
|
||||
targetElementId: "cc-number1",
|
||||
expectedResult: [
|
||||
"cc-number1",
|
||||
"cc-number2",
|
||||
"cc-number3",
|
||||
"cc-number4",
|
||||
"cc-name",
|
||||
"cc-exp-month",
|
||||
"cc-exp-year",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let markedFieldId = [];
|
||||
|
|
|
@ -7,9 +7,7 @@ add_task(async function setup() {
|
|||
));
|
||||
});
|
||||
|
||||
const MOCK_DOC = MockDocument.createTestDocument(
|
||||
"http://localhost:8080/test/",
|
||||
`<form id="form1">
|
||||
const DEFAULT_TEST_DOC = `<form id="form1">
|
||||
<input id="street-addr" autocomplete="street-address">
|
||||
<select id="address-level1" autocomplete="address-level1">
|
||||
<option value=""></option>
|
||||
|
@ -39,14 +37,15 @@ const MOCK_DOC = MockDocument.createTestDocument(
|
|||
<option value="amex">American Express</option>
|
||||
</select>
|
||||
<input id="submit" type="submit">
|
||||
</form>`
|
||||
);
|
||||
</form>`;
|
||||
const TARGET_ELEMENT_ID = "street-addr";
|
||||
|
||||
const TESTCASES = [
|
||||
{
|
||||
description:
|
||||
"Should not trigger address saving if the number of fields is less than 3",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
tel: "1-650-903-0800",
|
||||
|
@ -57,6 +56,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Should not trigger credit card saving if number is empty",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-exp-month": 12,
|
||||
|
@ -66,8 +67,69 @@ const TESTCASES = [
|
|||
formSubmission: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Should not trigger credit card saving if there are more than four cc-number fields",
|
||||
document: `<form id="form1">
|
||||
<input id="cc-type" autocomplete="cc-type">
|
||||
<input id="cc-name" autocomplete="cc-name">
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-number4" maxlength="4">
|
||||
<input id="cc-number5" maxlength="4">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
<input id="submit" type="submit">
|
||||
</form>
|
||||
`,
|
||||
targetElementId: "cc-name",
|
||||
formValue: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number1": "3714",
|
||||
"cc-number2": "4963",
|
||||
"cc-number3": "5398",
|
||||
"cc-number4": "431",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2000,
|
||||
"cc-type": "amex",
|
||||
},
|
||||
expectedResult: {
|
||||
formSubmission: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
"Should not trigger credit card saving if there is more than one cc-number field but less than four fields",
|
||||
document: `<form id="form1">
|
||||
<input id="cc-type" autocomplete="cc-type">
|
||||
<input id="cc-name" autocomplete="cc-name">
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
<input id="submit" type="submit">
|
||||
</form>
|
||||
`,
|
||||
targetElementId: "cc-name",
|
||||
formValue: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number1": "3714",
|
||||
"cc-number2": "4963",
|
||||
"cc-number3": "5398",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2000,
|
||||
"cc-type": "amex",
|
||||
},
|
||||
expectedResult: {
|
||||
formSubmission: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Trigger address saving",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
country: "US",
|
||||
|
@ -96,6 +158,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Trigger credit card saving",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: "cc-type",
|
||||
formValue: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "5105105105105100",
|
||||
|
@ -123,8 +187,54 @@ const TESTCASES = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Trigger credit card saving using multiple cc-number fields",
|
||||
document: `<form id="form1">
|
||||
<input id="cc-type" autocomplete="cc-type">
|
||||
<input id="cc-name" autocomplete="cc-name">
|
||||
<input id="cc-number1" maxlength="4">
|
||||
<input id="cc-number2" maxlength="4">
|
||||
<input id="cc-number3" maxlength="4">
|
||||
<input id="cc-number4" maxlength="4">
|
||||
<input id="cc-exp-month" autocomplete="cc-exp-month">
|
||||
<input id="cc-exp-year" autocomplete="cc-exp-year">
|
||||
<input id="submit" type="submit">
|
||||
</form>`,
|
||||
targetElementId: "cc-type",
|
||||
formValue: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number1": "3714",
|
||||
"cc-number2": "4963",
|
||||
"cc-number3": "5398",
|
||||
"cc-number4": "431",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2000,
|
||||
"cc-type": "amex",
|
||||
},
|
||||
expectedResult: {
|
||||
formSubmission: true,
|
||||
records: {
|
||||
address: [],
|
||||
creditCard: [
|
||||
{
|
||||
guid: null,
|
||||
record: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "371449635398431",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2000,
|
||||
"cc-type": "amex",
|
||||
},
|
||||
untouchedFields: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Trigger address and credit card saving",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
country: "US",
|
||||
|
@ -170,6 +280,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Profile saved with trimmed string",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue ",
|
||||
country: "US",
|
||||
|
@ -198,6 +310,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Eliminate the field that is empty after trimmed",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
country: "US",
|
||||
|
@ -227,6 +341,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Save state with regular select option",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "CA",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -255,6 +371,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Save state with lowercase value",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "ca",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -283,6 +401,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Save state with a country code prefixed to the label",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "AR",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -311,6 +431,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Save state with a country code prefixed to the value",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "US-CA",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -340,6 +462,8 @@ const TESTCASES = [
|
|||
{
|
||||
description:
|
||||
"Save state with a country code prefixed to the value and label",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "US-AZ",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -369,6 +493,8 @@ const TESTCASES = [
|
|||
{
|
||||
description:
|
||||
"Should save select label instead when failed to abbreviate the value",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "Ariz",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -397,6 +523,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Shouldn't save select with multiple selections",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": ["AL", "AK", "AP"],
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -426,6 +554,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Shouldn't save select with empty value",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"address-level1": "",
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
|
@ -455,6 +585,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Shouldn't save tel whose length is too short",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
"address-level1": "CA",
|
||||
|
@ -484,6 +616,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Shouldn't save tel whose length is too long",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
"address-level1": "CA",
|
||||
|
@ -513,6 +647,8 @@ const TESTCASES = [
|
|||
},
|
||||
{
|
||||
description: "Shouldn't save tel which contains invalid characters",
|
||||
document: DEFAULT_TEST_DOC,
|
||||
targetElementId: TARGET_ELEMENT_ID,
|
||||
formValue: {
|
||||
"street-addr": "331 E. Evelyn Avenue",
|
||||
"address-level1": "CA",
|
||||
|
@ -544,7 +680,11 @@ const TESTCASES = [
|
|||
|
||||
add_task(async function handle_invalid_form() {
|
||||
info("Starting testcase: Test an invalid form element");
|
||||
let fakeForm = MOCK_DOC.createElement("form");
|
||||
let doc = MockDocument.createTestDocument(
|
||||
"http://localhost:8080/test",
|
||||
DEFAULT_TEST_DOC
|
||||
);
|
||||
let fakeForm = doc.createElement("form");
|
||||
sinon.spy(FormAutofillContent, "_onFormSubmit");
|
||||
|
||||
FormAutofillContent.formSubmitted(fakeForm, null);
|
||||
|
@ -553,7 +693,11 @@ add_task(async function handle_invalid_form() {
|
|||
});
|
||||
|
||||
add_task(async function autofill_disabled() {
|
||||
let form = MOCK_DOC.getElementById("form1");
|
||||
let doc = MockDocument.createTestDocument(
|
||||
"http://localhost:8080/test",
|
||||
DEFAULT_TEST_DOC
|
||||
);
|
||||
let form = doc.getElementById("form1");
|
||||
form.reset();
|
||||
|
||||
let testcase = {
|
||||
|
@ -563,11 +707,11 @@ add_task(async function autofill_disabled() {
|
|||
"cc-number": "1111222233334444",
|
||||
};
|
||||
for (let key in testcase) {
|
||||
let input = MOCK_DOC.getElementById(key);
|
||||
let input = doc.getElementById(key);
|
||||
input.value = testcase[key];
|
||||
}
|
||||
|
||||
let element = MOCK_DOC.getElementById(TARGET_ELEMENT_ID);
|
||||
let element = doc.getElementById(TARGET_ELEMENT_ID);
|
||||
FormAutofillContent.identifyAutofillFields(element);
|
||||
|
||||
sinon.stub(FormAutofillContent, "_onFormSubmit");
|
||||
|
@ -642,13 +786,15 @@ TESTCASES.forEach(testcase => {
|
|||
"extensions.formautofill.creditCards.enabled",
|
||||
true
|
||||
);
|
||||
|
||||
let form = MOCK_DOC.getElementById("form1");
|
||||
let doc = MockDocument.createTestDocument(
|
||||
"http://localhost:8080/test/",
|
||||
testcase.document
|
||||
);
|
||||
let form = doc.getElementById("form1");
|
||||
form.reset();
|
||||
for (let key in testcase.formValue) {
|
||||
let input = MOCK_DOC.getElementById(key);
|
||||
let input = doc.getElementById(key);
|
||||
let value = testcase.formValue[key];
|
||||
|
||||
if (ChromeUtils.getClassName(input) === "HTMLSelectElement" && value) {
|
||||
input.multiple = Array.isArray(value);
|
||||
[...input.options].forEach(option => {
|
||||
|
@ -660,7 +806,7 @@ TESTCASES.forEach(testcase => {
|
|||
}
|
||||
sinon.stub(FormAutofillContent, "_onFormSubmit");
|
||||
|
||||
let element = MOCK_DOC.getElementById(TARGET_ELEMENT_ID);
|
||||
let element = doc.getElementById(testcase.targetElementId);
|
||||
FormAutofillContent.identifyAutofillFields(element);
|
||||
FormAutofillContent.formSubmitted(form, null);
|
||||
|
||||
|
|
|
@ -799,7 +799,6 @@ var FormAutofillContent = {
|
|||
this.debug("No control is removed or inserted since last collection.");
|
||||
return;
|
||||
}
|
||||
|
||||
let validDetails = formHandler.collectFormFields();
|
||||
|
||||
this._formsDetails.set(formHandler.form.rootElement, formHandler);
|
||||
|
|
|
@ -284,6 +284,13 @@ class FormAutofillSection {
|
|||
).slice(-2);
|
||||
const year2Digits = profile["cc-exp-year"].toString().slice(-2);
|
||||
profile[key] = `${month2Digits}/${year2Digits}`;
|
||||
} else if (key == "cc-number") {
|
||||
// We want to show the last four digits of credit card so that
|
||||
// the masked credit card previews correctly and appears correctly
|
||||
// in the autocomplete menu
|
||||
profile[key] = profile[key].substr(
|
||||
profile[key].length - maxLength
|
||||
);
|
||||
} else {
|
||||
profile[key] = profile[key].substr(0, maxLength);
|
||||
}
|
||||
|
@ -364,6 +371,10 @@ class FormAutofillSection {
|
|||
profile[`${fieldDetail.fieldName}-formatted`] ||
|
||||
profile[fieldDetail.fieldName];
|
||||
|
||||
// Bug 1688607: The transform function allows us to handle the multiple credit card number fields case
|
||||
if (fieldDetail.transform) {
|
||||
value = fieldDetail.transform(value);
|
||||
}
|
||||
if (ChromeUtils.getClassName(element) === "HTMLInputElement" && value) {
|
||||
// For the focused input element, it will be filled with a valid value
|
||||
// anyway.
|
||||
|
@ -561,6 +572,35 @@ class FormAutofillSection {
|
|||
return !!this.filledRecordGUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Condenses multiple credit card number fields into one fieldDetail
|
||||
* in order to submit the credit card record correctly.
|
||||
*
|
||||
* @param {Array.<object>} condensedDetails
|
||||
* An array of fieldDetails
|
||||
* @memberof FormAutofillSection
|
||||
*/
|
||||
_condenseMultipleCCNumberFields(condensedDetails) {
|
||||
let countOfCCNumbers = 0;
|
||||
// We ignore the cases where there are more than or less than four credit card number
|
||||
// fields in a form as this is not a valid case for filling the credit card number.
|
||||
for (let i = condensedDetails.length - 1; i >= 0; i--) {
|
||||
if (condensedDetails[i].fieldName == "cc-number") {
|
||||
countOfCCNumbers++;
|
||||
if (countOfCCNumbers == 4) {
|
||||
countOfCCNumbers = 0;
|
||||
condensedDetails[i].fieldValue =
|
||||
condensedDetails[i].elementWeakRef.get()?.value +
|
||||
condensedDetails[i + 1].elementWeakRef.get()?.value +
|
||||
condensedDetails[i + 2].elementWeakRef.get()?.value +
|
||||
condensedDetails[i + 3].elementWeakRef.get()?.value;
|
||||
condensedDetails.splice(i + 1, 3);
|
||||
}
|
||||
} else {
|
||||
countOfCCNumbers = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Return the record that is converted from `fieldDetails` and only valid
|
||||
* form record is included.
|
||||
|
@ -586,11 +626,16 @@ class FormAutofillSection {
|
|||
if (this.flowId) {
|
||||
data.flowId = this.flowId;
|
||||
}
|
||||
let condensedDetails = this.fieldDetails;
|
||||
// This NIGHTLY_BUILD statement should be removed as part of Bug 1735562 once the feature is stable
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
this._condenseMultipleCCNumberFields(condensedDetails);
|
||||
}
|
||||
|
||||
details.forEach(detail => {
|
||||
condensedDetails.forEach(detail => {
|
||||
let element = detail.elementWeakRef.get();
|
||||
// Remove the unnecessary spaces
|
||||
let value = element && element.value.trim();
|
||||
let value = detail.fieldValue ?? (element && element.value.trim());
|
||||
value = this.computeFillingValue(value, detail, element);
|
||||
|
||||
if (!value || value.length > FormAutofillUtils.MAX_FIELD_VALUE_LENGTH) {
|
||||
|
@ -1179,7 +1224,7 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Customize for previewing prorifle.
|
||||
* Customize for previewing profile
|
||||
*
|
||||
* @param {Object} profile
|
||||
* A profile for pre-processing before previewing values.
|
||||
|
@ -1190,11 +1235,15 @@ class FormAutofillCreditCardSection extends FormAutofillSection {
|
|||
// disabled.
|
||||
if (profile["cc-number-decrypted"]) {
|
||||
profile["cc-number"] = profile["cc-number-decrypted"];
|
||||
} else if (!profile["cc-number"].startsWith("****")) {
|
||||
// Show the previewed credit card as "**** 4444" which is
|
||||
// needed when a credit card number field has a maxlength of four.
|
||||
profile["cc-number"] = "****" + profile["cc-number"];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize for filling prorifle.
|
||||
* Customize for filling profile
|
||||
*
|
||||
* @param {Object} profile
|
||||
* A profile for pre-processing before filling values.
|
||||
|
|
|
@ -17,6 +17,10 @@ const { XPCOMUtils } = ChromeUtils.import(
|
|||
const { FormAutofill } = ChromeUtils.import(
|
||||
"resource://autofill/FormAutofill.jsm"
|
||||
);
|
||||
// This AppConstants import can be removed as part of Bug 1735562
|
||||
const { AppConstants } = ChromeUtils.import(
|
||||
"resource://gre/modules/AppConstants.jsm"
|
||||
);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"FormAutofillUtils",
|
||||
|
@ -143,17 +147,69 @@ class FieldScanner {
|
|||
});
|
||||
}
|
||||
|
||||
_classifyMultipleCCNumberFields() {
|
||||
if (this._sections.length != 4) {
|
||||
return;
|
||||
}
|
||||
let firstDetails = this._sections[0].fieldDetails;
|
||||
// Ensure that there is only one cc-number field and it is last in this subsection
|
||||
if (
|
||||
firstDetails.findIndex(detail => detail.fieldName == "cc-number") !=
|
||||
firstDetails.length - 1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let secondDetails = this._sections[1].fieldDetails;
|
||||
// Ensure that the second cc-number field is only element of this subsection
|
||||
if (
|
||||
!(secondDetails.length == 1 && secondDetails[0].fieldName == "cc-number")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let thirdDetails = this._sections[2].fieldDetails;
|
||||
// Ensure that the third cc-number field is only element of this subsection
|
||||
if (
|
||||
!(thirdDetails.length == 1 && thirdDetails[0].fieldName == "cc-number")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let fourthDetails = this._sections[3].fieldDetails;
|
||||
// Ensure that there is only one cc-number field and it is first element of 4th subsection
|
||||
let foundCCNumber = false;
|
||||
for (let [index, detail] of fourthDetails.entries()) {
|
||||
if (detail.fieldName == "cc-number") {
|
||||
if (index == 0) {
|
||||
foundCCNumber = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundCCNumber) {
|
||||
return;
|
||||
}
|
||||
// Collect the other subsections into the first subsection
|
||||
// and then remove the other subsections from the list
|
||||
this._sections[0].fieldDetails = firstDetails.concat(
|
||||
secondDetails,
|
||||
thirdDetails,
|
||||
fourthDetails
|
||||
);
|
||||
this._sections.splice(1);
|
||||
}
|
||||
_classifySections() {
|
||||
let fieldDetails = this._sections[0].fieldDetails;
|
||||
this._sections = [];
|
||||
let seenTypes = new Set();
|
||||
let previousType;
|
||||
let sectionCount = 0;
|
||||
|
||||
for (let fieldDetail of fieldDetails) {
|
||||
if (!fieldDetail.fieldName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
seenTypes.has(fieldDetail.fieldName) &&
|
||||
(previousType != fieldDetail.fieldName ||
|
||||
|
@ -169,6 +225,10 @@ class FieldScanner {
|
|||
fieldDetail
|
||||
);
|
||||
}
|
||||
// This NIGHTLY_BUILD statement should be removed as part of Bug 1735562 once the feature is stable
|
||||
if (AppConstants.NIGHTLY_BUILD) {
|
||||
this._classifyMultipleCCNumberFields();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,10 +326,36 @@ class FieldScanner {
|
|||
return (
|
||||
field1.section == field2.section &&
|
||||
field1.addressType == field2.addressType &&
|
||||
field1.fieldName == field2.fieldName
|
||||
field1.fieldName == field2.fieldName &&
|
||||
!field1.transform &&
|
||||
!field2.transform
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a site has four credit card number fields and
|
||||
* these fields have a max length of four
|
||||
* then we transform the credit card number into
|
||||
* four subsections in order to fill correctly.
|
||||
*
|
||||
* @param {Array<Object>} creditCardFieldDetails
|
||||
* The credit card field details to be transformed for multiple cc-number fields filling
|
||||
* @memberof FieldScanner
|
||||
*/
|
||||
_transformCCNumberForMultipleFields(creditCardFieldDetails) {
|
||||
let ccNumberFields = creditCardFieldDetails.filter(
|
||||
field =>
|
||||
field.fieldName == "cc-number" &&
|
||||
field.elementWeakRef.get().maxLength == 4
|
||||
);
|
||||
if (ccNumberFields.length == 4) {
|
||||
ccNumberFields[0].transform = fullCCNumber => fullCCNumber.slice(0, 4);
|
||||
ccNumberFields[1].transform = fullCCNumber => fullCCNumber.slice(4, 8);
|
||||
ccNumberFields[2].transform = fullCCNumber => fullCCNumber.slice(8, 12);
|
||||
ccNumberFields[3].transform = fullCCNumber => fullCCNumber.slice(12, 16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the final field details without invalid field name, and the
|
||||
* duplicated fields will be removed as well. For the debugging purpose,
|
||||
|
@ -301,7 +387,7 @@ class FieldScanner {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
this._transformCCNumberForMultipleFields(creditCardFieldDetails);
|
||||
return [
|
||||
{
|
||||
type: FormAutofillUtils.SECTION_TYPES.ADDRESS,
|
||||
|
|
Загрузка…
Ссылка в новой задаче