diff options
Diffstat (limited to 'browser/extensions/formautofill/test/browser/browser_editAddressDialog.js')
-rw-r--r-- | browser/extensions/formautofill/test/browser/browser_editAddressDialog.js | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/browser/extensions/formautofill/test/browser/browser_editAddressDialog.js b/browser/extensions/formautofill/test/browser/browser_editAddressDialog.js new file mode 100644 index 0000000000..0088f916bd --- /dev/null +++ b/browser/extensions/formautofill/test/browser/browser_editAddressDialog.js @@ -0,0 +1,951 @@ +"use strict"; + +const { FormAutofillUtils } = ChromeUtils.importESModule( + "resource://gre/modules/shared/FormAutofillUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + Region: "resource://gre/modules/Region.sys.mjs", +}); + +requestLongerTimeout(6); + +add_task(async function setup_supportedCountries() { + await SpecialPowers.pushPrefEnv({ + set: [[SUPPORTED_COUNTRIES_PREF, "US,CA,DE"]], + }); +}); + +add_task(async function test_cancelEditAddressDialog() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + win.document.querySelector("#cancel").click(); + }); +}); + +add_task(async function test_cancelEditAddressDialogWithESC() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + EventUtils.synthesizeKey("VK_ESCAPE", {}, win); + }); +}); + +add_task(async function test_defaultCountry() { + Region._setHomeRegion("CA", false); + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + is( + doc.querySelector("#country").value, + "CA", + "Default country set to Canada" + ); + doc.querySelector("#cancel").click(); + }); + Region._setHomeRegion("DE", false); + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + is( + doc.querySelector("#country").value, + "DE", + "Default country set to Germany" + ); + doc.querySelector("#cancel").click(); + }); + // Test unsupported country + Region._setHomeRegion("XX", false); + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + is(doc.querySelector("#country").value, "", "Default country set to empty"); + doc.querySelector("#cancel").click(); + }); + Region._setHomeRegion("US", false); +}); + +add_task(async function test_saveAddress() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + // Verify labels + is( + doc.querySelector("#address-level1-container > .label-text").textContent, + "State", + "US address-level1 label should be 'State'" + ); + is( + doc.querySelector("#postal-code-container > .label-text").textContent, + "ZIP Code", + "US postal-code label should be 'ZIP Code'" + ); + // Input address info and verify move through form with tab keys + let keypresses = [ + "VK_TAB", + TEST_ADDRESS_1["given-name"], + "VK_TAB", + TEST_ADDRESS_1["additional-name"], + "VK_TAB", + TEST_ADDRESS_1["family-name"], + "VK_TAB", + TEST_ADDRESS_1["street-address"], + "VK_TAB", + TEST_ADDRESS_1["address-level2"], + "VK_TAB", + TEST_ADDRESS_1["address-level1"], + "VK_TAB", + TEST_ADDRESS_1["postal-code"], + "VK_TAB", + TEST_ADDRESS_1.organization, + "VK_TAB", + // TEST_ADDRESS_1.country, // Country is already US + "VK_TAB", + TEST_ADDRESS_1.tel, + "VK_TAB", + TEST_ADDRESS_1.email, + "VK_TAB", + ]; + if (AppConstants.platform != "win") { + keypresses.push("VK_TAB", "VK_RETURN"); + } else { + keypresses.push("VK_RETURN"); + } + keypresses.forEach(keypress => { + if ( + doc.activeElement.localName == "select" && + !keypress.startsWith("VK_") + ) { + let field = doc.activeElement; + while (field.value != keypress) { + EventUtils.synthesizeKey(keypress[0], {}, win); + } + } else { + EventUtils.synthesizeKey(keypress, {}, win); + } + }); + }); + let addresses = await getAddresses(); + + is(addresses.length, 1, "only one address is in storage"); + is( + Object.keys(TEST_ADDRESS_1).length, + 11, + "Sanity check number of properties" + ); + for (let [fieldName, fieldValue] of Object.entries(TEST_ADDRESS_1)) { + is(addresses[0][fieldName], fieldValue, "check " + fieldName); + } +}); + +add_task(async function test_editAddress() { + let addresses = await getAddresses(); + await testDialog( + EDIT_ADDRESS_DIALOG_URL, + win => { + EventUtils.synthesizeKey("VK_TAB", {}, win); + EventUtils.synthesizeKey("VK_RIGHT", {}, win); + EventUtils.synthesizeKey("test", {}, win); + + let stateSelect = win.document.querySelector("#address-level1"); + is( + stateSelect.selectedOptions[0].value, + TEST_ADDRESS_1["address-level1"], + "address-level1 should be selected in the dropdown" + ); + + win.document.querySelector("#save").click(); + }, + { + record: addresses[0], + } + ); + addresses = await getAddresses(); + + is(addresses.length, 1, "only one address is in storage"); + is( + addresses[0]["given-name"], + TEST_ADDRESS_1["given-name"] + "test", + "given-name changed" + ); + await removeAddresses([addresses[0].guid]); + + addresses = await getAddresses(); + is(addresses.length, 0, "Address storage is empty"); +}); + +add_task( + async function test_editAddressFrenchCanadianChangedToEnglishRepresentation() { + let addressClone = Object.assign({}, TEST_ADDRESS_CA_1); + addressClone["address-level1"] = "Colombie-Britannique"; + await setStorage(addressClone); + + let addresses = await getAddresses(); + await testDialog( + EDIT_ADDRESS_DIALOG_URL, + win => { + let stateSelect = win.document.querySelector("#address-level1"); + is( + stateSelect.selectedOptions[0].value, + "BC", + "address-level1 should have 'BC' selected in the dropdown" + ); + + win.document.querySelector("#save").click(); + }, + { + record: addresses[0], + } + ); + addresses = await getAddresses(); + + is(addresses.length, 1, "only one address is in storage"); + is(addresses[0]["address-level1"], "BC", "address-level1 changed"); + await removeAddresses([addresses[0].guid]); + + addresses = await getAddresses(); + is(addresses.length, 0, "Address storage is empty"); + } +); + +add_task(async function test_editSparseAddress() { + let record = { ...TEST_ADDRESS_1 }; + info("delete some usually required properties"); + delete record["street-address"]; + delete record["address-level1"]; + delete record["address-level2"]; + await testDialog( + EDIT_ADDRESS_DIALOG_URL, + win => { + is( + win.document.querySelectorAll(":-moz-ui-invalid").length, + 0, + "Check no fields are visually invalid" + ); + EventUtils.synthesizeKey("VK_TAB", {}, win); + EventUtils.synthesizeKey("VK_RIGHT", {}, win); + EventUtils.synthesizeKey("test", {}, win); + is( + win.document.querySelector("#save").disabled, + false, + "Save button should be enabled after an edit" + ); + win.document.querySelector("#cancel").click(); + }, + { + record, + } + ); +}); + +add_task(async function test_saveAddressCA() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + // Change country to verify labels + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Canada", {}, win); + + await TestUtils.waitForCondition(() => { + return ( + doc.querySelector("#address-level1-container > .label-text") + .textContent == "Province" + ); + }, "Wait for the mutation observer to change the labels"); + is( + doc.querySelector("#address-level1-container > .label-text").textContent, + "Province", + "CA address-level1 label should be 'Province'" + ); + is( + doc.querySelector("#postal-code-container > .label-text").textContent, + "Postal Code", + "CA postal-code label should be 'Postal Code'" + ); + is( + doc.querySelector("#address-level3-container").style.display, + "none", + "CA address-level3 should be hidden" + ); + + // Input address info and verify move through form with tab keys + doc.querySelector("#given-name").focus(); + let keyInputs = [ + TEST_ADDRESS_CA_1["given-name"], + "VK_TAB", + TEST_ADDRESS_CA_1["additional-name"], + "VK_TAB", + TEST_ADDRESS_CA_1["family-name"], + "VK_TAB", + TEST_ADDRESS_CA_1.organization, + "VK_TAB", + TEST_ADDRESS_CA_1["street-address"], + "VK_TAB", + TEST_ADDRESS_CA_1["address-level2"], + "VK_TAB", + TEST_ADDRESS_CA_1["address-level1"], + "VK_TAB", + TEST_ADDRESS_CA_1["postal-code"], + "VK_TAB", + // TEST_ADDRESS_1.country, // Country is already selected above + "VK_TAB", + TEST_ADDRESS_CA_1.tel, + "VK_TAB", + TEST_ADDRESS_CA_1.email, + "VK_TAB", + ]; + if (AppConstants.platform != "win") { + keyInputs.push("VK_TAB", "VK_RETURN"); + } else { + keyInputs.push("VK_RETURN"); + } + keyInputs.forEach(input => EventUtils.synthesizeKey(input, {}, win)); + }); + let addresses = await getAddresses(); + for (let [fieldName, fieldValue] of Object.entries(TEST_ADDRESS_CA_1)) { + is(addresses[0][fieldName], fieldValue, "check " + fieldName); + } + await removeAllRecords(); +}); + +add_task(async function test_saveAddressDE() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + // Change country to verify labels + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Germany", {}, win); + await TestUtils.waitForCondition(() => { + return ( + doc.querySelector("#postal-code-container > .label-text").textContent == + "Postal Code" + ); + }, "Wait for the mutation observer to change the labels"); + is( + doc.querySelector("#postal-code-container > .label-text").textContent, + "Postal Code", + "DE postal-code label should be 'Postal Code'" + ); + is( + doc.querySelector("#address-level1-container").style.display, + "none", + "DE address-level1 should be hidden" + ); + is( + doc.querySelector("#address-level3-container").style.display, + "none", + "DE address-level3 should be hidden" + ); + // Input address info and verify move through form with tab keys + doc.querySelector("#given-name").focus(); + let keyInputs = [ + TEST_ADDRESS_DE_1["given-name"], + "VK_TAB", + TEST_ADDRESS_DE_1["additional-name"], + "VK_TAB", + TEST_ADDRESS_DE_1["family-name"], + "VK_TAB", + TEST_ADDRESS_DE_1.organization, + "VK_TAB", + TEST_ADDRESS_DE_1["street-address"], + "VK_TAB", + TEST_ADDRESS_DE_1["postal-code"], + "VK_TAB", + TEST_ADDRESS_DE_1["address-level2"], + "VK_TAB", + // TEST_ADDRESS_1.country, // Country is already selected above + "VK_TAB", + TEST_ADDRESS_DE_1.tel, + "VK_TAB", + TEST_ADDRESS_DE_1.email, + "VK_TAB", + ]; + if (AppConstants.platform != "win") { + keyInputs.push("VK_TAB", "VK_RETURN"); + } else { + keyInputs.push("VK_RETURN"); + } + keyInputs.forEach(input => EventUtils.synthesizeKey(input, {}, win)); + }); + let addresses = await getAddresses(); + for (let [fieldName, fieldValue] of Object.entries(TEST_ADDRESS_DE_1)) { + is(addresses[0][fieldName], fieldValue, "check " + fieldName); + } + await removeAllRecords(); +}); + +/** + * Test saving an address for a region from regionNames.properties but not in + * addressReferences.js (libaddressinput). + */ +add_task(async function test_saveAddress_nolibaddressinput() { + const TEST_ADDRESS = { + ...TEST_ADDRESS_IE_1, + ...{ + "address-level3": undefined, + country: "XG", + }, + }; + + isnot( + FormAutofillUtils.getCountryAddressData("XG").key, + "XG", + "Check that the region we're testing with isn't in libaddressinput" + ); + + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + + // Change country to verify labels + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Gaza Strip", {}, win); + await TestUtils.waitForCondition(() => { + return ( + doc.querySelector("#postal-code-container > .label-text").textContent == + "Postal Code" + ); + }, "Wait for the mutation observer to change the labels"); + is( + doc.querySelector("#postal-code-container > .label-text").textContent, + "Postal Code", + "XG postal-code label should be 'Postal Code'" + ); + isnot( + doc.querySelector("#address-level1-container").style.display, + "none", + "XG address-level1 should be hidden" + ); + is( + doc.querySelector("#address-level2").localName, + "input", + "XG address-level2 should be an <input>" + ); + // Input address info and verify move through form with tab keys + doc.querySelector("#given-name").focus(); + let keyInputs = [ + TEST_ADDRESS["given-name"], + "VK_TAB", + TEST_ADDRESS["additional-name"], + "VK_TAB", + TEST_ADDRESS["family-name"], + "VK_TAB", + TEST_ADDRESS.organization, + "VK_TAB", + TEST_ADDRESS["street-address"], + "VK_TAB", + TEST_ADDRESS["address-level2"], + "VK_TAB", + TEST_ADDRESS["address-level1"], + "VK_TAB", + TEST_ADDRESS["postal-code"], + "VK_TAB", + // TEST_ADDRESS_1.country, // Country is already selected above + "VK_TAB", + TEST_ADDRESS.tel, + "VK_TAB", + TEST_ADDRESS.email, + "VK_TAB", + ]; + if (AppConstants.platform != "win") { + keyInputs.push("VK_TAB", "VK_RETURN"); + } else { + keyInputs.push("VK_RETURN"); + } + keyInputs.forEach(input => EventUtils.synthesizeKey(input, {}, win)); + }); + let addresses = await getAddresses(); + for (let [fieldName, fieldValue] of Object.entries(TEST_ADDRESS)) { + is(addresses[0][fieldName], fieldValue, "check " + fieldName); + } + await removeAllRecords(); +}); + +add_task(async function test_saveAddressIE() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + // Change country to verify labels + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Ireland", {}, win); + await TestUtils.waitForCondition(() => { + return ( + doc.querySelector("#postal-code-container > .label-text").textContent == + "Eircode" + ); + }, "Wait for the mutation observer to change the labels"); + is( + doc.querySelector("#postal-code-container > .label-text").textContent, + "Eircode", + "IE postal-code label should be 'Eircode'" + ); + is( + doc.querySelector("#address-level1-container > .label-text").textContent, + "County", + "IE address-level1 should be 'County'" + ); + is( + doc.querySelector("#address-level3-container > .label-text").textContent, + "Townland", + "IE address-level3 should be 'Townland'" + ); + + // Input address info and verify move through form with tab keys + doc.querySelector("#given-name").focus(); + let keyInputs = [ + TEST_ADDRESS_IE_1["given-name"], + "VK_TAB", + TEST_ADDRESS_IE_1["additional-name"], + "VK_TAB", + TEST_ADDRESS_IE_1["family-name"], + "VK_TAB", + TEST_ADDRESS_IE_1.organization, + "VK_TAB", + TEST_ADDRESS_IE_1["street-address"], + "VK_TAB", + TEST_ADDRESS_IE_1["address-level3"], + "VK_TAB", + TEST_ADDRESS_IE_1["address-level2"], + "VK_TAB", + TEST_ADDRESS_IE_1["address-level1"], + "VK_TAB", + TEST_ADDRESS_IE_1["postal-code"], + "VK_TAB", + // TEST_ADDRESS_1.country, // Country is already selected above + "VK_TAB", + TEST_ADDRESS_IE_1.tel, + "VK_TAB", + TEST_ADDRESS_IE_1.email, + "VK_TAB", + ]; + if (AppConstants.platform != "win") { + keyInputs.push("VK_TAB", "VK_RETURN"); + } else { + keyInputs.push("VK_RETURN"); + } + keyInputs.forEach(input => EventUtils.synthesizeKey(input, {}, win)); + }); + + let addresses = await getAddresses(); + for (let [fieldName, fieldValue] of Object.entries(TEST_ADDRESS_IE_1)) { + is(addresses[0][fieldName], fieldValue, "check " + fieldName); + } + await removeAllRecords(); +}); + +add_task(async function test_countryAndStateFieldLabels() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + // Change country to verify labels + doc.querySelector("#country").focus(); + + let mutableLabels = [ + "postal-code-container", + "address-level1-container", + "address-level2-container", + "address-level3-container", + ].map(containerID => + doc.getElementById(containerID).querySelector(":scope > .label-text") + ); + + for (let countryOption of doc.querySelector("#country").options) { + if (countryOption.value == "") { + info("Skipping the empty country option"); + continue; + } + + // Clear L10N textContent to not leave leftovers between country tests + for (let labelEl of mutableLabels) { + doc.l10n.setAttributes(labelEl, ""); + labelEl.textContent = ""; + } + + info(`Selecting '${countryOption.label}' (${countryOption.value})`); + EventUtils.synthesizeKey(countryOption.label, {}, win); + + let l10nResolve; + let l10nReady = new Promise(resolve => { + l10nResolve = resolve; + }); + let verifyL10n = () => { + if (mutableLabels.every(labelEl => labelEl.textContent)) { + win.removeEventListener("MozAfterPaint", verifyL10n); + l10nResolve(); + } + }; + win.addEventListener("MozAfterPaint", verifyL10n); + await l10nReady; + + // Check that the labels were filled + for (let labelEl of mutableLabels) { + isnot( + labelEl.textContent, + "", + "Ensure textContent is non-empty for: " + countryOption.value + ); + } + + let stateOptions = doc.querySelector("#address-level1").options; + /* eslint-disable max-len */ + let expectedStateOptions = { + BS: { + // The Bahamas is an interesting testcase because they have some keys that are full names, and others are replaced with ISO IDs. + keys: "Abaco~AK~Andros~BY~BI~CI~Crooked Island~Eleuthera~EX~Grand Bahama~HI~IN~LI~MG~N.P.~RI~RC~SS~SW".split( + "~" + ), + names: + "Abaco Islands~Acklins~Andros Island~Berry Islands~Bimini~Cat Island~Crooked Island~Eleuthera~Exuma and Cays~Grand Bahama~Harbour Island~Inagua~Long Island~Mayaguana~New Providence~Ragged Island~Rum Cay~San Salvador~Spanish Wells".split( + "~" + ), + }, + US: { + keys: "AL~AK~AS~AZ~AR~AA~AE~AP~CA~CO~CT~DE~DC~FL~GA~GU~HI~ID~IL~IN~IA~KS~KY~LA~ME~MH~MD~MA~MI~FM~MN~MS~MO~MT~NE~NV~NH~NJ~NM~NY~NC~ND~MP~OH~OK~OR~PW~PA~PR~RI~SC~SD~TN~TX~UT~VT~VI~VA~WA~WV~WI~WY".split( + "~" + ), + names: + "Alabama~Alaska~American Samoa~Arizona~Arkansas~Armed Forces (AA)~Armed Forces (AE)~Armed Forces (AP)~California~Colorado~Connecticut~Delaware~District of Columbia~Florida~Georgia~Guam~Hawaii~Idaho~Illinois~Indiana~Iowa~Kansas~Kentucky~Louisiana~Maine~Marshall Islands~Maryland~Massachusetts~Michigan~Micronesia~Minnesota~Mississippi~Missouri~Montana~Nebraska~Nevada~New Hampshire~New Jersey~New Mexico~New York~North Carolina~North Dakota~Northern Mariana Islands~Ohio~Oklahoma~Oregon~Palau~Pennsylvania~Puerto Rico~Rhode Island~South Carolina~South Dakota~Tennessee~Texas~Utah~Vermont~Virgin Islands~Virginia~Washington~West Virginia~Wisconsin~Wyoming".split( + "~" + ), + }, + }; + /* eslint-enable max-len */ + + if (expectedStateOptions[countryOption.value]) { + let { keys, names } = expectedStateOptions[countryOption.value]; + is( + stateOptions.length, + keys.length + 1, + "stateOptions should list all options plus a blank entry" + ); + is(stateOptions[0].value, "", "First State option should be blank"); + for (let i = 1; i < stateOptions.length; i++) { + is( + stateOptions[i].value, + keys[i - 1], + "Each State should be listed in alphabetical name order (key)" + ); + is( + stateOptions[i].text, + names[i - 1], + "Each State should be listed in alphabetical name order (name)" + ); + } + } + // Dispatch a dummy key event so that <select>'s incremental search is cleared. + EventUtils.synthesizeKey("VK_ACCEPT", {}, win); + } + + doc.querySelector("#cancel").click(); + }); +}); + +add_task(async function test_combined_name_fields() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + let givenNameField = doc.querySelector("#given-name"); + let addtlNameField = doc.querySelector("#additional-name"); + let familyNameField = doc.querySelector("#family-name"); + + function getComputedPropertyValue(field, property) { + return win.getComputedStyle(field).getPropertyValue(property); + } + function checkNameComputedPropertiesMatch( + field, + property, + value, + checkFn = is + ) { + checkFn( + getComputedPropertyValue(field, property), + value, + `Check ${field.id}'s ${property} is ${value}` + ); + } + function checkNameFieldBorders(borderColorUnfocused, borderColorFocused) { + info("checking the perimeter colors"); + checkNameComputedPropertiesMatch( + givenNameField, + "border-top-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-top-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + familyNameField, + "border-top-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + familyNameField, + "border-right-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + givenNameField, + "border-bottom-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-bottom-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + familyNameField, + "border-bottom-color", + borderColorFocused + ); + checkNameComputedPropertiesMatch( + givenNameField, + "border-left-color", + borderColorFocused + ); + + info("checking the internal borders"); + checkNameComputedPropertiesMatch( + givenNameField, + "border-right-width", + "0px" + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-left-width", + "2px" + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-left-color", + borderColorFocused, + isnot + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-right-width", + "2px" + ); + checkNameComputedPropertiesMatch( + addtlNameField, + "border-right-color", + borderColorFocused, + isnot + ); + checkNameComputedPropertiesMatch( + familyNameField, + "border-left-width", + "0px" + ); + } + + // Set these variables since the test doesn't run from a subdialog and + // therefore doesn't get the additional common CSS files injected. + let borderColor = "rgb(0, 255, 0)"; + let borderColorFocused = "rgb(0, 0, 255)"; + doc.body.style.setProperty("--in-content-box-border-color", borderColor); + doc.body.style.setProperty( + "--in-content-focus-outline-color", + borderColorFocused + ); + + givenNameField.focus(); + checkNameFieldBorders(borderColor, borderColorFocused); + + addtlNameField.focus(); + checkNameFieldBorders(borderColor, borderColorFocused); + + familyNameField.focus(); + checkNameFieldBorders(borderColor, borderColorFocused); + + info("unfocusing the name fields"); + let cancelButton = doc.querySelector("#cancel"); + cancelButton.focus(); + borderColor = getComputedPropertyValue(givenNameField, "border-top-color"); + isnot( + borderColor, + borderColorFocused, + "Check that the border color is different" + ); + checkNameFieldBorders(borderColor, borderColor); + + cancelButton.click(); + }); +}); + +add_task(async function test_combined_name_fields_error() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + let givenNameField = doc.querySelector("#given-name"); + info("mark the given name field as invalid"); + givenNameField.value = ""; + givenNameField.focus(); + ok( + givenNameField.matches(":-moz-ui-invalid"), + "Check field is visually invalid" + ); + + let givenNameLabel = doc.querySelector("#given-name-container .label-text"); + // Override pointer-events so that we can use elementFromPoint to know if + // the label text is visible. + givenNameLabel.style.pointerEvents = "auto"; + let givenNameLabelRect = givenNameLabel.getBoundingClientRect(); + // Get the center of the label + let el = doc.elementFromPoint( + givenNameLabelRect.left + givenNameLabelRect.width / 2, + givenNameLabelRect.top + givenNameLabelRect.height / 2 + ); + + is( + el, + givenNameLabel, + "Check that the label text is visible in the error state" + ); + is( + win.getComputedStyle(givenNameField).getPropertyValue("border-top-color"), + "rgba(0, 0, 0, 0)", + "Border should be transparent so that only the error outline shows" + ); + doc.querySelector("#cancel").click(); + }); +}); + +add_task(async function test_hiddenFieldNotSaved() { + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + doc.querySelector("#address-level2").focus(); + EventUtils.synthesizeKey(TEST_ADDRESS_1["address-level2"], {}, win); + doc.querySelector("#address-level1").focus(); + EventUtils.synthesizeKey(TEST_ADDRESS_1["address-level1"], {}, win); + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Germany", {}, win); + doc.querySelector("#save").focus(); + EventUtils.synthesizeKey("VK_RETURN", {}, win); + }); + let addresses = await getAddresses(); + is(addresses[0].country, "DE", "check country"); + is( + addresses[0]["address-level2"], + TEST_ADDRESS_1["address-level2"], + "check address-level2" + ); + is( + addresses[0]["address-level1"], + undefined, + "address-level1 should not be saved" + ); + + await removeAllRecords(); +}); + +add_task(async function test_hiddenFieldRemovedWhenCountryChanged() { + let addresses = await getAddresses(); + ok(!addresses.length, "no addresses at start of test"); + await testDialog(EDIT_ADDRESS_DIALOG_URL, win => { + let doc = win.document; + doc.querySelector("#address-level2").focus(); + EventUtils.synthesizeKey(TEST_ADDRESS_1["address-level2"], {}, win); + doc.querySelector("#address-level1").focus(); + while ( + doc.querySelector("#address-level1").value != + TEST_ADDRESS_1["address-level1"] + ) { + EventUtils.synthesizeKey(TEST_ADDRESS_1["address-level1"][0], {}, win); + } + doc.querySelector("#save").focus(); + EventUtils.synthesizeKey("VK_RETURN", {}, win); + }); + addresses = await getAddresses(); + is(addresses[0].country, "US", "check country"); + is( + addresses[0]["address-level2"], + TEST_ADDRESS_1["address-level2"], + "check address-level2" + ); + is( + addresses[0]["address-level1"], + TEST_ADDRESS_1["address-level1"], + "check address-level1" + ); + + await testDialog( + EDIT_ADDRESS_DIALOG_URL, + win => { + let doc = win.document; + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Germany", {}, win); + win.document.querySelector("#save").click(); + }, + { + record: addresses[0], + } + ); + addresses = await getAddresses(); + + is(addresses.length, 1, "only one address is in storage"); + is( + addresses[0]["address-level2"], + TEST_ADDRESS_1["address-level2"], + "check address-level2" + ); + is( + addresses[0]["address-level1"], + undefined, + "address-level1 should be removed" + ); + is(addresses[0].country, "DE", "country changed"); + await removeAllRecords(); +}); + +add_task(async function test_countrySpecificFieldsGetRequiredness() { + Region._setHomeRegion("RO", false); + await testDialog(EDIT_ADDRESS_DIALOG_URL, async win => { + let doc = win.document; + is( + doc.querySelector("#country").value, + "RO", + "Default country set to Romania" + ); + let provinceField = doc.getElementById("address-level1"); + ok( + !provinceField.required, + "address-level1 should not be marked as required" + ); + ok(provinceField.disabled, "address-level1 should be marked as disabled"); + is( + provinceField.parentNode.style.display, + "none", + "address-level1 is hidden for Romania" + ); + + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("United States", {}, win); + + await TestUtils.waitForCondition( + () => { + provinceField = doc.getElementById("address-level1"); + return provinceField.parentNode.style.display != "none"; + }, + "Wait for address-level1 to become visible", + 10 + ); + + ok(provinceField.required, "address-level1 should be marked as required"); + ok( + !provinceField.disabled, + "address-level1 should not be marked as disabled" + ); + + // Dispatch a dummy key event so that <select>'s incremental search is cleared. + EventUtils.synthesizeKey("VK_ACCEPT", {}, win); + + doc.querySelector("#country").focus(); + EventUtils.synthesizeKey("Romania", {}, win); + + await TestUtils.waitForCondition( + () => { + provinceField = doc.getElementById("address-level1"); + return provinceField.parentNode.style.display == "none"; + }, + "Wait for address-level1 to become hidden", + 10 + ); + + ok( + provinceField.required, + "address-level1 will still be marked as required" + ); + ok(provinceField.disabled, "address-level1 should be marked as disabled"); + + doc.querySelector("#cancel").click(); + }); +}); |