338 lines
10 KiB
JavaScript
338 lines
10 KiB
JavaScript
/* 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",
|
|
UrlbarTokenizer: "resource:///modules/UrlbarTokenizer.sys.mjs",
|
|
});
|
|
|
|
let gTree;
|
|
const isRestrictKeywordsFeatureOn = () =>
|
|
UrlbarPrefs.getScotchBonnetPref("searchRestrictKeywords.featureGate");
|
|
|
|
add_setup(async function () {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["browser.urlbar.scotchBonnet.enableOverride", true]],
|
|
});
|
|
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"
|
|
);
|
|
|
|
let tokenToKeywords = await UrlbarTokenizer.getL10nRestrictKeywords();
|
|
let keywords = tokenToKeywords
|
|
.get(shortcut.restrict)
|
|
.map(keyword => `@${keyword.toLowerCase()}`)
|
|
.join(", ");
|
|
|
|
Assert.equal(
|
|
gTree.view.getCellText(row, col),
|
|
isRestrictKeywordsFeatureOn()
|
|
? `${keywords}, ${shortcut.restrict}`
|
|
: shortcut.restrict,
|
|
isRestrictKeywordsFeatureOn()
|
|
? "Sanity check: Keyword column has correct restriction keyword and char initially"
|
|
: "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),
|
|
isRestrictKeywordsFeatureOn()
|
|
? `${keywords}, ${shortcut.restrict}`
|
|
: shortcut.restrict,
|
|
isRestrictKeywordsFeatureOn()
|
|
? "Keyword column is still restriction keyword and char"
|
|
: "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) => {
|
|
let tokenToKeywords = await UrlbarTokenizer.getL10nRestrictKeywords();
|
|
let keywords = tokenToKeywords
|
|
.get(shortcut.restrict)
|
|
.map(keyword => `@${keyword.toLowerCase()}`)
|
|
.join(", ");
|
|
|
|
Assert.ok(
|
|
shortcut.restrict,
|
|
"Sanity check: Shortcut restriction char is non-empty"
|
|
);
|
|
Assert.equal(
|
|
gTree.view.getCellText(row, col),
|
|
isRestrictKeywordsFeatureOn()
|
|
? `${keywords}, ${shortcut.restrict}`
|
|
: shortcut.restrict,
|
|
isRestrictKeywordsFeatureOn()
|
|
? "Sanity check: Keyword column has correct restriction keyword and char initially"
|
|
: "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),
|
|
isRestrictKeywordsFeatureOn()
|
|
? `${keywords}, ${shortcut.restrict}`
|
|
: shortcut.restrict,
|
|
isRestrictKeywordsFeatureOn()
|
|
? "Keyword column is still restriction keyword and char"
|
|
: "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;
|
|
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}`;
|
|
}
|