diff options
Diffstat (limited to 'browser/extensions/formautofill/test/mochitest/creditCard')
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> |