diff options
Diffstat (limited to 'toolkit/components/autocomplete/tests')
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] |