summaryrefslogtreecommitdiffstats
path: root/toolkit/components/autocomplete/tests
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/autocomplete/tests')
-rw-r--r--toolkit/components/autocomplete/tests/unit/.eslintrc.js7
-rw-r--r--toolkit/components/autocomplete/tests/unit/head_autocomplete.js206
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_330578.js45
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_378079.js285
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_393191.js272
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_440866.js285
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_463023.js12
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_660156.js101
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js276
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js45
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js78
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js96
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js63
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js48
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js119
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js107
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js104
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js66
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_immediate_search.js157
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js14
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_previousResult.js280
-rw-r--r--toolkit/components/autocomplete/tests/unit/test_stopSearch.js187
-rw-r--r--toolkit/components/autocomplete/tests/unit/xpcshell.ini24
23 files changed, 2877 insertions, 0 deletions
diff --git a/toolkit/components/autocomplete/tests/unit/.eslintrc.js b/toolkit/components/autocomplete/tests/unit/.eslintrc.js
new file mode 100644
index 000000000..d35787cd2
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
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 000000000..1443879f0
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/head_autocomplete.js
@@ -0,0 +1,206 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+/**
+ * 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: function(aStart, aEnd) {
+ this._selStart = aStart;
+ this._selEnd = aEnd;
+ },
+
+ get searchCount() {
+ return this.searches.length;
+ },
+
+ getSearchAt: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ get popup() {
+ if (!this._popup) {
+ this._popup = new AutocompletePopupBase(this);
+ }
+ return this._popup;
+ },
+
+ // nsISupports implementation
+ QueryInterface: XPCOMUtils.generateQI([Ci.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: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this._finalCompleteValues[aIndex] || this._values[aIndex];
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: XPCOMUtils.generateQI([Ci.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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener) {
+ var result = this._result;
+
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory,
+ Ci.nsIAutoCompleteSearch]),
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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: XPCOMUtils.generateQI([Ci.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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator).
+ 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 000000000..c422dbb6a
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_330578.js
@@ -0,0 +1,45 @@
+/* -*- 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: "",
+ _lastRemoveFromDb: false,
+
+ onValueRemoved: function(aResult, aValue, aRemoveFromDb) {
+ this._lastResult = aResult;
+ this._lastValue = aValue;
+ this._lastRemoveFromDb = aRemoveFromDb;
+ }
+};
+
+
+// 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);
+ do_check_eq(result.matchCount, 3);
+ result.removeValueAt(0, true);
+ do_check_eq(result.matchCount, 2);
+ do_check_eq(gResultListener._lastResult, result);
+ do_check_eq(gResultListener._lastValue, "a");
+ do_check_eq(gResultListener._lastRemoveFromDb, true);
+
+ result.removeValueAt(0, false);
+ do_check_eq(result.matchCount, 1);
+ do_check_eq(gResultListener._lastValue, "b");
+ do_check_eq(gResultListener._lastRemoveFromDb, false);
+
+ // check that we don't get notified if the listener is unset
+ result.setListener(null);
+ result.removeValueAt(0, true); // "c"
+ do_check_eq(result.matchCount, 0);
+ do_check_eq(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 000000000..ad7e5590f
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_378079.js
@@ -0,0 +1,285 @@
+/* -*- 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: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex: function(aIndex) {},
+ invalidate: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompletePopup))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ },
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteInput))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+
+ if (this._values.length > 0) {
+ this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ } else {
+ this.searchResult = Ci.nsIAutoCompleteResult.NOMATCH;
+ }
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * 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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener)
+ {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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 = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.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++;
+ do_check_eq(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function() {
+
+ do_check_eq(numSearchesStarted, 1);
+
+ do_check_eq(controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
+ do_check_eq(controller.matchCount, 2);
+
+ // Confirm expected result values
+ for (var i = 0; i < expectedValues.length; i++) {
+ do_check_eq(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 000000000..6fb57e6c4
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_393191.js
@@ -0,0 +1,272 @@
+/* -*- 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: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex: function(aIndex) {},
+ invalidate: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompletePopup))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ },
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteInput))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+
+ if (this._values.length > 0) {
+ this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ } else {
+ this.searchResult = Ci.nsIAutoCompleteResult.NOMATCH;
+ }
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * 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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener)
+ {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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 = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.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++;
+ do_check_eq(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function() {
+
+ do_check_eq(numSearchesStarted, 1);
+
+ do_check_eq(controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
+ do_check_eq(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 000000000..e450aebbf
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_440866.js
@@ -0,0 +1,285 @@
+/* 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: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex: function(aIndex) {},
+ invalidate: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompletePopup))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ },
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteInput))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+
+ if (this._values.length > 0) {
+ this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ } else {
+ this.searchResult = Ci.nsIAutoCompleteResult.NOMATCH;
+ }
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * 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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener)
+ {
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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 = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.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++;
+ do_check_eq(numSearchesStarted, 1);
+ do_check_eq(input.searchCount, 2);
+ };
+
+ input.onSearchComplete = function() {
+ do_check_eq(numSearchesStarted, 1);
+
+ do_check_eq(controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
+ do_check_eq(controller.matchCount, 2);
+
+ // Confirm expected result values
+ for (var i = 0; i < expectedValues.length; i++) {
+ do_check_eq(expectedValues[i], controller.getValueAt(i));
+ }
+
+ do_check_true(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 000000000..a2639fd03
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_463023.js
@@ -0,0 +1,12 @@
+/* -*- 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);
+ do_check_eq(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 000000000..98acb243e
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_660156.js
@@ -0,0 +1,101 @@
+/**
+ * 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() {
+ do_check_eq(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 000000000..7fee48d55
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_multiple.js
@@ -0,0 +1,276 @@
+/* -*- 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: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex: function(aIndex) {},
+ invalidate: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompletePopup))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ },
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteInput))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * 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: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * 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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener)
+ {
+ var result = this._result;
+ if (result._values.length > 0) {
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS_ONGOING;
+ } else {
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH_ONGOING;
+ }
+ aListener.onSearchResult(this, result);
+
+ if (result._values.length > 0) {
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ } else {
+ result.searchResult = Ci.nsIAutoCompleteResult.RESULT_NOMATCH;
+ }
+ aListener.onSearchResult(this, result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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 = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.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++;
+ do_check_eq(numSearchesStarted, 1);
+ };
+
+ input.onSearchComplete = function() {
+
+ do_check_eq(numSearchesStarted, 1);
+
+ do_check_eq(controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
+ do_check_eq(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 000000000..c98db7f8f
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autocomplete_userContextId.js
@@ -0,0 +1,45 @@
+"use strict";
+
+Cu.import("resource://gre/modules/Promise.jsm");
+
+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(function *test_userContextId() {
+ let searchParam = yield doSearch("test", 1);
+ Assert.equal(searchParam, " user-context-id:1");
+});
+
+function doSearch(aString, aUserContextId) {
+ let deferred = Promise.defer();
+ let search = new AutoCompleteSearch("test");
+
+ search.startSearch = function (aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener) {
+ unregisterAutoCompleteSearch(search);
+ deferred.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);
+
+ return deferred.promise;
+ }
+
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 000000000..5fb93abc1
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_autofillSelectedPopupIndex.js
@@ -0,0 +1,78 @@
+"use strict";
+
+add_task(function* sameCaseAsMatch() {
+ yield runTest("moz");
+});
+
+add_task(function* differentCaseFromMatch() {
+ yield runTest("MOZ");
+});
+
+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.
+ yield 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.
+ controller.handleKeyNavigation(Ci.nsIDOMKeyEvent.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.
+ controller.handleKeyNavigation(Ci.nsIDOMKeyEvent.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 000000000..17f735388
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_badDefaultIndex.js
@@ -0,0 +1,96 @@
+/* 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) {
+ do_check_true(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);
+ do_check_eq(input.selectionStart, strLen);
+ do_check_eq(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.
+ do_check_eq(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);
+ do_check_eq(input.selectionStart, strLen);
+ do_check_eq(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.
+ do_check_eq(input.textValue, "moz");
+
+ // Clean up.
+ unregisterAutoCompleteSearch(searchBadIndex);
+ run_next_test();
+ };
+});
+
+function run_test() {
+ 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 000000000..c25b00907
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_completeDefaultIndex_casing.js
@@ -0,0 +1,63 @@
+/* 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);
+
+function run_test() {
+ run_next_test();
+}
+
+add_test(function test_keyNavigation() {
+ doSearch("MOZ", "mozilla", function(aController) {
+ do_check_eq(aController.input.textValue, "MOZilla");
+ aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
+ do_check_eq(aController.input.textValue, "mozilla");
+ });
+});
+
+add_test(function test_handleEnter() {
+ doSearch("MOZ", "mozilla", function(aController) {
+ do_check_eq(aController.input.textValue, "MOZilla");
+ aController.handleEnter(false);
+ do_check_eq(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 000000000..fcac8ae43
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue.js
@@ -0,0 +1,48 @@
+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) {
+ do_check_eq(aController.input.textValue, "moz");
+ do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
+ // Keyboard interaction is tested by test_finalCompleteValueSelectedIndex.js
+ // so here just test popup selection.
+ aController.handleEnter(true);
+ do_check_eq(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 000000000..6556a26dc
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValueSelectedIndex.js
@@ -0,0 +1,119 @@
+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);
+ aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.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 000000000..4942e7a9f
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_defaultIndex.js
@@ -0,0 +1,107 @@
+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 000000000..5642d3e3e
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalCompleteValue_forceComplete.js
@@ -0,0 +1,104 @@
+/* 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);
+
+function run_test() {
+ run_next_test();
+}
+
+add_test(function test_handleEnterWithDirectMatchCompleteSelectedIndex() {
+ doSearch("moz", "mozilla.com", "http://www.mozilla.com",
+ { forceComplete: true, completeSelectedIndex: true }, function(aController) {
+ do_check_eq(aController.input.textValue, "moz");
+ do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
+ aController.handleEnter(false);
+ // After enter the final complete value should be shown in the input.
+ do_check_eq(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.
+ do_check_eq(aController.input.textValue, "mozilla.com");
+ do_check_eq(aController.getFinalCompleteValueAt(0), "http://www.mozilla.com");
+ aController.handleEnter(false);
+ // After enter the final complete value should be shown in the input.
+ do_check_eq(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.
+ do_check_eq(aController.input.textValue, "mozilla.com");
+ do_check_eq(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);
+ do_check_eq(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.
+ do_check_eq(aController.input.textValue, "com >> mozilla.com");
+ do_check_eq(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.
+ do_check_eq(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 000000000..c983d969b
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_finalDefaultCompleteValue.js
@@ -0,0 +1,66 @@
+/* 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);
+
+function run_test() {
+ run_next_test();
+}
+
+add_test(function test_keyNavigation() {
+ doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
+ do_check_eq(aController.input.textValue, "mozilla.com");
+ aController.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_RIGHT);
+ do_check_eq(aController.input.textValue, "mozilla.com");
+ });
+});
+
+add_test(function test_handleEnter() {
+ doSearch("moz", "mozilla.com", "http://www.mozilla.com", function(aController) {
+ do_check_eq(aController.input.textValue, "mozilla.com");
+ aController.handleEnter(false);
+ do_check_eq(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 000000000..0579f5dcb
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_immediate_search.js
@@ -0,0 +1,157 @@
+/* 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 =
+ XPCOMUtils.generateQI([Ci.nsIFactory,
+ Ci.nsIAutoCompleteSearch,
+ Ci.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);
+
+function run_test() {
+ run_next_test();
+}
+
+/**
+ * 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.
+ do_check_eq(input.textValue, "moz-immediate");
+
+ // Wait for both queries to finish.
+ input.onSearchComplete = function() {
+ // Sanity check.
+ do_check_eq(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);
+ do_check_true(complete);
+
+ // Immediately check the result, the immediate search should have finished.
+ do_check_eq(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);
+ do_check_true(complete);
+
+ // Immediately check the result, the delayed search should have finished.
+ do_check_eq(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 000000000..14ee388b8
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_insertMatchAt.js
@@ -0,0 +1,14 @@
+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 000000000..bfe6c7aae
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_previousResult.js
@@ -0,0 +1,280 @@
+/* -*- 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: function(aIndex) {
+ return this.searches[aIndex];
+ },
+
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+
+ popupOpen: false,
+
+ popup: {
+ setSelectedIndex: function(aIndex) {},
+ invalidate: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompletePopup))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+ },
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteInput))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+
+/**
+ * nsIAutoCompleteResult implementation
+ */
+function AutoCompleteResult(aValues, aComments, aStyles) {
+ this._values = aValues;
+ this._comments = aComments;
+ this._styles = aStyles;
+ if (this._values.length > 0) {
+ this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
+ } else {
+ this.searchResult = Ci.nsIAutoCompleteResult.NOMATCH;
+ }
+}
+AutoCompleteResult.prototype = {
+ constructor: AutoCompleteResult,
+
+ // Arrays
+ _values: null,
+ _comments: null,
+ _styles: null,
+
+ searchString: "",
+ searchResult: null,
+
+ defaultIndex: 0,
+
+ get matchCount() {
+ return this._values.length;
+ },
+
+ getValueAt: function(aIndex) {
+ return this._values[aIndex];
+ },
+
+ getLabelAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ getCommentAt: function(aIndex) {
+ return this._comments[aIndex];
+ },
+
+ getStyleAt: function(aIndex) {
+ return this._styles[aIndex];
+ },
+
+ getImageAt: function(aIndex) {
+ return "";
+ },
+
+ getFinalCompleteValueAt: function(aIndex) {
+ return this.getValueAt(aIndex);
+ },
+
+ removeValueAt: function (aRowIndex, aRemoveFromDb) {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIAutoCompleteResult))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+
+/**
+ * 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: function(aSearchString,
+ aSearchParam,
+ aPreviousResult,
+ aListener)
+ {
+ this._previousResult = aPreviousResult;
+ aListener.onSearchResult(this, this._result);
+ },
+
+ stopSearch: function() {},
+
+ // nsISupports implementation
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIFactory) ||
+ iid.equals(Ci.nsIAutoCompleteSearch))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ // nsIFactory implementation
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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 = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.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() {
+ do_check_eq(controller.searchStatus,
+ Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
+ do_check_eq(controller.matchCount, 2);
+
+ if (numSearchesStarted == 1) {
+ do_check_eq(search1._previousResult, null);
+ do_check_eq(search2._previousResult, null);
+
+ // Now start it again
+ controller.startSearch("test");
+ return;
+ }
+ do_check_neq(search1._previousResult, null);
+ do_check_neq(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_stopSearch.js b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
new file mode 100644
index 000000000..5ef3454b4
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/test_stopSearch.js
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+
+/**
+ * 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) { return val; }, // ignore
+ get popupOpen() { return false; },
+ get searchCount() { return this.searches.length; },
+ getSearchAt: function(aIndex) { return this.searches[aIndex]; },
+ onSearchBegin: function() {},
+ onSearchComplete: function() {},
+ onTextReverted: function () {},
+ onTextEntered: function () {},
+ popup: {
+ selectBy: function() {},
+ invalidate: function() {},
+ set selectedIndex(val) { return val; }, // ignore
+ get selectedIndex() { return -1 },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup])
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput])
+}
+
+
+/**
+ * nsIAutoCompleteSearch implementation.
+ */
+function AutoCompleteSearch(aName)
+{
+ this.name = aName;
+}
+AutoCompleteSearch.prototype = {
+ constructor: AutoCompleteSearch,
+ stopSearchInvoked: true,
+ startSearch: function(aSearchString, aSearchParam, aPreviousResult, aListener)
+ {
+ print("Check stop search has been called");
+ do_check_true(this.stopSearchInvoked);
+ this.stopSearchInvoked = false;
+ },
+ stopSearch: function()
+ {
+ this.stopSearchInvoked = true;
+ },
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIFactory
+ , Ci.nsIAutoCompleteSearch
+ ]),
+ createInstance: function(outer, 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 = Cc["@mozilla.org/uuid-generator;1"].
+ getService(Ci.nsIUUIDGenerator);
+ 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) {
+ print("handleText");
+ controller.input.textValue = "hel";
+ controller.handleText();
+ },
+ function(controller) {
+ print("handleStartComposition");
+ controller.handleStartComposition();
+ },
+ function(controller) {
+ print("handleEndComposition");
+ controller.handleEndComposition();
+ // an input event always follows compositionend event.
+ controller.handleText();
+ },
+ function(controller) {
+ print("handleEscape");
+ controller.handleEscape();
+ },
+ function(controller) {
+ print("handleEnter");
+ controller.handleEnter(false);
+ },
+ function(controller) {
+ print("handleTab");
+ controller.handleTab();
+ },
+
+ function(controller) {
+ print("handleKeyNavigation");
+ controller.handleKeyNavigation(Ci.nsIDOMKeyEvent.DOM_VK_UP);
+ },
+];
+
+
+var gSearch;
+var gCurrentTest;
+function run_test() {
+ // Make an AutoCompleteSearch that always returns nothing
+ gSearch = new AutoCompleteSearch("test");
+ registerAutoCompleteSearch(gSearch);
+
+ let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+ getService(Ci.nsIAutoCompleteController);
+
+ // Make an AutoCompleteInput that uses our search.
+ let input = new AutoCompleteInput([gSearch.name]);
+ controller.input = input;
+
+ input.onSearchBegin = function() {
+ do_execute_soon(function() {
+ gCurrentTest(controller);
+ });
+ };
+ input.onSearchComplete = function() {
+ run_next_test(controller);
+ }
+
+ // Search is asynchronous, so don't let the test finish immediately
+ do_test_pending();
+
+ run_next_test(controller);
+}
+
+function run_next_test(controller) {
+ if (gTests.length == 0) {
+ unregisterAutoCompleteSearch(gSearch);
+ controller.stopSearch();
+ controller.input = null;
+ do_test_finished();
+ return;
+ }
+
+ gCurrentTest = gTests.shift();
+ controller.startSearch("hello");
+}
diff --git a/toolkit/components/autocomplete/tests/unit/xpcshell.ini b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
new file mode 100644
index 000000000..4d193965c
--- /dev/null
+++ b/toolkit/components/autocomplete/tests/unit/xpcshell.ini
@@ -0,0 +1,24 @@
+[DEFAULT]
+head = head_autocomplete.js
+tail =
+
+[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_finalCompleteValue_defaultIndex.js]
+[test_finalCompleteValue_forceComplete.js]
+[test_finalCompleteValueSelectedIndex.js]
+[test_finalDefaultCompleteValue.js]
+[test_immediate_search.js]
+[test_insertMatchAt.js]
+[test_previousResult.js]
+[test_stopSearch.js]