Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* Any copyright is dedicated to the Public Domain.
// Tests for input history related to autofill.
"use strict";
let addToInputHistorySpy;
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [["browser.urlbar.autoFill.adaptiveHistory.enabled", true]],
});
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
let sandbox = sinon.createSandbox();
addToInputHistorySpy = sandbox.spy(UrlbarUtils, "addToInputHistory");
registerCleanupFunction(async () => {
sandbox.restore();
});
});
// Input history use count should be bumped when an adaptive history autofill
// result is triggered and picked.
add_task(async function bumped() {
let input = "exam";
let tests = [
// Basic test where the search string = the adaptive history input.
{
searchString: "exam",
},
// The history with input "exam" should be bumped, not "example", even
// though the search string is "example".
{
searchString: "example",
},
// "example".
{
searchString: "exam",
},
];
for (let { url, searchString } of tests) {
info("Running subtest: " + JSON.stringify({ url, searchString }));
await PlacesTestUtils.addVisits({
url,
transition: PlacesUtils.history.TRANSITION_TYPED,
});
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
await UrlbarUtils.addToInputHistory(url, input);
addToInputHistorySpy.resetHistory();
let initialUseCount = await getUseCount({ url, input });
info("Got initial use count: " + initialUseCount);
await triggerAutofillAndPickResult(searchString, "example.com/test");
let calls = addToInputHistorySpy.getCalls();
Assert.equal(
calls.length,
1,
"UrlbarUtils.addToInputHistory() called once"
);
Assert.deepEqual(
calls[0].args,
[url, input],
"UrlbarUtils.addToInputHistory() called with expected args"
);
Assert.greater(
await getUseCount({ url, input }),
initialUseCount,
"New use count > initial use count"
);
if (searchString != input) {
Assert.strictEqual(
await getUseCount({ input: searchString }),
undefined,
"Search string not present in input history: " + searchString
);
}
await PlacesUtils.history.clear();
await PlacesTestUtils.clearInputHistory();
addToInputHistorySpy.resetHistory();
}
});
// Input history use count should be bumped when an origin autofill result
// is triggered and picked.
add_task(async function bumped_origin() {
addToInputHistorySpy.resetHistory();
// Add enough visits to trigger origin autofill.
await PlacesTestUtils.addVisits({
url,
transition: PlacesUtils.history.TRANSITION_TYPED,
});
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
await triggerAutofillAndPickResult("exam", "example.com/");
// addToInputHistoryWhenReady is fire-and-forget, so wait for the DB write
// to complete before checking the spy.
await TestUtils.waitForCondition(
async () =>
"Origin URL present in input history"
);
// Called once or twice depending on whether the origin was already in
// moz_places when addToInputHistoryWhenReady made its first attempt.
let calls = addToInputHistorySpy.getCalls();
Assert.greaterOrEqual(
calls.length,
1,
"UrlbarUtils.addToInputHistory() called at least once"
);
Assert.greater(
0,
"URL present in input history: " + url
);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearInputHistory();
addToInputHistorySpy.resetHistory();
});
// Input history use count should not be bumped when a URL autofill result is
// triggered and picked.
add_task(async function notBumped_url() {
addToInputHistorySpy.resetHistory();
await PlacesTestUtils.addVisits({
url,
transition: PlacesUtils.history.TRANSITION_TYPED,
});
await PlacesFrecencyRecalculator.recalculateAnyOutdatedFrecencies();
await triggerAutofillAndPickResult("example.com/t", "example.com/test");
let calls = addToInputHistorySpy.getCalls();
Assert.equal(calls.length, 0, "UrlbarUtils.addToInputHistory() not called");
Assert.strictEqual(
await getUseCount({ url }),
undefined,
"URL not present in input history: " + url
);
await PlacesUtils.history.clear();
});
// Input history use count should not be bumped when a search result is picked.
add_task(async function notBumped_search() {
addToInputHistorySpy.resetHistory();
let extension = await SearchTestUtils.installSearchExtension(
{},
{ setAsDefault: true, skipUnload: true }
);
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.suggest.searches", true],
["browser.urlbar.maxHistoricalSearchSuggestions", 1],
],
});
await UrlbarTestUtils.formHistory.add(["example search"]);
await BrowserTestUtils.withNewTab("about:blank", async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "example",
fireInputEvent: true,
});
let resultIndex = -1;
for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
if (
details.type == UrlbarShared.RESULT_TYPE.SEARCH &&
!details.result.heuristic
) {
resultIndex = i;
break;
}
}
Assert.greater(resultIndex, -1, "Found a non-heuristic search result");
gURLBar.view.selectedRowIndex = resultIndex;
await UrlbarTestUtils.promisePopupClose(window, () => {
EventUtils.synthesizeKey("KEY_Enter", {}, window);
});
});
let calls = addToInputHistorySpy.getCalls();
Assert.equal(calls.length, 0, "UrlbarUtils.addToInputHistory() not called");
await extension.unload();
await SpecialPowers.popPrefEnv();
await UrlbarTestUtils.formHistory.clear();
addToInputHistorySpy.resetHistory();
});
/**
* Performs a search and picks the first result.
*
* @param {string} searchString
* The search string. Assumed to trigger an autofill result.
* @param {string} autofilledValue
* The input's expected value after autofill occurs.
*/
async function triggerAutofillAndPickResult(searchString, autofilledValue) {
await BrowserTestUtils.withNewTab("about:blank", async () => {
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: searchString,
fireInputEvent: true,
});
let details = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
Assert.ok(details.autofill, "Result is autofill");
Assert.equal(gURLBar.value, autofilledValue, "gURLBar.value");
Assert.equal(gURLBar.selectionStart, searchString.length, "selectionStart");
Assert.equal(gURLBar.selectionEnd, autofilledValue.length, "selectionEnd");
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
EventUtils.synthesizeKey("KEY_Enter");
await loadPromise;
});
}
/**
* Gets the use count of an input history record.
*
* @param {object} options
* Options object.
* @param {string} [options.url]
* The URL of the `moz_places` row corresponding to the record.
* @param {string} [options.input]
* The `input` value in the record.
* @returns {number}
* The use count. If no record exists with the URL and/or input, undefined is
* returned.
*/
async function getUseCount({ url = undefined, input = undefined }) {
return PlacesUtils.withConnectionWrapper("test::getUseCount", async db => {
let rows;
if (input && url) {
rows = await db.executeCached(
`SELECT i.use_count
FROM moz_inputhistory i
JOIN moz_places h ON h.id = i.place_id
WHERE h.url = :url AND i.input = :input`,
{ url, input }
);
} else if (url) {
rows = await db.executeCached(
`SELECT i.use_count
FROM moz_inputhistory i
JOIN moz_places h ON h.id = i.place_id
WHERE h.url = :url`,
{ url }
);
} else if (input) {
rows = await db.executeCached(
`SELECT use_count
FROM moz_inputhistory i
WHERE input = :input`,
{ input }
);
}
return rows[0]?.getResultByIndex(0);
});
}