/* * Test for form auto fill content helper collectFormFields functions. */ "use strict"; var FormAutofillHandler; add_setup(async () => { ({ FormAutofillHandler } = ChromeUtils.importESModule( "resource://gre/modules/shared/FormAutofillHandler.sys.mjs" )); }); const TESTCASES = [ { description: "Form without autocomplete property", document: `
`, sections: [ [ { fieldName: "given-name" }, { fieldName: "family-name" }, { fieldName: "address-line1" }, { fieldName: "address-level2" }, { fieldName: "country" }, { fieldName: "email" }, { fieldName: "tel" }, ], ], ids: [ "given-name", "family-name", "street-addr", "city", "country", "email", "phone", ], }, { description: "An address and credit card form with autocomplete properties and 1 token", document: `
`, sections: [ [ { fieldName: "given-name" }, { fieldName: "family-name" }, { fieldName: "street-address" }, { fieldName: "address-level2" }, { fieldName: "country" }, { fieldName: "email" }, { fieldName: "tel" }, ], [ { fieldName: "cc-number" }, { fieldName: "cc-name" }, { fieldName: "cc-exp-month" }, { fieldName: "cc-exp-year" }, ], ], }, { description: "An address form with autocomplete properties and 2 tokens", document: `
`, sections: [ [ { addressType: "shipping", fieldName: "given-name" }, { addressType: "shipping", fieldName: "family-name" }, { addressType: "shipping", fieldName: "street-address" }, { addressType: "shipping", fieldName: "address-level2" }, { addressType: "shipping", fieldName: "country" }, { addressType: "shipping", fieldName: "email" }, { addressType: "shipping", fieldName: "tel" }, ], ], }, { description: "Form with autocomplete properties and profile is partly matched", document: `
`, sections: [ [ { addressType: "shipping", fieldName: "given-name" }, { addressType: "shipping", fieldName: "family-name" }, { addressType: "shipping", fieldName: "street-address" }, { addressType: "shipping", fieldName: "address-level2" }, { addressType: "shipping", fieldName: "country" }, { addressType: "shipping", fieldName: "email" }, { addressType: "shipping", fieldName: "tel" }, ], ], }, { description: "It's a valid address and credit card form.", document: `
`, sections: [ [ { addressType: "shipping", fieldName: "given-name" }, { addressType: "shipping", fieldName: "family-name" }, { addressType: "shipping", fieldName: "street-address" }, ], [{ addressType: "shipping", fieldName: "cc-number" }], ], }, { description: "An invalid address form due to less than 3 fields.", document: `
`, sections: [], }, /* * Valid Credit Card Form with autocomplete attribute */ { description: "@autocomplete - A valid credit card form", document: `
`, sections: [ [ { fieldName: "cc-number" }, { fieldName: "cc-name" }, { fieldName: "cc-exp" }, ], ], }, { description: "@autocomplete - A valid credit card form without cc-numner", document: `
`, sections: [[{ fieldName: "cc-name" }, { fieldName: "cc-exp" }]], }, { description: "@autocomplete - A valid cc-number only form", document: `
`, sections: [[{ fieldName: "cc-number" }]], }, { description: "@autocomplete - A valid cc-name only form", document: `
`, sections: [[{ fieldName: "cc-name" }]], }, { description: "@autocomplete - A valid cc-exp only form", document: `
`, sections: [[{ fieldName: "cc-exp" }]], }, { description: "@autocomplete - A valid cc-exp-month + cc-exp-year form", document: `
`, sections: [[{ fieldName: "cc-exp-month" }, { fieldName: "cc-exp-year" }]], }, { description: "@autocomplete - A valid cc-exp-month only form", document: `
`, sections: [[{ fieldName: "cc-exp-month" }]], }, { description: "@autocomplete - A valid cc-exp-year only form", document: `
`, sections: [[{ fieldName: "cc-exp-year" }]], }, /* * Valid Credit Card Form when cc-number or cc-name is detected by fathom */ { description: "A valid credit card form without autocomplete attribute (cc-number is detected by fathom)", document: `
`, sections: [ [ { fieldName: "cc-number" }, { fieldName: "cc-name" }, { fieldName: "cc-exp" }, ], ], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.8", ], ], }, { description: "A valid credit card form without autocomplete attribute (only cc-number is detected by fathom)", document: `
`, sections: [ [ { fieldName: "cc-number" }, { fieldName: "cc-name" }, { fieldName: "cc-exp" }, ], ], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.8", ], [ "extensions.formautofill.creditCards.heuristics.fathom.types", "cc-number", ], ], }, { description: "A valid credit card form without autocomplete attribute (only cc-name is detected by fathom)", document: `
`, sections: [[{ fieldName: "cc-name" }, { fieldName: "cc-exp" }]], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.8", ], [ "extensions.formautofill.creditCards.heuristics.fathom.types", "cc-name", ], ], }, /* * Invalid Credit Card Form when a cc-number or cc-name is detected by fathom */ { description: "A credit card form is invalid when a fathom detected cc-number field is the only field in the form", document: `
`, sections: [], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold", "0.9", ], [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.8", ], ], }, { description: "A credit card form is invalid when a fathom detected cc-name field is the only field in the form", document: `
`, sections: [], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold", "0.9", ], [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.8", ], ], }, /* * Valid Credit Card Form when a cc-number or cc-name only form is detected by fathom (field is high confidence) */ { description: "A cc-number only form is considered a valid credit card form when fathom is confident and there is no other in the form", document: `
`, sections: [[{ fieldName: "cc-number" }]], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold", "0.95", ], [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.99", ], ], }, { description: "A cc-name only form is considered a valid credit card form when fathom is confident and there is no other in the form", document: `
`, sections: [[{ fieldName: "cc-name" }]], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold", "0.95", ], [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.99", ], ], }, /* * Invalid Credit Card Form when none of the fields is identified by fathom */ { description: "A credit card form is invalid when none of the fields are identified by fathom or autocomplete", document: `
`, sections: [], prefs: [ ["extensions.formautofill.creditCards.heuristics.fathom.types", ""], ], }, // Special Cases { description: "A credit card form with a high-confidence cc-name field is still considered invalid when there is another field", document: `
`, sections: [], prefs: [ [ "extensions.formautofill.creditCards.heuristics.fathom.highConfidenceThreshold", "0.95", ], [ "extensions.formautofill.creditCards.heuristics.fathom.testConfidence", "0.96", ], ], }, { description: "A valid credit card form with multiple cc-number fields", document: `
`, sections: [ [ { fieldName: "cc-number" }, { fieldName: "cc-number" }, { fieldName: "cc-number" }, { fieldName: "cc-number" }, { fieldName: "cc-exp-month" }, { 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: `
`, sections: [ [ { fieldName: "tel-area-code" }, { fieldName: "tel-local-prefix" }, { fieldName: "tel-local-suffix" }, { fieldName: "tel-extension" }, ], [ { fieldName: "tel-area-code" }, { fieldName: "tel-local-prefix" }, { fieldName: "tel-local-suffix" }, // TODO Bug 1421181 - "tel-country-code" field should belong to the next // section. There should be a way to group the related fields during the // parsing stage. { fieldName: "tel-country-code" }, ], [ { fieldName: "tel-area-code" }, { fieldName: "tel-local-prefix" }, { fieldName: "tel-local-suffix" }, ], ], ids: [ "shippingAC", "shippingPrefix", "shippingSuffix", "shippingTelExt", "billingAC", "billingPrefix", "billingSuffix", "otherCC", "otherAC", "otherPrefix", "otherSuffix", ], }, { description: "Do not dedup the same field names of the different telephone fields.", document: `
`, sections: [ [ { fieldName: "given-name" }, { fieldName: "family-name" }, { fieldName: "street-address" }, { fieldName: "email" }, { fieldName: "tel" }, { fieldName: "tel" }, { fieldName: "tel" }, ], ], ids: ["i1", "i2", "i3", "i4", "homePhone", "mobilePhone", "officePhone"], }, { description: "The duplicated phones of a single one and a set with ac, prefix, suffix.", document: `
`, sections: [ [ { addressType: "shipping", fieldName: "given-name" }, { addressType: "shipping", fieldName: "family-name" }, { addressType: "shipping", fieldName: "street-address" }, { addressType: "shipping", fieldName: "email" }, // NOTES: Ideally, there is only one full telephone field(s) in a form for // this case. We can see if there is any better solution later. { addressType: "shipping", fieldName: "tel" }, { addressType: "shipping", fieldName: "tel-area-code" }, { addressType: "shipping", fieldName: "tel-local-prefix" }, { addressType: "shipping", fieldName: "tel-local-suffix" }, ], ], ids: [ "i1", "i2", "i3", "i4", "singlePhone", "shippingAreaCode", "shippingPrefix", "shippingSuffix", ], }, { description: "Always adopt the info from autocomplete attribute.", document: `
`, sections: [ [ { addressType: "shipping", fieldName: "given-name" }, { addressType: "shipping", fieldName: "family-name" }, { addressType: "shipping", fieldName: "tel" }, { addressType: "shipping", fieldName: "tel" }, { addressType: "shipping", fieldName: "tel" }, ], ], ids: [ "given-name", "family-name", "dummyAreaCode", "dummyPrefix", "dummySuffix", ], }, ]; function verifyDetails(handlerDetails, testCaseDetails) { if (handlerDetails === null) { Assert.equal(handlerDetails, testCaseDetails); return; } Assert.equal(handlerDetails.length, testCaseDetails.length, "field count"); handlerDetails.forEach((detail, index) => { Assert.equal( detail.fieldName, testCaseDetails[index].fieldName, "fieldName" ); Assert.equal( detail.section, testCaseDetails[index].section ?? "", "section" ); Assert.equal( detail.addressType, testCaseDetails[index].addressType ?? "", "addressType" ); Assert.equal( detail.contactType, testCaseDetails[index].contactType ?? "", "contactType" ); Assert.equal( detail.elementWeakRef.get(), testCaseDetails[index].elementWeakRef.get(), "DOM reference" ); }); } for (let tc of TESTCASES) { (function () { let testcase = tc; add_task(async function () { info("Starting testcase: " + testcase.description); if (testcase.prefs) { testcase.prefs.forEach(pref => SetPref(pref[0], pref[1])); } let doc = MockDocument.createTestDocument( "http://localhost:8080/test/", testcase.document ); testcase.sections.flat().forEach((field, idx) => { let elementRef = doc.getElementById( testcase.ids?.[idx] ?? field.fieldName ); field.elementWeakRef = Cu.getWeakReference(elementRef); }); let form = doc.querySelector("form"); let formLike = FormLikeFactory.createFromForm(form); let handler = new FormAutofillHandler(formLike); let validFieldDetails = handler.collectFormFields(); Assert.equal( handler.sections.length, testcase.sections.length, "section count" ); for (let i = 0; i < handler.sections.length; i++) { let section = handler.sections[i]; verifyDetails(section.fieldDetails, testcase.sections[i]); } verifyDetails(validFieldDetails, testcase.sections.flat()); if (testcase.prefs) { testcase.prefs.forEach(pref => Services.prefs.clearUserPref(pref[0])); } }); })(); }