tor-browser

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

browser_search_engineList.js (10885B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 // This tests the search engine list in about:preferences#search.
      5 
      6 "use strict";
      7 
      8 const { SearchTestUtils } = ChromeUtils.importESModule(
      9  "resource://testing-common/SearchTestUtils.sys.mjs"
     10 );
     11 const { SearchUtils } = ChromeUtils.importESModule(
     12  "moz-src:///toolkit/components/search/SearchUtils.sys.mjs"
     13 );
     14 const { sinon } = ChromeUtils.importESModule(
     15  "resource://testing-common/Sinon.sys.mjs"
     16 );
     17 
     18 SearchTestUtils.init(this);
     19 
     20 const CONFIG = [
     21  { identifier: "engine1" },
     22  { identifier: "engine2" },
     23  {
     24    identifier: "de_only_engine",
     25    base: {
     26      urls: {
     27        search: {
     28          base: "https://moz.test/",
     29          searchTermParamName: "search",
     30        },
     31      },
     32    },
     33    variants: [
     34      {
     35        environment: { locales: ["de"] },
     36      },
     37    ],
     38  },
     39 ];
     40 
     41 let userEngine;
     42 let extensionEngine;
     43 let installedEngines;
     44 let userInstalledAppEngine;
     45 
     46 function getCellText(tree, i, cellName) {
     47  return tree.view.getCellText(i, tree.columns.getNamedColumn(cellName));
     48 }
     49 
     50 async function engine_list_test(fn) {
     51  let task = async () => {
     52    let prefs = await openPreferencesViaOpenPreferencesAPI("search", {
     53      leaveOpen: true,
     54    });
     55    Assert.equal(
     56      prefs.selectedPane,
     57      "paneSearch",
     58      "Search pane is selected by default"
     59    );
     60    let doc = gBrowser.contentDocument;
     61    let tree = doc.querySelector("#engineList");
     62    Assert.ok(
     63      !tree.hidden,
     64      "The search engine list should be visible when Search is requested"
     65    );
     66    // Scroll the treeview into view since mouse operations
     67    // off screen can act confusingly.
     68    tree.scrollIntoView();
     69    await fn(tree, doc);
     70    BrowserTestUtils.removeTab(gBrowser.selectedTab);
     71  };
     72 
     73  // Make sure the name of the passed function is used in logs.
     74  Object.defineProperty(task, "name", { value: fn.name });
     75  add_task(task);
     76 }
     77 async function selectEngine(tree, index) {
     78  let rect = tree.getCoordsForCellItem(
     79    index,
     80    tree.columns.getNamedColumn("engineName"),
     81    "text"
     82  );
     83  let x = rect.x + rect.width / 2;
     84  let y = rect.y + rect.height / 2;
     85  let promise = BrowserTestUtils.waitForEvent(tree, "click");
     86  EventUtils.synthesizeMouse(
     87    tree.body,
     88    x,
     89    y,
     90    { clickCount: 1 },
     91    tree.ownerGlobal
     92  );
     93  return promise;
     94 }
     95 
     96 add_setup(async function () {
     97  await SearchTestUtils.updateRemoteSettingsConfig(CONFIG);
     98  installedEngines = await Services.search.getAppProvidedEngines();
     99 
    100  await SearchTestUtils.installSearchExtension({
    101    keyword: ["testing", "customkeyword"],
    102    search_url: "https://example.com/engine1",
    103    search_url_get_params: "search={searchTerms}",
    104    name: "Extension Engine",
    105  });
    106  extensionEngine = Services.search.getEngineByName("Extension Engine");
    107  installedEngines.push(extensionEngine);
    108 
    109  userEngine = await Services.search.addUserEngine({
    110    name: "User Engine",
    111    url: "https://example.com/user?q={searchTerms}&b=ff",
    112    alias: "u",
    113  });
    114  installedEngines.push(userEngine);
    115 
    116  userInstalledAppEngine =
    117    await Services.search.findContextualSearchEngineByHost("moz.test");
    118 
    119  await Services.search.addSearchEngine(userInstalledAppEngine);
    120  // The added engines are removed in the last test.
    121 });
    122 
    123 engine_list_test(async function test_engine_list(tree) {
    124  let userEngineIndex = installedEngines.length - 1;
    125  for (let i = 0; i < installedEngines.length; i++) {
    126    let engine = installedEngines[i];
    127    Assert.equal(
    128      getCellText(tree, i, "engineName"),
    129      engine.name,
    130      "Search engine " + engine.name + " displayed correctly."
    131    );
    132    Assert.equal(
    133      tree.view.isEditable(i, tree.columns.getNamedColumn("engineName")),
    134      i == userEngineIndex,
    135      "Only user engine name is editable."
    136    );
    137  }
    138 });
    139 
    140 engine_list_test(async function test_change_keyword(tree) {
    141  let extensionEngineIndex = installedEngines.length - 2;
    142  Assert.equal(
    143    getCellText(tree, extensionEngineIndex, "engineKeyword"),
    144    "testing, customkeyword",
    145    "Internal keywords are displayed."
    146  );
    147  let rect = tree.getCoordsForCellItem(
    148    extensionEngineIndex,
    149    tree.columns.getNamedColumn("engineKeyword"),
    150    "text"
    151  );
    152 
    153  // Test editing keyword of extension engine because it
    154  // has user-defined and extension-provided keywords.
    155  let x = rect.x + rect.width / 2;
    156  let y = rect.y + rect.height / 2;
    157  let win = tree.ownerGlobal;
    158 
    159  let promise = BrowserTestUtils.waitForEvent(tree, "dblclick");
    160  EventUtils.synthesizeMouse(tree.body, x, y, { clickCount: 1 }, win);
    161  EventUtils.synthesizeMouse(tree.body, x, y, { clickCount: 2 }, win);
    162  await promise;
    163 
    164  promise = SearchTestUtils.promiseSearchNotification(
    165    SearchUtils.MODIFIED_TYPE.CHANGED,
    166    SearchUtils.TOPIC_ENGINE_MODIFIED
    167  );
    168  EventUtils.sendString("newkeyword");
    169  EventUtils.sendKey("RETURN");
    170  await promise;
    171 
    172  Assert.equal(
    173    getCellText(tree, extensionEngineIndex, "engineKeyword"),
    174    "newkeyword, testing, customkeyword",
    175    "User-defined keyword was added."
    176  );
    177 
    178  // Test duplicated keywords (different capitalization).
    179  tree.view.setCellText(
    180    0,
    181    tree.columns.getNamedColumn("engineKeyword"),
    182    "keyword"
    183  );
    184  await TestUtils.waitForTick();
    185 
    186  let keywordBefore = getCellText(tree, 1, "engineKeyword");
    187  let alertSpy = sinon.spy(win, "alert");
    188  tree.view.setCellText(
    189    1,
    190    tree.columns.getNamedColumn("engineKeyword"),
    191    "Keyword"
    192  );
    193  await TestUtils.waitForTick();
    194 
    195  Assert.ok(alertSpy.calledOnce, "Warning was shown.");
    196  Assert.equal(
    197    getCellText(tree, 1, "engineKeyword"),
    198    keywordBefore,
    199    "Did not modify keywords."
    200  );
    201  alertSpy.restore();
    202 });
    203 
    204 engine_list_test(async function test_rename_engines(tree) {
    205  // Test editing name of user search engine because
    206  // only the names of user engines can be edited.
    207  let userEngineIndex = installedEngines.length - 1;
    208  let rect = tree.getCoordsForCellItem(
    209    userEngineIndex,
    210    tree.columns.getNamedColumn("engineName"),
    211    "text"
    212  );
    213  let x = rect.x + rect.width / 2;
    214  let y = rect.y + rect.height / 2;
    215  let win = tree.ownerGlobal;
    216 
    217  let promise = BrowserTestUtils.waitForEvent(tree, "dblclick");
    218  EventUtils.synthesizeMouse(tree.body, x, y, { clickCount: 1 }, win);
    219  EventUtils.synthesizeMouse(tree.body, x, y, { clickCount: 2 }, win);
    220  await promise;
    221 
    222  EventUtils.sendString("User Engine 2");
    223  promise = SearchTestUtils.promiseSearchNotification(
    224    SearchUtils.MODIFIED_TYPE.CHANGED,
    225    SearchUtils.TOPIC_ENGINE_MODIFIED
    226  );
    227  EventUtils.sendKey("RETURN");
    228  await promise;
    229 
    230  Assert.equal(userEngine.name, "User Engine 2", "Engine was renamed.");
    231 
    232  // Avoid duplicated engine names.
    233  let alertSpy = sinon.spy(win, "alert");
    234  tree.view.setCellText(
    235    userEngineIndex,
    236    tree.columns.getNamedColumn("engineName"),
    237    "Extension Engine"
    238  );
    239  await TestUtils.waitForTick();
    240 
    241  Assert.ok(alertSpy.calledOnce, "Warning was shown.");
    242  Assert.equal(
    243    getCellText(tree, userEngineIndex, "engineName"),
    244    "User Engine 2",
    245    "Name was not modified."
    246  );
    247 
    248  alertSpy.restore();
    249 });
    250 
    251 engine_list_test(async function test_remove_button_disabled_state(tree, doc) {
    252  let appProvidedEngines = await Services.search.getAppProvidedEngines();
    253  for (let i = 0; i < appProvidedEngines.length; i++) {
    254    let engine = appProvidedEngines[i];
    255    let isDefaultSearchEngine =
    256      engine.id == Services.search.defaultEngine.id ||
    257      engine.id == Services.search.defaultPrivateEngine.id;
    258 
    259    await selectEngine(tree, i);
    260    Assert.equal(
    261      doc.querySelector("#removeEngineButton").disabled,
    262      isDefaultSearchEngine,
    263      "Remove button is in correct disabled state."
    264    );
    265  }
    266 });
    267 
    268 engine_list_test(async function test_remove_button(tree, doc) {
    269  let win = tree.ownerGlobal;
    270  let alertSpy = sinon.stub(win, "alert");
    271 
    272  info("Removing user engine.");
    273  let userEngineIndex = installedEngines.findIndex(e => e.id == userEngine.id);
    274  await selectEngine(tree, userEngineIndex);
    275 
    276  let promptPromise = PromptTestUtils.handleNextPrompt(
    277    gBrowser.selectedBrowser,
    278    { modalType: Services.prompt.MODAL_TYPE_CONTENT },
    279    { buttonNumClick: 0 } // 0 = cancel, 1 = remove
    280  );
    281  let removedPromise = SearchTestUtils.promiseSearchNotification(
    282    SearchUtils.MODIFIED_TYPE.REMOVED,
    283    SearchUtils.TOPIC_ENGINE_MODIFIED
    284  );
    285 
    286  doc.querySelector("#removeEngineButton").click();
    287  await promptPromise;
    288  let removedEngine = await removedPromise;
    289  Assert.equal(
    290    removedEngine.id,
    291    userEngine.id,
    292    "User engine was removed after a prompt."
    293  );
    294 
    295  // Re-fetch the engines since removing the user engine changed it.
    296  installedEngines = await Services.search.getEngines();
    297 
    298  info("Removing extension engine.");
    299  let extensionEngineIndex = installedEngines.findIndex(
    300    e => e.id == extensionEngine.id
    301  );
    302  await selectEngine(tree, extensionEngineIndex);
    303 
    304  doc.querySelector("#removeEngineButton").click();
    305  await TestUtils.waitForCondition(() => alertSpy.calledOnce);
    306  Assert.ok(true, "Alert is shown when attempting to remove extension engine.");
    307 
    308  info("Removing user installed app engine.");
    309  let index = installedEngines.findIndex(
    310    e => e.id == userInstalledAppEngine.id
    311  );
    312 
    313  await selectEngine(tree, index);
    314 
    315  promptPromise = PromptTestUtils.handleNextPrompt(
    316    gBrowser.selectedBrowser,
    317    { modalType: Services.prompt.MODAL_TYPE_CONTENT },
    318    { buttonNumClick: 0 } // 0 = cancel, 1 = remove
    319  );
    320  removedPromise = SearchTestUtils.promiseSearchNotification(
    321    SearchUtils.MODIFIED_TYPE.REMOVED,
    322    SearchUtils.TOPIC_ENGINE_MODIFIED
    323  );
    324  doc.querySelector("#removeEngineButton").click();
    325  await promptPromise;
    326  removedEngine = await removedPromise;
    327  Assert.equal(
    328    removedEngine.id,
    329    userInstalledAppEngine.id,
    330    "User installed app engine was removed after a prompt."
    331  );
    332 
    333  info("Removing (last) app provided engine.");
    334  let appProvidedEngines = await Services.search.getAppProvidedEngines();
    335  let lastAppEngine = appProvidedEngines[appProvidedEngines.length - 1];
    336  let lastAppEngineIndex = installedEngines.findIndex(
    337    e => e.id == lastAppEngine.id
    338  );
    339  await selectEngine(tree, lastAppEngineIndex);
    340 
    341  doc.querySelector("#removeEngineButton").click();
    342  removedEngine = await SearchTestUtils.promiseSearchNotification(
    343    SearchUtils.MODIFIED_TYPE.REMOVED,
    344    SearchUtils.TOPIC_ENGINE_MODIFIED
    345  );
    346  Assert.equal(
    347    removedEngine.id,
    348    lastAppEngine.id,
    349    "Last app provided engine was removed without a prompt."
    350  );
    351 
    352  // Cleanup.
    353  alertSpy.restore();
    354  let updatedPromise = SearchTestUtils.promiseSearchNotification(
    355    SearchUtils.MODIFIED_TYPE.CHANGED,
    356    SearchUtils.TOPIC_ENGINE_MODIFIED
    357  );
    358  doc.getElementById("restoreDefaultSearchEngines").click();
    359  await updatedPromise;
    360  // The user engine is purposefully not re-added.
    361  // The extension engine is removed automatically on cleanup.
    362 });