|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 const Ci = Components.interfaces; |
|
6 const Cc = Components.classes; |
|
7 const Cr = Components.results; |
|
8 const Cu = Components.utils; |
|
9 |
|
10 Cu.import("resource://gre/modules/Services.jsm"); |
|
11 |
|
12 // Import common head. |
|
13 let (commonFile = do_get_file("../head_common.js", false)) { |
|
14 let uri = Services.io.newFileURI(commonFile); |
|
15 Services.scriptloader.loadSubScript(uri.spec, this); |
|
16 } |
|
17 |
|
18 // Put any other stuff relative to this test folder below. |
|
19 |
|
20 |
|
21 /** |
|
22 * Header file for autocomplete testcases that create a set of pages with uris, |
|
23 * titles, tags and tests that a given search term matches certain pages. |
|
24 */ |
|
25 |
|
26 let current_test = 0; |
|
27 |
|
28 function AutoCompleteInput(aSearches) { |
|
29 this.searches = aSearches; |
|
30 } |
|
31 AutoCompleteInput.prototype = { |
|
32 timeout: 10, |
|
33 textValue: "", |
|
34 searches: null, |
|
35 searchParam: "", |
|
36 popupOpen: false, |
|
37 minResultsForPopup: 0, |
|
38 invalidate: function() {}, |
|
39 disableAutoComplete: false, |
|
40 completeDefaultIndex: false, |
|
41 get popup() { return this; }, |
|
42 onSearchBegin: function() {}, |
|
43 onSearchComplete: function() {}, |
|
44 setSelectedIndex: function() {}, |
|
45 get searchCount() { return this.searches.length; }, |
|
46 getSearchAt: function(aIndex) this.searches[aIndex], |
|
47 QueryInterface: XPCOMUtils.generateQI([ |
|
48 Ci.nsIAutoCompleteInput, |
|
49 Ci.nsIAutoCompletePopup, |
|
50 ]) |
|
51 }; |
|
52 |
|
53 function toURI(aSpec) { |
|
54 return uri(aSpec); |
|
55 } |
|
56 |
|
57 let appendTags = true; |
|
58 // Helper to turn off tag matching in results |
|
59 function ignoreTags() |
|
60 { |
|
61 print("Ignoring tags from results"); |
|
62 appendTags = false; |
|
63 } |
|
64 |
|
65 function ensure_results(aSearch, aExpected) |
|
66 { |
|
67 let controller = Cc["@mozilla.org/autocomplete/controller;1"]. |
|
68 getService(Ci.nsIAutoCompleteController); |
|
69 |
|
70 // Make an AutoCompleteInput that uses our searches |
|
71 // and confirms results on search complete |
|
72 let input = new AutoCompleteInput(["history"]); |
|
73 |
|
74 controller.input = input; |
|
75 |
|
76 if (typeof kSearchParam == "string") |
|
77 input.searchParam = kSearchParam; |
|
78 |
|
79 let numSearchesStarted = 0; |
|
80 input.onSearchBegin = function() { |
|
81 numSearchesStarted++; |
|
82 do_check_eq(numSearchesStarted, 1); |
|
83 }; |
|
84 |
|
85 input.onSearchComplete = function() { |
|
86 do_check_eq(numSearchesStarted, 1); |
|
87 aExpected = aExpected.slice(); |
|
88 |
|
89 // Check to see the expected uris and titles match up (in any order) |
|
90 for (let i = 0; i < controller.matchCount; i++) { |
|
91 let value = controller.getValueAt(i); |
|
92 let comment = controller.getCommentAt(i); |
|
93 |
|
94 print("Looking for '" + value + "', '" + comment + "' in expected results..."); |
|
95 let j; |
|
96 for (j = 0; j < aExpected.length; j++) { |
|
97 // Skip processed expected results |
|
98 if (aExpected[j] == undefined) |
|
99 continue; |
|
100 |
|
101 let [uri, title, tags] = gPages[aExpected[j]]; |
|
102 |
|
103 // Load the real uri and titles and tags if necessary |
|
104 uri = toURI(kURIs[uri]).spec; |
|
105 title = kTitles[title]; |
|
106 if (tags && appendTags) |
|
107 title += " \u2013 " + tags.map(function(aTag) kTitles[aTag]); |
|
108 print("Checking against expected '" + uri + "', '" + title + "'..."); |
|
109 |
|
110 // Got a match on both uri and title? |
|
111 if (uri == value && title == comment) { |
|
112 print("Got it at index " + j + "!!"); |
|
113 // Make it undefined so we don't process it again |
|
114 aExpected[j] = undefined; |
|
115 break; |
|
116 } |
|
117 } |
|
118 |
|
119 // We didn't hit the break, so we must have not found it |
|
120 if (j == aExpected.length) |
|
121 do_throw("Didn't find the current result ('" + value + "', '" + comment + "') in expected: " + aExpected); |
|
122 } |
|
123 |
|
124 // Make sure we have the right number of results |
|
125 print("Expecting " + aExpected.length + " results; got " + |
|
126 controller.matchCount + " results"); |
|
127 do_check_eq(controller.matchCount, aExpected.length); |
|
128 |
|
129 // If we expect results, make sure we got matches |
|
130 do_check_eq(controller.searchStatus, aExpected.length ? |
|
131 Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH : |
|
132 Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH); |
|
133 |
|
134 // Fetch the next test if we have more |
|
135 if (++current_test < gTests.length) |
|
136 run_test(); |
|
137 |
|
138 do_test_finished(); |
|
139 }; |
|
140 |
|
141 print("Searching for.. '" + aSearch + "'"); |
|
142 controller.startSearch(aSearch); |
|
143 } |
|
144 |
|
145 // Get history services |
|
146 var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"]. |
|
147 getService(Ci.nsINavHistoryService); |
|
148 var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory); |
|
149 var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. |
|
150 getService(Ci.nsINavBookmarksService); |
|
151 var tagsvc = Cc["@mozilla.org/browser/tagging-service;1"]. |
|
152 getService(Ci.nsITaggingService); |
|
153 var iosvc = Cc["@mozilla.org/network/io-service;1"]. |
|
154 getService(Ci.nsIIOService); |
|
155 var prefs = Cc["@mozilla.org/preferences-service;1"]. |
|
156 getService(Ci.nsIPrefBranch); |
|
157 |
|
158 // Some date not too long ago |
|
159 let gDate = new Date(Date.now() - 1000 * 60 * 60) * 1000; |
|
160 // Store the page info for each uri |
|
161 let gPages = []; |
|
162 |
|
163 // Initialization tasks to be run before the next test |
|
164 let gNextTestSetupTasks = []; |
|
165 |
|
166 /** |
|
167 * Adds a page, and creates various properties for it depending on the |
|
168 * parameters passed in. This function will also add one visit, unless |
|
169 * aNoVisit is true. |
|
170 * |
|
171 * @param aURI |
|
172 * An index into kURIs that holds the string for the URI we are to add a |
|
173 * page for. |
|
174 * @param aTitle |
|
175 * An index into kTitles that holds the string for the title we are to |
|
176 * associate with the specified URI. |
|
177 * @param aBook [optional] |
|
178 * An index into kTitles that holds the string for the title we are to |
|
179 * associate with the bookmark. If this is undefined, no bookmark is |
|
180 * created. |
|
181 * @param aTags [optional] |
|
182 * An array of indexes into kTitles that hold the strings for the tags we |
|
183 * are to associate with the URI. If this is undefined (or aBook is), no |
|
184 * tags are added. |
|
185 * @param aKey [optional] |
|
186 * A string to associate as the keyword for this bookmark. aBook must be |
|
187 * a valid index into kTitles for this to be checked and used. |
|
188 * @param aTransitionType [optional] |
|
189 * The transition type to use when adding the visit. The default is |
|
190 * nsINavHistoryService::TRANSITION_LINK. |
|
191 * @param aNoVisit [optional] |
|
192 * If true, no visit is added for the URI. If false or undefined, a |
|
193 * visit is added. |
|
194 */ |
|
195 function addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit) |
|
196 { |
|
197 gNextTestSetupTasks.push([task_addPageBook, arguments]); |
|
198 } |
|
199 |
|
200 function task_addPageBook(aURI, aTitle, aBook, aTags, aKey, aTransitionType, aNoVisit) |
|
201 { |
|
202 // Add a page entry for the current uri |
|
203 gPages[aURI] = [aURI, aBook != undefined ? aBook : aTitle, aTags]; |
|
204 |
|
205 let uri = toURI(kURIs[aURI]); |
|
206 let title = kTitles[aTitle]; |
|
207 |
|
208 let out = [aURI, aTitle, aBook, aTags, aKey]; |
|
209 out.push("\nuri=" + kURIs[aURI]); |
|
210 out.push("\ntitle=" + title); |
|
211 |
|
212 // Add the page and a visit if we need to |
|
213 if (!aNoVisit) { |
|
214 yield promiseAddVisits({ |
|
215 uri: uri, |
|
216 transition: aTransitionType || TRANSITION_LINK, |
|
217 visitDate: gDate, |
|
218 title: title |
|
219 }); |
|
220 out.push("\nwith visit"); |
|
221 } |
|
222 |
|
223 // Add a bookmark if we need to |
|
224 if (aBook != undefined) { |
|
225 let book = kTitles[aBook]; |
|
226 let bmid = bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, uri, |
|
227 bmsvc.DEFAULT_INDEX, book); |
|
228 out.push("\nbook=" + book); |
|
229 |
|
230 // Add a keyword to the bookmark if we need to |
|
231 if (aKey != undefined) |
|
232 bmsvc.setKeywordForBookmark(bmid, aKey); |
|
233 |
|
234 // Add tags if we need to |
|
235 if (aTags != undefined && aTags.length > 0) { |
|
236 // Convert each tag index into the title |
|
237 let tags = aTags.map(function(aTag) kTitles[aTag]); |
|
238 tagsvc.tagURI(uri, tags); |
|
239 out.push("\ntags=" + tags); |
|
240 } |
|
241 } |
|
242 |
|
243 print("\nAdding page/book/tag: " + out.join(", ")); |
|
244 } |
|
245 |
|
246 function run_test() { |
|
247 print("\n"); |
|
248 // always search in history + bookmarks, no matter what the default is |
|
249 prefs.setIntPref("browser.urlbar.search.sources", 3); |
|
250 prefs.setIntPref("browser.urlbar.default.behavior", 0); |
|
251 |
|
252 // Search is asynchronous, so don't let the test finish immediately |
|
253 do_test_pending(); |
|
254 |
|
255 // Load the test and print a description then run the test |
|
256 let [description, search, expected, func] = gTests[current_test]; |
|
257 print(description); |
|
258 |
|
259 // By default assume we want to match tags |
|
260 appendTags = true; |
|
261 |
|
262 // Do an extra function if necessary |
|
263 if (func) |
|
264 func(); |
|
265 |
|
266 Task.spawn(function () { |
|
267 // Iterate over all tasks and execute them |
|
268 for (let [, [fn, args]] in Iterator(gNextTestSetupTasks)) { |
|
269 yield fn.apply(this, args); |
|
270 }; |
|
271 |
|
272 // Clean up to allow tests to register more functions. |
|
273 gNextTestSetupTasks = []; |
|
274 |
|
275 // At this point frecency could still be updating due to latest pages |
|
276 // updates. This is not a problem in real life, but autocomplete tests |
|
277 // should return reliable resultsets, thus we have to wait. |
|
278 yield promiseAsyncUpdates(); |
|
279 |
|
280 }).then(function () ensure_results(search, expected), |
|
281 do_report_unexpected_exception); |
|
282 } |
|
283 |
|
284 // Utility function to remove history pages |
|
285 function removePages(aURIs) |
|
286 { |
|
287 gNextTestSetupTasks.push([do_removePages, arguments]); |
|
288 } |
|
289 |
|
290 function do_removePages(aURIs) |
|
291 { |
|
292 for each (let uri in aURIs) |
|
293 histsvc.removePage(toURI(kURIs[uri])); |
|
294 } |
|
295 |
|
296 // Utility function to mark pages as typed |
|
297 function markTyped(aURIs, aTitle) |
|
298 { |
|
299 gNextTestSetupTasks.push([task_markTyped, arguments]); |
|
300 } |
|
301 |
|
302 function task_markTyped(aURIs, aTitle) |
|
303 { |
|
304 for (let uri of aURIs) { |
|
305 yield promiseAddVisits({ |
|
306 uri: toURI(kURIs[uri]), |
|
307 transition: TRANSITION_TYPED, |
|
308 title: kTitles[aTitle] |
|
309 }); |
|
310 } |
|
311 } |