tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_UrlbarProviderSemanticHistorySearch.js (9976B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
     11  EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
     12 });
     13 
     14 const { UrlbarProviderSemanticHistorySearch } = ChromeUtils.importESModule(
     15  "moz-src:///browser/components/urlbar/UrlbarProviderSemanticHistorySearch.sys.mjs"
     16 );
     17 const { getPlacesSemanticHistoryManager } = ChromeUtils.importESModule(
     18  "resource://gre/modules/PlacesSemanticHistoryManager.sys.mjs"
     19 );
     20 ChromeUtils.defineLazyGetter(this, "QuickSuggestTestUtils", () => {
     21  const { QuickSuggestTestUtils: module } = ChromeUtils.importESModule(
     22    "resource://testing-common/QuickSuggestTestUtils.sys.mjs"
     23  );
     24  module.init(this);
     25  return module;
     26 });
     27 
     28 let semanticManager = getPlacesSemanticHistoryManager();
     29 let hasSufficientEntriesStub = sinon
     30  .stub(semanticManager, "hasSufficientEntriesForSearching")
     31  .resolves(true);
     32 
     33 add_task(async function setup() {
     34  registerCleanupFunction(() => {
     35    sinon.restore();
     36  });
     37 
     38  // stub getEnrollmentMetadata once, then configure for both cases:
     39  const getEnrollmentStub = sinon.stub(
     40    lazy.NimbusFeatures.urlbar,
     41    "getEnrollmentMetadata"
     42  );
     43  getEnrollmentStub
     44    .withArgs(lazy.EnrollmentType.EXPERIMENT)
     45    .returns({ slug: "test-slug", branch: "control" });
     46  getEnrollmentStub.withArgs(lazy.EnrollmentType.ROLLOUT).returns(null);
     47  sinon.stub(lazy.NimbusFeatures.urlbar, "recordExposureEvent");
     48 
     49  // Set required prefs
     50  Services.prefs.setBoolPref("browser.ml.enable", true);
     51  Services.prefs.setBoolPref("places.semanticHistory.featureGate", true);
     52  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
     53  Services.prefs
     54    .getDefaultBranch("")
     55    .setIntPref("browser.urlbar.suggest.semanticHistory.minLength", 5);
     56 
     57  let cleanup = await QuickSuggestTestUtils.setRegionAndLocale({
     58    region: "US",
     59    locale: "en-US",
     60  });
     61  registerCleanupFunction(cleanup);
     62 });
     63 
     64 add_task(async function test_startQuery_adds_results() {
     65  const provider = new UrlbarProviderSemanticHistorySearch();
     66 
     67  const queryContext = { searchString: "test page" };
     68 
     69  // Trigger isActive() to initialize the semantic manager
     70  Assert.ok(await provider.isActive(queryContext), "Provider should be active");
     71 
     72  // Stub and simulate inference
     73  sinon.stub(semanticManager.embedder, "ensureEngine").callsFake(() => {});
     74  let url = "https://example.com";
     75  let inferStub = sinon.stub(semanticManager, "infer").resolves({
     76    results: [
     77      {
     78        id: 1,
     79        title: "Test Page",
     80        url,
     81        frecency: 100,
     82      },
     83    ],
     84  });
     85  await PlacesTestUtils.addVisits(url);
     86 
     87  let added = [];
     88  await provider.startQuery(queryContext, (_provider, result) => {
     89    added.push(result);
     90  });
     91 
     92  Assert.equal(added.length, 1, "One result should be added");
     93  Assert.equal(added[0].payload.url, url, "Correct URL should be used");
     94  Assert.equal(
     95    added[0].payload.icon,
     96    UrlbarUtils.getIconForUrl(url),
     97    "Correct icon should be used"
     98  );
     99  Assert.ok(added[0].payload.isBlockable, "Result should be blockable");
    100  Assert.equal(added[0].payload.frecency, 100, "Frecency is returned");
    101 
    102  let controller = UrlbarTestUtils.newMockController();
    103  let stub = sinon.stub(controller, "removeResult");
    104  let promiseRemoved = PlacesTestUtils.waitForNotification("page-removed");
    105  await provider.onEngagement(queryContext, controller, {
    106    selType: "dismiss",
    107    result: { payload: { url } },
    108  });
    109  Assert.ok(stub.calledOnce, "Result should be removed on dismissal");
    110  await promiseRemoved;
    111  let visited = await PlacesUtils.history.hasVisits(url);
    112  Assert.ok(!visited, "URL should have been removed from history");
    113  inferStub.restore();
    114 });
    115 
    116 add_task(async function test_isActive_conditions() {
    117  const provider = new UrlbarProviderSemanticHistorySearch();
    118 
    119  // Stub canUseSemanticSearch to control the return value
    120  const canUseStub = sinon.stub(semanticManager, "canUseSemanticSearch");
    121 
    122  const shortQuery = { searchString: "hi" };
    123  const validQuery = { searchString: "hello world" };
    124 
    125  // Pref is disabled
    126  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
    127  Assert.ok(
    128    !(await provider.isActive(validQuery)),
    129    "Should be inactive when pref is disabled"
    130  );
    131 
    132  // Pref enabled, but string too short
    133  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
    134  Assert.ok(
    135    !(await provider.isActive(shortQuery)),
    136    "Should be inactive for short search strings"
    137  );
    138 
    139  // All conditions met but semanticManager rejects
    140  canUseStub.get(() => false);
    141  hasSufficientEntriesStub.resetHistory();
    142  Assert.ok(
    143    !(await provider.isActive(validQuery)),
    144    "Should be inactive if canUseSemanticSearch returns false"
    145  );
    146  Assert.ok(
    147    hasSufficientEntriesStub.notCalled,
    148    "hasSufficientEntriesForSearching should not have been called"
    149  );
    150 
    151  // All conditions met
    152  canUseStub.get(() => true);
    153  Assert.ok(
    154    await provider.isActive(validQuery),
    155    "Should be active when all conditions are met"
    156  );
    157 
    158  const engineSearchMode = createContext("hello world", {
    159    searchMode: { engineName: "testEngine" },
    160  });
    161  Assert.ok(
    162    !(await provider.isActive(engineSearchMode)),
    163    "Should not be active when in search engine mode"
    164  );
    165 
    166  const historySearchMode = createContext("hello world", {
    167    searchMode: { source: UrlbarUtils.RESULT_SOURCE.HISTORY },
    168  });
    169  Assert.ok(
    170    await provider.isActive(historySearchMode),
    171    "Should be active when in history search mode"
    172  );
    173 });
    174 
    175 add_task(async function test_switchTab() {
    176  const userContextId1 = 2;
    177  const userContextId2 = 3;
    178  const privateContextId = -1;
    179  const url1 = "http://foo.mozilla.org/";
    180  const url2 = "http://foo2.mozilla.org/";
    181  await UrlbarProviderOpenTabs.registerOpenTab(
    182    url1,
    183    userContextId1,
    184    null,
    185    false
    186  );
    187  await UrlbarProviderOpenTabs.registerOpenTab(
    188    url2,
    189    userContextId1,
    190    null,
    191    false
    192  );
    193  await UrlbarProviderOpenTabs.registerOpenTab(
    194    url1,
    195    userContextId2,
    196    null,
    197    false
    198  );
    199  await UrlbarProviderOpenTabs.registerOpenTab(
    200    url1,
    201    privateContextId,
    202    null,
    203    false
    204  );
    205  await PlacesTestUtils.addVisits([url1, url2]);
    206  const provider = new UrlbarProviderSemanticHistorySearch();
    207 
    208  // Trigger isActive() to initialize the semantic manager
    209  const queryContext = createContext("firefox", { isPrivate: false });
    210  Assert.ok(await provider.isActive(queryContext), "Provider should be active");
    211  let inferStub = sinon.stub(semanticManager, "infer").resolves({
    212    results: [
    213      {
    214        id: 1,
    215        title: "Test Page 1",
    216        url: url1,
    217      },
    218      {
    219        id: 2,
    220        title: "Test Page 2",
    221        url: url2,
    222      },
    223    ],
    224  });
    225 
    226  function AssertSwitchToTabResult(result, url, userContextId, groupId = null) {
    227    Assert.equal(
    228      result.type,
    229      UrlbarUtils.RESULT_TYPE.TAB_SWITCH,
    230      "Check result type"
    231    );
    232    Assert.equal(result.payload.url, url, "Check result URL");
    233    Assert.equal(
    234      result.payload.userContextId,
    235      userContextId,
    236      "Check user context"
    237    );
    238    Assert.equal(result.payload.tabGroup, groupId, "Check tab group");
    239    Assert.equal(
    240      result.payload.icon,
    241      UrlbarUtils.getIconForUrl(url),
    242      "Check icon"
    243    );
    244  }
    245  function isUrlResult(result, url) {
    246    return (
    247      result.type === UrlbarUtils.RESULT_TYPE.URL && result.payload.url === url
    248    );
    249  }
    250 
    251  let added = [];
    252  await provider.startQuery(queryContext, (_provider, result) => {
    253    added.push(result);
    254  });
    255  Assert.equal(added.length, 3, "Threee result should be added");
    256  AssertSwitchToTabResult(added[0], url1, userContextId1);
    257  AssertSwitchToTabResult(added[1], url1, userContextId2);
    258  AssertSwitchToTabResult(added[2], url2, userContextId1);
    259 
    260  info("Test private browsing context.");
    261  const privateContext = createContext("firefox");
    262  added.length = 0;
    263  await provider.startQuery(privateContext, (_provider, result) => {
    264    added.push(result);
    265  });
    266  Assert.equal(added.length, 2, "Two results should be added");
    267  AssertSwitchToTabResult(added[0], url1, privateContextId);
    268  Assert.ok(isUrlResult(added[1], url2), "Second result should be URL");
    269 
    270  info("Test single container mode.");
    271  Services.prefs.setBoolPref(
    272    "browser.urlbar.switchTabs.searchAllContainers",
    273    false
    274  );
    275  const singleContext = createContext("firefox", {
    276    isPrivate: false,
    277    userContextId: userContextId1,
    278  });
    279  added.length = 0;
    280  await provider.startQuery(singleContext, (_provider, result) => {
    281    added.push(result);
    282  });
    283  Assert.equal(added.length, 2, "Two results should be added");
    284  AssertSwitchToTabResult(added[0], url1, userContextId1);
    285  AssertSwitchToTabResult(added[1], url2, userContextId1);
    286  Services.prefs.clearUserPref("browser.urlbar.switchTabs.searchAllContainers");
    287 
    288  info("Test tab groups and current page.");
    289  let tabGroudId1 = "group1";
    290  let tabGroudId2 = "group2";
    291  await UrlbarProviderOpenTabs.registerOpenTab(
    292    url1,
    293    userContextId1,
    294    tabGroudId1,
    295    false
    296  );
    297  await UrlbarProviderOpenTabs.registerOpenTab(
    298    url2,
    299    userContextId2,
    300    tabGroudId2,
    301    false
    302  );
    303  const groupContext = createContext("firefox", {
    304    isPrivate: false,
    305    currentPage: url1,
    306    userContextId: userContextId1,
    307    tabGroup: tabGroudId1,
    308  });
    309 
    310  added.length = 0;
    311  await provider.startQuery(groupContext, (_provider, result) => {
    312    added.push(result);
    313  });
    314  Assert.equal(added.length, 4, "Three results should be added");
    315  AssertSwitchToTabResult(added[0], url1, userContextId1);
    316  AssertSwitchToTabResult(added[1], url1, userContextId2);
    317  AssertSwitchToTabResult(added[2], url2, userContextId1);
    318  AssertSwitchToTabResult(added[3], url2, userContextId2, tabGroudId2);
    319 
    320  inferStub.restore();
    321 });