Bug 1626897 - Part 4 - Add tail suggestion tests. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D74120
This commit is contained in:
Harry Twyford 2020-05-12 17:01:19 +00:00
Родитель 10d99a6ca5
Коммит 6abd224a1a
10 изменённых файлов: 578 добавлений и 61 удалений

Просмотреть файл

@ -275,8 +275,8 @@ add_task(async function test_onProviderResultsRequested() {
// Check the results.
let expectedResults = [
// The first result should be a search result returned by the
// UnifiedComplete provider.
// The first result should be a search result returned by
// UrlbarProviderSearchSuggestions.
{
type: UrlbarUtils.RESULT_TYPE.SEARCH,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
@ -286,6 +286,7 @@ add_task(async function test_onProviderResultsRequested() {
query: "test",
engine: "Test engine",
suggestion: undefined,
tail: undefined,
keyword: undefined,
isSearchHistory: false,
icon: "",

Просмотреть файл

@ -208,6 +208,8 @@ function makeUrlbarResult(tokens, info) {
action.params.searchSuggestion,
UrlbarUtils.HIGHLIGHT.SUGGESTED,
],
// For test interoperabilty with UrlbarProviderSearchSuggestions.
tail: undefined,
keyword: [action.params.alias, UrlbarUtils.HIGHLIGHT.TYPED],
query: [
action.params.searchQuery.trim(),

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>engine-tail-suggestions.xml</ShortName>
<Url type="application/x-suggestions+json"
method="GET"
template="http://localhost:9001/suggest?{searchTerms}"/>
<Url type="text/html"
method="GET"
template="http://localhost:9001/search"
rel="searchform"/>
</SearchPlugin>

Просмотреть файл

@ -130,6 +130,10 @@ class TestProvider extends UrlbarTestUtils.TestProvider {
}
}
function convertToUtf8(str) {
return String.fromCharCode(...new TextEncoder().encode(str));
}
/**
* Helper function to clear the existing providers and register a basic provider
* that returns only the results given.
@ -221,6 +225,56 @@ async function addTestSuggestionsEngine(suggestionsFn = null) {
return engine;
}
/**
* Sets up a search engine that provides some tail suggestions by creating an
* array that mimics Google's tail suggestion responses.
*
* @param {function} suggestionsFn
* A function that returns an array that mimics Google's tail suggestion
* responses. See bug 1626897.
* NOTE: Consumers specifying suggestionsFn must include searchStr as a
* part of the array returned by suggestionsFn.
* @returns {nsISearchEngine} The new engine.
*/
async function addTestTailSuggestionsEngine(suggestionsFn = null) {
// This port number should match the number in engine-tail-suggestions.xml.
let server = makeTestServer(9001);
server.registerPathHandler("/suggest", (req, resp) => {
// URL query params are x-www-form-urlencoded, which converts spaces into
// plus signs, so un-convert any plus signs back to spaces.
let searchStr = decodeURIComponent(req.queryString.replace(/\+/g, " "));
let suggestions = suggestionsFn
? suggestionsFn(searchStr)
: [
"what time is it in t",
["what is the time today texas"].concat(
["toronto", "tunisia"].map(s => searchStr + s.slice(1))
),
[],
{
"google:irrelevantparameter": [],
"google:suggestdetail": [{}].concat(
["toronto", "tunisia"].map(s => ({
mp: "… ",
t: s,
}))
),
},
];
let data = suggestions;
let jsonString = JSON.stringify(data);
// This script must be evaluated as UTF-8 for this to write out the bytes of
// the string in UTF-8. If it's evaluated as Latin-1, the written bytes
// will be the result of UTF-8-encoding the result-string *twice*, which
// will break the "… " match prefixes.
let stringOfUtf8Bytes = convertToUtf8(jsonString);
resp.setHeader("Content-Type", "application/json", false);
resp.write(stringOfUtf8Bytes);
});
let engine = await addTestEngine("engine-tail-suggestions.xml", server);
return engine;
}
/**
* Creates a UrlbarResult for a search result.
* @param {UrlbarQueryContext} queryContext
@ -249,6 +303,7 @@ function makeSearchResult(
queryContext,
{
suggestion,
tail,
engineName,
alias,
query,
@ -274,6 +329,7 @@ function makeSearchResult(
...UrlbarResult.payloadAndSimpleHighlights(queryContext.tokens, {
engine: [engineName, UrlbarUtils.HIGHLIGHT.TYPED],
suggestion: [suggestion, UrlbarUtils.HIGHLIGHT.SUGGESTED],
tail: [tail, UrlbarUtils.HIGHLIGHT.SUGGESTED],
keyword: [alias, UrlbarUtils.HIGHLIGHT.TYPED],
// Check against undefined so consumers can pass in the empty string.
query: [

Просмотреть файл

@ -27,8 +27,8 @@ var previousSuggestionsFn;
* @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
* Note: `fn` should return > 0 suggestions in most cases. Otherwise, you may
* encounter unexpected behaviour with UrlbarProviderSuggestion's
* _lastLowResultsSearchSuggestion safeguard.
*/
function setSuggestionsFn(fn) {
@ -1247,9 +1247,13 @@ add_task(async function avoid_url_suggestions() {
await cleanUpSuggestions();
});
add_task(async function restrict_suggestions_after_low_results() {
add_task(async function restrict_suggestions_after_no_results() {
// We don't fetch suggestions if a query with a length over
// maxCharsForSearchSuggestions returns 0 results. We set it to 4 here to
// avoid constructing a 100+ character string.
Services.prefs.setIntPref("browser.urlbar.maxCharsForSearchSuggestions", 4);
setSuggestionsFn(searchStr => {
return [searchStr + "s"];
return [];
});
const query = "hello";
@ -1258,10 +1262,6 @@ add_task(async function restrict_suggestions_after_low_results() {
context,
matches: [
makeSearchResult(context, { engineName: ENGINE_NAME, heuristic: true }),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: `${query}s`,
}),
],
});
@ -1270,12 +1270,13 @@ add_task(async function restrict_suggestions_after_low_results() {
context,
matches: [
makeSearchResult(context, { engineName: ENGINE_NAME, heuristic: true }),
// Because the previous search only returned one suggestion, we will not
// fetch suggestions for this query that is just a longer version of the
// Because the previous search returned no suggestions, we will not fetch
// suggestions for this query that is just a longer version of the
// previous query.
],
});
Services.prefs.clearUserPref("browser.urlbar.maxCharsForSearchSuggestions");
await cleanUpSuggestions();
});

Просмотреть файл

@ -0,0 +1,275 @@
/* 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 { FormHistory } = ChromeUtils.import(
"resource://gre/modules/FormHistory.jsm"
);
const ENGINE_NAME = "engine-tail-suggestions.xml";
const SUGGEST_PREF = "browser.urlbar.suggest.searches";
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() {
Services.prefs.setCharPref(
"browser.urlbar.matchBuckets",
"general:5,suggestion:Infinity"
);
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);
Services.prefs.clearUserPref(PRIVATE_SEARCH_PREF);
Services.prefs.clearUserPref(TAIL_SUGGESTIONS_PREF);
Services.prefs.clearUserPref(SUGGEST_ENABLED_PREF);
});
Services.search.setDefault(engine);
Services.prefs.setBoolPref(PRIVATE_SEARCH_PREF, false);
Services.prefs.setBoolPref(TAIL_SUGGESTIONS_PREF, true);
Services.prefs.setBoolPref(SUGGEST_ENABLED_PREF, true);
// We must make sure the FormHistoryStartup component is initialized.
Cc["@mozilla.org/satchel/form-history-startup;1"]
.getService(Ci.nsIObserver)
.observe(null, "profile-after-change", null);
});
/**
* 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);
const query = "hello world";
let context = createContext(query, { isPrivate: false });
await check_results({
context,
matches: [
makeSearchResult(context, {
engineName: "engine-suggestions.xml",
heuristic: true,
}),
makeSearchResult(context, {
engineName: "engine-suggestions.xml",
suggestion: query + " foo",
}),
makeSearchResult(context, {
engineName: "engine-suggestions.xml",
suggestion: query + " bar",
}),
],
});
Services.search.setDefault(tailEngine);
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: ENGINE_NAME, heuristic: true }),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "oronto",
tail: "… toronto",
}),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "unisia",
tail: "… tunisia",
}),
],
});
await cleanUpSuggestions();
});
/**
* Tests a suggestions provider that returns both normal and tail suggestions.
*/
add_task(async function mixed_results() {
// 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: ENGINE_NAME, heuristic: true }),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: "what is the time today texas",
tail: undefined,
}),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "oronto",
tail: "… toronto",
}),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "unisia",
tail: "… tunisia",
}),
],
});
await cleanUpSuggestions();
});
/**
* Tests that tail suggestions are deduped if their full-text form is a dupe of
* a local search suggestion.
*/
add_task(async function dedupe_local() {
Services.prefs.setIntPref("browser.urlbar.maxHistoricalSearchSuggestions", 1);
await updateSearchHistory("bump", "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: ENGINE_NAME, heuristic: true }),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "oronto",
tail: undefined,
}),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "unisia",
tail: "… tunisia",
}),
],
});
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() {
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: ENGINE_NAME, heuristic: true }),
makeSearchResult(context, {
engineName: ENGINE_NAME,
suggestion: query + "oronto",
tail: "… toronto",
}),
],
});
await cleanUpSuggestions();
});
function updateSearchHistory(op, value) {
return new Promise((resolve, reject) => {
FormHistory.update(
{ op, fieldname: "searchbar-history", value },
{
handleError(error) {
do_throw("Error occurred updating form history: " + error);
reject(error);
},
handleCompletion(reason) {
if (reason) {
reject(reason);
} else {
resolve();
}
},
}
);
});
}

Просмотреть файл

@ -3,6 +3,7 @@ head = head.js
firefox-appdir = browser
support-files =
data/engine-suggestions.xml
data/engine-tail-suggestions.xml
[test_muxer.js]
[test_providerOpenTabs.js]
@ -14,6 +15,7 @@ support-files =
[test_queryScorer.js]
[test_search_suggestions.js]
[test_search_suggestions_aliases.js]
[test_search_suggestions_tail.js]
[test_tokenizer.js]
[test_UrlbarController_integration.js]
[test_UrlbarController_telemetry.js]

Просмотреть файл

@ -19,7 +19,6 @@ function handleRequest(request, response) {
}
function writeSuggestions(query, completions = []) {
let result = [query, completions];
let jsonString = JSON.stringify([query, completions]);
// This script must be evaluated as UTF-8 for this to write out the bytes of
@ -31,6 +30,20 @@ function handleRequest(request, response) {
response.write(stringOfUtf8Bytes);
}
/**
* Sends `data` as suggestions directly. This is useful when testing rich
* suggestions, which do not conform to the object shape sent by
* writeSuggestions.
*
* @param {array} data
*/
function writeSuggestionsDirectly(data) {
let jsonString = JSON.stringify(data);
let stringOfUtf8Bytes = convertToUtf8(jsonString);
response.setHeader("Content-Type", "application/json", false);
response.write(stringOfUtf8Bytes);
}
response.setStatusLine(request.httpVersion, 200, "OK");
let q = request.method == "GET" ? query.q : undefined;
@ -49,6 +62,70 @@ function handleRequest(request, response) {
writeSuggestions(q, ["Mozilla", "modern", "mom"]);
} else if (q && q.startsWith("I ❤️")) {
writeSuggestions(q, ["I ❤️ Mozilla"]);
} else if (q && q.startsWith("tailjunk ")) {
writeSuggestionsDirectly([
q,
[q + " normal", q + " tail 1", q + " tail 2"],
[],
{
"google:irrelevantparameter": [],
"google:badformat": {
"google:suggestdetail": [
{},
{ mp: "… ", t: "tail 1" },
{ mp: "… ", t: "tail 2" },
],
},
},
]);
} else if (q && q.startsWith("tailjunk few ")) {
writeSuggestionsDirectly([
q,
[q + " normal", q + " tail 1", q + " tail 2"],
[],
{
"google:irrelevantparameter": [],
"google:badformat": {
"google:suggestdetail": [{ mp: "… ", t: "tail 1" }],
},
},
]);
} else if (q && q.startsWith("tailalt ")) {
writeSuggestionsDirectly([
q,
[q + " normal", q + " tail 1", q + " tail 2"],
{
"google:suggestdetail": [
{},
{ mp: "… ", t: "tail 1" },
{ mp: "… ", t: "tail 2" },
],
},
]);
} else if (q && q.startsWith("tail ")) {
writeSuggestionsDirectly([
q,
[q + " normal", q + " tail 1", q + " tail 2"],
[],
{
"google:irrelevantparameter": [],
"google:suggestdetail": [
{},
{ mp: "… ", t: "tail 1" },
{ mp: "… ", t: "tail 2" },
],
},
]);
} else if (q && q.startsWith("richempty ")) {
writeSuggestionsDirectly([
q,
[q + " normal", q + " tail 1", q + " tail 2"],
[],
{
"google:irrelevantparameter": [],
"google:suggestdetail": [],
},
]);
} else if (q && q.startsWith("letter ")) {
let letters = [];
for (let charCode = "A".charCodeAt(); charCode <= "Z".charCodeAt(); charCode++) {

Просмотреть файл

@ -142,9 +142,9 @@ add_task(async function simple_remote_no_local_result() {
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0], "Mozilla");
Assert.equal(result.remote[1], "modern");
Assert.equal(result.remote[2], "mom");
Assert.equal(result.remote[0].value, "Mozilla");
Assert.equal(result.remote[1].value, "modern");
Assert.equal(result.remote[2].value, "mom");
});
add_task(async function simple_remote_no_local_result_telemetry() {
@ -173,9 +173,9 @@ add_task(async function simple_remote_no_local_result_alternative_type() {
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0], "Mozilla");
Assert.equal(result.remote[1], "modern");
Assert.equal(result.remote[2], "mom");
Assert.equal(result.remote[0].value, "Mozilla");
Assert.equal(result.remote[1].value, "modern");
Assert.equal(result.remote[2].value, "mom");
});
add_task(async function remote_term_case_mismatch() {
@ -183,7 +183,7 @@ add_task(async function remote_term_case_mismatch() {
let result = await controller.fetch("Query Case Mismatch", false, getEngine);
Assert.equal(result.term, "Query Case Mismatch");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "Query Case Mismatch");
Assert.equal(result.remote[0].value, "Query Case Mismatch");
});
add_task(async function simple_local_no_remote_result() {
@ -193,7 +193,7 @@ add_task(async function simple_local_no_remote_result() {
let result = await controller.fetch("no remote", false, getEngine);
Assert.equal(result.term, "no remote");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "no remote entries");
Assert.equal(result.local[0].value, "no remote entries");
Assert.equal(result.remote.length, 0);
await updateSearchHistory("remove", "no remote entries");
@ -206,9 +206,9 @@ add_task(async function simple_non_ascii() {
let result = await controller.fetch("I ❤️", false, getEngine);
Assert.equal(result.term, "I ❤️");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "I ❤️ XUL");
Assert.equal(result.local[0].value, "I ❤️ XUL");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "I ❤️ Mozilla");
Assert.equal(result.remote[0].value, "I ❤️ Mozilla");
});
add_task(async function both_local_remote_result_dedupe() {
@ -218,10 +218,10 @@ add_task(async function both_local_remote_result_dedupe() {
let result = await controller.fetch("mo", false, getEngine);
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "Mozilla");
Assert.equal(result.local[0].value, "Mozilla");
Assert.equal(result.remote.length, 2);
Assert.equal(result.remote[0], "modern");
Assert.equal(result.remote[1], "mom");
Assert.equal(result.remote[0].value, "modern");
Assert.equal(result.remote[1].value, "mom");
});
add_task(async function POST_both_local_remote_result_dedupe() {
@ -229,10 +229,10 @@ add_task(async function POST_both_local_remote_result_dedupe() {
let result = await controller.fetch("mo", false, postEngine);
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "Mozilla");
Assert.equal(result.local[0].value, "Mozilla");
Assert.equal(result.remote.length, 2);
Assert.equal(result.remote[0], "modern");
Assert.equal(result.remote[1], "mom");
Assert.equal(result.remote[0].value, "modern");
Assert.equal(result.remote[1].value, "mom");
});
add_task(async function both_local_remote_result_dedupe2() {
@ -242,10 +242,10 @@ add_task(async function both_local_remote_result_dedupe2() {
let result = await controller.fetch("mo", false, getEngine);
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 2);
Assert.equal(result.local[0], "mom");
Assert.equal(result.local[1], "Mozilla");
Assert.equal(result.local[0].value, "mom");
Assert.equal(result.local[1].value, "Mozilla");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "modern");
Assert.equal(result.remote[0].value, "modern");
});
add_task(async function both_local_remote_result_dedupe3() {
@ -256,12 +256,97 @@ add_task(async function both_local_remote_result_dedupe3() {
let result = await controller.fetch("mo", false, getEngine);
Assert.equal(result.term, "mo");
Assert.equal(result.local.length, 3);
Assert.equal(result.local[0], "modern");
Assert.equal(result.local[1], "mom");
Assert.equal(result.local[2], "Mozilla");
Assert.equal(result.local[0].value, "modern");
Assert.equal(result.local[1].value, "mom");
Assert.equal(result.local[2].value, "Mozilla");
Assert.equal(result.remote.length, 0);
});
add_task(async function valid_tail_results() {
let controller = new SearchSuggestionController();
let result = await controller.fetch("tail query", false, getEngine);
Assert.equal(result.term, "tail query");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0].value, "tail query normal");
Assert.ok(!result.remote[0].matchPrefix);
Assert.ok(!result.remote[0].tail);
Assert.equal(result.remote[1].value, "tail query tail 1");
Assert.equal(result.remote[1].matchPrefix, "… ");
Assert.equal(result.remote[1].tail, "tail 1");
Assert.equal(result.remote[2].value, "tail query tail 2");
Assert.equal(result.remote[2].matchPrefix, "… ");
Assert.equal(result.remote[2].tail, "tail 2");
});
add_task(async function alt_tail_results() {
let controller = new SearchSuggestionController();
let result = await controller.fetch("tailalt query", false, getEngine);
Assert.equal(result.term, "tailalt query");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0].value, "tailalt query normal");
Assert.ok(!result.remote[0].matchPrefix);
Assert.ok(!result.remote[0].tail);
Assert.equal(result.remote[1].value, "tailalt query tail 1");
Assert.equal(result.remote[1].matchPrefix, "… ");
Assert.equal(result.remote[1].tail, "tail 1");
Assert.equal(result.remote[2].value, "tailalt query tail 2");
Assert.equal(result.remote[2].matchPrefix, "… ");
Assert.equal(result.remote[2].tail, "tail 2");
});
add_task(async function invalid_tail_results() {
let controller = new SearchSuggestionController();
let result = await controller.fetch("tailjunk query", false, getEngine);
Assert.equal(result.term, "tailjunk query");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0].value, "tailjunk query normal");
Assert.ok(!result.remote[0].matchPrefix);
Assert.ok(!result.remote[0].tail);
Assert.equal(result.remote[1].value, "tailjunk query tail 1");
Assert.ok(!result.remote[1].matchPrefix);
Assert.ok(!result.remote[1].tail);
Assert.equal(result.remote[2].value, "tailjunk query tail 2");
Assert.ok(!result.remote[2].matchPrefix);
Assert.ok(!result.remote[2].tail);
});
add_task(async function too_few_tail_results() {
let controller = new SearchSuggestionController();
let result = await controller.fetch("tailjunk few query", false, getEngine);
Assert.equal(result.term, "tailjunk few query");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0].value, "tailjunk few query normal");
Assert.ok(!result.remote[0].matchPrefix);
Assert.ok(!result.remote[0].tail);
Assert.equal(result.remote[1].value, "tailjunk few query tail 1");
Assert.ok(!result.remote[1].matchPrefix);
Assert.ok(!result.remote[1].tail);
Assert.equal(result.remote[2].value, "tailjunk few query tail 2");
Assert.ok(!result.remote[2].matchPrefix);
Assert.ok(!result.remote[2].tail);
});
add_task(async function empty_rich_results() {
let controller = new SearchSuggestionController();
let result = await controller.fetch("richempty query", false, getEngine);
Assert.equal(result.term, "richempty query");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 3);
Assert.equal(result.remote[0].value, "richempty query normal");
Assert.ok(!result.remote[0].matchPrefix);
Assert.ok(!result.remote[0].tail);
Assert.equal(result.remote[1].value, "richempty query tail 1");
Assert.ok(!result.remote[1].matchPrefix);
Assert.ok(!result.remote[1].tail);
Assert.equal(result.remote[2].value, "richempty query tail 2");
Assert.ok(!result.remote[2].matchPrefix);
Assert.ok(!result.remote[2].tail);
});
add_task(async function fetch_twice_in_a_row() {
// Two entries since the first will match the first fetch but not the second.
await updateSearchHistory("bump", "delay local");
@ -277,9 +362,9 @@ add_task(async function fetch_twice_in_a_row() {
let result = await resultPromise2;
Assert.equal(result.term, "delayed ");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "delayed local");
Assert.equal(result.local[0].value, "delayed local");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "delayed ");
Assert.equal(result.remote[0].value, "delayed ");
});
add_task(async function fetch_twice_subset_reuse_formHistoryResult() {
@ -292,10 +377,10 @@ add_task(async function fetch_twice_subset_reuse_formHistoryResult() {
let result = await controller.fetch("delay", false, getEngine);
Assert.equal(result.term, "delay");
Assert.equal(result.local.length, 2);
Assert.equal(result.local[0], "delay local");
Assert.equal(result.local[1], "delayed local");
Assert.equal(result.local[0].value, "delay local");
Assert.equal(result.local[1].value, "delayed local");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "delay");
Assert.equal(result.remote[0].value, "delay");
// Remove the entry from the DB but it should remain in the cached formHistoryResult.
await updateSearchHistory("remove", "delayed local");
@ -303,9 +388,9 @@ add_task(async function fetch_twice_subset_reuse_formHistoryResult() {
let result2 = await controller.fetch("delayed ", false, getEngine);
Assert.equal(result2.term, "delayed ");
Assert.equal(result2.local.length, 1);
Assert.equal(result2.local[0], "delayed local");
Assert.equal(result2.local[0].value, "delayed local");
Assert.equal(result2.remote.length, 1);
Assert.equal(result2.remote[0], "delayed ");
Assert.equal(result2.remote[0].value, "delayed ");
});
add_task(async function both_identical_with_more_than_max_results() {
@ -329,14 +414,14 @@ add_task(async function both_identical_with_more_than_max_results() {
Assert.equal(result.local.length, 7);
for (let i = 0; i < controller.maxLocalResults; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
Assert.equal(result.local.length + result.remote.length, 10);
for (let i = 0; i < result.remote.length; i++) {
Assert.equal(
result.remote[i],
result.remote[i].value,
"letter " +
String.fromCharCode("A".charCodeAt() + controller.maxLocalResults + i)
);
@ -352,7 +437,7 @@ add_task(async function noremote_maxLocal() {
Assert.equal(result.local.length, 26);
for (let i = 0; i < result.local.length; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
@ -368,7 +453,7 @@ add_task(async function someremote_maxLocal() {
Assert.equal(result.local.length, 2);
for (let i = 0; i < result.local.length; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
@ -376,7 +461,7 @@ add_task(async function someremote_maxLocal() {
// "A" and "B" will have been de-duped, start at C for remote results
for (let i = 0; i < result.remote.length; i++) {
Assert.equal(
result.remote[i],
result.remote[i].value,
"letter " + String.fromCharCode("C".charCodeAt() + i)
);
}
@ -389,9 +474,9 @@ add_task(async function one_of_each() {
let result = await controller.fetch("letter ", false, getEngine);
Assert.equal(result.term, "letter ");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "letter A");
Assert.equal(result.local[0].value, "letter A");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "letter B");
Assert.equal(result.remote[0].value, "letter B");
});
add_task(async function local_result_returned_remote_result_disabled() {
@ -404,7 +489,7 @@ add_task(async function local_result_returned_remote_result_disabled() {
Assert.equal(result.local.length, 26);
for (let i = 0; i < 26; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
@ -423,7 +508,7 @@ add_task(
Assert.equal(result.local.length, 26);
for (let i = 0; i < 26; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
@ -442,9 +527,9 @@ add_task(
let result = await controller.fetch("letter ", false, getEngine);
Assert.equal(result.term, "letter ");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "letter A");
Assert.equal(result.local[0].value, "letter A");
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "letter B");
Assert.equal(result.remote[0].value, "letter B");
Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
}
@ -459,7 +544,7 @@ add_task(async function one_local_zero_remote() {
Assert.equal(result.local.length, 26);
for (let i = 0; i < 26; i++) {
Assert.equal(
result.local[i],
result.local[i].value,
"letter " + String.fromCharCode("A".charCodeAt() + i)
);
}
@ -474,7 +559,7 @@ add_task(async function zero_local_one_remote() {
Assert.equal(result.term, "letter ");
Assert.equal(result.local.length, 0);
Assert.equal(result.remote.length, 1);
Assert.equal(result.remote[0], "letter A");
Assert.equal(result.remote[0].value, "letter A");
});
add_task(async function stop_search() {
@ -502,7 +587,7 @@ add_task(async function slow_timeout() {
function check_result(result) {
Assert.equal(result.term, "slow ");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "slow local result");
Assert.equal(result.local[0].value, "slow local result");
Assert.equal(result.remote.length, 0);
}
await updateSearchHistory("bump", "slow local result");
@ -543,7 +628,7 @@ add_task(async function remote_term_mismatch() {
let result = await controller.fetch("Query Mismatch", false, getEngine);
Assert.equal(result.term, "Query Mismatch");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "Query Mismatch Entry");
Assert.equal(result.local[0].value, "Query Mismatch Entry");
Assert.equal(result.remote.length, 0);
});
@ -554,7 +639,7 @@ add_task(async function http_404() {
let result = await controller.fetch("HTTP 404", false, getEngine);
Assert.equal(result.term, "HTTP 404");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "HTTP 404 Entry");
Assert.equal(result.local[0].value, "HTTP 404 Entry");
Assert.equal(result.remote.length, 0);
});
@ -565,7 +650,7 @@ add_task(async function http_500() {
let result = await controller.fetch("HTTP 500", false, getEngine);
Assert.equal(result.term, "HTTP 500");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "HTTP 500 Entry");
Assert.equal(result.local[0].value, "HTTP 500 Entry");
Assert.equal(result.remote.length, 0);
});
@ -580,7 +665,7 @@ add_task(async function unresolvable_server() {
);
Assert.equal(result.term, "Unresolvable Server");
Assert.equal(result.local.length, 1);
Assert.equal(result.local[0], "Unresolvable Server Entry");
Assert.equal(result.local[0].value, "Unresolvable Server Entry");
Assert.equal(result.remote.length, 0);
});

Просмотреть файл

@ -117,7 +117,11 @@ async function test_engine(engines, privateMode) {
await new Promise(resolve => {
controller = new SearchSuggestionController(result => {
Assert.equal(result.local.length, 0, "Should have no local suggestions");
Assert.equal(result.remote.length, 0, "Should have no remte suggestions");
Assert.equal(
result.remote.length,
0,
"Should have no remote suggestions"
);
if (result.term == "cookie") {
resolve();
}