--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -203,16 +203,23 @@ function sendFakeAutocompleteEvent(eleme
acEvent.initEvent("DOMAutoComplete", true, false);
element.dispatchEvent(acEvent);
}
function spinEventLoop() {
return Promise.resolve();
}
+function promisePopupClosedAndFormsProcessed() {
+ return Promise.all([
+ promiseFormsProcessed(),
+ closeAutoCompletePopup(),
+ ]);
+}
+
add_task(function* setup() {
listenForUnexpectedPopupShown();
});
add_task(function* test_form1_initial_empty() {
yield SimpleTest.promiseFocus(window);
// Make sure initial form is empty.
@@ -220,119 +227,100 @@ add_task(function* test_form1_initial_em
let popupState = yield getPopupState();
is(popupState.open, false, "Check popup is initially closed");
});
add_task(function* test_form1_first_entry() {
yield SimpleTest.promiseFocus(window);
// Trigger autocomplete popup
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
let popupState = yield getPopupState();
is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
doKey("down"); // first
checkACForm("", ""); // value shouldn't update just by selecting
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("tempuser1", "temppass1");
});
add_task(function* test_form1_second_entry() {
// Trigger autocomplete popup
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("down"); // first
doKey("down"); // second
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_third_entry() {
// Trigger autocomplete popup
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("down"); // first
doKey("down"); // second
doKey("down"); // third
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser3", "testpass3");
});
add_task(function* test_form1_fourth_entry() {
// Trigger autocomplete popup
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("down"); // first
doKey("down"); // second
doKey("down"); // third
doKey("down"); // fourth
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_wraparound_first_entry() {
// Trigger autocomplete popup
restoreForm();
yield spinEventLoop(); // let focus happen
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("down"); // first
doKey("down"); // second
doKey("down"); // third
doKey("down"); // fourth
doKey("down"); // deselects
doKey("down"); // first
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("tempuser1", "temppass1");
});
add_task(function* test_form1_wraparound_up_last_entry() {
// Trigger autocomplete popup
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("up"); // last (fourth)
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_wraparound_down_up_up() {
// Trigger autocomplete popup
restoreForm();
let shownPromise = promiseACShown();
doKey("down"); // open
yield shownPromise;
doKey("down"); // select first entry
doKey("up"); // selects nothing!
doKey("up"); // select last entry
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_wraparound_up_last() {
restoreForm();
let shownPromise = promiseACShown();
doKey("down"); // open
yield shownPromise;
@@ -340,73 +328,60 @@ add_task(function* test_form1_wraparound
doKey("down");
doKey("up"); // deselects
doKey("up"); // last entry
doKey("up");
doKey("up");
doKey("up"); // first entry
doKey("up"); // deselects
doKey("up"); // last entry
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_fill_username_without_autofill_right() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Set first entry w/o triggering autocomplete
doKey("down"); // first
- doKey("right");
- yield spinEventLoop();
+ yield closeAutoCompletePopup("right");
checkACForm("tempuser1", ""); // empty password
});
add_task(function* test_form1_fill_username_without_autofill_left() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
-
+ yield openAutoCompletePopup();
// Set first entry w/o triggering autocomplete
doKey("down"); // first
- doKey("left");
+ yield closeAutoCompletePopup("left");
checkACForm("tempuser1", ""); // empty password
});
add_task(function* test_form1_pageup_first() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry (page up)
doKey("down"); // first
doKey("down"); // second
doKey("page_up"); // first
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("tempuser1", "temppass1");
});
add_task(function* test_form1_pagedown_last() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
/* test 13 */
// Check last entry (page down)
doKey("down"); // first
doKey("page_down"); // last
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_untrusted_event() {
restoreForm();
yield spinEventLoop();
// Send a fake (untrusted) event.
@@ -414,19 +389,17 @@ add_task(function* test_form1_untrusted_
uname.value = "zzzuser4";
sendFakeAutocompleteEvent(uname);
yield spinEventLoop();
checkACForm("zzzuser4", "");
});
add_task(function* test_form1_delete() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// XXX tried sending character "t" before/during dropdown to test
// filtering, but had no luck. Seemed like the character was getting lost.
// Setting uname.value didn't seem to work either. This works with a human
// driver, so I'm not sure what's up.
// Delete the first entry (of 4), "tempuser1"
doKey("down");
@@ -439,102 +412,84 @@ add_task(function* test_form1_delete() {
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
doKey("delete", shiftModifier);
yield deletionPromise;
checkACForm("", "");
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 4, "Correct number of logins after deleting one");
notifyMenuChanged(4);
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_first_after_deletion() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check the new first entry (of 3)
doKey("down");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_delete_second() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Delete the second entry (of 3), "testuser3"
doKey("down");
doKey("down");
doKey("delete", shiftModifier);
checkACForm("", "");
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 3, "Correct number of logins after deleting one");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("zzzuser4", "zzzpass4");
});
add_task(function* test_form1_first_after_deletion2() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check the new first entry (of 2)
doKey("down");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_delete_last() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
/* test 54 */
// Delete the last entry (of 2), "zzzuser4"
doKey("down");
doKey("down");
doKey("delete", shiftModifier);
checkACForm("", "");
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
is(numLogins, 2, "Correct number of logins after deleting one");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_first_after_3_deletions() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check the only remaining entry
doKey("down");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser2", "testpass2");
});
add_task(function* test_form1_check_only_entry_remaining() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
/* test 56 */
// Delete the only remaining entry, "testuser2"
doKey("down");
doKey("delete", shiftModifier);
//doKey("return");
checkACForm("", "");
numLogins = LoginManager.countLogins("http://mochi.test:8888", "http://autocomplete:8888", null);
@@ -547,76 +502,64 @@ add_task(function* test_form1_check_only
/* Tests for single-user forms for ignoring autocomplete=off */
add_task(function* test_form2() {
// Turn our attention to form2
uname = $_(2, "uname");
pword = $_(2, "pword");
checkACForm("singleuser5", "singlepass5");
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("singleuser5", "singlepass5");
});
add_task(function* test_form3() {
uname = $_(3, "uname");
pword = $_(3, "pword");
checkACForm("singleuser5", "singlepass5");
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("singleuser5", "singlepass5");
});
add_task(function* test_form4() {
uname = $_(4, "uname");
pword = $_(4, "pword");
checkACForm("singleuser5", "singlepass5");
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("singleuser5", "singlepass5");
});
add_task(function* test_form5() {
uname = $_(5, "uname");
pword = $_(5, "pword");
checkACForm("singleuser5", "singlepass5");
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("singleuser5", "singlepass5");
});
add_task(function* test_form6() {
// (this is a control, w/o autocomplete=off, to ensure the login
// that was being suppressed would have been filled in otherwise)
uname = $_(6, "uname");
pword = $_(6, "pword");
@@ -653,24 +596,22 @@ add_task(function* test_form7() {
// Delete login6B. It was created just to prevent filling in a login
// automatically, removing it makes it more likely that we'll catch a
// future regression with form filling here.
setupScript.sendSyncMessage("removeLogin", "login6B");
});
add_task(function* test_form7_2() {
restoreForm();
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Check first entry
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
+ yield closeAutoCompletePopup();
// The form changes, so we expect the old username field to get the
// selected autocomplete value, but neither the new username field nor
// the password field should have any values filled in.
yield spinEventLoop();
checkACForm("form7user1", "");
is($_(7, "uname2").value, "", "Verifying empty uname2");
restoreForm(); // clear field, so reloading test doesn't fail
@@ -700,32 +641,28 @@ add_task(function* test_form8_3() {
setupScript.sendSyncMessage("removeLogin", "login7");
});
add_task(function* test_form9_filtering() {
// Turn our attention to form9 to test the dropdown - bug 497541
uname = $_(9, "uname");
pword = $_(9, "pword");
uname.focus();
- let shownPromise = promiseACShown();
- sendString("form9userAB");
- yield shownPromise;
+ yield openAutoCompletePopup("form9userAB");
checkACForm("form9userAB", "");
uname.focus();
- doKey("left");
- shownPromise = promiseACShown();
- sendChar("A");
- let results = yield shownPromise;
+ yield closeAutoCompletePopup("left");
+
+ let results = yield openAutoCompletePopup("A");
checkACForm("form9userAAB", "");
checkArrayValues(results, ["form9userAAB"], "Check dropdown is updated after inserting 'A'");
doKey("down");
- doKey("return");
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("form9userAAB", "form9pass");
});
add_task(function* test_form9_autocomplete_cache() {
// Note that this addLogin call will only be seen by the autocomplete
// attempt for the sendChar if we do not successfully cache the
// autocomplete results.
setupScript.sendSyncMessage("addLogin", "login8C");
@@ -757,24 +694,21 @@ add_task(function* test_form11_recipes()
// First test DOMAutocomplete
// Switch the password field to type=password so _fillForm marks the username
// field for autocomplete.
pword.type = "password";
yield promiseFormsProcessed();
restoreForm();
checkACForm("", "");
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
doKey("down");
checkACForm("", ""); // value shouldn't update
- doKey("return"); // not "enter"!
- yield promiseFormsProcessed();
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser10", "testpass10");
// Now test recipes with blur on the username field.
restoreForm();
checkACForm("", "");
uname.value = "testuser10";
checkACForm("testuser10", "");
doKey("tab");
@@ -784,24 +718,20 @@ add_task(function* test_form11_recipes()
});
add_task(function* test_form12_formless() {
// Test form-less autocomplete
uname = $_(12, "uname");
pword = $_(12, "pword");
restoreForm();
checkACForm("", "");
- let shownPromise = promiseACShown();
- doKey("down"); // open
- yield shownPromise;
+ yield openAutoCompletePopup();
// Trigger autocomplete
doKey("down");
checkACForm("", ""); // value shouldn't update
- let processedPromise = promiseFormsProcessed();
- doKey("return"); // not "enter"!
- yield processedPromise;
+ yield promisePopupClosedAndFormsProcessed();
checkACForm("testuser", "testpass");
});
</script>
</pre>
</body>
</html>
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -1,14 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Form History Autocomplete</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="satchel_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Form History test: form field autocomplete
<p id="display"></p>
<!-- We presumably can't hide the content for this test. The large top padding is to allow
@@ -129,940 +130,792 @@ Form History test: form field autocomple
<button type="submit">Submit</button>
</form>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
-/** Test for Form History autocomplete **/
+/** Tests for Form History autocomplete **/
-var input = $_(1, "field1");
-const shiftModifier = Event.SHIFT_MASK;
+add_task(function* setup() {
+ yield SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.formfill.debug", true],
+ ],
+ });
+ yield clearFormHistory();
+ listenForUnexpectedPopupShown();
+});
-function setupFormHistory(aCallback) {
- updateFormHistory([
- { op : "remove" },
- { op : "add", fieldname : "field1", value : "value1" },
- { op : "add", fieldname : "field1", value : "value2" },
- { op : "add", fieldname : "field1", value : "value3" },
- { op : "add", fieldname : "field1", value : "value4" },
- { op : "add", fieldname : "field2", value : "value1" },
- { op : "add", fieldname : "field3", value : "a" },
- { op : "add", fieldname : "field3", value : "aa" },
- { op : "add", fieldname : "field3", value : "aaz" },
- { op : "add", fieldname : "field3", value : "aa\xe6" }, // 0xae == latin ae pair (0xc6 == AE)
- { op : "add", fieldname : "field3", value : "az" },
- { op : "add", fieldname : "field3", value : "z" },
- { op : "add", fieldname : "field4", value : "a\xe6" },
- { op : "add", fieldname : "field4", value : "aa a\xe6" },
- { op : "add", fieldname : "field4", value : "aba\xe6" },
- { op : "add", fieldname : "field4", value : "bc d\xe6" },
- { op : "add", fieldname : "field5", value : "1" },
- { op : "add", fieldname : "field5", value : "12" },
- { op : "add", fieldname : "field5", value : "123" },
- { op : "add", fieldname : "field5", value : "1234" },
- { op : "add", fieldname : "field6", value : "value" },
- { op : "add", fieldname : "field7", value : "value" },
- { op : "add", fieldname : "field8", value : "value" },
- { op : "add", fieldname : "field9", value : "value" },
- { op : "add", fieldname : "field10", value : "42" },
- { op : "add", fieldname : "field11", value : "2010-10-10" },
- { op : "add", fieldname : "field12", value : "21:21" },
- { op : "add", fieldname : "field13", value : "32" }, // not used, since type=range doesn't have a drop down menu
- { op : "add", fieldname : "field14", value : "#ffffff" }, // not used, since type=color doesn't have autocomplete currently
- { op : "add", fieldname : "field15", value : "2016-08" },
- { op : "add", fieldname : "field16", value : "2016-W32" },
- { op : "add", fieldname : "searchbar-history", value : "blacklist test" },
- ], aCallback);
-}
+/**
+ * Given an input with 4 autocomplete values, ensures that we can select each
+ * one and have the input take that value.
+ */
+add_task(function* test_basic_selection() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "value1",
+ "value2",
+ "value3",
+ "value4",
+ ];
-// All these non-implemeted types might need autocomplete tests in the future.
-var todoTypes = [ "datetime", "datetime-local" ];
-var todoInput = document.createElement("input");
-for (var type of todoTypes) {
- todoInput.type = type;
- todo_is(todoInput.type, type, type + " type shouldn't be implemented");
-}
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ // This will loop through each value, ensure that we can reach it in the
+ // popup, and have it become the field value if we press enter while it's
+ // selected.
+ for (let i = 0; i < VALUES.length; ++i) {
+ let items = yield openAutoCompletePopup();
+ is(items.length, VALUES.length,
+ `Should still be showing ${VALUES.length} entries.`);
+ // Now select the first item...
+ doKey("down");
+ // And ensure that the field has not updated
+ is(input.value, "", "Field should still be blank.");
+
+ // Depending on which VALUE we're on, we need to hit down (i - 1) number
+ // of times in order to hit it.
+ for (let j = 0; j < i; ++j) {
+ doKey("down");
+ }
-function setForm(value) {
- input.value = value;
- input.focus();
-}
-
-// Restore the form to the default state.
-function restoreForm() {
- setForm("");
-}
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[i],
+ "Should have updated the field to the right value.");
+ input.value = "";
+ }
+ });
+});
-// Check for expected form data.
-function checkForm(expectedValue) {
- var formID = input.parentNode.id;
- is(input.value, expectedValue, "Checking " + formID + " input");
-}
-
-var testNum = 0;
-var expectingPopup = false;
-
-function expectPopup()
-{
- info("expecting popup for test " + testNum);
- expectingPopup = true;
-}
+/**
+ * Tests that if we have some entries in an autocomplete popup, that when we
+ * cursor through to the bottom, that we wrap around back to the top. Also
+ * tests that we can then wraparound from the top back to the bottom.
+ */
+add_task(function* test_wraparounds() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "value1",
+ "value2",
+ "value3",
+ ];
-function popupShownListener()
-{
- info("popup shown for test " + testNum);
- if (expectingPopup) {
- expectingPopup = false;
- SimpleTest.executeSoon(runTest);
- }
- else {
- ok(false, "Autocomplete popup not expected during test " + testNum);
- }
-}
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup();
-registerPopupShownListener(popupShownListener);
+ // Initially, nothing is selected. We want to try to wrap around, which means
+ // pressing down 5 times (note that the 4th time will deselect everything in
+ // the popup).
+ repeatKey("down", 5);
+ yield closeAutoCompletePopup();
+ // We should have wrapped around back to the first entry, so the field should
+ // be at the first entry now.
+ is(input.value, VALUES[0],
+ "Should have wrapped around and selected the first entry.");
-/*
- * Main section of test...
- *
- * This is a bit hacky, as many operations happen asynchronously.
- * Various mechanisms call runTests as a result of operations:
- * - set expectingPopup to true, and the next test will occur when the autocomplete popup is shown
- * - call waitForMenuChange(x) to run the next test when the autocomplete popup to have x items in it
- * - addEntry calls runs the test when an entry has been added
- * - some tests scroll the window. This is because the form fill controller happens to scroll
- * the field into view near the end of the search, and there isn't any other good notification
- * to listen to for when the search is complete.
- * - some items still use setTimeout
- */
-function runTest() {
- testNum++;
+ // Now let's try going the other direction, without anything initially
+ // selected...
+ input.value = "";
+ yield openAutoCompletePopup();
- ok(true, "Starting test #" + testNum);
+ doKey("up");
+ yield closeAutoCompletePopup();
+
+ // We should have wrapped around over the top and gone back to the bottom to
+ // the last entry.
+ is(input.value, VALUES[2],
+ "Should have wrapped around and selected the last entry.");
- switch (testNum) {
- case 1:
- // Make sure initial form is empty.
- checkForm("");
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Now let's try going the same direction again, but after having first
+ // selected the first item.
+ input.value = "";
+ yield openAutoCompletePopup();
+
+ // Select the first item
+ doKey("down");
+ // Selects nothing, and then the last item
+ repeatKey("up", 2);
- case 2:
- checkMenuEntries(["value1", "value2", "value3", "value4"], testNum);
- // Check first entry
- doKey("down");
- checkForm(""); // value shouldn't update
- doKey("return"); // not "enter"!
- checkForm("value1");
+ yield closeAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // We should have wrapped around over the top and gone back to the bottom to
+ // the last entry.
+ is(input.value, VALUES[2],
+ "Should have wrapped around and selected the last entry.");
+
+ // And finally, let's see if we can go around twice.
+ input.value = "";
+ yield openAutoCompletePopup();
- case 3:
- // Check second entry
- doKey("down");
- doKey("down");
- doKey("return"); // not "enter"!
- checkForm("value2");
+ // Select the first item
+ doKey("down");
+ // Selects nothing, then the last item, then goes all the way
+ // back to the first item, unselects, and then selects the last
+ // item again. That should be 6 up key events in total.
+ repeatKey("up", 6);
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ yield closeAutoCompletePopup();
- case 4:
- // Check third entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("value3");
+ // We should have wrapped around over the top and gone back to the bottom to
+ // the last entry.
+ is(input.value, VALUES[2],
+ "Should have wrapped around and selected the last entry.");
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests that we can select an item using the left and right cursor keys.
+ */
+add_task(function* test_left_and_right_cursors() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "value1",
+ ];
- case 5:
- // Check fourth entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("value4");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Select the first item...
+ doKey("down");
+ // using the right cursor key.
+ yield closeAutoCompletePopup("right");
+
+ is(input.value, VALUES[0],
+ "Should have selected the first item.");
+
+ input.value = "";
- case 6:
- // Check first entry (wraparound)
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("down"); // deselects
- doKey("down");
- doKey("return");
- checkForm("value1");
+ // Now do this again, but with the left cursor.
+ yield openAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Select the first item...
+ doKey("down");
+ yield closeAutoCompletePopup("left");
- case 7:
- // Check the last entry via arrow-up
- doKey("up");
- doKey("return");
- checkForm("value4");
+ is(input.value, VALUES[0],
+ "Should have selected the first item.");
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 8:
- // Check the last entry via arrow-up
- doKey("down"); // select first entry
- doKey("up"); // selects nothing!
- doKey("up"); // select last entry
- doKey("return");
- checkForm("value4");
+/**
+ * Tests that we can get to the top or bottom of the page of items
+ * using Page Up and Page Down.
+ */
+add_task(function* test_page_up_page_down() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "value1",
+ "value2",
+ "value3",
+ "value4",
+ ];
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup();
+
+ // Select the second item to start...
+ repeatKey("down", 2);
- case 9:
- // Check the last entry via arrow-up (wraparound)
- doKey("down");
- doKey("up"); // deselects
- doKey("up"); // last entry
- doKey("up");
- doKey("up");
- doKey("up"); // first entry
- doKey("up"); // deselects
- doKey("up"); // last entry
- doKey("return");
- checkForm("value4");
+ // Then hitting Page Up should select the first item.
+ doKey("page_up");
+ yield closeAutoCompletePopup();
+
+ is(input.value, VALUES[0],
+ "Should have selected the first item.");
+
+ input.value = "";
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Now let's try Page Down. This should select the
+ // last item in the currently displayed list of
+ // results.
- case 10:
- // Set first entry w/o triggering autocomplete
- doKey("down");
- doKey("right");
- checkForm("value1");
+ yield openAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ doKey("page_down");
+ yield closeAutoCompletePopup();
- case 11:
- // Set first entry w/o triggering autocomplete
- doKey("down");
- doKey("left");
- checkForm("value1");
+ is(input.value, VALUES[3],
+ "Should have selected the last item.");
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests that items can be deleted from the form autocomplete popup.
+ */
+add_task(function* test_deletion() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ // A const Array is still mutable (it just can't be re-assigned). In this
+ // test, note that items will be removed from this Array.
+ const VALUES = [
+ "value1",
+ "value2",
+ "value3",
+ "value4",
+ ];
+ const DUMMY_VALUE = "init";
+ const DELETION_ORDER = [
+ "value1",
+ "value3",
+ "value4",
+ "value2",
+ ];
- case 12:
- // Check first entry (page up)
- doKey("down");
- doKey("down");
- doKey("page_up");
- doKey("return");
- checkForm("value1");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ // This loop is going to open up the autocomplete popup, and then remove
+ // items in the list according to DELETION_ORDER. It will then ensure that
+ // the selected item is correctly set after the deletion, if applicable.
+ for (let toDelete of DELETION_ORDER) {
+ input.value = "";
+ info("Values is: " + JSON.stringify(VALUES) + " and we're deleting " + toDelete);
+ let indexToDelete = VALUES.indexOf(toDelete);
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 13:
- // Check last entry (page down)
- doKey("down");
- doKey("page_down");
- doKey("return");
- checkForm("value4");
+ let results = yield openAutoCompletePopup();
- // Trigger autocomplete popup
- testNum = 49;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Make sure we're showing what's expected
+ assertResultsMatch(results, VALUES);
- /* Test removing entries from the dropdown */
+ // If we add 1 to the indexToDelete, that's how many times we need to
+ // hit the down arrow key to select the value we're going to get rid
+ // of.
+ repeatKey("down", indexToDelete + 1);
- case 50:
- checkMenuEntries(["value1", "value2", "value3", "value4"], testNum);
- // Delete the first entry (of 4)
- setForm("value");
- doKey("down");
+ // Now delete the value!
+ doKey("delete", Event.SHIFT_MASK);
+
+ info("Waiting for menu change to length " + (VALUES.length - 1) + "\n");
+ yield notifyMenuChanged(VALUES.length - 1);
- // On OS X, shift-backspace and shift-delete work, just delete does not.
- // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
- if (SpecialPowers.OS == "Darwin")
- doKey("back_space", shiftModifier);
- else
- doKey("delete", shiftModifier);
+ // Now let's make sure that the item was deleted from FormHistory.
+ let count = yield countEntries(FIELD_NAME, toDelete);
+ is(count, 0, "Should have removed the autocomplete value.");
- // This tests that on OS X shift-backspace didn't delete the last character
- // in the input (bug 480262).
- waitForMenuChange(3);
- break;
+ // Update our internal notion of what is being displayed.
+ VALUES.splice(indexToDelete, 1);
- case 51:
- checkForm("value");
- countEntries("field1", "value1",
- function (num) {
- ok(!num, testNum + " checking that f1/v1 was deleted");
- runTest();
- });
- break;
+ // If there is an item after the one we just deleted, it should
+ // now be selected. Otherwise, it'll be the one before, unless
+ // there's nothing left.
+ let expected;
+ if (VALUES.length) {
+ expected = VALUES[indexToDelete] || VALUES[indexToDelete - 1];
+ }
- case 52:
- doKey("return");
- checkForm("value2");
+ yield closeAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ if (expected) {
+ is(input.value, expected,
+ "Should have selected the right value now that one was " +
+ "deleted.");
+ }
- case 53:
- checkMenuEntries(["value2", "value3", "value4"], testNum);
- // Check the new first entry (of 3)
- doKey("down");
- doKey("return");
- checkForm("value2");
-
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // We only need to do the following if there are any items left
+ // over - otherwise, no autocomplete popups are expected anymore.
+ if (VALUES.length) {
+ // Now if we re-open the popup, that first value should not be there.
+ results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ yield closeAutoCompletePopup("escape");
+ }
+ }
+ });
+});
- case 54:
- // Delete the second entry (of 3)
- doKey("down");
- doKey("down");
- doKey("delete", shiftModifier);
- waitForMenuChange(2);
- break;
+/**
+ * Tests that we do not show an autocomplete popup for fields
+ * and forms that have the autocomplete attribute set to "off".
+ */
+add_task(function* test_autocomplete_off() {
+ const FORM_NUM = 2;
+ const FIELD_NAME = "field2";
+ const VALUES = [
+ "value1"
+ ];
- case 55:
- checkForm("");
- countEntries("field1", "value3",
- function (num) {
- ok(!num, testNum + " checking that f1/v3 was deleted");
- runTest();
- });
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup();
+ // Select the first entry
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[0], "Should have selected the first value.")
- case 56:
- doKey("return");
- checkForm("value4")
-
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Look at form 3, try to trigger autocomplete popup on an item
+ // with the same name.
+ let otherInput = $_(3, "field2");
+ otherInput.value = "";
+ otherInput.focus();
- case 57:
- checkMenuEntries(["value2", "value4"], testNum);
- // Check the new first entry (of 2)
- doKey("down");
- doKey("return");
- checkForm("value2");
+ doKey("down");
+ yield notifyMenuChanged(0);
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Ensure there's no autocomplete dropdown (autocomplete=off is present on
+ // the input).
+ doKey("down");
+ doKey("return");
+ is(otherInput.value, "", "Should not have filled in this input.");
- case 58:
- // Delete the last entry (of 2)
- doKey("down");
- doKey("down");
- doKey("delete", shiftModifier);
- checkForm("");
- waitForMenuChange(1);
- break;
+ // Look at form 4, try to trigger autocomplete popup
+ otherInput = $_(4, "field2");
+ otherInput.value = "";
+ otherInput.focus();
+
+ doKey("down");
+ yield notifyMenuChanged(0);
- case 59:
- countEntries("field1", "value4",
- function (num) {
- ok(!num, testNum + " checking that f1/v4 was deleted");
- runTest();
- });
- break;
-
- case 60:
- doKey("return");
- checkForm("value2");
+ // Ensure there's no autocomplete dropdown (autocomplete=off is present on
+ // the form this time).
+ doKey("down");
+ doKey("return");
+ is(otherInput.value, "", "Should not have filled in this input.");
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 61:
- checkMenuEntries(["value2"], testNum);
- // Check the new first entry (of 1)
- doKey("down");
- doKey("return");
- checkForm("value2");
+/**
+ * Tests that the autocomplete popup will update itself if the
+ * user types in more characters after it has been opened.
+ */
+add_task(function* test_autocomplete_while_typing() {
+ const FORM_NUM = 5;
+ const FIELD_NAME = "field3";
+ const VALUES = [
+ "a",
+ "aa",
+ "aaz",
+ "aa\xe6", // 0xae == latin ae pair (0xc6 == AE)
+ "az",
+ "z",
+ ];
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 62:
- // Delete the only remaining entry
- doKey("down");
- doKey("delete", shiftModifier);
- waitForMenuChange(0);
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ // open the autocomplete popup with "a"
+ let results = yield openAutoCompletePopup("a");
+ assertResultsMatch(results, ["a", "aa", "aaz", "aa\xe6", "az"]);
- case 63:
- checkForm("");
- countEntries("field1", "value2",
- function (num) {
- ok(!num, testNum + " checking that f1/v2 was deleted");
- runTest();
- });
- break;
+ sendChar("a");
+ // input is now "aa";
+ results = yield notifyMenuChanged(3);
+ assertResultsMatch(results, ["aa", "aaz", "aa\xe6"]);
- case 64:
- // Look at form 2, trigger autocomplete popup
- input = $_(2, "field2");
- testNum = 99;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ sendChar("\xc6");
+ // input is now "aa\xc6"
+
+ results = yield notifyMenuChanged(1);
+ assertResultsMatch(results, ["aa\xe6"]);
- /* Test entries with autocomplete=off */
-
- case 100:
- // Select first entry
- doKey("down");
- doKey("return");
- checkForm("value1");
+ doKey("back_space");
+ // input is now "aa"
+ results = yield notifyMenuChanged(3);
+ assertResultsMatch(results, ["aa", "aaz", "aa\xe6"]);
- // Look at form 3, try to trigger autocomplete popup
- input = $_(3, "field2");
- restoreForm();
- // Sometimes, this will fail if scrollTo(0, 0) is called, so that doesn't
- // happen here. Fortunately, a different input is used from the last test,
- // so a scroll should still occur.
- doKey("down");
- waitForScroll();
- break;
+ doKey("back_space");
+ // input is now "a"
+ results = yield notifyMenuChanged(5);
+ assertResultsMatch(results, ["a", "aa", "aaz", "aa\xe6", "az"]);
+
+ sendChar("z");
+ // input is now "az"
+ results = yield notifyMenuChanged(2);
+ assertResultsMatch(results, ["az", "aaz"]);
+
+ yield closeAutoCompletePopup("left");
+ // input is still "az", but carat is after the "a"
- case 101:
- // Ensure there's no autocomplete dropdown (autocomplete=off is present)
- doKey("down");
- doKey("return");
- checkForm("");
-
- // Look at form 4, try to trigger autocomplete popup
- input = $_(4, "field2");
- restoreForm();
- doKey("down");
- waitForMenuChange(0);
- break;
-
- case 102:
- // Ensure there's no autocomplete dropdown (autocomplete=off is present)
- doKey("down");
- doKey("return");
- checkForm("");
-
- // Look at form 5, try to trigger autocomplete popup
- input = $_(5, "field3");
- restoreForm();
- testNum = 199;
- expectPopup();
- input.focus();
- sendChar("a");
- break;
+ // Check case-insensitivity.
+ results = yield openAutoCompletePopup("A");
+ // input is now "aAz"
+ assertResultsMatch(results, ["aaz"]);
- /* Test filtering as characters are typed. */
-
- case 200:
- checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"], testNum);
- input.focus();
- sendChar("a");
- waitForMenuChange(3);
- break;
+ yield addFormHistory("field3", ["aazq"]);
+ // check that results were cached
+ input.focus();
+ yield closeAutoCompletePopup("right");
+ // input is still "aAz" but carat is after "z"
+ sendChar("q");
+ // input is now "aAzq"
+ yield notifyMenuChanged(0);
+ yield addFormHistory("field3", ["aazqq"]);
+ input.focus();
+ window.scrollTo(0, 0);
+ sendChar("q");
+ // check that empty results were cached - bug 496466
+ yield notifyMenuChanged(0);
+ doKey("escape");
+ });
+});
- case 201:
- checkMenuEntries(["aa", "aaz", "aa\xe6"], testNum);
- input.focus();
- sendChar("\xc6");
- waitForMenuChange(1);
- break;
-
- case 202:
- checkMenuEntries(["aa\xe6"], testNum);
- doKey("back_space");
- waitForMenuChange(3);
- break;
-
- case 203:
- checkMenuEntries(["aa", "aaz", "aa\xe6"], testNum);
- doKey("back_space");
- waitForMenuChange(5);
- break;
+/**
+ * Tests that autocomplete popups should be shown if the inputted
+ * characters match any part of the form history strings for a
+ * named field.
+ */
+add_task(function* test_substring_matches() {
+ const FORM_NUM = 6;
+ const FIELD_NAME = "field4";
+ const VALUES = [
+ "a\xe6",
+ "aa a\xe6",
+ "aba\xe6",
+ "bc d\xe6",
+ ];
- case 204:
- checkMenuEntries(["a", "aa", "aaz", "aa\xe6", "az"], testNum);
- input.focus();
- sendChar("z");
- waitForMenuChange(2);
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup("a");
+ // Input should now be "a";
+ // We should get alphabetical results for the first character
+ assertResultsMatch(results, ["aa a\xe6", "aba\xe6", "a\xe6"]);
- case 205:
- checkMenuEntries(["az", "aaz"], testNum);
- input.focus();
- doKey("left");
- expectPopup();
- // Check case-insensitivity.
- sendChar("A");
- break;
+ sendChar("\xe6");
+ // Input should now be "a\xe6"
+ results = yield notifyMenuChanged(3, "a\xe6");
+ // prefix match comes first, then word boundary match
+ // followed by substring match
+ assertResultsMatch(results, ["a\xe6", "aa a\xe6", "aba\xe6"]);
- case 206:
- checkMenuEntries(["aaz"], testNum);
- addEntry("field3", "aazq");
- break;
-
- case 207:
- // check that results were cached
- input.focus();
- doKey("right");
- sendChar("q");
- waitForMenuChange(0);
- break;
+ input.value = "";
+ sendChar("b");
+ // Input should now be "b"
+ results = yield notifyMenuChanged(1, "bc d\xe6");
+ assertResultsMatch(results, ["bc d\xe6"]);
- case 208:
- // check that results were cached
- checkMenuEntries([], testNum);
- addEntry("field3", "aazqq");
- break;
+ sendChar(" ");
+ // Input should now be "b "
+ results = yield notifyMenuChanged(1);
+ assertResultsMatch(results, ["bc d\xe6"]);
- case 209:
- input.focus();
- window.scrollTo(0, 0);
- sendChar("q");
- waitForMenuChange(0);
- break;
+ // check multi-word substring matches
+ sendChar("\xc6");
+ // Input should now be "b \xc6"
+ results = yield notifyMenuChanged(2);
+ assertResultsMatch(results, ["bc d\xe6", "aba\xe6"]);
- case 210:
- // check that empty results were cached - bug 496466
- checkMenuEntries([], testNum);
- doKey("escape");
+ yield closeAutoCompletePopup("left");
+ // Input is still "b \xc6", but the carat is just after the " "
+ results = yield openAutoCompletePopup("d");
+ // Input should now be "b d\xc6"
+ assertResultsMatch(results, ["bc d\xe6"]);
- // Look at form 6, try to trigger autocomplete popup
- input = $_(6, "field4");
- restoreForm();
- testNum = 249;
- expectPopup();
- input.focus();
- sendChar("a");
- break;
-
- /* Test substring matches and word boundary bonuses */
+ // Now check inserting in multi-word searches
+ sendChar("z");
+ // Input should now be "b dz\xc6"
+ results = yield notifyMenuChanged(0);
+ assertResultsMatch(results, []);
+ yield closeAutoCompletePopup();
+ });
+});
- case 250:
- // alphabetical results for first character
- checkMenuEntries(["aa a\xe6", "aba\xe6", "a\xe6"], testNum);
- input.focus();
-
- sendChar("\xe6");
- waitForMenuChange(3, "a\xe6");
- break;
+/**
+ * Tests that autocomplete popups will only return results
+ * that abide by the maxLength attribute on the input field.
+ */
+add_task(function* test_max_length() {
+ const FORM_NUM = 7;
+ const FIELD_NAME = "field5";
+ const VALUES = [
+ "1",
+ "12",
+ "123",
+ "1234",
+ ];
- case 251:
- // prefix match comes first, then word boundary match
- // followed by substring match
- checkMenuEntries(["a\xe6", "aa a\xe6", "aba\xe6"], testNum);
-
- restoreForm();
- input.focus();
- sendChar("b");
- waitForMenuChange(1, "bc d\xe6");
- break;
-
- case 252:
- checkMenuEntries(["bc d\xe6"], testNum);
- input.focus();
- sendChar(" ");
- waitForMenuChange(1);
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, ["1", "12", "123", "1234"]);
+ yield closeAutoCompletePopup();
- case 253:
- // check that trailing space has no effect after single char.
- checkMenuEntries(["bc d\xe6"], testNum);
- input.focus();
- sendChar("\xc6");
- waitForMenuChange(2);
- break;
+ // This first loop reduces the maxLength on the input from
+ // VALUES.length until 1, and ensures that only the strings
+ // with the right lengths appear in the popup.
+ for (let maxLength = VALUES.length; maxLength > 0; --maxLength) {
+ input.maxLength = maxLength;
+ let expectedResults = VALUES.filter(s => (s.length <= maxLength));
+ input.focus();
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, expectedResults);
+ yield closeAutoCompletePopup();
+ input.blur();
+ }
- case 254:
- // check multi-word substring matches
- checkMenuEntries(["bc d\xe6", "aba\xe6"]);
- input.focus();
- expectPopup();
- doKey("left");
- sendChar("d");
- break;
+ // The 0-case - we expect no popup if maxLength is 0.
+ input.maxLength = 0;
+ input.focus();
+ yield notifyMenuChanged(0);
+ input.blur();
- case 255:
- // check inserting in multi-word searches
- checkMenuEntries(["bc d\xe6"], testNum);
- input.focus();
- sendChar("z");
- waitForMenuChange(0);
- break;
-
- case 256:
- checkMenuEntries([], testNum);
+ // This second loop works the input maxLength back up from 1 to
+ // VALUES.length, ensuring that only the strings with the right
+ // lengths appear in the popup.
+ for (let maxLength = 1; maxLength <= VALUES.length; ++maxLength) {
+ input.maxLength = maxLength;
+ let expectedResults = VALUES.filter(s => (s.length <= maxLength));
+ input.focus();
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, expectedResults);
+ yield closeAutoCompletePopup();
+ input.blur();
+ }
+ });
+});
- // Look at form 7, try to trigger autocomplete popup
- input = $_(7, "field5");
- testNum = 299;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests autocomplete on a field of type "email".
+ */
+add_task(function* test_email_field() {
+ const FORM_NUM = 8;
+ const FIELD_NAME = "field6";
+ // Non email values are also currently allowed.
+ const VALUES = [
+ "123",
+ "holtzmann@ghostbusters.org",
+ "mconley@mozilla.com",
+ "value",
+ ];
- case 300:
- checkMenuEntries(["1", "12", "123", "1234"], testNum);
- input.maxLength = 4;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
-
- case 301:
- checkMenuEntries(["1", "12", "123", "1234"], testNum);
- input.maxLength = 3;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ yield closeAutoCompletePopup();
+ });
+});
- case 302:
- checkMenuEntries(["1", "12", "123"], testNum);
- input.maxLength = 2;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
-
- case 303:
- checkMenuEntries(["1", "12"], testNum);
- input.maxLength = 1;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
+/**
+ * Tests autocomplete on a field of type "tel".
+ */
+add_task(function* test_tel_field() {
+ const FORM_NUM = 9;
+ const FIELD_NAME = "field7";
+ // Non phonenumber values are also allowed.
+ const VALUES = [
+ "1-800-267-2001", // Alaaaaaaarm Force
+ "555-555-5555",
+ "Ask me about LOOM!",
+ "value",
+ ];
- case 304:
- checkMenuEntries(["1"], testNum);
- input.maxLength = 0;
- doKey("escape");
- doKey("down");
- waitForMenuChange(0);
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ yield closeAutoCompletePopup();
+ });
+});
- case 305:
- checkMenuEntries([], testNum);
- input.maxLength = 4;
-
- // now again with a character typed
- input.focus();
- sendChar("1");
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
+/**
+ * Tests autocomplete on a field of type "url".
+ */
+add_task(function* test_url_field() {
+ const FORM_NUM = 10;
+ const FIELD_NAME = "field8";
+ // Non URL values are also allowed.
+ const VALUES = [
+ "ftp://ftp.mozilla.org",
+ "http://www.mozilla.org",
+ "https://www.mozilla.org",
+ "value",
+ ];
- case 306:
- checkMenuEntries(["1", "12", "123", "1234"], testNum);
- input.maxLength = 3;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
-
- case 307:
- checkMenuEntries(["1", "12", "123"], testNum);
- input.maxLength = 2;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
-
- case 308:
- checkMenuEntries(["1", "12"], testNum);
- input.maxLength = 1;
- expectPopup();
- doKey("escape");
- doKey("down");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ yield closeAutoCompletePopup();
+ });
+});
- case 309:
- checkMenuEntries(["1"], testNum);
- input.maxLength = 0;
- doKey("escape");
- doKey("down");
- waitForMenuChange(0);
- break;
-
- case 310:
- checkMenuEntries([], testNum);
+/**
+ * Tests autocomplete on a field of type "search".
+ */
+add_task(function* test_search_field() {
+ const FORM_NUM = 11;
+ const FIELD_NAME = "field9";
+ const VALUES = [
+ "how do I add Content Security Policy to apache?",
+ "how do I contact yahoo",
+ "how do I delete my facebook account?",
+ "how do I know if I'm running e10s",
+ "value",
+ ];
- input = $_(8, "field6");
- testNum = 399;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ yield closeAutoCompletePopup();
+ });
+});
- case 400:
- case 401:
- case 402:
- case 403:
- checkMenuEntries(["value"], testNum);
- doKey("down");
- doKey("return");
- checkForm("value");
+/**
+ * Tests autocomplete on a field of type "date".
+ */
+add_task(function* test_date_field() {
+ const FORM_NUM = 14;
+ const FIELD_NAME = "field11";
+ const VALUES = [
+ "2010-10-10",
+ "2011-01-01",
+ ];
- if (testNum == 400) {
- input = $_(9, "field7");
- } else if (testNum == 401) {
- input = $_(10, "field8");
- } else if (testNum == 402) {
- input = $_(11, "field9");
- } else if (testNum == 403) {
- todo(false, "Fix input type=number");
- input = $_(12, "field10");
- }
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ repeatKey("down", 2);
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[1], "Should have selected the second value.");
+ });
+});
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 404:
- checkMenuEntries(["42"], testNum);
- doKey("down");
- doKey("return");
- checkForm("42");
-
- input = $_(14, "field11");
- restoreForm();
- expectPopup();
- doKey("down");
- break;
+/**
+ * Tests autocomplete on a field of type "time".
+ */
+add_task(function* test_time_field() {
+ const FORM_NUM = 15;
+ const FIELD_NAME = "field12";
+ const VALUES = [
+ "01:13",
+ "21:21",
+ ];
- case 405:
- checkMenuEntries(["2010-10-10"]);
- doKey("down");
- doKey("return");
- checkForm("2010-10-10");
-
- input = $_(15, "field12");
- restoreForm();
- expectPopup();
- doKey("down");
- break;
-
- case 406:
- checkMenuEntries(["21:21"]);
- doKey("down");
- doKey("return");
- checkForm("21:21");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[0], "Should have selected the first value.");
+ });
+});
- input = $_(16, "field13");
- restoreForm();
- doKey("down");
- waitForMenuChange(0);
- break;
+/**
+ * Tests that there is no autocomplete on a field of type "range".
+ */
+add_task(function* test_range_field() {
+ const FORM_NUM = 16;
+ const FIELD_NAME = "field13";
+ const VALUES = [
+ "32", // Should never be visible, since "range" has no autocomplete.
+ ];
- case 407:
- checkMenuEntries([]); // type=range does not have a drop down menu
- doKey("down");
- doKey("return");
- checkForm("30"); // default (midway between minimum (0) and maximum (64)) - step
-
- input = $_(17, "field14");
- restoreForm();
- waitForMenuChange(0);
- break;
-
- case 408:
- checkMenuEntries([]); // type=color does not have a drop down menu
- checkForm("#000000"); // default color value
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ doKey("down");
+ yield notifyMenuChanged(0);
+ doKey("down");
+ doKey("return");
+ // This range should have a max of 64, and start at the default
+ // midway of 32. Two presses of "down" with a step of 1 should
+ // decrease the value to 30.
+ is(input.value, "30", "Got expected value on the range.");
+ });
+});
- input = $_(18, "field15");
- restoreForm();
- expectPopup();
- doKey("down");
- break;
-
- case 409:
- checkMenuEntries(["2016-08"]);
- doKey("down");
- doKey("return");
- checkForm("2016-08");
+/**
+ * Tests that there is no autocomplete on a field of type "color".
+ */
+add_task(function* test_color_field() {
+ const FORM_NUM = 17;
+ const FIELD_NAME = "field14";
+ const VALUES = [
+ "#ffffff", // Should never be visible, since "color" has no autocomplete.
+ ];
- input = $_(19, "field16");
- restoreForm();
- expectPopup();
- doKey("down");
- break;
-
- case 410:
- checkMenuEntries(["2016-W32"]);
- doKey("down");
- doKey("return");
- checkForm("2016-W32");
-
- addEntry("field1", "value1");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ doKey("down");
+ yield notifyMenuChanged(0);
+ // This range should have a max of 64, and start at the default
+ // midway of 32. Two presses of "down" with a step of 1 should
+ // decrease the value to 30.
+ is(input.value, "#000000", "Got expected default value on the color.");
+ });
+});
- case 411:
- input = $_(1, "field1");
- // Go to test 500.
- testNum = 499;
-
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests autocomplete on a field of type "month".
+ */
+add_task(function* test_month_field() {
+ const FORM_NUM = 18;
+ const FIELD_NAME = "field15";
+ const VALUES = [
+ "2016-08",
+ ];
- // Check that the input event is fired.
- case 500:
- input.addEventListener("input", function(event) {
- input.removeEventListener("input", arguments.callee, false);
- ok(true, testNum + " oninput should have been received");
- ok(event.bubbles, testNum + " input event should bubble");
- ok(event.cancelable, testNum + " input event should be cancelable");
- }, false);
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[0], "Should have selected the first value.");
+ });
+});
- doKey("down");
- checkForm("");
- doKey("return");
- checkForm("value1");
- testNum = 599;
- setTimeout(runTest, 100);
- break;
-
- case 600:
- // check we don't show autocomplete for searchbar-history
- input = $_(13, "searchbar-history");
+/**
+ * Tests autocomplete on a field of type "week".
+ */
+add_task(function* test_week_field() {
+ const FORM_NUM = 19;
+ const FIELD_NAME = "field16";
+ const VALUES = [
+ "2016-W32",
+ "2016-W44",
+ ];
- // Trigger autocomplete popup
- checkForm("");
- restoreForm();
- doKey("down");
- waitForMenuChange(0);
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[0], "Should have selected the first value.");
+ });
+});
- case 601:
- checkMenuEntries([], testNum);
- input.blur();
- SimpleTest.finish();
- return;
+/**
+ * Tests that "input" events are fired when selecting an item
+ * from an autocomplete list.
+ */
+add_task(function* test_input_event_is_fired() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "value",
+ ];
- default:
- ok(false, "Unexpected invocation of test #" + testNum);
- SimpleTest.finish();
- return;
- }
-}
-
-function addEntry(name, value)
-{
- updateFormHistory({ op : "add", fieldname : name, value: value }, runTest);
-}
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, VALUES);
+ doKey("down");
-// Runs the next test when scroll event occurs
-function waitForScroll()
-{
- addEventListener("scroll", function() {
- if (!window.pageYOffset)
- return;
-
- removeEventListener("scroll", arguments.callee, false);
- setTimeout(runTest, 100);
- }, false);
-}
-
-function waitForMenuChange(expectedCount, expectedFirstValue)
-{
- notifyMenuChanged(expectedCount, expectedFirstValue, runTest);
-}
+ let inputEventPromise = waitForInputEvent(input);
+ // closeAutoCompletePopup defaults to closing with "return",
+ // which should select the first value.
+ yield closeAutoCompletePopup();
+ yield inputEventPromise;
+ is(input.value, VALUES[0], "Should have selected the first value.");
+ });
+});
-function checkMenuEntries(expectedValues, testNum) {
- var actualValues = getMenuEntries();
- is(actualValues.length, expectedValues.length, testNum + " Checking length of expected menu");
- for (var i = 0; i < expectedValues.length; i++)
- is(actualValues[i], expectedValues[i], testNum + " Checking menu entry #"+i);
-}
+/**
+ * Tests that the field name "searchbar-history" is blacklisted
+ * from autocomplete popups (See bug 461820).
+ */
+add_task(function* test_searchbar_history_blacklist() {
+ const FORM_NUM = 13;
+ const FIELD_NAME = "searchbar-history";
+ const VALUES = [
+ "definitely",
+ "should",
+ "not",
+ "see",
+ "any",
+ "of",
+ "these.",
+ ];
-function startTest() {
- setupFormHistory(function() {
- runTest();
- });
-}
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ doKey("down");
+ yield notifyMenuChanged(0);
+ });
+});
-window.onload = startTest;
-
-SimpleTest.waitForExplicitFinish();
-SimpleTest.requestFlakyTimeout("untriaged");
</script>
</pre>
</body>
</html>
--- a/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete_with_list.html
@@ -1,14 +1,15 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Form History Autocomplete</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="satchel_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Form History test: form field autocomplete
<p id="display"></p>
<!-- we presumably can't hide the content for this test. -->
@@ -37,470 +38,355 @@ Form History test: form field autocomple
<option value="Reddit">PASS2</option>
<option value="final"></option>
</datalist>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
-/** Test for Form History autocomplete **/
+/** Test for Form History autocomplete with datalists **/
-var input = $_(1, "field1");
-const shiftModifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
+let gDataListLabels;
+let gDataListValues;
-function setupFormHistory(aCallback) {
- updateFormHistory([
- { op : "remove" },
- { op : "add", fieldname : "field1", value : "historyvalue" },
- { op : "add", fieldname : "field2", value : "othervalue" },
- ], aCallback);
-}
-
-function setForm(value) {
- input.value = value;
- input.focus();
-}
-
-// Restore the form to the default state.
-function restoreForm() {
- setForm("");
-}
+add_task(function* setup() {
+ let datalist = document.getElementById("suggest");
+ gDataListLabels = Array.from(datalist.options).map((option) => {
+ // The autocomplete popup will show the label if one exists,
+ // and fallback to the value if one doesn't.
+ return option.label || option.value;
+ });
-// Check for expected form data.
-function checkForm(expectedValue) {
- var formID = input.parentNode.id;
- is(input.value, expectedValue, "Checking " + formID + " input");
-}
+ gDataListValues = Array.from(datalist.options).map((option) => {
+ // The autocomplete popup will show the label if one exists,
+ // and fallback to the value if one doesn't.
+ return option.value;
+ });
-var testNum = 0;
-var prevValue;
-var expectingPopup = false;
+ listenForUnexpectedPopupShown();
+});
-function expectPopup() {
- info("expecting popup for test " + testNum);
- expectingPopup = true;
-}
-
-function popupShownListener() {
- info("popup shown for test " + testNum);
- if (expectingPopup) {
- expectingPopup = false;
- SimpleTest.executeSoon(runTest);
- }
- else {
- ok(false, "Autocomplete popup not expected during test " + testNum);
- }
-}
-
-registerPopupShownListener(popupShownListener);
+/**
+ * Tests that autocomplete will show Form History entries
+ * along with datalist entries.
+ */
+add_task(function* test_form_history_datalist_mix() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "historyvalue",
+ ];
-/*
-* Main section of test...
-*
-* This is a bit hacky, as many operations happen asynchronously.
-* Various mechanisms call runTests as a result of operations:
-* - set expectingPopup to true, and the next test will occur when the autocomplete popup is shown
-* - call waitForMenuChange(x) to run the next test when the autocomplete popup to have x items in it
-*/
-function runTest() {
- testNum++;
-
- info("Starting test #" + testNum);
-
- switch (testNum) {
- case 1:
- // Make sure initial form is empty.
- checkForm("");
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
- case 2:
- checkMenuEntries(["historyvalue", "PASS1", "PASS2", "final"], testNum);
- // Check first entry
- doKey("down");
- checkForm(""); // value shouldn't update
- doKey("return"); // not "enter"!
- checkForm("historyvalue");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ // Form History entries should always be displayed before datalist
+ // entries. Note that we're showing the labels in the popup, which
+ // might not equal their values.
+ assertResultsMatch(results, [...VALUES, ...gDataListLabels]);
+ doKey("down");
+ is(input.value, "", "Field should not have updated yet.");
+ yield closeAutoCompletePopup();
+ is(input.value, VALUES[0], "Should have selected the first value.");
+ input.value = "";
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 3:
- // Check second entry
- doKey("down");
- doKey("down");
- doKey("return"); // not "enter"!
- checkForm("Google");
+ for (let i = 0; i < gDataListLabels.length; ++i) {
+ yield openAutoCompletePopup();
+ // Skip past the first Form History entry, and select the first
+ // datalist entry.
+ repeatKey("down", 2);
+ // Then skip down to the datalist entry we want.
+ repeatKey("down", i);
+ is(input.value, "", "Field should not have updated yet.");
+ yield closeAutoCompletePopup();
+ is(input.value, gDataListValues[i],
+ "Should have selected the right datalist value.");
+ input.value = "";
+ }
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests that Form History and datalist entries can be deleted
+ * from the autocomplete popup.
+ */
+add_task(function* test_form_history_datalist_deletion() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "historyvalue1",
+ "historyvalue2",
+ ];
- case 4:
- // Check third entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("Reddit");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, [...VALUES, ...gDataListLabels]);
+ let total = results.length;
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Select the first item...
+ doKey("down");
- case 5:
- // Check fourth entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("final");
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 6:
- //Delete the first entry (of 3)
- doKey("down");
- doKey("delete", shiftModifier);
- waitForMenuChange(3);
- break;
+ // Delete each entry one by one.
+ for (let i = 1; i <= total; ++i) {
+ doKey("delete", Event.SHIFT_MASK);
+ yield notifyMenuChanged(total - i);
+ }
- case 7:
- checkForm("");
- countEntries("field1", "historyvalue",
- function (num) {
- ok(!num, testNum + " checking that form history value was deleted");
- runTest();
- });
- break;
-
- case 8:
- doKey("return");
- checkForm("Google")
+ // With the last value gone, the popup should be
+ // closed. If we blur and refocus, the datalist entries
+ // should be back.
+ input.blur();
+ input.focus();
+ results = yield openAutoCompletePopup();
+ // Should only be showing the datalist entries now.
+ assertResultsMatch(results, gDataListLabels);
+ yield closeAutoCompletePopup();
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 9:
- //Test deletion
- checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
- // Check the new first entry (of 3)
- doKey("down");
- doKey("return");
- checkForm("Google");
-
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests that a autocomplete results list composed of Form History
+ * and datalist entries will update as characters are put into the
+ * input and the search is refined.
+ */
+add_task(function* test_search_datalist_values() {
+ const FORM_NUM = 1;
+ const FIELD_NAME = "field1";
+ const VALUES = [
+ "historyvalue1",
+ "historyvalue2",
+ "historyvalue3",
+ ];
- case 10:
- // Test autocompletion of datalists with cached results.
- sendString("PAS");
- waitForMenuChange(2);
- break;
-
- case 11:
- // Continuation of test 10
- sendString("S1");
- waitForMenuChange(1);
- break;
-
- case 12:
- doKey("down");
- doKey("return");
- checkForm("Google");
-
- // Trigger autocomplete popup
- // Look at form 3, try to trigger autocomplete popup
- input.value = "";
- input = $_(3, "field2");
- testNum = 99;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup("PAS");
+ // Input should now be "PAS"
+ is(results.length, 2, "Expect 2 entries for 'PAS'.");
+ sendString("S2");
+ // Input should now be "PASS2", which should only match one entry.
+ yield notifyMenuChanged(1);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, "Reddit",
+ "Should have selected the only matching datalist value.");
+ });
+});
- case 100:
- checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
- // Check first entry
- doKey("down");
- checkForm(""); // value shouldn't update
- doKey("return"); // not "enter"!
- checkForm("Google");
-
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests that if a field has autocomplete="off" set, that datalist
+ * entries will appear, but no Form History entries. Also tests
+ * that the user can select datalist entries from the popup despite
+ * autocomplete being disabled.
+ */
+add_task(function* test_autocomplete_off() {
+ const FORM_NUM = 3;
+ const FIELD_NAME = "field2";
+ const VALUES = [
+ "this",
+ "should",
+ "never",
+ "appear!",
+ ];
- case 101:
- // Check second entry
- doKey("down");
- doKey("down");
- doKey("return"); // not "enter"!
- checkForm("Reddit");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, gDataListLabels);
+ yield closeAutoCompletePopup();
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
-
- case 102:
- // Check third entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("final");
+ for (let i = 0; i < gDataListValues.length; ++i) {
+ yield openAutoCompletePopup();
+ // Select the first datalist value in the list.
+ doKey("down");
+ // And maybe go down further if we're selecting a
+ // later entry.
+ repeatKey("down", i);
+ yield closeAutoCompletePopup();
+ is(input.value, gDataListValues[i],
+ "Should have selected the right datalist value.");
+ input.value = "";
+ }
+ });
+});
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+/**
+ * Tests what occurs to autocomplete popups when JavaScript dynamically
+ * manipulates datalist entries. This test will remove and change entries
+ * from the "suggest" datalist, but will restore the DOM to its original state
+ * before finishing.
+ */
+add_task(function* test_dynamic_options() {
+ const FORM_NUM = 3;
+ const FIELD_NAME = "field2";
+ const VALUES = [];
- case 103:
- checkMenuEntries(["PASS1", "PASS2", "final"], testNum);
- // Check first entry
- doKey("down");
- checkForm(""); // value shouldn't update
- doKey("return"); // not "enter"!
- checkForm("Google");
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ let results = yield openAutoCompletePopup();
+ assertResultsMatch(results, [...VALUES, ...gDataListLabels]);
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Select the first datalist value.
+ doKey("down");
- case 104:
- // Check second entry
- doKey("down");
- doKey("down");
- doKey("return"); // not "enter"!
- checkForm("Reddit");
-
- // Trigger autocomplete popup
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ let datalist = document.getElementById("suggest");
+ // Now dynamically remove the second value.
+ let toRemove = datalist.children[1];
+ toRemove.remove();
+ yield notifyMenuChanged(gDataListLabels.length - 1);
+ // Note that selection is lost when the popup is updated, so currently
+ // nothing is selected. We'll select the second displayed entry, which
+ // should be the third entry from the original list, pre-removal.
+ repeatKey("down", 2);
+ yield closeAutoCompletePopup();
+ is(input.value, gDataListValues[2],
+ "Should have selected the third datalist entry.");
- case 105:
- // Check third entry
- doKey("down");
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("final");
+ // Now restore the removed element.
+ datalist.insertBefore(toRemove, datalist.children[1]);
+ input.value = "";
- testNum = 199;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // We have to blur and re-focus the element in order to
+ // clear the autocomplete cache.
+ input.blur();
+ input.focus();
- // Test dynamic updates.
- // For some reasons, when there is an update of the list, the selection is
- // lost so we need to go down like if we were at the beginning of the list
- // again.
- case 200:
- // Removing the second element while on the first then going down and
- // push enter. Value should be one from the third suggesion.
- doKey("down");
- var datalist = document.getElementById('suggest');
- var toRemove = datalist.children[1]
- datalist.removeChild(toRemove);
+ // Next, let's try adding an <option> while the popup is opened. Strangely,
+ // initially the results returned by openAutoCompletePopup has only a
+ // single entry in non-e10s mode, but this quickly updates itself to
+ // display all entries.
+ yield openAutoCompletePopup();
+ yield results =
+ yield notifyMenuChanged(VALUES.length + gDataListLabels.length);
+ assertResultsMatch(results, [...VALUES, ...gDataListLabels]);
- SimpleTest.executeSoon(function() {
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("final");
-
- // Restore the element.
- datalist.insertBefore(toRemove, datalist.children[1]);
- expectPopup();
- restoreForm();
- doKey("down");
- });
- break;
+ // Select the first value.
+ doKey("down");
+ let added = new Option("Foo");
+ datalist.insertBefore(added, datalist.children[1]);
+ yield notifyMenuChanged(4);
- case 201:
- // Adding an attribute after the first one while on the first then going
- // down and push enter. Value should be the on from the new suggestion.
- doKey("down");
- datalist = document.getElementById('suggest');
- var added = new Option("Foo");
- datalist.insertBefore(added, datalist.children[1]);
- waitForMenuChange(4);
- break;
+ // Adding the new <option> cleared our selection. Let's select
+ // the new <option>, which should be the second value.
+ repeatKey("down", 2);
+ yield closeAutoCompletePopup();
+ is(input.value, "Foo", "Should have selected the newly added value.");
+ // Now remove the element.
+ added.remove();
+ // The input is still "Foo", which shouldn't match anything.
+ yield notifyMenuChanged(0);
- case 202:
- doKey("down");
- doKey("down");
- doKey("return");
- checkForm("Foo");
+ input.value = "";
- // Remove the element.
- datalist = document.getElementById('suggest');
- datalist.removeChild(datalist.children[1]);
- waitForMenuChange(0);
- break;
+ // Now let's programmatically change some values.
+ let option = datalist.children[0];
+ let oldValue = option.value;
+ let oldLabel = option.label;
+ option.value = "apple";
- case 203:
- // Change the first element value attribute.
- restoreForm();
- datalist = document.getElementById('suggest');
- prevValue = datalist.children[0].value;
- datalist.children[0].value = "foo";
- expectPopup();
- break;
+ results = yield openAutoCompletePopup();
+ assertResultsMatch(results, gDataListLabels);
+ yield notifyMenuChanged(3);
- case 204:
- doKey("down");
- doKey("return");
- checkForm("foo");
+ // Select the first value, which is the one we modified.
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, "apple",
+ "Should have gotten the dynamically modified option.")
- datalist = document.getElementById('suggest');
- datalist.children[0].value = prevValue;
- waitForMenuChange(0);
- break;
+ // Now that "apple" is selected, nothing should match.
+ yield notifyMenuChanged(0);
- case 205:
- // Change the textContent to update the value attribute.
- restoreForm();
- datalist = document.getElementById('suggest');
- prevValue = datalist.children[0].getAttribute('value');
- datalist.children[0].removeAttribute('value');
- datalist.children[0].textContent = "foobar";
- expectPopup();
- break;
+ input.value = "";
+ // Now we'll change the textContent to update the value attribute.
+ option.removeAttribute("label");
+ option.removeAttribute("value");
+ option.textContent = "orange";
- case 206:
- doKey("down");
- doKey("return");
- checkForm("foobar");
+ input.blur();
+ input.focus();
+
+ yield openAutoCompletePopup();
- datalist = document.getElementById('suggest');
- datalist.children[0].setAttribute('value', prevValue);
- testNum = 299;
- waitForMenuChange(0);
- break;
+ // Now select the option we just modified...
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, "orange",
+ "Got the value we modified by setting the textContent.");
- // Tests for filtering (or not).
- case 300:
- // Filters with first letter of the word.
- restoreForm();
- synthesizeKey("f", {});
- expectPopup();
- break;
-
- case 301:
- doKey("down");
- doKey("return");
- checkForm("final");
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // And now clean up.
+ input.blur();
+ option.textContent = "";
+ option.label = oldLabel;
+ option.value = oldValue;
+ });
+});
- case 302:
- // Filter with a letter in the middle of the word.
- synthesizeKey("i", {});
- synthesizeKey("n", {});
- waitForMenuChange(1);
- break;
+/**
+ * Tests filtering of datalists, as well as the special "mozNoFilter"
+ * input attribute that disables filtering.
+ */
+add_task(function* test_filtering() {
+ const FORM_NUM = 3;
+ const FIELD_NAME = "field2";
+ const VALUES = [];
- case 303:
- // Continuation of test 302.
- doKey("down");
- doKey("return");
- checkForm("final");
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Expects that there's a "final" value in the datalist.
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup("f");
+ // Input is now "f". There should only be one result.
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, "final", "Got expected datalist entry after filtering.");
+ input.value = "";
- case 304:
- // Filter is disabled with mozNoFilter.
- input.setAttribute('mozNoFilter', 'true');
- synthesizeKey("f", {});
- waitForMenuChange(3); // no change
- break;
+ // Now try matching just part of the word.
+ yield openAutoCompletePopup();
+ sendChar("i");
+ sendChar("n");
+ yield notifyMenuChanged(1);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, "final", "Got expected datalist entry after filtering.");
+ input.value = "";
- case 305:
- // Continuation of test 304.
- doKey("down");
- doKey("return");
- checkForm("Google");
- input.removeAttribute('mozNoFilter');
- testNum = 399;
- expectPopup();
- restoreForm();
- doKey("down");
- break;
+ // Now test that filtering is disabled with the special "mozNoFilter"
+ // attribute.
+ input.setAttribute("mozNoFilter", "true");
+ yield openAutoCompletePopup("f");
+ // Shouldn't have done any filtering - we should still be showing
+ // the full list.
+ yield notifyMenuChanged(3);
- case 400:
- // Check that the input event is fired.
- input.addEventListener("input", function(event) {
- input.removeEventListener("input", arguments.callee, false);
- ok(true, "oninput should have been received");
- ok(event.bubbles, "input event should bubble");
- ok(event.cancelable, "input event should be cancelable");
- checkForm("Google");
- input.blur();
- SimpleTest.finish();
- }, false);
+ doKey("down");
+ yield closeAutoCompletePopup();
+ is(input.value, gDataListValues[0],
+ "Selected the first value after disabling filtering.");
- doKey("down");
- checkForm("");
- doKey("return");
- break;
+ // And now clean up.
+ input.removeAttribute("mozNoFilter");
+ });
+});
- default:
- ok(false, "Unexpected invocation of test #" + testNum);
- SimpleTest.finish();
- return;
- }
-}
+/**
+ * Tests that "input" events are fired when selecting an item
+ * from an autocomplete list with datalist values.
+ */
+add_task(function*() {
+ const FORM_NUM = 3;
+ const FIELD_NAME = "field2";
+ const VALUES = [];
-function waitForMenuChange(expectedCount) {
- notifyMenuChanged(expectedCount, null, runTest);
-}
+ // Expects that there's a "final" value in the datalist.
+ yield prepareTest(FORM_NUM, FIELD_NAME, VALUES, function*(input) {
+ yield openAutoCompletePopup();
+ doKey("down");
+ // Just a double-check that the input value hasn't changed yet.
+ is(input.value, "",
+ "The input value should not have changed just yet.");
+ let inputPromise = waitForInputEvent(input);
+ yield closeAutoCompletePopup();
+ yield inputPromise;
+ is(input.value, gDataListValues[0], "Got the first datalist value.");
+ });
+});
-function checkMenuEntries(expectedValues, testNum) {
- var actualValues = getMenuEntries();
- is(actualValues.length, expectedValues.length, testNum + " Checking length of expected menu");
- for (var i = 0; i < expectedValues.length; i++)
- is(actualValues[i], expectedValues[i], testNum + " Checking menu entry #"+i);
-}
-
-function startTest() {
- setupFormHistory(runTest);
-}
-
-window.onload = startTest;
-
-SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>