michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cr = Components.results; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: // Import common head. michael@0: let (commonFile = do_get_file("../head_common.js", false)) { michael@0: let uri = Services.io.newFileURI(commonFile); michael@0: Services.scriptloader.loadSubScript(uri.spec, this); michael@0: } michael@0: michael@0: // Put any other stuff relative to this test folder below. michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "gHistory", michael@0: "@mozilla.org/browser/history;1", michael@0: "mozIAsyncHistory"); michael@0: michael@0: /** michael@0: * @param aSearches michael@0: * Array of AutoCompleteSearch names. michael@0: */ michael@0: function AutoCompleteInput(aSearches) { michael@0: this.searches = aSearches; michael@0: } michael@0: AutoCompleteInput.prototype = { michael@0: searches: null, michael@0: minResultsForPopup: 0, michael@0: timeout: 10, michael@0: searchParam: "", michael@0: textValue: "", michael@0: disableAutoComplete: false, michael@0: michael@0: completeDefaultIndex: true, michael@0: defaultIndex: 0, michael@0: michael@0: // Text selection range michael@0: _selStart: 0, michael@0: _selEnd: 0, michael@0: get selectionStart() { michael@0: return this._selStart; michael@0: }, michael@0: get selectionEnd() { michael@0: return this._selEnd; michael@0: }, michael@0: selectTextRange: function(aStart, aEnd) { michael@0: this._selStart = aStart; michael@0: this._selEnd = aEnd; michael@0: }, michael@0: michael@0: onTextEntered: function() false, michael@0: onTextReverted: function() false, michael@0: michael@0: get searchCount() { michael@0: return this.searches.length; michael@0: }, michael@0: getSearchAt: function(aIndex) { michael@0: return this.searches[aIndex]; michael@0: }, michael@0: michael@0: onSearchBegin: function () {}, michael@0: onSearchComplete: function () {}, michael@0: michael@0: popupOpen: false, michael@0: michael@0: popup: { michael@0: selectedIndex: -1, michael@0: invalidate: function () {}, michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup]) michael@0: }, michael@0: michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput]) michael@0: } michael@0: michael@0: /** michael@0: * @param aSearchString michael@0: * String to search. michael@0: * @param aExpectedValue michael@0: * Expected value returned by autoFill. michael@0: * May be a string, or an object like michael@0: * { michael@0: * autoFilled: the value suggested by autofill, michael@0: * completed: the value completed on user's confirmation michael@0: * } michael@0: * In the latter case this will also check that on user's confirmation michael@0: * the result's casing is correctly applied. michael@0: */ michael@0: function ensure_results(aSearchString, aExpectedValue) { michael@0: let autoFilledValue, completedValue; michael@0: if (typeof(aExpectedValue) == "string") { michael@0: autoFilledValue = aExpectedValue; michael@0: } michael@0: else { michael@0: autoFilledValue = aExpectedValue.autoFilled; michael@0: completedValue = aExpectedValue.completed; michael@0: } michael@0: michael@0: // Make an AutoCompleteInput that uses our searches and confirms results. michael@0: let input = new AutoCompleteInput(["urlinline"]); michael@0: input.textValue = aSearchString; michael@0: michael@0: // Caret must be at the end for autoFill to happen. michael@0: let strLen = aSearchString.length; michael@0: input.selectTextRange(strLen, strLen); michael@0: do_check_eq(input.selectionStart, strLen); michael@0: do_check_eq(input.selectionEnd, strLen); michael@0: michael@0: let controller = Cc["@mozilla.org/autocomplete/controller;1"]. michael@0: getService(Ci.nsIAutoCompleteController); michael@0: controller.input = input; michael@0: michael@0: let numSearchesStarted = 0; michael@0: input.onSearchBegin = function() { michael@0: numSearchesStarted++; michael@0: do_check_eq(numSearchesStarted, 1); michael@0: }; michael@0: michael@0: input.onSearchComplete = function() { michael@0: // We should be running only one query. michael@0: do_check_eq(numSearchesStarted, 1); michael@0: michael@0: // Check the autoFilled result. michael@0: do_check_eq(input.textValue, autoFilledValue); michael@0: michael@0: if (completedValue) { michael@0: // Now force completion and check correct casing of the result. michael@0: // This ensures the controller is able to do its magic case-preserving michael@0: // stuff and correct replacement of the user's casing with result's one. michael@0: controller.handleEnter(false); michael@0: do_check_eq(input.textValue, completedValue); michael@0: } michael@0: michael@0: waitForCleanup(run_next_test); michael@0: }; michael@0: michael@0: do_log_info("Searching for: '" + aSearchString + "'"); michael@0: controller.startSearch(aSearchString); michael@0: } michael@0: michael@0: function run_test() { michael@0: do_register_cleanup(function () { michael@0: Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled"); michael@0: Services.prefs.clearUserPref("browser.urlbar.autoFill"); michael@0: Services.prefs.clearUserPref("browser.urlbar.autoFill.typed"); michael@0: }); michael@0: michael@0: gAutoCompleteTests.forEach(function (testData) { michael@0: let [description, searchString, expectedValue, setupFunc] = testData; michael@0: add_test(function () { michael@0: do_log_info(description); michael@0: Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", true); michael@0: Services.prefs.setBoolPref("browser.urlbar.autoFill", true); michael@0: Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false); michael@0: michael@0: if (setupFunc) { michael@0: setupFunc(); michael@0: } michael@0: michael@0: // At this point frecency could still be updating due to latest pages michael@0: // updates. michael@0: // This is not a problem in real life, but autocomplete tests should michael@0: // return reliable resultsets, thus we have to wait. michael@0: promiseAsyncUpdates().then(function () ensure_results(searchString, michael@0: expectedValue)); michael@0: }) michael@0: }, this); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: let gAutoCompleteTests = []; michael@0: function add_autocomplete_test(aTestData) { michael@0: gAutoCompleteTests.push(aTestData); michael@0: } michael@0: michael@0: function waitForCleanup(aCallback) { michael@0: remove_all_bookmarks(); michael@0: promiseClearHistory().then(aCallback); michael@0: } michael@0: michael@0: function addBookmark(aBookmarkObj) { michael@0: do_check_true(!!aBookmarkObj.url); michael@0: let parentId = aBookmarkObj.parentId ? aBookmarkObj.parentId michael@0: : PlacesUtils.unfiledBookmarksFolderId; michael@0: let itemId = PlacesUtils.bookmarks michael@0: .insertBookmark(parentId, michael@0: NetUtil.newURI(aBookmarkObj.url), michael@0: PlacesUtils.bookmarks.DEFAULT_INDEX, michael@0: "A bookmark"); michael@0: if (aBookmarkObj.keyword) { michael@0: PlacesUtils.bookmarks.setKeywordForBookmark(itemId, aBookmarkObj.keyword); michael@0: } michael@0: }