summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/unit/test_search_suggestions_tail.js
blob: da43463a69b31021e8c32a54da02402809310a90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * Tests that tailed search engine suggestions are returned by
 * UrlbarProviderSearchSuggestions when available.
 */

const SUGGEST_ENABLED_PREF = "browser.search.suggest.enabled";
const PRIVATE_SEARCH_PREF = "browser.search.separatePrivateDefault.ui.enabled";
const TAIL_SUGGESTIONS_PREF = "browser.urlbar.richSuggestions.tail";

var suggestionsFn;
var previousSuggestionsFn;

/**
 * Set the current suggestion funciton.
 *
 * @param {Function} fn
 *   A function that that a search string and returns an array of strings that
 *   will be used as search suggestions.
 *   Note: `fn` should return > 1 suggestion in most cases. Otherwise, you may
 *         encounter unexceptede behaviour with UrlbarProviderSuggestion's
 *         _lastLowResultsSearchSuggestion safeguard.
 */
function setSuggestionsFn(fn) {
  previousSuggestionsFn = suggestionsFn;
  suggestionsFn = fn;
}

async function cleanup() {
  await PlacesUtils.bookmarks.eraseEverything();
  await PlacesUtils.history.clear();
}

async function cleanUpSuggestions() {
  await cleanup();
  if (previousSuggestionsFn) {
    suggestionsFn = previousSuggestionsFn;
    previousSuggestionsFn = null;
  }
}

add_task(async function setup() {
  let engine = await addTestTailSuggestionsEngine(searchStr => {
    return suggestionsFn(searchStr);
  });
  setSuggestionsFn(searchStr => {
    let suffixes = ["toronto", "tunisia"];
    return [
      "what time is it in t",
      suffixes.map(s => searchStr + s.slice(1)),
      [],
      {
        "google:irrelevantparameter": [],
        "google:suggestdetail": suffixes.map(s => ({
          mp: "… ",
          t: s,
        })),
      },
    ];
  });

  // Install the test engine.
  let oldDefaultEngine = await Services.search.getDefault();
  registerCleanupFunction(async () => {
    Services.search.setDefault(
      oldDefaultEngine,
      Ci.nsISearchService.CHANGE_REASON_UNKNOWN
    );
    Services.prefs.clearUserPref(PRIVATE_SEARCH_PREF);
    Services.prefs.clearUserPref(TAIL_SUGGESTIONS_PREF);
    Services.prefs.clearUserPref(SUGGEST_ENABLED_PREF);
  });
  Services.search.setDefault(engine, Ci.nsISearchService.CHANGE_REASON_UNKNOWN);
  Services.prefs.setBoolPref(PRIVATE_SEARCH_PREF, false);
  Services.prefs.setBoolPref(TAIL_SUGGESTIONS_PREF, true);
  Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true);
});

/**
 * Tests that non-tail suggestion providers still return results correctly when
 * the tailSuggestions pref is enabled.
 */
add_task(async function normal_suggestions_provider() {
  let engine = await addTestSuggestionsEngine();
  let tailEngine = await Services.search.getDefault();
  Services.search.setDefault(engine, Ci.nsISearchService.CHANGE_REASON_UNKNOWN);

  const query = "hello world";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: SUGGESTIONS_ENGINE_NAME,
        suggestion: query + " foo",
      }),
      makeSearchResult(context, {
        engineName: SUGGESTIONS_ENGINE_NAME,
        suggestion: query + " bar",
      }),
    ],
  });

  Services.search.setDefault(
    tailEngine,
    Ci.nsISearchService.CHANGE_REASON_UNKNOWN
  );
  await cleanUpSuggestions();
});

/**
 * Tests a suggestions provider that returns only tail suggestions.
 */
add_task(async function basic_tail() {
  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: query + "oronto",
        tail: "toronto",
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: query + "unisia",
        tail: "tunisia",
      }),
    ],
  });
  await cleanUpSuggestions();
});

/**
 * Tests a suggestions provider that returns both normal and tail suggestions.
 * Only normal results should be shown.
 */
add_task(async function mixed_suggestions() {
  // When normal suggestions are mixed with tail suggestions, they appear at the
  // correct position in the google:suggestdetail array as empty objects.
  setSuggestionsFn(searchStr => {
    let suffixes = ["toronto", "tunisia"];
    return [
      "what time is it in t",
      ["what is the time today texas"].concat(
        suffixes.map(s => searchStr + s.slice(1))
      ),
      [],
      {
        "google:irrelevantparameter": [],
        "google:suggestdetail": [{}].concat(
          suffixes.map(s => ({
            mp: "… ",
            t: s,
          }))
        ),
      },
    ];
  });

  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: "what is the time today texas",
        tail: undefined,
      }),
    ],
  });
  await cleanUpSuggestions();
});

/**
 * Tests a suggestions provider that returns both normal and tail suggestions,
 * with tail suggestions listed before normal suggestions.  In the real world
 * we don't expect that to happen, but we should handle it by showing only the
 * normal suggestions.
 */
add_task(async function mixed_suggestions_tail_first() {
  setSuggestionsFn(searchStr => {
    let suffixes = ["toronto", "tunisia"];
    return [
      "what time is it in t",
      suffixes
        .map(s => searchStr + s.slice(1))
        .concat(["what is the time today texas"]),
      [],
      {
        "google:irrelevantparameter": [],
        "google:suggestdetail": suffixes
          .map(s => ({
            mp: "… ",
            t: s,
          }))
          .concat([{}]),
      },
    ];
  });

  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: "what is the time today texas",
        tail: undefined,
      }),
    ],
  });
  await cleanUpSuggestions();
});

/**
 * Tests a search that returns history results, bookmark results and tail
 * suggestions. Only the history and bookmark results should be shown.
 */
add_task(async function mixed_results() {
  await PlacesTestUtils.addVisits([
    {
      uri: Services.io.newURI("http://example.com/1"),
      title: "what time is",
    },
  ]);

  await PlacesUtils.bookmarks.insert({
    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
    url: "http://example.com/2",
    title: "what time is",
  });
  await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();

  // Tail suggestions should not be shown.
  const query = "what time is";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeBookmarkResult(context, {
        uri: "http://example.com/2",
        title: "what time is",
      }),
      makeVisitResult(context, {
        uri: "http://example.com/1",
        title: "what time is",
      }),
    ],
  });

  // Once we make the query specific enough to exclude the history and bookmark
  // results, we should show tail suggestions.
  const tQuery = "what time is it in t";
  context = createContext(tQuery, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: tQuery + "oronto",
        tail: "toronto",
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: tQuery + "unisia",
        tail: "tunisia",
      }),
    ],
  });

  await cleanUpSuggestions();
});

/**
 * Tests that tail suggestions are deduped if their full-text form is a dupe of
 * a local search suggestion. Remaining tail suggestions should also not be
 * shown since we do not mix tail and non-tail suggestions.
 */
add_task(async function dedupe_local() {
  Services.prefs.setIntPref("browser.urlbar.maxHistoricalSearchSuggestions", 1);
  await UrlbarTestUtils.formHistory.add(["what time is it in toronto"]);

  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeFormHistoryResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: query + "oronto",
      }),
    ],
  });

  Services.prefs.clearUserPref("browser.urlbar.maxHistoricalSearchSuggestions");
  await cleanUpSuggestions();
});

/**
 * Tests that the correct number of suggestion results are displayed if
 * maxResults is limited, even when tail suggestions are returned.
 */
add_task(async function limit_results() {
  await UrlbarTestUtils.formHistory.clear();
  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  context.maxResults = 2;
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        suggestion: query + "oronto",
        tail: "toronto",
      }),
    ],
  });
  await cleanUpSuggestions();
});

/**
 * Tests that tail suggestions are hidden if the pref is disabled.
 */
add_task(async function disable_pref() {
  let oldPrefValue = Services.prefs.getBoolPref(TAIL_SUGGESTIONS_PREF);
  Services.prefs.setBoolPref(TAIL_SUGGESTIONS_PREF, false);
  const query = "what time is it in t";
  let context = createContext(query, { isPrivate: false });
  await check_results({
    context,
    matches: [
      makeSearchResult(context, {
        engineName: TAIL_SUGGESTIONS_ENGINE_NAME,
        heuristic: true,
      }),
    ],
  });

  Services.prefs.setBoolPref(TAIL_SUGGESTIONS_PREF, oldPrefValue);
  await cleanUpSuggestions();
});