mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 04:15:43 +00:00
Bug 1364823 - Populate select elements with form autofill profile data. r=lchang
MozReview-Commit-ID: 21K5mC2tYQn --HG-- extra : rebase_source : a0697ca4a383e0fb387fb51bad7dab4ce407fd31
This commit is contained in:
parent
aa7d5e7676
commit
49107d9741
@ -84,16 +84,35 @@ FormAutofillHandler.prototype = {
|
||||
// 1. the focused input which is filled in FormFillController.
|
||||
// 2. a non-empty input field
|
||||
// 3. the invalid value set
|
||||
// 4. value already chosen in select element
|
||||
|
||||
let element = fieldDetail.elementWeakRef.get();
|
||||
if (!element || element === focusedInput || element.value) {
|
||||
if (!element || element === focusedInput) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = profile[fieldDetail.fieldName];
|
||||
// TODO: Bug 1364823 is implemeting the value filling of select element.
|
||||
if (element instanceof Ci.nsIDOMHTMLInputElement && value) {
|
||||
if (element.value) {
|
||||
continue;
|
||||
}
|
||||
element.setUserInput(value);
|
||||
} else if (element instanceof Ci.nsIDOMHTMLSelectElement) {
|
||||
for (let option of element.options) {
|
||||
if (value === option.textContent || value === option.value) {
|
||||
// Do not change value if the option is already selected.
|
||||
// Use case for multiple select is not considered here.
|
||||
if (option.selected) {
|
||||
break;
|
||||
}
|
||||
// TODO: Using dispatchEvent does not 100% simulate select change.
|
||||
// Should investigate further in Bug 1365895.
|
||||
option.selected = true;
|
||||
element.dispatchEvent(new Event("input", {"bubbles": true}));
|
||||
element.dispatchEvent(new Event("change", {"bubbles": true}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -24,10 +24,12 @@ let MOCK_STORAGE = [{
|
||||
organization: "Sesame Street",
|
||||
"street-address": "123 Sesame Street.",
|
||||
tel: "1-345-345-3456",
|
||||
country: "US",
|
||||
}, {
|
||||
organization: "Mozilla",
|
||||
"street-address": "331 E. Evelyn Avenue",
|
||||
tel: "1-650-903-0800",
|
||||
country: "US",
|
||||
}];
|
||||
|
||||
function expectPopup() {
|
||||
@ -85,7 +87,7 @@ async function setupAddressStorage() {
|
||||
async function setupFormHistory() {
|
||||
await updateFormHistory([
|
||||
{op: "add", fieldname: "tel", value: "1-234-567-890"},
|
||||
{op: "add", fieldname: "country", value: "US"},
|
||||
{op: "add", fieldname: "email", value: "foo@mozilla.com"},
|
||||
]);
|
||||
}
|
||||
|
||||
@ -127,10 +129,10 @@ add_task(async function check_menu_when_both_existed() {
|
||||
|
||||
// Display history search result if no matched data in addresses.
|
||||
add_task(async function check_fallback_for_mismatched_field() {
|
||||
setInput("#country", "");
|
||||
setInput("#email", "");
|
||||
doKey("down");
|
||||
await expectPopup();
|
||||
checkMenuEntries(["US"]);
|
||||
checkMenuEntries(["foo@mozilla.com"]);
|
||||
});
|
||||
|
||||
// Autofill the address from dropdown menu.
|
||||
@ -166,7 +168,11 @@ registerPopupShownListener(popupShownListener);
|
||||
<p><label>organization: <input id="organization" name="organization" autocomplete="organization" type="text"></label></p>
|
||||
<p><label>streetAddress: <input id="street-address" name="street-address" autocomplete="street-address" type="text"></label></p>
|
||||
<p><label>tel: <input id="tel" name="tel" autocomplete="tel" type="text"></label></p>
|
||||
<p><label>country: <input id="country" name="country" autocomplete="country" type="text"></label></p>
|
||||
<p><label>email: <input id="email" name="email" autocomplete="email" type="text"></label></p>
|
||||
<p><label>country: <select id="country" name="country" autocomplete="country">
|
||||
<option/>
|
||||
<option value="US">United States</option>
|
||||
</label></p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -28,7 +28,10 @@ const TESTCASES = [
|
||||
<input id="family-name" autocomplete="family-name">
|
||||
<input id="street-addr" autocomplete="street-address">
|
||||
<input id="city" autocomplete="address-level2">
|
||||
<select id="country" autocomplete="country"></select>
|
||||
<select id="country" autocomplete="country">
|
||||
<option/>
|
||||
<option value="US">United States</option>
|
||||
</select>
|
||||
<input id="email" autocomplete="email">
|
||||
<input id="tel" autocomplete="tel"></form>`,
|
||||
fieldDetails: [
|
||||
@ -62,7 +65,10 @@ const TESTCASES = [
|
||||
<input id="family-name" autocomplete="shipping family-name">
|
||||
<input id="street-addr" autocomplete="shipping street-address">
|
||||
<input id="city" autocomplete="shipping address-level2">
|
||||
<select id="country" autocomplete="shipping country"></select>
|
||||
<select id="country" autocomplete="shipping country">
|
||||
<option/>
|
||||
<option value="US">United States</option>
|
||||
</select>
|
||||
<input id='email' autocomplete="shipping email">
|
||||
<input id="tel" autocomplete="shipping tel"></form>`,
|
||||
fieldDetails: [
|
||||
@ -159,49 +165,153 @@ const TESTCASES = [
|
||||
"tel": "1234567",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Form with autocomplete select elements and matching option values",
|
||||
document: `<form>
|
||||
<select id="country" autocomplete="shipping country">
|
||||
<option value=""></option>
|
||||
<option value="US">United States</option>
|
||||
</select>
|
||||
<select id="state" autocomplete="shipping address-level1">
|
||||
<option value=""></option>
|
||||
<option value="CA">California</option>
|
||||
<option value="WA">Washington</option>
|
||||
</select>
|
||||
</form>`,
|
||||
fieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level1", "element": {}},
|
||||
],
|
||||
profileData: {
|
||||
"guid": "123",
|
||||
"country": "US",
|
||||
"address-level1": "CA",
|
||||
},
|
||||
expectedResult: {
|
||||
"country": "US",
|
||||
"state": "CA",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Form with autocomplete select elements and matching option texts",
|
||||
document: `<form>
|
||||
<select id="country" autocomplete="shipping country">
|
||||
<option value=""></option>
|
||||
<option value="US">United States</option>
|
||||
</select>
|
||||
<select id="state" autocomplete="shipping address-level1">
|
||||
<option value=""></option>
|
||||
<option value="CA">California</option>
|
||||
<option value="WA">Washington</option>
|
||||
</select>
|
||||
</form>`,
|
||||
fieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level1", "element": {}},
|
||||
],
|
||||
profileData: {
|
||||
"guid": "123",
|
||||
"country": "United States",
|
||||
"address-level1": "California",
|
||||
},
|
||||
expectedResult: {
|
||||
"country": "US",
|
||||
"state": "CA",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
(function() {
|
||||
let testcase = tc;
|
||||
add_task(async function() {
|
||||
do_print("Starting testcase: " + testcase.description);
|
||||
const TESTCASES_INPUT_UNCHANGED = [
|
||||
{
|
||||
description: "Form with autocomplete select elements; with default and no matching options",
|
||||
document: `<form>
|
||||
<select id="country" autocomplete="shipping country">
|
||||
<option value="US">United States</option>
|
||||
</select>
|
||||
<select id="state" autocomplete="shipping address-level1">
|
||||
<option value=""></option>
|
||||
<option value="CA">California</option>
|
||||
<option value="WA">Washington</option>
|
||||
</select>
|
||||
</form>`,
|
||||
fieldDetails: [
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country", "element": {}},
|
||||
{"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level1", "element": {}},
|
||||
],
|
||||
profileData: {
|
||||
"guid": "123",
|
||||
"country": "US",
|
||||
"address-level1": "unknown state",
|
||||
},
|
||||
expectedResult: {
|
||||
"country": "US",
|
||||
"state": "",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
|
||||
testcase.document);
|
||||
let form = doc.querySelector("form");
|
||||
let handler = new FormAutofillHandler(form);
|
||||
let onChangePromises = [];
|
||||
function do_test(testcases, testFn) {
|
||||
for (let tc of testcases) {
|
||||
(function() {
|
||||
let testcase = tc;
|
||||
add_task(async function() {
|
||||
do_print("Starting testcase: " + testcase.description);
|
||||
|
||||
handler.fieldDetails = testcase.fieldDetails;
|
||||
handler.fieldDetails.forEach((field, index) => {
|
||||
let element = doc.querySelectorAll("input, select")[index];
|
||||
field.elementWeakRef = Cu.getWeakReference(element);
|
||||
if (element instanceof Ci.nsIDOMHTMLSelectElement) {
|
||||
// TODO: Bug 1364823 should remove the condition and handle filling
|
||||
// value in <select>
|
||||
return;
|
||||
}
|
||||
if (!testcase.profileData[field.fieldName]) {
|
||||
// Avoid waiting for `change` event of a input with a blank value to
|
||||
// be filled.
|
||||
return;
|
||||
}
|
||||
onChangePromises.push(new Promise(resolve => {
|
||||
element.addEventListener("change", () => {
|
||||
let id = element.id;
|
||||
Assert.equal(element.value, testcase.expectedResult[id],
|
||||
"Check the " + id + " fields were filled with correct data");
|
||||
resolve();
|
||||
}, {once: true});
|
||||
}));
|
||||
let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
|
||||
testcase.document);
|
||||
let form = doc.querySelector("form");
|
||||
let handler = new FormAutofillHandler(form);
|
||||
let promises = [];
|
||||
|
||||
handler.fieldDetails = testcase.fieldDetails;
|
||||
handler.fieldDetails.forEach((field, index) => {
|
||||
let element = doc.querySelectorAll("input, select")[index];
|
||||
field.elementWeakRef = Cu.getWeakReference(element);
|
||||
if (!testcase.profileData[field.fieldName]) {
|
||||
// Avoid waiting for `change` event of a input with a blank value to
|
||||
// be filled.
|
||||
return;
|
||||
}
|
||||
promises.push(testFn(testcase, element));
|
||||
});
|
||||
|
||||
handler.autofillFormFields(testcase.profileData);
|
||||
Assert.equal(handler.filledProfileGUID, testcase.profileData.guid,
|
||||
"Check if filledProfileGUID is set correctly");
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
handler.autofillFormFields(testcase.profileData);
|
||||
|
||||
Assert.equal(handler.filledProfileGUID, testcase.profileData.guid,
|
||||
"Check if filledProfileGUID is set correctly");
|
||||
await Promise.all(onChangePromises);
|
||||
});
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
do_test(TESTCASES, (testcase, element) => {
|
||||
return new Promise(resolve => {
|
||||
element.addEventListener("change", () => {
|
||||
let id = element.id;
|
||||
Assert.equal(element.value, testcase.expectedResult[id],
|
||||
"Check the " + id + " field was filled with correct data");
|
||||
resolve();
|
||||
}, {once: true});
|
||||
});
|
||||
});
|
||||
|
||||
do_test(TESTCASES_INPUT_UNCHANGED, (testcase, element) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Make sure no change or input event is fired when no change occurs.
|
||||
let cleaner;
|
||||
let timer = setTimeout(() => {
|
||||
let id = element.id;
|
||||
element.removeEventListener("change", cleaner);
|
||||
element.removeEventListener("input", cleaner);
|
||||
Assert.equal(element.value, testcase.expectedResult[id],
|
||||
"Check no value is changed on the " + id + " field");
|
||||
resolve();
|
||||
}, 1000);
|
||||
cleaner = event => {
|
||||
clearTimeout(timer);
|
||||
reject(`${event.type} event should not fire`);
|
||||
};
|
||||
element.addEventListener("change", cleaner);
|
||||
element.addEventListener("input", cleaner);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user