fune/browser/components/urlbar/tests/unit/test_tokenizer.js
Drew Willcoxon e2ab883d49 Bug 1657918 - Don't add a heuristic result for empty searches in local search modes, and modify the view to allow it to stay open when empty. r=harry
This excludes the heuristic for empty searches when in search mode. I haven't
heard back from Verdi yet about excluding it for all searches in search mode. We
can add that in a follow-up if necessary.

Since we're now excluding the heuristic but we want the view to remain open,
it's possible for the view to be empty while it's open. I had to make some
changes to allow that to happen. There are three cases I want to call out:

1. When the search string is empty, the view shows top sites. If you then enter
   search mode and the resulting search doesn't return any results, we
   previously closed the view. This patch keeps it open. An example of this
   scenario is when you don't have any bookmarks and you click the bookmarks
   one-off.
2. When the urlbar isn't focused, it's in search mode, the input is empty, and
   the search didn't return any results, then focusing the urlbar didn't do
   anything previously. This patch auto-opens the view.
3. When the input contains a single char and it's in search mode, deleting the
   char previously closed the view if the empty search didn't return any
   results. This patch keeps the view open.

When the view is empty, we also need to hide the one-offs' top border that
usually separates them from the results. Otherwise there are two separator lines
right next to each other, the one above the one-offs and the one at the bottom
edge of the input. I don't think there's any CSS selector that will let us
easily do this due to the DOM structure, so I added a new `noresults` attribute
on the view for this.

I had to call `context.searchString.trim()` to tell whether the search string is
empty. Since we do that in a bunch of places, I added
`context.trimmedSearchString`, and a lot of this patch is replacing those `trim`
calls.

Differential Revision: https://phabricator.services.mozilla.com/D86908
2020-08-13 19:00:14 +00:00

450 lines
13 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function test_tokenizer() {
let testContexts = [
{ desc: "Empty string", searchString: "", expectedTokens: [] },
{ desc: "Spaces string", searchString: " ", expectedTokens: [] },
{
desc: "Single word string",
searchString: "test",
expectedTokens: [{ value: "test", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "Multi word string with mixed whitespace types",
searchString: " test1 test2\u1680test3\u2004test4\u1680",
expectedTokens: [
{ value: "test1", type: UrlbarTokenizer.TYPE.TEXT },
{ value: "test2", type: UrlbarTokenizer.TYPE.TEXT },
{ value: "test3", type: UrlbarTokenizer.TYPE.TEXT },
{ value: "test4", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "separate restriction char at beginning",
searchString: `${UrlbarTokenizer.RESTRICT.BOOKMARK} test`,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "separate restriction char at end",
searchString: `test ${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
expectedTokens: [
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
],
},
{
desc: "boundary restriction char at end",
searchString: `test${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
expectedTokens: [
{
value: `test${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
type: UrlbarTokenizer.TYPE.TEXT,
},
],
},
{
desc: "boundary search restriction char at end",
searchString: `test${UrlbarTokenizer.RESTRICT.SEARCH}`,
expectedTokens: [
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
{
value: UrlbarTokenizer.RESTRICT.SEARCH,
type: UrlbarTokenizer.TYPE.RESTRICT_SEARCH,
},
],
},
{
desc: "separate restriction char in the middle",
searchString: `test ${UrlbarTokenizer.RESTRICT.BOOKMARK} test`,
expectedTokens: [
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.TEXT,
},
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "restriction char in the middle",
searchString: `test${UrlbarTokenizer.RESTRICT.BOOKMARK}test`,
expectedTokens: [
{
value: `test${UrlbarTokenizer.RESTRICT.BOOKMARK}test`,
type: UrlbarTokenizer.TYPE.TEXT,
},
],
},
{
desc: "restriction char in the middle 2",
searchString: `test${UrlbarTokenizer.RESTRICT.BOOKMARK} test`,
expectedTokens: [
{
value: `test${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
type: UrlbarTokenizer.TYPE.TEXT,
},
{ value: `test`, type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "double boundary restriction char",
searchString: `${UrlbarTokenizer.RESTRICT.BOOKMARK}test${UrlbarTokenizer.RESTRICT.TITLE}`,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
{
value: `test${UrlbarTokenizer.RESTRICT.TITLE}`,
type: UrlbarTokenizer.TYPE.TEXT,
},
],
},
{
desc: "double non-combinable restriction char, single char string",
searchString: `t${UrlbarTokenizer.RESTRICT.BOOKMARK}${UrlbarTokenizer.RESTRICT.SEARCH}`,
expectedTokens: [
{
value: `t${UrlbarTokenizer.RESTRICT.BOOKMARK}`,
type: UrlbarTokenizer.TYPE.TEXT,
},
{
value: UrlbarTokenizer.RESTRICT.SEARCH,
type: UrlbarTokenizer.TYPE.RESTRICT_SEARCH,
},
],
},
{
desc: "only boundary restriction chars",
searchString: `${UrlbarTokenizer.RESTRICT.BOOKMARK}${UrlbarTokenizer.RESTRICT.TITLE}`,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
{
value: UrlbarTokenizer.RESTRICT.TITLE,
type: UrlbarTokenizer.TYPE.RESTRICT_TITLE,
},
],
},
{
desc: "only the boundary restriction char",
searchString: UrlbarTokenizer.RESTRICT.BOOKMARK,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
],
},
// Some restriction chars may be # or ?, that are also valid path parts.
// The next 2 tests will check we consider those as part of url paths.
{
desc: "boundary # char on path",
searchString: "test/#",
expectedTokens: [
{ value: "test/#", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "boundary ? char on path",
searchString: "test/?",
expectedTokens: [
{ value: "test/?", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "multiple boundary restriction chars suffix",
searchString: `test ${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.TAG}`,
expectedTokens: [
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
{
value: UrlbarTokenizer.RESTRICT.HISTORY,
type: UrlbarTokenizer.TYPE.TEXT,
},
{
value: UrlbarTokenizer.RESTRICT.TAG,
type: UrlbarTokenizer.TYPE.RESTRICT_TAG,
},
],
},
{
desc: "multiple boundary restriction chars prefix",
searchString: `${UrlbarTokenizer.RESTRICT.HISTORY} ${UrlbarTokenizer.RESTRICT.TAG} test`,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.HISTORY,
type: UrlbarTokenizer.TYPE.RESTRICT_HISTORY,
},
{
value: UrlbarTokenizer.RESTRICT.TAG,
type: UrlbarTokenizer.TYPE.TEXT,
},
{ value: "test", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "Math with division",
searchString: "3.6/1.2",
expectedTokens: [{ value: "3.6/1.2", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "ipv4 in bookmarks",
searchString: `${UrlbarTokenizer.RESTRICT.BOOKMARK} 192.168.1.1:8`,
expectedTokens: [
{
value: UrlbarTokenizer.RESTRICT.BOOKMARK,
type: UrlbarTokenizer.TYPE.RESTRICT_BOOKMARK,
},
{ value: "192.168.1.1:8", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
],
},
{
desc: "email",
searchString: "test@mozilla.com",
expectedTokens: [
{ value: "test@mozilla.com", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "email2",
searchString: "test.test@mozilla.co.uk",
expectedTokens: [
{ value: "test.test@mozilla.co.uk", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "protocol",
searchString: "http://test",
expectedTokens: [
{ value: "http://test", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc:
"bogus protocol with host (we allow visits to http://///example.com)",
searchString: "http:///test",
expectedTokens: [
{ value: "http:///test", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "file protocol with path",
searchString: "file:///home",
expectedTokens: [
{ value: "file:///home", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "almost a protocol",
searchString: "http:",
expectedTokens: [
{ value: "http:", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "almost a protocol 2",
searchString: "http:/",
expectedTokens: [
{ value: "http:/", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "bogus protocol (we allow visits to http://///example.com)",
searchString: "http:///",
expectedTokens: [
{ value: "http:///", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "file protocol",
searchString: "file:///",
expectedTokens: [
{ value: "file:///", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "userinfo",
searchString: "user:pass@test",
expectedTokens: [
{ value: "user:pass@test", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
],
},
{
desc: "domain",
searchString: "www.mozilla.org",
expectedTokens: [
{
value: "www.mozilla.org",
type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN,
},
],
},
{
desc: "data uri",
searchString: "data:text/plain,Content",
expectedTokens: [
{
value: "data:text/plain,Content",
type: UrlbarTokenizer.TYPE.POSSIBLE_URL,
},
],
},
{
desc: "ipv6",
searchString: "[2001:db8::1]",
expectedTokens: [
{ value: "[2001:db8::1]", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
],
},
{
desc: "numeric domain",
searchString: "test1001.com",
expectedTokens: [
{ value: "test1001.com", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
],
},
{
desc: "invalid ip",
searchString: "192.2134.1.2",
expectedTokens: [
{ value: "192.2134.1.2", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "ipv4",
searchString: "1.2.3.4",
expectedTokens: [
{ value: "1.2.3.4", type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN },
],
},
{
desc: "host/path",
searchString: "test/test",
expectedTokens: [
{ value: "test/test", type: UrlbarTokenizer.TYPE.POSSIBLE_URL },
],
},
{
desc: "percent encoded string",
searchString: "%E6%97%A5%E6%9C%AC",
expectedTokens: [
{ value: "%E6%97%A5%E6%9C%AC", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "Uppercase",
searchString: "TEST",
expectedTokens: [{ value: "TEST", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "Mixed case 1",
searchString: "TeSt",
expectedTokens: [{ value: "TeSt", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "Mixed case 2",
searchString: "tEsT",
expectedTokens: [{ value: "tEsT", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "Uppercase with spaces",
searchString: "TEST EXAMPLE",
expectedTokens: [
{ value: "TEST", type: UrlbarTokenizer.TYPE.TEXT },
{ value: "EXAMPLE", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "Mixed case with spaces",
searchString: "TeSt eXaMpLe",
expectedTokens: [
{ value: "TeSt", type: UrlbarTokenizer.TYPE.TEXT },
{ value: "eXaMpLe", type: UrlbarTokenizer.TYPE.TEXT },
],
},
{
desc: "plain number",
searchString: "1001",
expectedTokens: [{ value: "1001", type: UrlbarTokenizer.TYPE.TEXT }],
},
{
desc: "data uri with spaces",
searchString: "data:text/html,oh hi?",
expectedTokens: [
{
value: "data:text/html,oh hi?",
type: UrlbarTokenizer.TYPE.POSSIBLE_URL,
},
],
},
{
desc: "data uri with spaces ignored with other tokens",
searchString: "hi data:text/html,oh hi?",
expectedTokens: [
{
value: "hi",
type: UrlbarTokenizer.TYPE.TEXT,
},
{
value: "data:text/html,oh",
type: UrlbarTokenizer.TYPE.POSSIBLE_URL,
},
{
value: "hi",
type: UrlbarTokenizer.TYPE.TEXT,
},
{
value: UrlbarTokenizer.RESTRICT.SEARCH,
type: UrlbarTokenizer.TYPE.RESTRICT_SEARCH,
},
],
},
{
desc: "whitelisted host",
searchString: "test whitelisted",
expectedTokens: [
{
value: "test",
type: UrlbarTokenizer.TYPE.TEXT,
},
{
value: "whitelisted",
type: UrlbarTokenizer.TYPE.POSSIBLE_ORIGIN,
},
],
},
];
Services.prefs.setBoolPref("browser.fixup.domainwhitelist.whitelisted", true);
for (let queryContext of testContexts) {
info(queryContext.desc);
queryContext.trimmedSearchString = queryContext.searchString.trim();
for (let token of queryContext.expectedTokens) {
token.lowerCaseValue = token.value.toLocaleLowerCase();
}
let newQueryContext = UrlbarTokenizer.tokenize(queryContext);
Assert.equal(
queryContext,
newQueryContext,
"The queryContext object is the same"
);
Assert.deepEqual(
queryContext.tokens,
queryContext.expectedTokens,
"Check the expected tokens"
);
}
});