summaryrefslogtreecommitdiffstats
path: root/toolkit/components/autocomplete/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/components/autocomplete/tests/unit/head_autocomplete.js203
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_330578.js41
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_378079.js252
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_393191.js237
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_440866.js253
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_463023.js13
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_660156.js113
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js246
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js44
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js109
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js100
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js64
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js62
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js139
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js112
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js138
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js80
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_immediate_search.js174
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js15
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_previousResult.js255
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_removeMatchAt.js16
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_stopSearch.js171
-rw-r--r--toolkit/components/autocomplete/tests/unit/xpcshell.toml44
23 files changed, 2881 insertions, 0 deletions
diff --git a/toolkit/components/autocomplete/tests/unit/head_autocomplete.js b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
new file mode 100644
index 0000000000..6aa0382d82
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInputBase(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInputBase.prototype = {
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ // Text selection range
+ _selStart: 0,
+ _selEnd: 0,
+ get selectionStart() {
+ return this._selStart;
+ },
+ get selectionEnd() {
+ return this._selEnd;
+ },
+ selectTextRange(aStart, aEnd) {
+ this._selStart = aStart;
+ this._selEnd = aEnd;
+ },
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ get popup() {
+ if (!this._popup) {
+ this._popup = new AutocompletePopupBase(this);
+ }
+ return this._popup;
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResultBase(aValues) {
+ this._values = aValues;
+}
+AutoCompleteResultBase.prototype = {
+ // Arrays
+ _values: null,
+ _comments: [],
+ _styles: [],
+ _finalCompleteValues: [],
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: -1,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this._finalCompleteValues[aIndex] || this._values[aIndex];
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearchBase(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearchBase.prototype = {
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ var result = this._result;
+
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+function AutocompletePopupBase(input) {
+ this.input = input;
+}
+AutocompletePopupBase.prototype = {
+ selectedIndex: 0,
+ invalidate() {},
+ selectBy(reverse, page) {
+ let numRows = this.input.controller.matchCount;
+ if (numRows > 0) {
+ let delta = reverse ? -1 : 1;
+ this.selectedIndex = (this.selectedIndex + delta) % numRows;
+ if (this.selectedIndex < 0) {
+ this.selectedIndex = numRows - 1;
+ }
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+ var cid = Services.uuid.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_330578.js b/toolkit/components/autocomplete/tests/unit/test_330578.js
new file mode 100644
index 0000000000..5f549cb4c7
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_330578.js
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var gResultListener = {
+ _lastResult: null,
+ _lastValue: "",
+
+ onValueRemoved(aResult, aValue) {
+ this._lastResult = aResult;
+ this._lastValue = aValue;
+ },
+};
+
+// main
+function run_test() {
+ var result = Cc["@mozilla.org/autocomplete/simple-result;1"].createInstance(
+ Ci.nsIAutoCompleteSimpleResult
+ );
+ result.appendMatch("a", "");
+ result.appendMatch("b", "");
+ result.appendMatch("c", "");
+ result.setListener(gResultListener);
+ Assert.equal(result.matchCount, 3);
+ result.removeValueAt(0);
+ Assert.equal(result.matchCount, 2);
+ Assert.equal(gResultListener._lastResult, result);
+ Assert.equal(gResultListener._lastValue, "a");
+
+ result.removeValueAt(0);
+ Assert.equal(result.matchCount, 1);
+ Assert.equal(gResultListener._lastValue, "b");
+
+ // check that we don't get notified if the listener is unset
+ result.setListener(null);
+ result.removeValueAt(0); // "c"
+ Assert.equal(result.matchCount, 0);
+ Assert.equal(gResultListener._lastValue, "b");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_378079.js b/toolkit/components/autocomplete/tests/unit/test_378079.js
new file mode 100644
index 0000000000..06819df40b
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_378079.js
@@ -0,0 +1,252 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Unit test for Bug 378079 - AutoComplete returns invalid rows when
+ * more than one AutoCompleteSearch is used.
+ */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex(aIndex) {},
+ invalidate() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+ this.searchResult = this._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ /**
+ * Return the same result set for every search
+ */
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+ var uuidGenerator = Services.uuid;
+ var cid = uuidGenerator.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+/**
+ * Test AutoComplete with multiple AutoCompleteSearch sources.
+ */
+function run_test() {
+ // Make an AutoCompleteSearch that always returns nothing
+ var emptySearch = new AutoCompleteSearch(
+ "test-empty-search",
+ new AutoCompleteResult([], [], [])
+ );
+
+ // Make an AutoCompleteSearch that returns two values
+ var expectedValues = ["test1", "test2"];
+ var regularSearch = new AutoCompleteSearch(
+ "test-regular-search",
+ new AutoCompleteResult(expectedValues, [], [])
+ );
+
+ // Register searches so AutoCompleteController can find them
+ registerAutoCompleteSearch(emptySearch);
+ registerAutoCompleteSearch(regularSearch);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches
+ // and confirms results on search complete
+ var input = new AutoCompleteInput([emptySearch.name, regularSearch.name]);
+ var numSearchesStarted = 0;
+
+ input.onSearchBegin = function () {
+ numSearchesStarted++;
+ Assert.equal(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function () {
+ Assert.equal(numSearchesStarted, 1);
+
+ Assert.equal(
+ controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH
+ );
+ Assert.equal(controller.matchCount, 2);
+
+ // Confirm expected result values
+ for (var i = 0; i < expectedValues.length; i++) {
+ Assert.equal(expectedValues[i], controller.getValueAt(i));
+ }
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(emptySearch);
+ unregisterAutoCompleteSearch(regularSearch);
+
+ do_test_finished();
+ };
+
+ controller.input = input;
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ controller.startSearch("test");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_393191.js b/toolkit/components/autocomplete/tests/unit/test_393191.js
new file mode 100644
index 0000000000..9a0481718d
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_393191.js
@@ -0,0 +1,237 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Unit test for Bug 393191 - AutoComplete crashes if result is null
+ */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex(aIndex) {},
+ invalidate() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+ this.searchResult = this._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ /**
+ * Return the same result set for every search
+ */
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+ var uuidGenerator = Services.uuid;
+ var cid = uuidGenerator.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+/**
+ * Test AutoComplete with a search that returns a null result
+ */
+function run_test() {
+ // Make an AutoCompleteSearch that always returns nothing
+ var emptySearch = new AutoCompleteSearch(
+ "test-empty-search",
+ new AutoCompleteResult([], [], [])
+ );
+
+ // Register search so AutoCompleteController can find them
+ registerAutoCompleteSearch(emptySearch);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our search
+ // and confirms results on search complete
+ var input = new AutoCompleteInput([emptySearch.name]);
+ var numSearchesStarted = 0;
+
+ input.onSearchBegin = function () {
+ numSearchesStarted++;
+ Assert.equal(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function () {
+ Assert.equal(numSearchesStarted, 1);
+
+ Assert.equal(
+ controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH
+ );
+ Assert.equal(controller.matchCount, 0);
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(emptySearch);
+
+ do_test_finished();
+ };
+
+ controller.input = input;
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ controller.startSearch("test");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_440866.js b/toolkit/components/autocomplete/tests/unit/test_440866.js
new file mode 100644
index 0000000000..ef538cd931
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_440866.js
@@ -0,0 +1,253 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Unit test for Bug 440866 - First AutoCompleteSearch that returns
+ * RESULT_NOMATCH cancels all other searches when popup is open
+ */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex(aIndex) {},
+ invalidate() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+ this.searchResult = this._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ /**
+ * Return the same result set for every search
+ */
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+ var uuidGenerator = Services.uuid;
+ var cid = uuidGenerator.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+/**
+ * Test AutoComplete with multiple AutoCompleteSearch sources.
+ */
+function run_test() {
+ // Make an AutoCompleteSearch that always returns nothing
+ var emptySearch = new AutoCompleteSearch(
+ "test-empty-search",
+ new AutoCompleteResult([], [], [])
+ );
+
+ // Make an AutoCompleteSearch that returns two values
+ var expectedValues = ["test1", "test2"];
+ var regularSearch = new AutoCompleteSearch(
+ "test-regular-search",
+ new AutoCompleteResult(expectedValues, [], [])
+ );
+
+ // Register searches so AutoCompleteController can find them
+ registerAutoCompleteSearch(emptySearch);
+ registerAutoCompleteSearch(regularSearch);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches
+ // and confirms results on search complete
+ var input = new AutoCompleteInput([emptySearch.name, regularSearch.name]);
+ var numSearchesStarted = 0;
+
+ input.onSearchBegin = function () {
+ numSearchesStarted++;
+ Assert.equal(numSearchesStarted, 1);
+ Assert.equal(input.searchCount, 2);
+ };
+
+ input.onSearchComplete = function () {
+ Assert.equal(numSearchesStarted, 1);
+
+ Assert.equal(
+ controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH
+ );
+ Assert.equal(controller.matchCount, 2);
+
+ // Confirm expected result values
+ for (var i = 0; i < expectedValues.length; i++) {
+ Assert.equal(expectedValues[i], controller.getValueAt(i));
+ }
+
+ Assert.ok(input.popupOpen);
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(emptySearch);
+ unregisterAutoCompleteSearch(regularSearch);
+
+ do_test_finished();
+ };
+
+ controller.input = input;
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ controller.startSearch("test");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_463023.js b/toolkit/components/autocomplete/tests/unit/test_463023.js
new file mode 100644
index 0000000000..48d2284c1b
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_463023.js
@@ -0,0 +1,13 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// main
+function run_test() {
+ var result = Cc["@mozilla.org/autocomplete/controller;1"].createInstance(
+ Ci.nsIAutoCompleteController
+ );
+ Assert.equal(result.searchStatus, Ci.nsIAutoCompleteController.STATUS_NONE);
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_660156.js b/toolkit/components/autocomplete/tests/unit/test_660156.js
new file mode 100644
index 0000000000..a5f167e15b
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_660156.js
@@ -0,0 +1,113 @@
+/**
+ * Search object that returns results at different times.
+ * First, the search that returns results asynchronously.
+ */
+function AutoCompleteAsyncSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteAsyncSearch.prototype = Object.create(
+ AutoCompleteSearchBase.prototype
+);
+AutoCompleteAsyncSearch.prototype.startSearch = function (
+ aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener
+) {
+ this._result.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH_ONGOING;
+ aListener.onSearchResult(this, this._result);
+
+ do_timeout(500, () => {
+ this._returnResults(aListener);
+ });
+};
+
+AutoCompleteAsyncSearch.prototype._returnResults = function (aListener) {
+ var result = this._result;
+
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ aListener.onSearchResult(this, result);
+};
+
+/**
+ * The synchronous version
+ */
+function AutoCompleteSyncSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSyncSearch.prototype = Object.create(
+ AutoCompleteAsyncSearch.prototype
+);
+AutoCompleteSyncSearch.prototype.startSearch = function (
+ aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener
+) {
+ this._returnResults(aListener);
+};
+
+/**
+ * Results object
+ */
+function AutoCompleteResult(aValues, aDefaultIndex) {
+ this._values = aValues;
+ this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+/**
+ * Test AutoComplete with multiple AutoCompleteSearch sources, with one of them
+ * (index != 0) returning before the rest.
+ */
+function run_test() {
+ do_test_pending();
+
+ var results = ["mozillaTest"];
+ var inputStr = "moz";
+
+ // Async search
+ var asyncSearch = new AutoCompleteAsyncSearch(
+ "Async",
+ new AutoCompleteResult(results, -1)
+ );
+ // Sync search
+ var syncSearch = new AutoCompleteSyncSearch(
+ "Sync",
+ new AutoCompleteResult(results, 0)
+ );
+
+ // Register searches so AutoCompleteController can find them
+ registerAutoCompleteSearch(asyncSearch);
+ registerAutoCompleteSearch(syncSearch);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches
+ // and confirms results on search complete.
+ // Async search MUST be FIRST to trigger the bug this tests.
+ var input = new AutoCompleteInputBase([asyncSearch.name, syncSearch.name]);
+ input.completeDefaultIndex = true;
+ input.textValue = inputStr;
+
+ // Caret must be at the end. Autofill doesn't happen unless you're typing
+ // characters at the end.
+ var strLen = inputStr.length;
+ input.selectTextRange(strLen, strLen);
+
+ controller.input = input;
+ controller.startSearch(inputStr);
+
+ input.onSearchComplete = function () {
+ Assert.equal(input.textValue, results[0]);
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(asyncSearch);
+ unregisterAutoCompleteSearch(syncSearch);
+ do_test_finished();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
new file mode 100644
index 0000000000..68885dc4ab
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
@@ -0,0 +1,246 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex(aIndex) {},
+ invalidate() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ /**
+ * Return the same result set for every search
+ */
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ var result = this._result;
+ result.searchResult = result._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS_ONGOING
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH_ONGOING;
+ aListener.onSearchResult(this, result);
+
+ result.searchResult = result._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+ var uuidGenerator = Services.uuid;
+ var cid = uuidGenerator.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+/**
+ * Test AutoComplete with multiple AutoCompleteSearch sources.
+ */
+function run_test() {
+ var expected1 = ["1", "2", "3"];
+ var expected2 = ["a", "b", "c"];
+ var search1 = new AutoCompleteSearch(
+ "search1",
+ new AutoCompleteResult(expected1, [], [])
+ );
+ var search2 = new AutoCompleteSearch(
+ "search2",
+ new AutoCompleteResult(expected2, [], [])
+ );
+
+ // Register searches so AutoCompleteController can find them
+ registerAutoCompleteSearch(search1);
+ registerAutoCompleteSearch(search2);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches
+ // and confirms results on search complete
+ var input = new AutoCompleteInput([search1.name, search2.name]);
+ var numSearchesStarted = 0;
+
+ input.onSearchBegin = function () {
+ numSearchesStarted++;
+ Assert.equal(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function () {
+ Assert.equal(numSearchesStarted, 1);
+
+ Assert.equal(
+ controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH
+ );
+ Assert.equal(controller.matchCount, expected1.length + expected2.length);
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(search1);
+ unregisterAutoCompleteSearch(search2);
+
+ do_test_finished();
+ };
+
+ controller.input = input;
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ controller.startSearch("test");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js b/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
new file mode 100644
index 0000000000..4fbec408dc
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
@@ -0,0 +1,44 @@
+"use strict";
+
+function AutoCompleteInput(aSearches, aUserContextId) {
+ this.searches = aSearches;
+ this.userContextId = aUserContextId;
+ this.popup.selectedIndex = -1;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+function AutoCompleteSearch(aName) {
+ this.name = aName;
+}
+AutoCompleteSearch.prototype = Object.create(AutoCompleteSearchBase.prototype);
+
+add_task(async function test_userContextId() {
+ let searchParam = await doSearch("test", 1);
+ Assert.equal(searchParam, " user-context-id:1");
+});
+
+function doSearch(aString, aUserContextId) {
+ return new Promise(resolve => {
+ let search = new AutoCompleteSearch("test");
+
+ search.startSearch = function (
+ aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener
+ ) {
+ unregisterAutoCompleteSearch(search);
+ resolve(aSearchParam);
+ };
+
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ let input = new AutoCompleteInput([search.name], aUserContextId);
+ controller.input = input;
+ controller.startSearch(aString);
+ });
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js b/toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js
new file mode 100644
index 0000000000..9b38b95c9c
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js
@@ -0,0 +1,109 @@
+"use strict";
+
+add_task(async function sameCaseAsMatch() {
+ await runTest("moz");
+});
+
+add_task(async function differentCaseFromMatch() {
+ await runTest("MOZ");
+});
+
+async function runTest(searchStr) {
+ let matches = ["mozilla.org", "example.com"];
+ let result = new AutoCompleteResultBase(matches);
+ result.defaultIndex = 0;
+
+ let search = new AutoCompleteSearchBase("search", result);
+ registerAutoCompleteSearch(search);
+
+ let input = new AutoCompleteInputBase([search.name]);
+ input.completeSelectedIndex = true;
+ input.completeDefaultIndex = true;
+
+ // Start off with the search string in the input. The selection must be
+ // collapsed and the caret must be at the end to trigger autofill below.
+ input.textValue = searchStr;
+ input.selectTextRange(searchStr.length, searchStr.length);
+ Assert.equal(
+ input.selectionStart,
+ searchStr.length,
+ "Selection should start at the end of the input"
+ );
+ Assert.equal(
+ input.selectionEnd,
+ searchStr.length,
+ "Selection should end at the end of the input"
+ );
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].createInstance(
+ Ci.nsIAutoCompleteController
+ );
+ controller.input = input;
+ input.controller = controller;
+
+ // Start a search.
+ await new Promise(resolve => {
+ controller.startSearch(searchStr);
+ input.onSearchComplete = () => {
+ // The first match should have autofilled, but the case of the search
+ // string should be preserved.
+ let expectedValue = searchStr + matches[0].substr(searchStr.length);
+ Assert.equal(input.textValue, expectedValue, "Should have autofilled");
+ Assert.equal(
+ input.selectionStart,
+ searchStr.length,
+ "Selection should start after search string"
+ );
+ Assert.equal(
+ input.selectionEnd,
+ expectedValue.length,
+ "Selection should end at the end of the input"
+ );
+ resolve();
+ };
+ });
+
+ // Key down to select the second match in the popup.
+ // Hardcode KeyboardEvent.DOM_VK_DOWN, because we can't easily
+ // include KeyboardEvent here.
+ controller.handleKeyNavigation(0x28 /* KeyboardEvent.DOM_VK_DOWN */);
+ let expectedValue = matches[1];
+ Assert.equal(
+ input.textValue,
+ expectedValue,
+ "Should have filled second match"
+ );
+ Assert.equal(
+ input.selectionStart,
+ expectedValue.length,
+ "Selection should start at the end of the input"
+ );
+ Assert.equal(
+ input.selectionEnd,
+ expectedValue.length,
+ "Selection should end at the end of the input"
+ );
+
+ // Key up to select the first match again. The input should be restored
+ // exactly as it was when the first match was autofilled above: the search
+ // string's case should be preserved, and the selection should be preserved.
+ // Hardcode KeyboardEvent.DOM_VK_UP, because we can't easily
+ // include KeyboardEvent here.
+ controller.handleKeyNavigation(0x26 /* KeyboardEvent.DOM_VK_UP */);
+ expectedValue = searchStr + matches[0].substr(searchStr.length);
+ Assert.equal(
+ input.textValue,
+ expectedValue,
+ "Should have filled first match again"
+ );
+ Assert.equal(
+ input.selectionStart,
+ searchStr.length,
+ "Selection should start after search string again"
+ );
+ Assert.equal(
+ input.selectionEnd,
+ expectedValue.length,
+ "Selection should end at the end of the input again"
+ );
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js b/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js
new file mode 100644
index 0000000000..52da4f4b4a
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * A results that wants to defaultComplete to 0, but it has no matches,
+ * though it notifies SUCCESS to the controller.
+ */
+function AutoCompleteNoMatchResult() {
+ this.defaultIndex = 0;
+}
+AutoCompleteNoMatchResult.prototype = Object.create(
+ AutoCompleteResultBase.prototype
+);
+
+/**
+ * A results that wants to defaultComplete to an index greater than the number
+ * of matches.
+ */
+function AutoCompleteBadIndexResult(aValues, aDefaultIndex) {
+ Assert.ok(aValues.length <= aDefaultIndex);
+ this._values = aValues;
+ this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteBadIndexResult.prototype = Object.create(
+ AutoCompleteResultBase.prototype
+);
+
+add_test(function autocomplete_noMatch_success() {
+ const INPUT_STR = "moz";
+
+ let searchNoMatch = new AutoCompleteSearchBase(
+ "searchNoMatch",
+ new AutoCompleteNoMatchResult()
+ );
+ registerAutoCompleteSearch(searchNoMatch);
+
+ // Make an AutoCompleteInput that uses our search and confirms results.
+ let input = new AutoCompleteInputBase([searchNoMatch.name]);
+ input.completeDefaultIndex = true;
+ input.textValue = INPUT_STR;
+
+ // Caret must be at the end for autoFill to happen.
+ let strLen = INPUT_STR.length;
+ input.selectTextRange(strLen, strLen);
+ Assert.equal(input.selectionStart, strLen);
+ Assert.equal(input.selectionEnd, strLen);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+ controller.input = input;
+ controller.startSearch(INPUT_STR);
+
+ input.onSearchComplete = function () {
+ // Should not try to autoFill to an empty value.
+ Assert.equal(input.textValue, "moz");
+
+ // Clean up.
+ unregisterAutoCompleteSearch(searchNoMatch);
+ run_next_test();
+ };
+});
+
+add_test(function autocomplete_defaultIndex_exceeds_matchCount() {
+ const INPUT_STR = "moz";
+
+ // Result returning matches, but a bad defaultIndex.
+ let searchBadIndex = new AutoCompleteSearchBase(
+ "searchBadIndex",
+ new AutoCompleteBadIndexResult(["mozillaTest"], 1)
+ );
+ registerAutoCompleteSearch(searchBadIndex);
+
+ // Make an AutoCompleteInput that uses our search and confirms results.
+ let input = new AutoCompleteInputBase([searchBadIndex.name]);
+ input.completeDefaultIndex = true;
+ input.textValue = INPUT_STR;
+
+ // Caret must be at the end for autoFill to happen.
+ let strLen = INPUT_STR.length;
+ input.selectTextRange(strLen, strLen);
+ Assert.equal(input.selectionStart, strLen);
+ Assert.equal(input.selectionEnd, strLen);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+ controller.input = input;
+ controller.startSearch(INPUT_STR);
+
+ input.onSearchComplete = function () {
+ // Should not try to autoFill to an empty value.
+ Assert.equal(input.textValue, "moz");
+
+ // Clean up.
+ unregisterAutoCompleteSearch(searchBadIndex);
+ run_next_test();
+ };
+});
diff --git a/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js b/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
new file mode 100644
index 0000000000..2d1dfdf34d
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteResult(aValues) {
+ this._values = aValues;
+ this.defaultIndex = 0;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = -1;
+ this.completeDefaultIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_keyNavigation() {
+ doSearch("MOZ", "mozilla", function (aController) {
+ Assert.equal(aController.input.textValue, "MOZilla");
+ // Hardcode KeyboardEvent.DOM_VK_RIGHT, because we can't easily
+ // include KeyboardEvent here.
+ aController.handleKeyNavigation(0x27 /* KeyboardEvent.DOM_VK_RIGHT */);
+ Assert.equal(aController.input.textValue, "mozilla");
+ });
+});
+
+add_test(function test_handleEnter() {
+ doSearch("MOZ", "mozilla", function (aController) {
+ Assert.equal(aController.input.textValue, "MOZilla");
+ aController.handleEnter(false);
+ Assert.equal(aController.input.textValue, "mozilla");
+ });
+});
+
+function doSearch(aSearchString, aResultValue, aOnCompleteCallback) {
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult(["mozilla", "toolkit"], 0)
+ );
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches and confirms results.
+ let input = new AutoCompleteInput([search.name]);
+ input.textValue = aSearchString;
+
+ // Caret must be at the end for autofill to happen.
+ let strLen = aSearchString.length;
+ input.selectTextRange(strLen, strLen);
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ // Clean up.
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
new file mode 100644
index 0000000000..9b777d08a5
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
@@ -0,0 +1,62 @@
+function AutoCompleteResult(aValues, aFinalCompleteValues) {
+ this._values = aValues;
+ this._finalCompleteValues = aFinalCompleteValues;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = 0;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_handleEnter_mouse() {
+ doSearch(
+ "moz",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ function (aController) {
+ Assert.equal(aController.input.textValue, "moz");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ // Keyboard interaction is tested by test_finalCompleteValueSelectedIndex.js
+ // so here just test popup selection.
+ aController.handleEnter(true);
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ }
+ );
+});
+
+function doSearch(
+ aSearchString,
+ aResultValue,
+ aFinalCompleteValue,
+ aOnCompleteCallback
+) {
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult([aResultValue], [aFinalCompleteValue])
+ );
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches and confirms results.
+ let input = new AutoCompleteInput([search.name]);
+ input.textValue = aSearchString;
+
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ // Clean up.
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js
new file mode 100644
index 0000000000..ec9491d781
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js
@@ -0,0 +1,139 @@
+function AutoCompleteResult(aResultValues) {
+ this._values = aResultValues.map(x => x[0]);
+ this._finalCompleteValues = aResultValues.map(x => x[1]);
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+var selectByWasCalled = false;
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = 0;
+ this.popup.selectBy = function (reverse, page) {
+ Assert.equal(selectByWasCalled, false);
+ selectByWasCalled = true;
+ Assert.equal(reverse, false);
+ Assert.equal(page, false);
+ this.selectedIndex += (reverse ? -1 : 1) * (page ? 100 : 1);
+ };
+ this.completeSelectedIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_handleEnter_key() {
+ let results = [
+ ["mozilla.com", "http://www.mozilla.com"],
+ ["mozilla.org", "http://www.mozilla.org"],
+ ];
+ // First check the case where we do select a value with the keyboard:
+ doSearch("moz", results, function (aController) {
+ Assert.equal(aController.input.textValue, "moz");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ Assert.equal(
+ aController.getFinalCompleteValueAt(1),
+ "http://www.mozilla.org"
+ );
+
+ Assert.equal(aController.input.popup.selectedIndex, 0);
+ // Hardcode KeyboardEvent.DOM_VK_DOWN, because we can't easily
+ // include KeyboardEvent here.
+ aController.handleKeyNavigation(0x28 /* KeyboardEvent.DOM_VK_DOWN */);
+ Assert.equal(aController.input.popup.selectedIndex, 1);
+ // Simulate mouse interaction changing selectedIndex
+ // ie NOT keyboard interaction:
+ aController.input.popup.selectedIndex = 0;
+
+ aController.handleEnter(false);
+ // Verify that the keyboard-selected thing got inserted,
+ // and not the mouse selection:
+ Assert.equal(aController.input.textValue, "http://www.mozilla.org");
+ });
+});
+
+add_test(function test_handleEnter_mouse() {
+ let results = [
+ ["mozilla.com", "http://www.mozilla.com"],
+ ["mozilla.org", "http://www.mozilla.org"],
+ ];
+ // Then the case where we do not:
+ doSearch("moz", results, function (aController) {
+ Assert.equal(aController.input.textValue, "moz");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ Assert.equal(
+ aController.getFinalCompleteValueAt(1),
+ "http://www.mozilla.org"
+ );
+
+ Assert.equal(aController.input.popup.selectedIndex, 0);
+ aController.input.popupOpen = true;
+ // Simulate mouse interaction changing selectedIndex
+ // ie NOT keyboard interaction:
+ aController.input.popup.selectedIndex = 1;
+ Assert.equal(selectByWasCalled, false);
+ Assert.equal(aController.input.popup.selectedIndex, 1);
+
+ aController.handleEnter(false);
+ // Verify that the input stayed the same, because no selection was made
+ // with the keyboard:
+ Assert.equal(aController.input.textValue, "moz");
+ });
+});
+
+add_test(function test_handleEnter_preselected() {
+ let results = [
+ ["mozilla.com", "http://www.mozilla.com"],
+ ["mozilla.org", "http://www.mozilla.org"],
+ ];
+ // Then test a preselection.
+ doSearch("moz", results, function (aController) {
+ Assert.equal(aController.input.textValue, "moz");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ Assert.equal(
+ aController.getFinalCompleteValueAt(1),
+ "http://www.mozilla.org"
+ );
+
+ aController.setInitiallySelectedIndex(0);
+
+ aController.handleEnter(false);
+ // Verify that the input stayed the same, because no selection was made
+ // with the keyboard:
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ });
+});
+
+function doSearch(aSearchString, aResults, aOnCompleteCallback) {
+ selectByWasCalled = false;
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult(aResults)
+ );
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches and confirms results.
+ let input = new AutoCompleteInput([search.name]);
+ input.textValue = aSearchString;
+
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ // Clean up.
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js
new file mode 100644
index 0000000000..7b8a51f03a
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js
@@ -0,0 +1,112 @@
+function AutoCompleteResult(aResultValues) {
+ this.defaultIndex = 0;
+ this._values = aResultValues.map(x => x[0]);
+ this._finalCompleteValues = aResultValues.map(x => x[1]);
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = 0;
+ this.completeSelectedIndex = true;
+ this.completeDefaultIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_handleEnter() {
+ let results = [
+ ["mozilla.com", "https://www.mozilla.com"],
+ ["gomozilla.org", "http://www.gomozilla.org"],
+ ];
+ doSearch("moz", results, { selectedIndex: 0 }, controller => {
+ let input = controller.input;
+ Assert.equal(input.textValue, "mozilla.com");
+ Assert.equal(controller.getFinalCompleteValueAt(0), results[0][1]);
+ Assert.equal(controller.getFinalCompleteValueAt(1), results[1][1]);
+ Assert.equal(input.popup.selectedIndex, 0);
+
+ controller.handleEnter(false);
+ // Verify that the keyboard-selected thing got inserted,
+ // and not the mouse selection:
+ Assert.equal(controller.input.textValue, "https://www.mozilla.com");
+ });
+});
+
+add_test(function test_handleEnter_otherSelected() {
+ // The popup selection may not coincide with what is filled into the input
+ // field, for example if the user changed it with the mouse and then pressed
+ // Enter. In such a case we should still use the inputField value and not the
+ // popup selected value.
+ let results = [
+ ["mozilla.com", "https://www.mozilla.com"],
+ ["gomozilla.org", "http://www.gomozilla.org"],
+ ];
+ doSearch("moz", results, { selectedIndex: 1 }, controller => {
+ let input = controller.input;
+ Assert.equal(input.textValue, "mozilla.com");
+ Assert.equal(controller.getFinalCompleteValueAt(0), results[0][1]);
+ Assert.equal(controller.getFinalCompleteValueAt(1), results[1][1]);
+ Assert.equal(input.popup.selectedIndex, 1);
+
+ controller.handleEnter(false);
+ // Verify that the keyboard-selected thing got inserted,
+ // and not the mouse selection:
+ Assert.equal(controller.input.textValue, "https://www.mozilla.com");
+ });
+});
+
+add_test(function test_handleEnter_otherSelected_nocompleteselectedindex() {
+ let results = [
+ ["mozilla.com", "https://www.mozilla.com"],
+ ["gomozilla.org", "http://www.gomozilla.org"],
+ ];
+ doSearch(
+ "moz",
+ results,
+ { selectedIndex: 1, completeSelectedIndex: false },
+ controller => {
+ let input = controller.input;
+ Assert.equal(input.textValue, "mozilla.com");
+ Assert.equal(controller.getFinalCompleteValueAt(0), results[0][1]);
+ Assert.equal(controller.getFinalCompleteValueAt(1), results[1][1]);
+ Assert.equal(input.popup.selectedIndex, 1);
+
+ controller.handleEnter(false);
+ // Verify that the keyboard-selected result is inserted, not the
+ // defaultComplete.
+ Assert.equal(controller.input.textValue, "http://www.gomozilla.org");
+ }
+ );
+});
+
+function doSearch(aSearchString, aResults, aOptions, aOnCompleteCallback) {
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult(aResults)
+ );
+ registerAutoCompleteSearch(search);
+
+ let input = new AutoCompleteInput([search.name]);
+ input.textValue = aSearchString;
+ if ("selectedIndex" in aOptions) {
+ input.popup.selectedIndex = aOptions.selectedIndex;
+ }
+ if ("completeSelectedIndex" in aOptions) {
+ input.completeSelectedIndex = aOptions.completeSelectedIndex;
+ }
+ // Needed for defaultIndex completion.
+ input.selectTextRange(aSearchString.length, aSearchString.length);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
new file mode 100644
index 0000000000..5456a8d2ab
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
@@ -0,0 +1,138 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteResult(aValues, aFinalCompleteValues) {
+ this._values = aValues;
+ this._finalCompleteValues = aFinalCompleteValues;
+ this.defaultIndex = 0;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = -1;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_handleEnterWithDirectMatchCompleteSelectedIndex() {
+ doSearch(
+ "moz",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ { forceComplete: true, completeSelectedIndex: true },
+ function (aController) {
+ Assert.equal(aController.input.textValue, "moz");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ aController.handleEnter(false);
+ // After enter the final complete value should be shown in the input.
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ }
+ );
+});
+
+add_test(function test_handleEnterWithDirectMatch() {
+ doSearch(
+ "mozilla",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ { forceComplete: true, completeDefaultIndex: true },
+ function (aController) {
+ // Should autocomplete the search string to a suggestion.
+ Assert.equal(aController.input.textValue, "mozilla.com");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ aController.handleEnter(false);
+ // After enter the final complete value should be shown in the input.
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ }
+ );
+});
+
+add_test(function test_handleEnterWithNoMatch() {
+ doSearch(
+ "mozilla",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ { forceComplete: true, completeDefaultIndex: true },
+ function (aController) {
+ // Should autocomplete the search string to a suggestion.
+ Assert.equal(aController.input.textValue, "mozilla.com");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ // Now input something that does not match...
+ aController.input.textValue = "mozillax";
+ // ... and confirm. We don't want one of the values from the previous
+ // results to be taken, since what's now in the input field doesn't match.
+ aController.handleEnter(false);
+ Assert.equal(aController.input.textValue, "mozillax");
+ }
+ );
+});
+
+add_test(function test_handleEnterWithIndirectMatch() {
+ doSearch(
+ "com",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ { forceComplete: true, completeDefaultIndex: true },
+ function (aController) {
+ // Should autocomplete the search string to a suggestion.
+ Assert.equal(aController.input.textValue, "com >> mozilla.com");
+ Assert.equal(
+ aController.getFinalCompleteValueAt(0),
+ "http://www.mozilla.com"
+ );
+ aController.handleEnter(false);
+ // After enter the final complete value from the suggestion should be shown
+ // in the input.
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ }
+ );
+});
+
+function doSearch(
+ aSearchString,
+ aResultValue,
+ aFinalCompleteValue,
+ aInputProps,
+ aOnCompleteCallback
+) {
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult([aResultValue], [aFinalCompleteValue])
+ );
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches and confirms results.
+ let input = new AutoCompleteInput([search.name]);
+ for (var p in aInputProps) {
+ input[p] = aInputProps[p];
+ }
+ input.textValue = aSearchString;
+ // Place the cursor at the end of the input so that completion to
+ // default index will kick in.
+ input.selectTextRange(aSearchString.length, aSearchString.length);
+
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ // Clean up.
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
new file mode 100644
index 0000000000..95cff2d4a8
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteResult(aValues, aFinalCompleteValues) {
+ this._values = aValues;
+ this._finalCompleteValues = aFinalCompleteValues;
+ this.defaultIndex = 0;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+ this.popup.selectedIndex = -1;
+ this.completeDefaultIndex = true;
+}
+AutoCompleteInput.prototype = Object.create(AutoCompleteInputBase.prototype);
+
+add_test(function test_keyNavigation() {
+ doSearch(
+ "moz",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ function (aController) {
+ Assert.equal(aController.input.textValue, "mozilla.com");
+ // Hardcode KeyboardEvent.DOM_VK_RIGHT, because we can't easily
+ // include KeyboardEvent here.
+ aController.handleKeyNavigation(0x27 /* KeyboardEvent.DOM_VK_RIGHT */);
+ Assert.equal(aController.input.textValue, "mozilla.com");
+ }
+ );
+});
+
+add_test(function test_handleEnter() {
+ doSearch(
+ "moz",
+ "mozilla.com",
+ "http://www.mozilla.com",
+ function (aController) {
+ Assert.equal(aController.input.textValue, "mozilla.com");
+ aController.handleEnter(false);
+ Assert.equal(aController.input.textValue, "http://www.mozilla.com");
+ }
+ );
+});
+
+function doSearch(
+ aSearchString,
+ aResultValue,
+ aFinalCompleteValue,
+ aOnCompleteCallback
+) {
+ let search = new AutoCompleteSearchBase(
+ "search",
+ new AutoCompleteResult([aResultValue], [aFinalCompleteValue])
+ );
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our searches and confirms results.
+ let input = new AutoCompleteInput([search.name]);
+ input.textValue = aSearchString;
+
+ // Caret must be at the end for autofill to happen.
+ let strLen = aSearchString.length;
+ input.selectTextRange(strLen, strLen);
+ controller.input = input;
+ controller.startSearch(aSearchString);
+
+ input.onSearchComplete = function onSearchComplete() {
+ aOnCompleteCallback(controller);
+
+ // Clean up.
+ unregisterAutoCompleteSearch(search);
+ run_next_test();
+ };
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_immediate_search.js b/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
new file mode 100644
index 0000000000..a4524ca5c9
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function AutoCompleteImmediateSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteImmediateSearch.prototype = Object.create(
+ AutoCompleteSearchBase.prototype
+);
+AutoCompleteImmediateSearch.prototype.searchType =
+ Ci.nsIAutoCompleteSearchDescriptor.SEARCH_TYPE_IMMEDIATE;
+AutoCompleteImmediateSearch.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ "nsIAutoCompleteSearchDescriptor",
+]);
+
+function AutoCompleteDelayedSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteDelayedSearch.prototype = Object.create(
+ AutoCompleteSearchBase.prototype
+);
+
+function AutoCompleteResult(aValues, aDefaultIndex) {
+ this._values = aValues;
+ this.defaultIndex = aDefaultIndex;
+}
+AutoCompleteResult.prototype = Object.create(AutoCompleteResultBase.prototype);
+
+/**
+ * An immediate search should be executed synchronously.
+ */
+add_test(function test_immediate_search() {
+ let inputStr = "moz";
+
+ let immediateSearch = new AutoCompleteImmediateSearch(
+ "immediate",
+ new AutoCompleteResult(["moz-immediate"], 0)
+ );
+ registerAutoCompleteSearch(immediateSearch);
+ let delayedSearch = new AutoCompleteDelayedSearch(
+ "delayed",
+ new AutoCompleteResult(["moz-delayed"], 0)
+ );
+ registerAutoCompleteSearch(delayedSearch);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ let input = new AutoCompleteInputBase([
+ delayedSearch.name,
+ immediateSearch.name,
+ ]);
+ input.completeDefaultIndex = true;
+ input.textValue = inputStr;
+
+ // Caret must be at the end. Autofill doesn't happen unless you're typing
+ // characters at the end.
+ let strLen = inputStr.length;
+ input.selectTextRange(strLen, strLen);
+
+ controller.input = input;
+ controller.startSearch(inputStr);
+
+ // Immediately check the result, the immediate search should have finished.
+ Assert.equal(input.textValue, "moz-immediate");
+
+ // Wait for both queries to finish.
+ input.onSearchComplete = function () {
+ // Sanity check.
+ Assert.equal(input.textValue, "moz-immediate");
+
+ unregisterAutoCompleteSearch(immediateSearch);
+ unregisterAutoCompleteSearch(delayedSearch);
+ run_next_test();
+ };
+});
+
+/**
+ * An immediate search should be executed before any delayed search.
+ */
+add_test(function test_immediate_search_notimeout() {
+ let inputStr = "moz";
+
+ let immediateSearch = new AutoCompleteImmediateSearch(
+ "immediate",
+ new AutoCompleteResult(["moz-immediate"], 0)
+ );
+ registerAutoCompleteSearch(immediateSearch);
+
+ let delayedSearch = new AutoCompleteDelayedSearch(
+ "delayed",
+ new AutoCompleteResult(["moz-delayed"], 0)
+ );
+ registerAutoCompleteSearch(delayedSearch);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ let input = new AutoCompleteInputBase([
+ delayedSearch.name,
+ immediateSearch.name,
+ ]);
+ input.completeDefaultIndex = true;
+ input.textValue = inputStr;
+ input.timeout = 0;
+
+ // Caret must be at the end. Autofill doesn't happen unless you're typing
+ // characters at the end.
+ let strLen = inputStr.length;
+ input.selectTextRange(strLen, strLen);
+
+ controller.input = input;
+ let complete = false;
+ input.onSearchComplete = function () {
+ complete = true;
+ };
+ controller.startSearch(inputStr);
+ Assert.ok(complete);
+
+ // Immediately check the result, the immediate search should have finished.
+ Assert.equal(input.textValue, "moz-immediate");
+
+ unregisterAutoCompleteSearch(immediateSearch);
+ unregisterAutoCompleteSearch(delayedSearch);
+ run_next_test();
+});
+
+/**
+ * A delayed search should be executed synchronously with a zero timeout.
+ */
+add_test(function test_delayed_search_notimeout() {
+ let inputStr = "moz";
+
+ let delayedSearch = new AutoCompleteDelayedSearch(
+ "delayed",
+ new AutoCompleteResult(["moz-delayed"], 0)
+ );
+ registerAutoCompleteSearch(delayedSearch);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ let input = new AutoCompleteInputBase([delayedSearch.name]);
+ input.completeDefaultIndex = true;
+ input.textValue = inputStr;
+ input.timeout = 0;
+
+ // Caret must be at the end. Autofill doesn't happen unless you're typing
+ // characters at the end.
+ let strLen = inputStr.length;
+ input.selectTextRange(strLen, strLen);
+
+ controller.input = input;
+ let complete = false;
+ input.onSearchComplete = function () {
+ complete = true;
+ };
+ controller.startSearch(inputStr);
+ Assert.ok(complete);
+
+ // Immediately check the result, the delayed search should have finished.
+ Assert.equal(input.textValue, "moz-delayed");
+
+ unregisterAutoCompleteSearch(delayedSearch);
+ run_next_test();
+});
diff --git a/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js b/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js
new file mode 100644
index 0000000000..cef3fb9683
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js
@@ -0,0 +1,15 @@
+function run_test() {
+ let result = Cc["@mozilla.org/autocomplete/simple-result;1"].createInstance(
+ Ci.nsIAutoCompleteSimpleResult
+ );
+ result.appendMatch("a", "");
+ result.appendMatch("c", "");
+ result.insertMatchAt(1, "b", "");
+ result.insertMatchAt(3, "d", "");
+
+ Assert.equal(result.matchCount, 4);
+ Assert.equal(result.getValueAt(0), "a");
+ Assert.equal(result.getValueAt(1), "b");
+ Assert.equal(result.getValueAt(2), "c");
+ Assert.equal(result.getValueAt(3), "d");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_previousResult.js b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
new file mode 100644
index 0000000000..85db3f08c5
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
@@ -0,0 +1,255 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Unit test for Bug 438861 - Previous search results not returned to multiple
+ * searches.
+ */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+
+ // Array of AutoCompleteSearch names
+ searches: null,
+
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin() {},
+ onSearchComplete() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex(aIndex) {},
+ invalidate() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+ this.searchResult = this._values.length
+ ? Ci.nsIAutoCompleteResult.RESULT_SUCCESS
+ : Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ isRemovableAt(aRowIndex) {
+ return true;
+ },
+
+ removeValueAt(aRowIndex) {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteResult"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation that always returns
+ * the same result set.
+ */
+function AutoCompleteSearch(aName, aResult) {
+ this.name = aName;
+ this._result = aResult;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+
+ // Search name. Used by AutoCompleteController
+ name: null,
+
+ // AutoCompleteResult
+ _result: null,
+
+ _previousResult: null,
+
+ /**
+ * Return the same result set for every search
+ */
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ this._previousResult = aPreviousResult;
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch() {},
+
+ // nsISupports implementation
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+
+ // nsIFactory implementation
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ var name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+
+ var uuidGenerator = Services.uuid;
+ var cid = uuidGenerator.generateUUID();
+
+ var desc = "Test AutoCompleteSearch";
+
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ var componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+/**
+ */
+function run_test() {
+ // Make an AutoCompleteSearch that always returns nothing
+ var search1 = new AutoCompleteSearch(
+ "test-previous-result1",
+ new AutoCompleteResult(["hello1"], [""], [""])
+ );
+
+ var search2 = new AutoCompleteSearch(
+ "test-previous-result2",
+ new AutoCompleteResult(["hello2"], [""], [""])
+ );
+
+ // Register search so AutoCompleteController can find them
+ registerAutoCompleteSearch(search1);
+ registerAutoCompleteSearch(search2);
+
+ var controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our search
+ // and confirms results on search complete
+ var input = new AutoCompleteInput([search1.name, search2.name]);
+ var numSearchesStarted = 0;
+
+ input.onSearchBegin = function () {
+ numSearchesStarted++;
+ };
+
+ input.onSearchComplete = function () {
+ Assert.equal(
+ controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH
+ );
+ Assert.equal(controller.matchCount, 2);
+
+ if (numSearchesStarted == 1) {
+ Assert.equal(search1._previousResult, null);
+ Assert.equal(search2._previousResult, null);
+
+ // Now start it again
+ controller.startSearch("test");
+ return;
+ }
+ Assert.notEqual(search1._previousResult, null);
+ Assert.notEqual(search2._previousResult, null);
+
+ // Unregister searches
+ unregisterAutoCompleteSearch(search1);
+ unregisterAutoCompleteSearch(search2);
+
+ do_test_finished();
+ };
+
+ controller.input = input;
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ controller.startSearch("test");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_removeMatchAt.js b/toolkit/components/autocomplete/tests/unit/test_removeMatchAt.js
new file mode 100644
index 0000000000..c8abf0b832
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_removeMatchAt.js
@@ -0,0 +1,16 @@
+function run_test() {
+ let result = Cc["@mozilla.org/autocomplete/simple-result;1"].createInstance(
+ Ci.nsIAutoCompleteSimpleResult
+ );
+ result.appendMatch("a", "");
+ result.appendMatch("b", "");
+ result.removeMatchAt(0);
+ Assert.equal(result.matchCount, 1);
+ Assert.equal(result.getValueAt(0), "b");
+ result.appendMatch("c", "");
+ result.removeMatchAt(1);
+ Assert.equal(result.matchCount, 1);
+ Assert.equal(result.getValueAt(0), "b");
+ result.removeMatchAt(0);
+ Assert.equal(result.matchCount, 0);
+}
diff --git a/toolkit/components/autocomplete/tests/unit/test_stopSearch.js b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
new file mode 100644
index 0000000000..aecf572df0
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
@@ -0,0 +1,171 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Purpose of the test is to check that a stopSearch call comes always before a
+ * startSearch call.
+ */
+
+/**
+ * Dummy nsIAutoCompleteInput source that returns
+ * the given list of AutoCompleteSearch names.
+ *
+ * Implements only the methods needed for this test.
+ */
+function AutoCompleteInput(aSearches) {
+ this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+ constructor: AutoCompleteInput,
+ minResultsForPopup: 0,
+ timeout: 10,
+ searchParam: "",
+ textValue: "hello",
+ disableAutoComplete: false,
+ completeDefaultIndex: false,
+ set popupOpen(val) {}, // ignore
+ get popupOpen() {
+ return false;
+ },
+ get searchCount() {
+ return this.searches.length;
+ },
+ getSearchAt(aIndex) {
+ return this.searches[aIndex];
+ },
+ onSearchBegin() {},
+ onSearchComplete() {},
+ onTextReverted() {},
+ onTextEntered() {},
+ popup: {
+ selectBy() {},
+ invalidate() {},
+ set selectedIndex(val) {}, // ignore
+ get selectedIndex() {
+ return -1;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompletePopup"]),
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIAutoCompleteInput"]),
+};
+
+/**
+ * nsIAutoCompleteSearch implementation.
+ */
+function AutoCompleteSearch(aName) {
+ this.name = aName;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+ stopSearchInvoked: true,
+ startSearch(aSearchString, aSearchParam, aPreviousResult, aListener) {
+ info("Check stop search has been called");
+ Assert.ok(this.stopSearchInvoked);
+ this.stopSearchInvoked = false;
+ },
+ stopSearch() {
+ this.stopSearchInvoked = true;
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIFactory",
+ "nsIAutoCompleteSearch",
+ ]),
+ createInstance(iid) {
+ return this.QueryInterface(iid);
+ },
+};
+
+/**
+ * Helper to register an AutoCompleteSearch with the given name.
+ * Allows the AutoCompleteController to find the search.
+ */
+function registerAutoCompleteSearch(aSearch) {
+ let name = "@mozilla.org/autocomplete/search;1?name=" + aSearch.name;
+ let uuidGenerator = Services.uuid;
+ let cid = uuidGenerator.generateUUID();
+ let desc = "Test AutoCompleteSearch";
+ let componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.registerFactory(cid, desc, name, aSearch);
+ // Keep the id on the object so we can unregister later
+ aSearch.cid = cid;
+}
+
+/**
+ * Helper to unregister an AutoCompleteSearch.
+ */
+function unregisterAutoCompleteSearch(aSearch) {
+ let componentManager = Components.manager.QueryInterface(
+ Ci.nsIComponentRegistrar
+ );
+ componentManager.unregisterFactory(aSearch.cid, aSearch);
+}
+
+var gTests = [
+ function (controller) {
+ info("handleText");
+ controller.input.textValue = "hel";
+ controller.handleText();
+ },
+ function (controller) {
+ info("handleStartComposition");
+ controller.handleStartComposition();
+ info("handleEndComposition");
+ controller.handleEndComposition();
+ // an input event always follows compositionend event.
+ controller.handleText();
+ },
+ function (controller) {
+ info("handleEscape");
+ controller.handleEscape();
+ },
+ function (controller) {
+ info("handleEnter");
+ controller.handleEnter(false);
+ },
+ function (controller) {
+ info("handleTab");
+ controller.handleTab();
+ },
+
+ function (controller) {
+ info("handleKeyNavigation");
+ // Hardcode KeyboardEvent.DOM_VK_RIGHT, because we can't easily
+ // include KeyboardEvent here.
+ controller.handleKeyNavigation(0x26 /* KeyboardEvent.DOM_VK_UP */);
+ },
+];
+
+add_task(async function () {
+ // Make an AutoCompleteSearch that always returns nothing
+ let search = new AutoCompleteSearch("test");
+ registerAutoCompleteSearch(search);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].getService(
+ Ci.nsIAutoCompleteController
+ );
+
+ // Make an AutoCompleteInput that uses our search.
+ let input = new AutoCompleteInput([search.name]);
+ controller.input = input;
+
+ for (let testFn of gTests) {
+ input.onSearchBegin = function () {
+ executeSoon(() => testFn(controller));
+ };
+ let promise = new Promise(resolve => {
+ input.onSearchComplete = function () {
+ resolve();
+ };
+ });
+ controller.startSearch("hello");
+ await promise;
+ }
+
+ unregisterAutoCompleteSearch(search);
+ controller.stopSearch();
+ controller.input = null;
+});
diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.toml b/toolkit/components/autocomplete/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..00d238cdf7
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.toml
@@ -0,0 +1,44 @@
+[DEFAULT]
+head = "head_autocomplete.js"
+
+["test_330578.js"]
+
+["test_378079.js"]
+
+["test_393191.js"]
+
+["test_440866.js"]
+
+["test_463023.js"]
+
+["test_660156.js"]
+
+["test_autocomplete_multiple.js"]
+
+["test_autocomplete_userContextId.js"]
+
+["test_autofillSelectedPopupIndex.js"]
+
+["test_badDefaultIndex.js"]
+
+["test_completeDefaultIndex_casing.js"]
+
+["test_finalCompleteValue.js"]
+
+["test_finalCompleteValueSelectedIndex.js"]
+
+["test_finalCompleteValue_defaultIndex.js"]
+
+["test_finalCompleteValue_forceComplete.js"]
+
+["test_finalDefaultCompleteValue.js"]
+
+["test_immediate_search.js"]
+
+["test_insertMatchAt.js"]
+
+["test_previousResult.js"]
+
+["test_removeMatchAt.js"]
+
+["test_stopSearch.js"]