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 michael@0: * file, 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: michael@0: /** michael@0: * Header file for autocomplete testcases that create a set of pages with uris, michael@0: * titles, tags and tests that a given search term matches certain pages. michael@0: */ michael@0: michael@0: let current_test = 0; michael@0: michael@0: function AutoCompleteInput(aSearches) { michael@0: this.searches = aSearches; michael@0: } michael@0: AutoCompleteInput.prototype = { michael@0: timeout: 10, michael@0: textValue: "", michael@0: searches: null, michael@0: searchParam: "", michael@0: popupOpen: false, michael@0: minResultsForPopup: 0, michael@0: invalidate: function() {}, michael@0: disableAutoComplete: false, michael@0: completeDefaultIndex: false, michael@0: get popup() { return this; }, michael@0: onSearchBegin: function() {}, michael@0: onSearchComplete: function() {}, michael@0: setSelectedIndex: function() {}, michael@0: get searchCount() { return this.searches.length; }, michael@0: getSearchAt: function(aIndex) this.searches[aIndex], michael@0: QueryInterface: XPCOMUtils.generateQI([ michael@0: Ci.nsIAutoCompleteInput, michael@0: Ci.nsIAutoCompletePopup, michael@0: ]) michael@0: }; michael@0: michael@0: function toURI(aSpec) { michael@0: return uri(aSpec); michael@0: } michael@0: michael@0: let appendTags = true; michael@0: // Helper to turn off tag matching in results michael@0: function ignoreTags() michael@0: { michael@0: print("Ignoring tags from results"); michael@0: appendTags = false; michael@0: } michael@0: michael@0: function ensure_results(aSearch, aExpected) michael@0: { michael@0: let controller = Cc["@mozilla.org/autocomplete/controller;1"]. michael@0: getService(Ci.nsIAutoCompleteController); michael@0: michael@0: // Make an AutoCompleteInput that uses our searches michael@0: // and confirms results on search complete michael@0: let input = new AutoCompleteInput(["history"]); michael@0: michael@0: controller.input = input; michael@0: michael@0: if (typeof kSearchParam == "string") michael@0: input.searchParam = kSearchParam; 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: do_check_eq(numSearchesStarted, 1); michael@0: aExpected = aExpected.slice(); michael@0: michael@0: // Check to see the expected uris and titles match up (in any order) michael@0: for (let i = 0; i < controller.matchCount; i++) { michael@0: let value = controller.getValueAt(i); michael@0: let comment = controller.getCommentAt(i); michael@0: michael@0: print("Looking for '" + value + "', '" + comment + "' in expected results..."); michael@0: let j; michael@0: for (j = 0; j < aExpected.length; j++) { michael@0: // Skip processed expected results michael@0: if (aExpected[j] == undefined) michael@0: continue; michael@0: michael@0: let [uri, title, tags] = gPages[aExpected[j]]; michael@0: michael@0: // Load the real uri and titles and tags if necessary michael@0: uri = toURI(kURIs[uri]).spec; michael@0: title = kTitles[title]; michael@0: if (tags && appendTags) michael@0: title += " \u2013 " + tags.map(function(aTag) kTitles[aTag]); michael@0: print("Checking against expected '" + uri + "', '" + title + "'..."); michael@0: michael@0: // Got a match on both uri and title? michael@0: if (uri == value && title == comment) { michael@0: print("Got it at index " + j + "!!"); michael@0: // Make it undefined so we don't process it again michael@0: aExpected[j] = undefined; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // We didn't hit the break, so we must have not found it michael@0: if (j == aExpected.length) michael@0: do_throw("Didn't find the current result ('" + value + "', '" + comment + "') in expected: " + aExpected); michael@0: } michael@0: michael@0: // Make sure we have the right number of results michael@0: print("Expecting " + aExpected.length + " results; got " + michael@0: controller.matchCount + " results"); michael@0: do_check_eq(controller.matchCount, aExpected.length); michael@0: michael@0: // If we expect results, make sure we got matches michael@0: do_check_eq(controller.searchStatus, aExpected.length ? michael@0: Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH : michael@0: Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH); michael@0: michael@0: // Fetch the next test if we have more michael@0: if (++current_test < gTests.length) michael@0: run_test(); michael@0: michael@0: do_test_finished(); michael@0: }; michael@0: michael@0: print("Searching for.. '" + aSearch + "'"); michael@0: controller.startSearch(aSearch); michael@0: } michael@0: michael@0: // Get history services michael@0: var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"]. michael@0: getService(Ci.nsINavHistoryService); michael@0: var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory); michael@0: var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. michael@0: getService(Ci.nsINavBookmarksService); michael@0: var tagsvc = Cc["@mozilla.org/browser/tagging-service;1"]. michael@0: getService(Ci.nsITaggingService); michael@0: var iosvc = Cc["@mozilla.org/network/io-service;1"]. michael@0: getService(Ci.nsIIOService); michael@0: var prefs = Cc["@mozilla.org/preferences-service;1"]. michael@0: getService(Ci.nsIPrefBranch); michael@0: michael@0: // Some date not too long ago michael@0: let gDate = new Date(Date.now() - 1000 * 60 * 60) * 1000; michael@0: // Store the page info for each uri michael@0: let gPages = []; michael@0: michael@0: // Initialization tasks to be run before the next test michael@0: let gNextTestSetupTasks = []; michael@0: michael@0: /** michael@0: * Adds a page, and creates various properties for it depending on the michael@0: * parameters passed in. This function will also add one visit, unless michael@0: * aNoVisit is true. michael@0: * michael@0: * @param aURI michael@0: * An index into kURIs that holds the string for the URI we are to add a michael@0: * page for. michael@0: * @param aTitle michael@0: * An index into kTitles that holds the string for the title we are to michael@0: * associate with the specified URI. michael@0: * @param aBook [optional] michael@0: * An index into kTitles that holds the string for the title we are to michael@0: * associate with the bookmark. If this is undefined, no bookmark is michael@0: * created. michael@0: * @param aTags [optional] michael@0: * An array of indexes into kTitles that hold the strings for the tags we michael@0: * are to associate with the URI. If this is undefined (or aBook is), no michael@0: * tags are added. michael@0: * @param aKey [optional] michael@0: * A string to associate as the keyword for this bookmark. aBook must be michael@0: * a valid index into kTitles for this to be checked and used. michael@0: * @param aTransitionType [optional] michael@0: * The transition type to use when adding the visit. The default is michael@0: * nsINavHistoryService::TRANSITION_LINK. michael@0: * @param aNoVisit [optional] michael@0: * If true, no visit is added for the URI. If false or undefined, a michael@0: * visit is added. michael@0: */ michael@0: function addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit) michael@0: { michael@0: gNextTestSetupTasks.push([task_addPageBook, arguments]); michael@0: } michael@0: michael@0: function task_addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit) michael@0: { michael@0: // Add a page entry for the current uri michael@0: gPages[aURI] = [aURI, aBook != undefined ? aBook : aTitle, aTags]; michael@0: michael@0: let uri = toURI(kURIs[aURI]); michael@0: let title = kTitles[aTitle]; michael@0: michael@0: let out = [aURI, aTitle, aBook, aTags, aKey]; michael@0: out.push("\nuri=" + kURIs[aURI]); michael@0: out.push("\ntitle=" + title); michael@0: michael@0: // Add the page and a visit if we need to michael@0: if (!aNoVisit) { michael@0: yield promiseAddVisits({ michael@0: uri: uri, michael@0: transition: aTransitionType || TRANSITION_LINK, michael@0: visitDate: gDate, michael@0: title: title michael@0: }); michael@0: out.push("\nwith visit"); michael@0: } michael@0: michael@0: // Add a bookmark if we need to michael@0: if (aBook != undefined) { michael@0: let book = kTitles[aBook]; michael@0: let bmid = bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, uri, michael@0: bmsvc.DEFAULT_INDEX, book); michael@0: out.push("\nbook=" + book); michael@0: michael@0: // Add a keyword to the bookmark if we need to michael@0: if (aKey != undefined) michael@0: bmsvc.setKeywordForBookmark(bmid, aKey); michael@0: michael@0: // Add tags if we need to michael@0: if (aTags != undefined && aTags.length > 0) { michael@0: // Convert each tag index into the title michael@0: let tags = aTags.map(function(aTag) kTitles[aTag]); michael@0: tagsvc.tagURI(uri, tags); michael@0: out.push("\ntags=" + tags); michael@0: } michael@0: } michael@0: michael@0: print("\nAdding page/book/tag: " + out.join(", ")); michael@0: } michael@0: michael@0: function run_test() { michael@0: print("\n"); michael@0: // always search in history + bookmarks, no matter what the default is michael@0: prefs.setIntPref("browser.urlbar.search.sources", 3); michael@0: prefs.setIntPref("browser.urlbar.default.behavior", 0); michael@0: michael@0: // Search is asynchronous, so don't let the test finish immediately michael@0: do_test_pending(); michael@0: michael@0: // Load the test and print a description then run the test michael@0: let [description, search, expected, func] = gTests[current_test]; michael@0: print(description); michael@0: michael@0: // By default assume we want to match tags michael@0: appendTags = true; michael@0: michael@0: // Do an extra function if necessary michael@0: if (func) michael@0: func(); michael@0: michael@0: Task.spawn(function () { michael@0: // Iterate over all tasks and execute them michael@0: for (let [, [fn, args]] in Iterator(gNextTestSetupTasks)) { michael@0: yield fn.apply(this, args); michael@0: }; michael@0: michael@0: // Clean up to allow tests to register more functions. michael@0: gNextTestSetupTasks = []; michael@0: michael@0: // At this point frecency could still be updating due to latest pages michael@0: // updates. This is not a problem in real life, but autocomplete tests michael@0: // should return reliable resultsets, thus we have to wait. michael@0: yield promiseAsyncUpdates(); michael@0: michael@0: }).then(function () ensure_results(search, expected), michael@0: do_report_unexpected_exception); michael@0: } michael@0: michael@0: // Utility function to remove history pages michael@0: function removePages(aURIs) michael@0: { michael@0: gNextTestSetupTasks.push([do_removePages, arguments]); michael@0: } michael@0: michael@0: function do_removePages(aURIs) michael@0: { michael@0: for each (let uri in aURIs) michael@0: histsvc.removePage(toURI(kURIs[uri])); michael@0: } michael@0: michael@0: // Utility function to mark pages as typed michael@0: function markTyped(aURIs, aTitle) michael@0: { michael@0: gNextTestSetupTasks.push([task_markTyped, arguments]); michael@0: } michael@0: michael@0: function task_markTyped(aURIs, aTitle) michael@0: { michael@0: for (let uri of aURIs) { michael@0: yield promiseAddVisits({ michael@0: uri: toURI(kURIs[uri]), michael@0: transition: TRANSITION_TYPED, michael@0: title: kTitles[aTitle] michael@0: }); michael@0: } michael@0: }