summaryrefslogtreecommitdiffstats
path: root/browser/components/preferences/tests/browser_localSearchShortcuts.js
blob: 7f0f4869b725c4e11973d6849978a213c8910660 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

/**
 * Checks the local shortcut rows in the engines list of the search pane.
 */

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
  UrlbarUtils: "resource:///modules/UrlbarUtils.sys.mjs",
});

let gTree;

add_setup(async function () {
  let prefs = await openPreferencesViaOpenPreferencesAPI("search", {
    leaveOpen: true,
  });
  registerCleanupFunction(() => {
    BrowserTestUtils.removeTab(gBrowser.selectedTab);
  });

  Assert.equal(
    prefs.selectedPane,
    "paneSearch",
    "Sanity check: Search pane is selected by default"
  );

  gTree = gBrowser.contentDocument.querySelector("#engineList");
  gTree.scrollIntoView();
  gTree.focus();
});

// The rows should be visible and checked by default.
add_task(async function visible() {
  await checkRowVisibility(true);
  await forEachLocalShortcutRow(async row => {
    Assert.equal(
      gTree.view.getCellValue(row, gTree.columns.getNamedColumn("engineShown")),
      "true",
      "Row is checked initially"
    );
  });
});

// Toggling the browser.urlbar.shortcuts.* prefs should toggle the corresponding
// checkboxes in the rows.
add_task(async function syncFromPrefs() {
  let col = gTree.columns.getNamedColumn("engineShown");
  await forEachLocalShortcutRow(async (row, shortcut) => {
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "true",
      "Row is checked initially"
    );
    await SpecialPowers.pushPrefEnv({
      set: [[getUrlbarPrefName(shortcut.pref), false]],
    });
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "false",
      "Row is unchecked after disabling pref"
    );
    await SpecialPowers.popPrefEnv();
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "true",
      "Row is checked after re-enabling pref"
    );
  });
});

// Pressing the space key while a row is selected should toggle its checkbox
// and pref.
add_task(async function syncToPrefs_spaceKey() {
  let col = gTree.columns.getNamedColumn("engineShown");
  await forEachLocalShortcutRow(async (row, shortcut) => {
    Assert.ok(
      UrlbarPrefs.get(shortcut.pref),
      "Sanity check: Pref is enabled initially"
    );
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "true",
      "Row is checked initially"
    );
    gTree.view.selection.select(row);
    EventUtils.synthesizeKey(" ", {}, gTree.ownerGlobal);
    Assert.ok(
      !UrlbarPrefs.get(shortcut.pref),
      "Pref is disabled after pressing space key"
    );
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "false",
      "Row is unchecked after pressing space key"
    );
    Services.prefs.clearUserPref(getUrlbarPrefName(shortcut.pref));
  });
});

// Clicking the checkbox in a local shortcut row should toggle the checkbox and
// pref.
add_task(async function syncToPrefs_click() {
  let col = gTree.columns.getNamedColumn("engineShown");
  await forEachLocalShortcutRow(async (row, shortcut) => {
    Assert.ok(
      UrlbarPrefs.get(shortcut.pref),
      "Sanity check: Pref is enabled initially"
    );
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "true",
      "Row is checked initially"
    );

    let rect = gTree.getCoordsForCellItem(row, col, "cell");
    let x = rect.x + rect.width / 2;
    let y = rect.y + rect.height / 2;
    EventUtils.synthesizeMouse(gTree.body, x, y, {}, gTree.ownerGlobal);

    Assert.ok(
      !UrlbarPrefs.get(shortcut.pref),
      "Pref is disabled after clicking checkbox"
    );
    Assert.equal(
      gTree.view.getCellValue(row, col),
      "false",
      "Row is unchecked after clicking checkbox"
    );
    Services.prefs.clearUserPref(getUrlbarPrefName(shortcut.pref));
  });
});

// The keyword column should not be editable according to isEditable().
add_task(async function keywordNotEditable_isEditable() {
  await forEachLocalShortcutRow(async row => {
    Assert.ok(
      !gTree.view.isEditable(
        row,
        gTree.columns.getNamedColumn("engineKeyword")
      ),
      "Keyword column is not editable"
    );
  });
});

// Pressing the enter key while a row is selected shouldn't allow the keyword to
// be edited.
add_task(async function keywordNotEditable_enterKey() {
  let col = gTree.columns.getNamedColumn("engineKeyword");
  await forEachLocalShortcutRow(async (row, shortcut) => {
    Assert.ok(
      shortcut.restrict,
      "Sanity check: Shortcut restriction char is non-empty"
    );
    Assert.equal(
      gTree.view.getCellText(row, col),
      shortcut.restrict,
      "Sanity check: Keyword column has correct restriction char initially"
    );

    gTree.view.selection.select(row);
    EventUtils.synthesizeKey("KEY_Enter", {}, gTree.ownerGlobal);
    EventUtils.sendString("newkeyword");
    EventUtils.synthesizeKey("KEY_Enter", {}, gTree.ownerGlobal);

    // Wait a moment to allow for any possible asynchronicity.
    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    await new Promise(r => setTimeout(r, 500));

    Assert.equal(
      gTree.view.getCellText(row, col),
      shortcut.restrict,
      "Keyword column is still restriction char"
    );
  });
});

// Double-clicking the keyword column shouldn't allow the keyword to be edited.
add_task(async function keywordNotEditable_click() {
  let col = gTree.columns.getNamedColumn("engineKeyword");
  await forEachLocalShortcutRow(async (row, shortcut) => {
    Assert.ok(
      shortcut.restrict,
      "Sanity check: Shortcut restriction char is non-empty"
    );
    Assert.equal(
      gTree.view.getCellText(row, col),
      shortcut.restrict,
      "Sanity check: Keyword column has correct restriction char initially"
    );

    let rect = gTree.getCoordsForCellItem(row, col, "text");
    let x = rect.x + rect.width / 2;
    let y = rect.y + rect.height / 2;

    let promise = BrowserTestUtils.waitForEvent(gTree, "dblclick");

    // Click once to select the row.
    EventUtils.synthesizeMouse(
      gTree.body,
      x,
      y,
      { clickCount: 1 },
      gTree.ownerGlobal
    );

    // Now double-click the keyword column.
    EventUtils.synthesizeMouse(
      gTree.body,
      x,
      y,
      { clickCount: 2 },
      gTree.ownerGlobal
    );

    await promise;

    EventUtils.sendString("newkeyword");
    EventUtils.synthesizeKey("KEY_Enter", {}, gTree.ownerGlobal);

    // Wait a moment to allow for any possible asynchronicity.
    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
    await new Promise(r => setTimeout(r, 500));

    Assert.equal(
      gTree.view.getCellText(row, col),
      shortcut.restrict,
      "Keyword column is still restriction char"
    );
  });
});

/**
 * Asserts that the engine and local shortcut rows are present in the tree.
 */
async function checkRowVisibility() {
  let engines = await Services.search.getVisibleEngines();

  Assert.equal(
    gTree.view.rowCount,
    engines.length + UrlbarUtils.LOCAL_SEARCH_MODES.length,
    "Expected number of tree rows"
  );

  // Check the engine rows.
  for (let row = 0; row < engines.length; row++) {
    let engine = engines[row];
    let text = gTree.view.getCellText(
      row,
      gTree.columns.getNamedColumn("engineName")
    );
    Assert.equal(
      text,
      engine.name,
      `Sanity check: Tree row ${row} has expected engine name`
    );
  }

  // Check the shortcut rows.
  await forEachLocalShortcutRow(async (row, shortcut) => {
    let text = gTree.view.getCellText(
      row,
      gTree.columns.getNamedColumn("engineName")
    );
    let name = UrlbarUtils.getResultSourceName(shortcut.source);
    let l10nName = await gTree.ownerDocument.l10n.formatValue(
      `urlbar-search-mode-${name}`
    );
    Assert.ok(l10nName, "Sanity check: l10n name is non-empty");
    Assert.equal(text, l10nName, `Tree row ${row} has expected shortcut name`);
  });
}

/**
 * Calls a callback for each local shortcut row in the tree.
 *
 * @param {function} callback
 *   Called for each local shortcut row like: callback(rowIndex, shortcutObject)
 */
async function forEachLocalShortcutRow(callback) {
  let engines = await Services.search.getVisibleEngines();
  for (let i = 0; i < UrlbarUtils.LOCAL_SEARCH_MODES.length; i++) {
    let shortcut = UrlbarUtils.LOCAL_SEARCH_MODES[i];
    let row = engines.length + i;
    // These tests assume LOCAL_SEARCH_MODES are enabled, this can be removed
    // when we enable QuickActions. We cant just enable the pref in browser.ini
    // as this test calls clearUserPref.
    if (shortcut.pref == "shortcuts.quickactions") {
      continue;
    }
    await callback(row, shortcut);
  }
}

/**
 * Prepends the `browser.urlbar.` branch to the given relative pref.
 *
 * @param {string} relativePref
 *   A pref name relative to the `browser.urlbar.`.
 * @returns {string}
 *   The full pref name with `browser.urlbar.` prepended.
 */
function getUrlbarPrefName(relativePref) {
  return `browser.urlbar.${relativePref}`;
}