summaryrefslogtreecommitdiffstats
path: root/browser/extensions/formautofill/test/mochitest/creditCard
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /browser/extensions/formautofill/test/mochitest/creditCard
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/extensions/formautofill/test/mochitest/creditCard')
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini26
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html251
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html205
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html211
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html96
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html174
-rw-r--r--browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html110
7 files changed, 1073 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini b/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini
new file mode 100644
index 0000000000..d7a7ea2fd8
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/mochitest.ini
@@ -0,0 +1,26 @@
+[DEFAULT]
+prefs =
+ extensions.formautofill.creditCards.supported=on
+ extensions.formautofill.creditCards.enabled=true
+ extensions.formautofill.reauth.enabled=true
+support-files =
+ !/toolkit/components/satchel/test/satchel_common.js
+ ../../../../../../toolkit/components/satchel/test/parent_utils.js
+ !/toolkit/components/satchel/test/parent_utils.js
+ !/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+ !/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+skip-if = xorigin
+ toolkit == 'android' # bug 1730213
+
+[test_basic_creditcard_autocomplete_form.html]
+scheme=https
+[test_clear_form.html]
+scheme=https
+[test_clear_form_expiry_select_elements.html]
+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
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html
new file mode 100644
index 0000000000..0764bef0eb
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html
@@ -0,0 +1,251 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test basic autofill</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: simple form credit card autofill
+
+<script>
+"use strict";
+
+const MOCK_STORAGE = [{
+ "cc-name": "John Doe",
+ "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 reducedMockRecord = {
+ "cc-name": "John Doe",
+ "cc-number": "4929001587121045",
+};
+
+async function setupCreditCardStorage() {
+ await addCreditCard(MOCK_STORAGE[0]);
+ await addCreditCard(MOCK_STORAGE[1]);
+}
+
+async function setupFormHistory() {
+ await updateFormHistory([
+ {op: "add", fieldname: "cc-name", value: "John Smith"},
+ {op: "add", fieldname: "cc-exp-year", value: 2023},
+ ]);
+}
+
+initPopupListener();
+
+// Form with history only.
+add_task(async function history_only_menu_checking() {
+ // TODO: eliminate the timeout when we're able to indicate the right
+ // timing to start.
+ //
+ // After test process was re-spawning to https scheme. Wait 2 secs
+ // to ensure the environment is ready to do storage setup.
+ await sleep(2000);
+ await setupFormHistory();
+
+ await setInput("#cc-exp-year", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(["2023"], false);
+});
+
+// Display credit card result even if the number of fillable fields is less than the threshold.
+add_task(async function all_saved_fields_less_than_threshold() {
+ await addCreditCard(reducedMockRecord);
+
+ await setInput("#cc-name", "");
+ await expectPopup();
+ synthesizeKey("KEY_ArrowDown");
+ checkMenuEntries([reducedMockRecord].map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `Visa ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+
+ await cleanUpCreditCards();
+});
+
+// Form with both history and credit card storage.
+add_task(async function check_menu_when_both_existed() {
+ await setupCreditCardStorage();
+
+ await setInput("#cc-number", "");
+ await expectPopup();
+ synthesizeKey("KEY_ArrowDown");
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primaryAffix: cc.ccNumberFmt.affix,
+ primary: cc.ccNumberFmt.label,
+ secondary: cc["cc-name"],
+ ariaLabel: `${getCCTypeName(cc)} ${cc.ccNumberFmt.affix} ${cc.ccNumberFmt.label} ${cc["cc-name"]}`,
+ })));
+
+ await setInput("#cc-name", "");
+ await expectPopup();
+ synthesizeKey("KEY_ArrowDown");
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+
+ await setInput("#cc-exp-year", "");
+ await expectPopup();
+ synthesizeKey("KEY_ArrowDown");
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-exp-year"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+
+ await setInput("#cc-exp-month", "");
+ await expectPopup();
+ synthesizeKey("KEY_ArrowDown");
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-exp-month"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-month"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+
+ await cleanUpCreditCards();
+});
+
+// Display history search result if no matched data in credit card.
+add_task(async function check_fallback_for_mismatched_field() {
+ await addCreditCard(reducedMockRecord);
+
+ await setInput("#cc-exp-year", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(["2023"], false);
+
+ await cleanUpCreditCards();
+});
+
+// Display history search result if credit card autofill is disabled.
+add_task(async function check_search_result_for_pref_off() {
+ await setupCreditCardStorage();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["extensions.formautofill.creditCards.enabled", false]],
+ });
+
+ await setInput("#cc-name", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(["John Smith"], false);
+
+ await SpecialPowers.popPrefEnv();
+});
+
+let canTest;
+
+// Autofill the credit card from dropdown menu.
+add_task(async function check_fields_after_form_autofill() {
+ canTest = await canTestOSKeyStoreLogin();
+ if (!canTest) {
+ todo(canTest, "Cannot test OS key store login on official builds.");
+ return;
+ }
+
+ await setInput("#cc-exp-year", 202);
+
+ synthesizeKey("KEY_ArrowDown");
+ // The popup doesn't auto-show on focus because the field isn't empty
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-exp-year"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-exp-year"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+
+ synthesizeKey("KEY_ArrowDown");
+ let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
+ await new Promise(resolve => SimpleTest.executeSoon(resolve));
+ await triggerAutofillAndCheckProfile(MOCK_STORAGE[1]);
+ await osKeyStoreLoginShown;
+});
+
+// Fallback to history search after autofill values (for non-empty fields).
+add_task(async function check_fallback_after_form_autofill() {
+ if (!canTest) {
+ return;
+ }
+
+ await setInput("#cc-name", "J", true);
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(["John Smith"], false);
+});
+
+// Present credit card popup immediately when user blanks a field
+add_task(async function check_cc_popup_on_field_blank() {
+ if (!canTest) {
+ return;
+ }
+
+ await setInput("#cc-name", "", true);
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+});
+
+// Resume form autofill once all the autofilled fileds are changed.
+add_task(async function check_form_autofill_resume() {
+ if (!canTest) {
+ return;
+ }
+
+ document.querySelector("#cc-name").blur();
+ document.querySelector("#form1").reset();
+
+ await setInput("#cc-name", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+});
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+
+ <form id="form1">
+ <p>This is a basic form.</p>
+ <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ <p><label>Expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></label></p>
+ <p><label>Expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></label></p>
+ <p><label>Card Type: <select id="cc-type" autocomplete="cc-type">
+ <option value="discover">Discover</option>
+ <option value="jcb">JCB</option>
+ <option value="visa">Visa</option>
+ <option value="mastercard">MasterCard</option>
+ </select></label></p>
+ <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
+ </form>
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
new file mode 100644
index 0000000000..3d8049f053
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
@@ -0,0 +1,205 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test form autofill - clear form button</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: clear form button
+
+<script>
+"use strict";
+
+const MOCK_ADDR_STORAGE = [{
+ organization: "Sesame Street",
+ "street-address": "2 Harrison St\nline2\nline3",
+ tel: "+13453453456",
+}, {
+ organization: "Mozilla",
+ "street-address": "331 E. Evelyn Avenue",
+}, {
+ organization: "Tel org",
+ tel: "+12223334444",
+}];
+const MOCK_CC_STORAGE = [{
+ "cc-name": "John Doe",
+ "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_CC_STORAGE_EXPECTED_FILL = [{
+ "cc-name": "John Doe",
+ "cc-number": "4929001587121045",
+ "cc-exp-month": "04",
+ "cc-exp-year": 2017,
+}, {
+ "cc-name": "Timothy Berners-Lee",
+ "cc-number": "5103059495477870",
+ "cc-exp-month": "12",
+ "cc-exp-year": 2022,
+}];
+
+initPopupListener();
+
+add_task(async function setup_storage() {
+ await addAddress(MOCK_ADDR_STORAGE[0]);
+ await addAddress(MOCK_ADDR_STORAGE[1]);
+ await addAddress(MOCK_ADDR_STORAGE[2]);
+
+ await addCreditCard(MOCK_CC_STORAGE[0]);
+ await addCreditCard(MOCK_CC_STORAGE[1]);
+});
+
+
+async function checkIsFormCleared(patch = {}) {
+ const form = document.getElementById("form1");
+
+ for (const elem of form.elements) {
+ const expectedValue = patch[elem.id] || "";
+ checkFieldValue(elem, expectedValue);
+ await checkFieldHighlighted(elem, false);
+ await checkFieldPreview(elem, "");
+ }
+}
+
+async function confirmClear(selector) {
+ info("Await for clearing input");
+ let promise = new Promise(resolve => {
+ let beforeInputFired = false;
+ let element = document.querySelector(selector);
+ element.addEventListener("beforeinput", (event) => {
+ beforeInputFired = true;
+ ok(event instanceof InputEvent,
+ '"beforeinput" event should be dispatched with InputEvent interface');
+ is(event.cancelable, SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input"),
+ `"beforeinput" event should be cancelable unless it's disabled by the pref`);
+ is(event.bubbles, true,
+ '"beforeinput" event should always bubble');
+ is(event.inputType, "insertReplacementText",
+ 'inputType value of "beforeinput" should be "insertReplacementText"');
+ is(event.data, "",
+ 'data value of "beforeinput" should be empty string');
+ is(event.dataTransfer, null,
+ 'dataTransfer value of "beforeinput" should be null');
+ is(event.getTargetRanges().length, 0,
+ 'getTargetRanges() of "beforeinput" event should return empty array');
+ }, {once: true});
+ element.addEventListener("input", (event) => {
+ ok(beforeInputFired, `"beforeinput" event should've been fired before "input" on <${element.tagName} type="${element.type}">`);
+ ok(event instanceof InputEvent,
+ '"input" event should be dispatched with InputEvent interface');
+ is(event.cancelable, false,
+ '"input" event should be never cancelable');
+ is(event.bubbles, true,
+ '"input" event should always bubble');
+ is(event.inputType, "insertReplacementText",
+ 'inputType value of "input" should be "insertReplacementText"');
+ is(event.data, "",
+ 'data value of "input" should be empty string');
+ is(event.dataTransfer, null,
+ 'dataTransfer value of "input" should be null');
+ is(event.getTargetRanges().length, 0,
+ 'getTargetRanges() of "input" should return empty array');
+ resolve();
+ }, {once: true})
+ });
+ synthesizeKey("KEY_Enter");
+ await promise;
+}
+
+add_task(async function simple_clear() {
+ await triggerPopupAndHoverItem("#organization", 0);
+ await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
+
+ await triggerPopupAndHoverItem("#tel", 0);
+ await confirmClear("#tel");
+ await checkIsFormCleared();
+});
+
+add_task(async function clear_adapted_record() {
+ await triggerPopupAndHoverItem("#street-address", 0);
+ await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
+
+ await triggerPopupAndHoverItem("#street-address", 0);
+ await confirmClear("#street-address");
+ await checkIsFormCleared();
+});
+
+add_task(async function clear_modified_form() {
+ await triggerPopupAndHoverItem("#organization", 0);
+ await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
+
+ await setInput("#tel", "+1111111111", true);
+
+ await triggerPopupAndHoverItem("#street-address", 0);
+ await confirmClear("#street-address");
+ await checkIsFormCleared({tel: "+1111111111"});
+});
+
+add_task(async function clear_distinct_section() {
+ if (!(await canTestOSKeyStoreLogin())) {
+ todo(false, "Cannot test OS key store login on official builds.");
+ return;
+ }
+
+ document.getElementById("form1").reset();
+ await triggerPopupAndHoverItem("#cc-name", 0);
+ let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
+ await triggerAutofillAndCheckProfile(MOCK_CC_STORAGE_EXPECTED_FILL[0]);
+ await osKeyStoreLoginShown;
+
+ await triggerPopupAndHoverItem("#organization", 0);
+ await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
+ await triggerPopupAndHoverItem("#street-address", 0);
+ await confirmClear("#street-address");
+
+ for (const [id, val] of Object.entries(MOCK_CC_STORAGE_EXPECTED_FILL[0])) {
+ const element = document.getElementById(id);
+ if (!element) {
+ return;
+ }
+ checkFieldValue(element, val);
+ await checkFieldHighlighted(element, true);
+ }
+
+ await triggerPopupAndHoverItem("#cc-name", 0);
+ await confirmClear("#cc-name");
+ await checkIsFormCleared();
+});
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+
+ <form id="form1">
+ <p>This is a basic form.</p>
+ <p><label>organization: <input id="organization" autocomplete="organization"></label></p>
+ <p><label>streetAddress: <input id="street-address" autocomplete="street-address"></label></p>
+ <p><label>tel: <input id="tel" autocomplete="tel"></label></p>
+ <p><label>country: <input id="country" autocomplete="country"></label></p>
+
+ <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ <p><label>Expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></label></p>
+ <p><label>Expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></label></p>
+ <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
+ </form>
+
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html
new file mode 100644
index 0000000000..4fc989a36e
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form_expiry_select_elements.html
@@ -0,0 +1,211 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test form autofill - clear form button with select elements</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: clear form button with select elements.
+
+<script>
+"use strict";
+const MOCK_ADDR_STORAGE = [{
+ organization: "Sesame Street",
+ "street-address": "2 Harrison St\nline2\nline3",
+ tel: "+13453453456",
+}, {
+ organization: "Mozilla",
+ "street-address": "331 E. Evelyn Avenue",
+}, {
+ organization: "Tel org",
+ tel: "+12223334444",
+}];
+
+const MOCK_CC_STORAGE = [{
+ "cc-name": "John Doe",
+ "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,
+}];
+
+initPopupListener();
+
+add_task(async function setup_storage() {
+ await addAddress(MOCK_ADDR_STORAGE[0]);
+ await addCreditCard(MOCK_CC_STORAGE[0]);
+ await addCreditCard(MOCK_CC_STORAGE[1]);
+});
+
+
+async function checkIsFormCleared(patch = {}) {
+ const form = document.getElementById("form1");
+
+ for (const elem of form.elements) {
+ const expectedValue = patch[elem.id] || "";
+ checkFieldValue(elem, expectedValue);
+ await checkFieldHighlighted(elem, false);
+ await checkFieldPreview(elem, "");
+ }
+}
+
+async function confirmClear(selector) {
+ info("Await for clearing input");
+ let promise = new Promise(resolve => {
+ let beforeInputFired = false;
+ let element = document.querySelector(selector);
+ info(`Which element are we clearing? ${element.id}`);
+ element.addEventListener("beforeinput", (event) => {
+ beforeInputFired = true;
+ ok(event instanceof InputEvent,
+ '"beforeinput" event should be dispatched with InputEvent interface');
+ is(event.cancelable, SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input"),
+ `"beforeinput" event should be cancelable unless it's disabled by the pref`);
+ is(event.bubbles, true,
+ '"beforeinput" event should always bubble');
+ is(event.inputType, "insertReplacementText",
+ 'inputType value of "beforeinput" should be "insertReplacementText"');
+ is(event.data, "",
+ 'data value of "beforeinput" should be empty string');
+ is(event.dataTransfer, null,
+ 'dataTransfer value of "beforeinput" should be null');
+ is(event.getTargetRanges().length, 0,
+ 'getTargetRanges() of "beforeinput" event should return empty array');
+ }, {once: true});
+ element.addEventListener("input", (event) => {
+ ok(beforeInputFired, `"beforeinput" event should've been fired before "input" on <${element.tagName} type="${element.type}">`);
+ ok(event instanceof InputEvent,
+ '"input" event should be dispatched with InputEvent interface');
+ is(event.cancelable, false,
+ '"input" event should be never cancelable');
+ is(event.bubbles, true,
+ '"input" event should always bubble');
+ is(event.inputType, "insertReplacementText",
+ 'inputType value of "input" should be "insertReplacementText"');
+ is(event.data, "",
+ 'data value of "input" should be empty string');
+ is(event.dataTransfer, null,
+ 'dataTransfer value of "input" should be null');
+ is(event.getTargetRanges().length, 0,
+ 'getTargetRanges() of "input" should return empty array');
+ resolve();
+ }, {once: true})
+ });
+ synthesizeKey("KEY_Enter");
+ await promise;
+}
+
+// tgiles: We need this task due to timing issues between focusAndWaitForFieldsIdentified and popupShownListener.
+// There's a 300ms delay in focusAndWaitForFieldsIdentified that can cause triggerPopupAndHoverItem to get out of sync
+// and cause the popup to appear before the test expects a popup to appear.
+
+// Without this task we end up either getting a consistent timeout or getting the following exception:
+// 0:20.55 GECKO(31108) JavaScript error: , line 0: uncaught exception: Checking selected index - timed out after 50 tries.
+// This exception appears if you attempt to create the expectPopup promise earlier than it currently is in triggetPopupAndHoverItem
+add_task(async function a_dummy_task() {
+ await triggerPopupAndHoverItem("#organization", 0);
+ await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);
+
+ await triggerPopupAndHoverItem("#tel", 0);
+ await confirmClear("#tel");
+ await checkIsFormCleared({
+ "cc-exp-month": "MM",
+ "cc-exp-year": "YY"
+ });
+});
+
+add_task(async function clear_distinct_section() {
+ if (!(await canTestOSKeyStoreLogin())) {
+ todo(false, "Cannot test OS key store login on official builds.");
+ return;
+ }
+ let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
+ await triggerPopupAndHoverItem("#cc-name", 0);
+ await triggerAutofillAndCheckProfile(MOCK_CC_STORAGE[0]);
+ await osKeyStoreLoginShown;
+
+ for (const [id, val] of Object.entries(MOCK_CC_STORAGE[0])) {
+ const element = document.getElementById(id);
+ if (!element) {
+ return;
+ }
+ checkFieldValue(element, val);
+ await checkFieldHighlighted(element, true);
+ }
+
+ await triggerPopupAndHoverItem("#cc-name", 0);
+ await confirmClear("#cc-name");
+ await checkIsFormCleared({
+ "cc-exp-month": "MM",
+ "cc-exp-year": "YY"
+ });
+});
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+
+ <form id="form1">
+ <p>This is a basic form.</p>
+ <p><label>organization: <input id="organization" autocomplete="organization"></label></p>
+ <p><label>streetAddress: <input id="street-address" autocomplete="street-address"></label></p>
+ <p><label>tel: <input id="tel" autocomplete="tel"></label></p>
+ <p><label>country: <input id="country" autocomplete="country"></label></p>
+
+ <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ <!-- NOTE: If you're going to write a test like this,
+ ensure that the selected option doesn't match the data that you're trying to autofill,
+ otherwise your test will wait forever for an event that will never fire.
+ I.e, if your saved cc-exp-month is 01, make sure your selected option ISN'T 01.
+ -->
+ <p><label>Expiration month: <select id="cc-exp-month" autocomplete="cc-exp-month">
+ <option value="MM" selected>MM</option>
+ <option value="1">01</option>
+ <option value="2">02</option>
+ <option value="3">03</option>
+ <option value="4">04</option>
+ <option value="5">05</option>
+ <option value="6">06</option>
+ <option value="7">07</option>
+ <option value="8">08</option>
+ <option value="9">09</option>
+ <option value="10">10</option>
+ <option value="11">11</option>
+ <option value="12">12</option>
+ </select>
+ </label></p>
+ <!-- NOTE: If you're going to write a test like this,
+ ensure that the selected option doesn't match the data that you're trying to autofill,
+ otherwise your test will wait forever for an event that will never fire.
+ I.e, if your saved cc-exp-year is 2017, make sure your selected option ISN'T 2017.
+ -->
+ <p><label>Expiration year: <select id="cc-exp-year" autocomplete="cc-exp-year">
+ <option value="YY" selected>YY</option>
+ <option value="2017">2017</option>
+ <option value="2018">2018</option>
+ <option value="2019">2019</option>
+ <option value="2020">2020</option>
+ <option value="2021">2021</option>
+ <option value="2022">2022</option>
+ </select>
+ </label></p>
+ <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
+ </form>
+
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
new file mode 100644
index 0000000000..225d828ccb
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test basic autofill</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: simple form credit card autofill
+
+<script>
+"use strict";
+
+const MOCK_STORAGE = [{
+ "cc-name": "John Doe",
+ "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,
+}];
+
+async function setupCreditCardStorage() {
+ await addCreditCard(MOCK_STORAGE[0]);
+ await addCreditCard(MOCK_STORAGE[1]);
+}
+
+async function setupFormHistory() {
+ await updateFormHistory([
+ {op: "add", fieldname: "cc-name", value: "John Smith"},
+ {op: "add", fieldname: "cc-number", value: "6011029476355493"},
+ ]);
+}
+
+initPopupListener();
+
+// Show Form History popup for non-autocomplete="off" field only
+add_task(async function history_only_menu_checking() {
+ await setupFormHistory();
+
+ await setInput("#cc-number", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(["6011029476355493"], false);
+
+ await setInput("#cc-name", "");
+ synthesizeKey("KEY_ArrowDown");
+ await notExpectPopup();
+});
+
+// Show Form Autofill popup for the credit card fields.
+add_task(async function check_menu_when_both_with_autocomplete_off() {
+ await setupCreditCardStorage();
+
+ await setInput("#cc-number", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primaryAffix: cc.ccNumberFmt.affix,
+ primary: cc.ccNumberFmt.label,
+ secondary: cc["cc-name"],
+ ariaLabel: `${getCCTypeName(cc)} ${cc.ccNumberFmt.affix} ${cc.ccNumberFmt.label} ${cc["cc-name"]}`,
+ })));
+
+ await setInput("#cc-name", "");
+ synthesizeKey("KEY_ArrowDown");
+ await expectPopup();
+ checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
+ primary: cc["cc-name"],
+ secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
+ ariaLabel: `${getCCTypeName(cc)} ${cc["cc-name"]} ${cc.ccNumberFmt.affix}${cc.ccNumberFmt.label}`,
+ })));
+});
+
+</script>
+
+<p id="display"></p>
+
+<div id="content">
+ <form id="form1">
+ <p>This is a Credit Card form with autocomplete="off" cc-name field.</p>
+ <p><label>Name: <input id="cc-name" autocomplete="off"></label></p>
+ <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
+ </form>
+</div>
+
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html
new file mode 100644
index 0000000000..c39877d1b7
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_multiple_cc_number_fields.html
@@ -0,0 +1,174 @@
+<!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_EXPECTED_FILL = [{
+ "cc-name": "Test Name",
+ "cc-number": "4929001587121045",
+ "cc-exp-month": "04",
+ "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": "04",
+ "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_EXPECTED_FILL[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>
diff --git a/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html
new file mode 100644
index 0000000000..090eb9290e
--- /dev/null
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_preview_highlight_with_site_prefill.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test form autofill - preview and highlight with site prefill</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 field that has been filled by site
+
+<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": "04",
+ "cc-exp-year": "2017",
+}, {
+ "cc-name": "Timothy Berners-Lee",
+ "cc-number": "************7870",
+ "cc-exp-month": "12",
+ "cc-exp-year": "2022",
+}];
+
+const MOCK_STORAGE_EXPECTED_FILL = [{
+ "cc-name": "Test Name",
+ "cc-number": "4929001587121045",
+ "cc-exp-month": "04",
+ "cc-exp-year": 2017,
+}, {
+ "cc-name": "Timothy Berners-Lee",
+ "cc-number": "5103059495477870",
+ "cc-exp-month": "12",
+ "cc-exp-year": 2022,
+}];
+
+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 cardholderName = document.querySelector("#cc-name");
+ let sitePrefillValue = cardholderName.value;
+ let popup = expectPopup();
+ const focusedInput = await setInput("#cc-number", "");
+ await popup;
+ for (let i = 0; i < MOCK_STORAGE_PREVIEW.length; i++) {
+ synthesizeKey("KEY_ArrowDown");
+ await notifySelectedIndex(i);
+ await checkFormFieldsStyle(MOCK_STORAGE_PREVIEW[i]);
+ }
+
+ focusedInput.blur();
+ is(cardholderName.value, sitePrefillValue, "value should not have changed because previous value was a site prefill");
+});
+
+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-number", 0);
+ let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
+ // filled 1st credit card option
+ await triggerAutofillAndCheckProfile(MOCK_STORAGE_EXPECTED_FILL[0]);
+ await osKeyStoreLoginShown;
+ await checkFormFieldsStyle(MOCK_STORAGE_EXPECTED_FILL[0], false);
+});
+</script>
+<p id="display"></p>
+<div id="content">
+
+ <form id="form1">
+ <p>This is a basic credit card form.</p>
+ <p>card number: <input id="cc-number" autocomplete="cc-number"></p>
+ <p>cardholder name: <input id="cc-name" autocomplete="cc-name" value="JOHN DOE"></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>