Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
});
const { UrlbarProviderSemanticHistorySearch } = ChromeUtils.importESModule(
"resource:///modules/UrlbarProviderSemanticHistorySearch.sys.mjs"
);
const { getPlacesSemanticHistoryManager } = ChromeUtils.importESModule(
"resource://gre/modules/PlacesSemanticHistoryManager.sys.mjs"
);
let semanticManager = getPlacesSemanticHistoryManager();
let hasSufficientEntriesStub = sinon
.stub(semanticManager, "hasSufficientEntriesForSearching")
.resolves(true);
add_task(async function setup() {
registerCleanupFunction(() => {
sinon.restore();
});
// stub getEnrollmentMetadata once, then configure for both cases:
const getEnrollmentStub = sinon.stub(
lazy.NimbusFeatures.urlbar,
"getEnrollmentMetadata"
);
getEnrollmentStub
.withArgs(lazy.EnrollmentType.EXPERIMENT)
.returns({ slug: "test-slug", branch: "control" });
getEnrollmentStub.withArgs(lazy.EnrollmentType.ROLLOUT).returns(null);
sinon.stub(lazy.NimbusFeatures.urlbar, "recordExposureEvent");
// Set required prefs
Services.prefs.setBoolPref("browser.ml.enable", true);
Services.prefs.setBoolPref("places.semanticHistory.featureGate", true);
Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
Services.prefs
.getDefaultBranch("")
.setIntPref("browser.urlbar.suggest.semanticHistory.minLength", 5);
});
add_task(async function test_startQuery_adds_results() {
const provider = UrlbarProviderSemanticHistorySearch;
const queryContext = { searchString: "test page" };
// Trigger isActive() to initialize the semantic manager
Assert.ok(await provider.isActive(queryContext), "Provider should be active");
// Stub and simulate inference
sinon.stub(semanticManager.embedder, "ensureEngine").callsFake(() => {});
let url = "https://example.com";
let inferStub = sinon.stub(semanticManager, "infer").resolves({
results: [
{
id: 1,
title: "Test Page",
url,
frecency: 100,
},
],
});
await PlacesTestUtils.addVisits(url);
let added = [];
await provider.startQuery(queryContext, (_provider, result) => {
added.push(result);
});
Assert.equal(added.length, 1, "One result should be added");
Assert.equal(added[0].payload.url, url, "Correct URL should be used");
Assert.equal(
added[0].payload.icon,
UrlbarUtils.getIconForUrl(url),
"Correct icon should be used"
);
Assert.ok(added[0].payload.isBlockable, "Result should be blockable");
Assert.equal(added[0].payload.frecency, 100, "Frecency is returned");
let controller = UrlbarTestUtils.newMockController();
let stub = sinon.stub(controller, "removeResult");
let promiseRemoved = PlacesTestUtils.waitForNotification("page-removed");
await provider.onEngagement(queryContext, controller, {
selType: "dismiss",
result: { payload: { url } },
});
Assert.ok(stub.calledOnce, "Result should be removed on dismissal");
await promiseRemoved;
let visited = await PlacesUtils.history.hasVisits(url);
Assert.ok(!visited, "URL should have been removed from history");
inferStub.restore();
});
add_task(async function test_isActive_conditions() {
const provider = UrlbarProviderSemanticHistorySearch;
// Stub canUseSemanticSearch to control the return value
const canUseStub = sinon.stub(semanticManager, "canUseSemanticSearch");
const shortQuery = { searchString: "hi" };
const validQuery = { searchString: "hello world" };
// Pref is disabled
Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
Assert.ok(
!(await provider.isActive(validQuery)),
"Should be inactive when pref is disabled"
);
// Pref enabled, but string too short
Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
Assert.ok(
!(await provider.isActive(shortQuery)),
"Should be inactive for short search strings"
);
// All conditions met but semanticManager rejects
canUseStub.get(() => false);
hasSufficientEntriesStub.resetHistory();
Assert.ok(
!(await provider.isActive(validQuery)),
"Should be inactive if canUseSemanticSearch returns false"
);
Assert.ok(
hasSufficientEntriesStub.notCalled,
"hasSufficientEntriesForSearching should not have been called"
);
// All conditions met
canUseStub.get(() => true);
Assert.ok(
await provider.isActive(validQuery),
"Should be active when all conditions are met"
);
const engineSearchMode = createContext("hello world", {
searchMode: { engineName: "testEngine" },
});
Assert.ok(
!(await provider.isActive(engineSearchMode)),
"Should not be active when in search engine mode"
);
const historySearchMode = createContext("hello world", {
searchMode: { source: UrlbarUtils.RESULT_SOURCE.HISTORY },
});
Assert.ok(
await provider.isActive(historySearchMode),
"Should be active when in history search mode"
);
});
add_task(async function test_switchTab() {
const userContextId1 = 2;
const userContextId2 = 3;
const privateContextId = -1;
const url1 = "http://foo.mozilla.org/";
const url2 = "http://foo2.mozilla.org/";
await UrlbarProviderOpenTabs.registerOpenTab(
url1,
userContextId1,
null,
false
);
await UrlbarProviderOpenTabs.registerOpenTab(
url2,
userContextId1,
null,
false
);
await UrlbarProviderOpenTabs.registerOpenTab(
url1,
userContextId2,
null,
false
);
await UrlbarProviderOpenTabs.registerOpenTab(
url1,
privateContextId,
null,
false
);
await PlacesTestUtils.addVisits([url1, url2]);
const provider = UrlbarProviderSemanticHistorySearch;
// Trigger isActive() to initialize the semantic manager
const queryContext = createContext("firefox", { isPrivate: false });
Assert.ok(await provider.isActive(queryContext), "Provider should be active");
let inferStub = sinon.stub(semanticManager, "infer").resolves({
results: [
{
id: 1,
title: "Test Page 1",
url: url1,
},
{
id: 2,
title: "Test Page 2",
url: url2,
},
],
});
function AssertSwitchToTabResult(result, url, userContextId, groupId = null) {
Assert.equal(
result.type,
UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
"Check result type"
);
Assert.equal(result.payload.url, url, "Check result URL");
Assert.equal(
result.payload.userContextId,
userContextId,
"Check user context"
);
Assert.equal(result.payload.tabGroup, groupId, "Check tab group");
Assert.equal(
result.payload.icon,
UrlbarUtils.getIconForUrl(url),
"Check icon"
);
}
function isUrlResult(result, url) {
return (
result.type === UrlbarUtils.RESULT_TYPE.URL && result.payload.url === url
);
}
let added = [];
await provider.startQuery(queryContext, (_provider, result) => {
added.push(result);
});
Assert.equal(added.length, 3, "Threee result should be added");
AssertSwitchToTabResult(added[0], url1, userContextId1);
AssertSwitchToTabResult(added[1], url1, userContextId2);
AssertSwitchToTabResult(added[2], url2, userContextId1);
info("Test private browsing context.");
const privateContext = createContext("firefox");
added.length = 0;
await provider.startQuery(privateContext, (_provider, result) => {
added.push(result);
});
Assert.equal(added.length, 2, "Two results should be added");
AssertSwitchToTabResult(added[0], url1, privateContextId);
Assert.ok(isUrlResult(added[1], url2), "Second result should be URL");
info("Test single container mode.");
Services.prefs.setBoolPref(
"browser.urlbar.switchTabs.searchAllContainers",
false
);
const singleContext = createContext("firefox", {
isPrivate: false,
userContextId: userContextId1,
});
added.length = 0;
await provider.startQuery(singleContext, (_provider, result) => {
added.push(result);
});
Assert.equal(added.length, 2, "Two results should be added");
AssertSwitchToTabResult(added[0], url1, userContextId1);
AssertSwitchToTabResult(added[1], url2, userContextId1);
Services.prefs.clearUserPref("browser.urlbar.switchTabs.searchAllContainers");
info("Test tab groups and current page.");
let tabGroudId1 = "group1";
let tabGroudId2 = "group2";
await UrlbarProviderOpenTabs.registerOpenTab(
url1,
userContextId1,
tabGroudId1,
false
);
await UrlbarProviderOpenTabs.registerOpenTab(
url2,
userContextId2,
tabGroudId2,
false
);
const groupContext = createContext("firefox", {
isPrivate: false,
currentPage: url1,
userContextId: userContextId1,
tabGroup: tabGroudId1,
});
added.length = 0;
await provider.startQuery(groupContext, (_provider, result) => {
added.push(result);
});
Assert.equal(added.length, 4, "Three results should be added");
AssertSwitchToTabResult(added[0], url1, userContextId1);
AssertSwitchToTabResult(added[1], url1, userContextId2);
AssertSwitchToTabResult(added[2], url2, userContextId1);
AssertSwitchToTabResult(added[3], url2, userContextId2, tabGroudId2);
inferStub.restore();
});