Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android'
- Manifest: browser/components/urlbar/tests/unit/xpcshell.toml
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
// Test for adaptive history autofill.
testEngine_setup();
registerCleanupFunction(async () => {
Services.prefs.clearUserPref("browser.urlbar.suggest.quickactions");
});
Services.prefs.setBoolPref("browser.urlbar.suggest.quickactions", false);
const TEST_DATA = [
{
description: "Basic behavior for adaptive history autofill",
pref: true,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "URL that has www",
pref: true,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "User's input starts with www",
pref: true,
userInput: "www.exa",
expected: {
autofilled: "www.example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "Case differences for user's input are ignored",
pref: true,
userInput: "eXA",
expected: {
autofilled: "eXAmple.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description:
"Case differences for user's input that starts with www are ignored",
pref: true,
userInput: "WWW.exa",
expected: {
autofilled: "WWW.example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "Mutiple case difference input history",
pref: true,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/yes",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Multiple input history count",
pref: true,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/many",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Multiple input history count with same input",
pref: true,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/many",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Multiple input history count with same input but different frecency",
pref: true,
visitHistory: [
],
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/many",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "User input is shorter than the input history",
pref: true,
userInput: "e",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "User input is longer than the input history",
pref: true,
userInput: "example",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description:
"User input starts with input history and includes path of the url",
pref: true,
userInput: "example.com/te",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "User input starts with input history and but another url",
pref: true,
userInput: "example.o",
expected: {
autofilled: "example.org/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "User input does not start with input history",
pref: true,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"User input does not start with input history, but it includes as part of URL",
pref: true,
userInput: "test",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "User input does not start with visited URL",
pref: true,
userInput: "exa",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Visited page is bookmarked",
pref: true,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
title: "test bookmark",
heuristic: true,
}),
],
},
},
{
description: "Visit history and no bookamrk with HISTORY source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "Visit history and no bookamrk with BOOKMARK source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
userInput: "exa",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
],
},
},
{
description: "Bookmarked visit history with HISTORY source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
bookmarks: [
],
inputHistory: [
{
input: "exa",
},
{
input: "exa",
},
],
userInput: "exa",
expected: {
autofilled: "example.com/bookmarked",
results: [
context =>
makeVisitResult(context, {
title: "test bookmark",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Bookmarked visit history with BOOKMARK source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
bookmarks: [
],
inputHistory: [
{
input: "exa",
},
{
input: "exa",
},
],
userInput: "exa",
expected: {
autofilled: "example.com/bookmarked",
results: [
context =>
makeVisitResult(context, {
title: "test bookmark",
heuristic: true,
}),
],
},
},
{
description: "No visit history with HISTORY source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
userInput: "exa",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
],
},
},
{
description: "No visit history with BOOKMARK source",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
userInput: "exa",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
],
},
},
{
description: "Match with path expression",
pref: true,
visitHistory: [
],
userInput: "example.com/te",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Prefixed URL for input history and the same string for user input",
pref: true,
visitHistory: [
],
inputHistory: [
],
expected: {
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Prefixed URL for input history and URL expression for user input",
pref: true,
visitHistory: [
],
inputHistory: [
],
expected: {
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Prefixed URL for input history and path expression for user input",
pref: true,
visitHistory: [
],
inputHistory: [
],
userInput: "example.com/te",
expected: {
autofilled: "example.com/testMany",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Prefixed URL for input history and 'http' for user input",
pref: true,
userInput: "http",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Prefixed URL for input history and 'http:' for user input",
pref: true,
userInput: "http:",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Prefixed URL for input history and 'http:/' for user input",
pref: true,
userInput: "http:/",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Prefixed URL for input history and 'http://' for user input",
pref: true,
userInput: "http://",
expected: {
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
pref: true,
expected: {
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description:
pref: true,
expected: {
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description:
"Those that match with fixed URL take precedence over those that match prefixed URL",
pref: true,
inputHistory: [
],
userInput: "http",
expected: {
autofilled: "http.example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Input history is totally different string from the URL",
pref: true,
visitHistory: [
],
inputHistory: [
],
userInput: "totally",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Input history is totally different string from the URL and there is a visit history whose URL starts with the input",
pref: true,
inputHistory: [
],
userInput: "totally",
expected: {
autofilled: "totally.example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Use count threshold is as same as use count of input history",
pref: true,
useCountThreshold: 1 * 0.9 + 1,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "Use count threshold is less than use count of input history",
pref: true,
useCountThreshold: 3,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "Use count threshold is more than use count of input history",
pref: true,
useCountThreshold: 10,
inputHistory: [
],
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "minCharsThreshold pref equals to the user input length",
pref: true,
minCharsThreshold: 3,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "minCharsThreshold pref is smaller than the user input length",
pref: true,
minCharsThreshold: 2,
userInput: "exa",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description: "minCharsThreshold pref is larger than the user input length",
pref: true,
minCharsThreshold: 4,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Prioritize path component with case-sensitive and that is visited",
pref: true,
visitHistory: [
],
inputHistory: [
],
userInput: "example.com/test",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"Prioritize path component with case-sensitive but no visited data",
pref: true,
inputHistory: [
],
userInput: "example.com/test",
expected: {
autofilled: "example.com/test",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"With history and bookmarks sources, foreign_count == 0, frecency <= 0: No adaptive history autofill",
pref: true,
frecency: 0,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description:
"With history source, visit_count == 0, foreign_count != 0: No adaptive history autofill",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
userInput: "exa",
expected: {
results: [
context =>
makeSearchResult(context, {
engineName: "Suggestions",
heuristic: true,
}),
],
},
},
{
description:
"With history source, visit_count > 0, foreign_count != 0, frecency <= 20: No adaptive history autofill",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
frecency: 0,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
],
},
},
{
description:
"With history source, visit_count > 0, foreign_count == 0, frecency <= 20: No adaptive history autofill",
pref: true,
source: UrlbarUtils.RESULT_SOURCE.HISTORY,
frecency: 0,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Empty input string",
pref: true,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
{
description: "Turn the pref off",
pref: false,
userInput: "exa",
expected: {
autofilled: "example.com/",
results: [
context =>
makeVisitResult(context, {
heuristic: true,
}),
context =>
makeVisitResult(context, {
}),
],
},
},
];
add_task(async function inputTest() {
for (const {
description,
pref,
minCharsThreshold,
useCountThreshold,
source,
visitHistory,
inputHistory,
bookmarks,
frecency,
userInput,
expected,
} of TEST_DATA) {
info(description);
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", pref);
if (!isNaN(minCharsThreshold)) {
UrlbarPrefs.set(
"autoFill.adaptiveHistory.minCharsThreshold",
minCharsThreshold
);
}
if (!isNaN(useCountThreshold)) {
UrlbarPrefs.set(
"autoFill.adaptiveHistory.useCountThreshold",
useCountThreshold
);
}
if (visitHistory && visitHistory.length) {
await PlacesTestUtils.addVisits(visitHistory);
}
for (const { uri, input } of inputHistory) {
await UrlbarUtils.addToInputHistory(uri, input);
}
for (const bookmark of bookmarks || []) {
await PlacesTestUtils.addBookmarkWithDetails(bookmark);
}
if (typeof frecency == "number") {
await PlacesUtils.withConnectionWrapper("test::setFrecency", db =>
db.execute(
`UPDATE moz_places SET frecency = :frecency WHERE url = :url`,
{
frecency,
url: visitHistory[0],
}
)
);
}
const sources = source
? [source]
: [
UrlbarUtils.RESULT_SOURCE.HISTORY,
UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
];
const context = createContext(userInput, {
sources,
isPrivate: false,
});
await check_results({
context,
autofilled: expected.autofilled,
completed: expected.completed,
hasAutofillTitle: expected.hasAutofillTitle,
matches: expected.results.map(f => f(context)),
});
await cleanupPlaces();
UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
UrlbarPrefs.clear("autoFill.adaptiveHistory.minCharsThreshold");
UrlbarPrefs.clear("autoFill.adaptiveHistory.useCountThreshold");
}
});
add_task(async function urlCase() {
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", true);
const testVisitFixed = "example.com/ABC/DEF";
const testInput = "example";
await PlacesTestUtils.addVisits([testVisitURL]);
await UrlbarUtils.addToInputHistory(testVisitURL, testInput);
const userInput = "example.COM/abc/def";
for (let i = 1; i <= userInput.length; i++) {
const currentUserInput = userInput.substring(0, i);
const context = createContext(currentUserInput, { isPrivate: false });
if (currentUserInput.length < testInput.length) {
// Autofill with host.
await check_results({
context,
autofilled: "example.com/",
matches: [
makeVisitResult(context, {
heuristic: true,
}),
makeVisitResult(context, {
}),
],
});
} else if (currentUserInput.length !== testVisitFixed.length) {
// Autofill using input history.
const autofilled = currentUserInput + testVisitFixed.substring(i);
await check_results({
context,
autofilled,
matches: [
makeVisitResult(context, {
heuristic: true,
}),
],
});
} else {
// Autofill using user's input.
await check_results({
context,
autofilled: "example.COM/abc/def",
matches: [
makeVisitResult(context, {
fallbackTitle: UrlbarTestUtils.trimURL(
),
heuristic: true,
}),
makeVisitResult(context, {
}),
],
});
}
}
await cleanupPlaces();
UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
});
add_task(async function decayTest() {
UrlbarPrefs.set("autoFill.adaptiveHistory.enabled", true);
const initContext = createContext("exa", { isPrivate: false });
await check_results({
context: initContext,
autofilled: "example.com/test",
matches: [
makeVisitResult(initContext, {
heuristic: true,
}),
],
});
// The decay rate for a day is 0.975, as defined in PlacesFrecencyRecalculator
// Therefore, after 30 days, as use_count will be 0.975^30 = 0.468, we set the
// useCountThreshold 0.47 to not take the input history passed 30 days.
UrlbarPrefs.set("autoFill.adaptiveHistory.useCountThreshold", 0.47);
// Make 29 days later.
for (let i = 0; i < 29; i++) {
await Cc["@mozilla.org/places/frecency-recalculator;1"]
.getService(Ci.nsIObserver)
.wrappedJSObject.decay();
}
const midContext = createContext("exa", { isPrivate: false });
await check_results({
context: midContext,
autofilled: "example.com/test",
matches: [
makeVisitResult(midContext, {
heuristic: true,
}),
],
});
// Total 30 days later.
await Cc["@mozilla.org/places/frecency-recalculator;1"]
.getService(Ci.nsIObserver)
.wrappedJSObject.decay();
const context = createContext("exa", { isPrivate: false });
await check_results({
context,
autofilled: "example.com/",
matches: [
makeVisitResult(context, {
heuristic: true,
}),
makeVisitResult(context, {
}),
],
});
await cleanupPlaces();
UrlbarPrefs.clear("autoFill.adaptiveHistory.enabled");
UrlbarPrefs.clear("autoFill.adaptiveHistory.useCountThreshold");
});