summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_tokenAlias.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_tokenAlias.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_tokenAlias.js861
1 files changed, 861 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_tokenAlias.js b/browser/components/urlbar/tests/browser/browser_tokenAlias.js
new file mode 100644
index 0000000000..d215c2536f
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_tokenAlias.js
@@ -0,0 +1,861 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test checks "@" search engine aliases ("token aliases") in the urlbar.
+
+"use strict";
+
+const TEST_ALIAS_ENGINE_NAME = "Test";
+const ALIAS = "@test";
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+
+// We make sure that aliases and search terms are correctly recognized when they
+// are separated by each of these different types of spaces and combinations of
+// spaces. U+3000 is the ideographic space in CJK and is commonly used by CJK
+// speakers.
+const TEST_SPACES = [" ", "\u3000", " \u3000", "\u3000 "];
+
+// Allow more time for Mac machines so they don't time out in verify mode. See
+// bug 1673062.
+if (AppConstants.platform == "macosx") {
+ requestLongerTimeout(5);
+}
+
+add_setup(async function () {
+ // Add a default engine with suggestions, to avoid hitting the network when
+ // fetching them.
+ let defaultEngine = await SearchTestUtils.promiseNewSearchEngine({
+ url: getRootDirectory(gTestPath) + TEST_ENGINE_BASENAME,
+ setAsDefault: true,
+ });
+ defaultEngine.alias = "@default";
+ await SearchTestUtils.installSearchExtension({
+ name: TEST_ALIAS_ENGINE_NAME,
+ keyword: ALIAS,
+ });
+
+ // Search results aren't shown in quantumbar unless search suggestions are
+ // enabled.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.suggest.searches", true]],
+ });
+});
+
+// Simple test that tries different variations of an alias, without reverting
+// the urlbar value in between.
+add_task(async function testNoRevert() {
+ await doSimpleTest(false);
+});
+
+// Simple test that tries different variations of an alias, reverting the urlbar
+// value in between.
+add_task(async function testRevert() {
+ await doSimpleTest(true);
+});
+
+async function doSimpleTest(revertBetweenSteps) {
+ // When autofill is enabled, searching for "@tes" will autofill to "@test",
+ // which gets in the way of this test task, so temporarily disable it.
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.autoFill", false]],
+ });
+
+ // "@tes" -- not an alias, no search mode
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS.substr(0, ALIAS.length - 1),
+ });
+ await UrlbarTestUtils.assertSearchMode(window, null);
+ Assert.equal(
+ gURLBar.value,
+ ALIAS.substr(0, ALIAS.length - 1),
+ "value should be alias substring"
+ );
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+
+ // "@test" -- alias but no trailing space, no search mode
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS,
+ });
+ await UrlbarTestUtils.assertSearchMode(window, null);
+ Assert.equal(gURLBar.value, ALIAS, "value should be alias");
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+
+ // "@test " -- alias with trailing space, search mode
+ for (let spaces of TEST_SPACES) {
+ info("Testing: " + JSON.stringify({ spaces: codePoints(spaces) }));
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS + spaces,
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+ await UrlbarTestUtils.exitSearchMode(window);
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+ }
+
+ // "@test foo" -- alias, search mode
+ for (let spaces of TEST_SPACES) {
+ info("Testing: " + JSON.stringify({ spaces: codePoints(spaces) }));
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS + spaces + "foo",
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "foo", "value should be query");
+ await UrlbarTestUtils.exitSearchMode(window);
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+ }
+
+ // "@test " -- alias with trailing space, search mode
+ for (let spaces of TEST_SPACES) {
+ info("Testing: " + JSON.stringify({ spaces: codePoints(spaces) }));
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS + spaces,
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+ await UrlbarTestUtils.exitSearchMode(window);
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+ }
+
+ // "@test" -- alias but no trailing space, no highlight
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS,
+ });
+ await UrlbarTestUtils.assertSearchMode(window, null);
+ Assert.equal(gURLBar.value, ALIAS, "value should be alias");
+
+ if (revertBetweenSteps) {
+ gURLBar.handleRevert();
+ gURLBar.blur();
+ }
+
+ // "@tes" -- not an alias, no highlight
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS.substr(0, ALIAS.length - 1),
+ });
+ await UrlbarTestUtils.assertSearchMode(window, null);
+ Assert.equal(
+ gURLBar.value,
+ ALIAS.substr(0, ALIAS.length - 1),
+ "value should be alias substring"
+ );
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+
+ await SpecialPowers.popPrefEnv();
+}
+
+// An alias should be recognized even when there are spaces before it, and
+// search mode should be entered.
+add_task(async function spacesBeforeAlias() {
+ for (let spaces of TEST_SPACES) {
+ info("Testing: " + JSON.stringify({ spaces: codePoints(spaces) }));
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: spaces + ALIAS + spaces,
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+ }
+});
+
+// An alias in the middle of a string should not be recognized and search mode
+// should not be entered.
+add_task(async function charsBeforeAlias() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "not an alias " + ALIAS + " ",
+ });
+ await UrlbarTestUtils.assertSearchMode(window, null);
+ Assert.equal(
+ gURLBar.value,
+ "not an alias " + ALIAS + " ",
+ "value should be unchanged"
+ );
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// While already in search mode, an alias should not be recognized.
+add_task(async function alreadyInSearchMode() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await UrlbarTestUtils.enterSearchMode(window, {
+ source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
+ });
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: ALIAS + " ",
+ });
+
+ // Search mode source should still be bookmarks.
+ await UrlbarTestUtils.assertSearchMode(window, {
+ source: UrlbarUtils.RESULT_SOURCE.BOOKMARKS,
+ entry: "oneoff",
+ });
+ Assert.equal(gURLBar.value, ALIAS + " ", "value should be unchanged");
+
+ // Exit search mode, but first remove the value in the input. Since the value
+ // is "alias ", we'd actually immediately re-enter search mode otherwise.
+ gURLBar.value = "";
+
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Types a space while typing an alias to ensure we stop autofilling.
+add_task(async function spaceWhileTypingAlias() {
+ for (let spaces of TEST_SPACES) {
+ if (spaces.length != 1) {
+ continue;
+ }
+ info("Testing: " + JSON.stringify({ spaces: codePoints(spaces) }));
+
+ let value = ALIAS.substring(0, ALIAS.length - 1);
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value,
+ selectionStart: value.length,
+ selectionEnd: value.length,
+ });
+ Assert.equal(gURLBar.value, ALIAS + " ", "Alias should be autofilled");
+
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey(spaces);
+ await searchPromise;
+
+ Assert.equal(
+ gURLBar.value,
+ value + spaces,
+ "Alias should not be autofilled"
+ );
+ await UrlbarTestUtils.assertSearchMode(window, null);
+
+ await UrlbarTestUtils.promisePopupClose(window);
+ }
+});
+
+// Aliases are case insensitive. Make sure that searching with an alias using a
+// weird case still causes the alias to be recognized and search mode entered.
+add_task(async function aliasCase() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@TeSt ",
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Same as previous but with a query.
+add_task(async function aliasCase_query() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@tEsT query",
+ });
+ // Wait for the second new search that starts when search mode is entered.
+ await UrlbarTestUtils.promiseSearchComplete(window);
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "query", "value should be query");
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Selecting a non-heuristic (non-first) search engine result with an alias and
+// empty query should put the alias in the urlbar and highlight it.
+// Also checks that internal aliases appear with the "@" keyword.
+add_task(async function nonHeuristicAliases() {
+ // Get the list of token alias engines (those with aliases that start with
+ // "@").
+ let tokenEngines = [];
+ for (let engine of await Services.search.getEngines()) {
+ let aliases = [];
+ if (engine.alias) {
+ aliases.push(engine.alias);
+ }
+ aliases.push(...engine.aliases);
+ let tokenAliases = aliases.filter(a => a.startsWith("@"));
+ if (tokenAliases.length) {
+ tokenEngines.push({ engine, tokenAliases });
+ }
+ }
+ if (!tokenEngines.length) {
+ Assert.ok(true, "No token alias engines, skipping task.");
+ return;
+ }
+ info(
+ "Got token alias engines: " + tokenEngines.map(({ engine }) => engine.name)
+ );
+
+ // Populate the results with the list of token alias engines by searching for
+ // "@".
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ });
+ await UrlbarTestUtils.waitForAutocompleteResultAt(
+ window,
+ tokenEngines.length - 1
+ );
+ // Key down to select each result in turn. The urlbar should preview search
+ // mode for each engine.
+ for (let { tokenAliases } of tokenEngines) {
+ let alias = tokenAliases[0];
+ let engineName = (await UrlbarSearchUtils.engineForAlias(alias)).name;
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ let expectedSearchMode = {
+ engineName,
+ entry: "keywordoffer",
+ isPreview: true,
+ };
+ if (Services.search.getEngineByName(engineName).isGeneralPurposeEngine) {
+ expectedSearchMode.source = UrlbarUtils.RESULT_SOURCE.SEARCH;
+ }
+ await UrlbarTestUtils.assertSearchMode(window, expectedSearchMode);
+ Assert.ok(!gURLBar.value, "The Urlbar should be empty.");
+ }
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Clicking on an @ alias offer (an @ alias with an empty search string) in the
+// view should enter search mode.
+add_task(async function clickAndFillAlias() {
+ // Do a search for "@" to show all the @ aliases.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ });
+
+ // Find our test engine in the results. It's probably last, but for
+ // robustness don't assume it is.
+ let testEngineItem;
+ for (let i = 0; !testEngineItem; i++) {
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ Assert.equal(
+ details.displayed.title,
+ `Search with ${details.searchParams.engine}`,
+ "The result's title is set correctly."
+ );
+ Assert.ok(!details.action, "The result should have no action text.");
+ if (details.searchParams && details.searchParams.keyword == ALIAS) {
+ testEngineItem = await UrlbarTestUtils.waitForAutocompleteResultAt(
+ window,
+ i
+ );
+ }
+ }
+
+ // Click it.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeMouseAtCenter(testEngineItem, {});
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: testEngineItem.result.payload.engine,
+ entry: "keywordoffer",
+ });
+
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Pressing enter on an @ alias offer (an @ alias with an empty search string)
+// in the view should enter search mode.
+add_task(async function enterAndFillAlias() {
+ // Do a search for "@" to show all the @ aliases.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ });
+
+ // Find our test engine in the results. It's probably last, but for
+ // robustness don't assume it is.
+ let details;
+ let index = 0;
+ for (; ; index++) {
+ details = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
+ if (details.searchParams && details.searchParams.keyword == ALIAS) {
+ index++;
+ break;
+ }
+ }
+
+ // Key down to it and press enter.
+ EventUtils.synthesizeKey("KEY_ArrowDown", { repeat: index });
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: details.searchParams.engine,
+ entry: "keywordoffer",
+ });
+
+ await UrlbarTestUtils.exitSearchMode(window);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Pressing Enter on an @ alias autofill should enter search mode.
+add_task(async function enterAutofillsAlias() {
+ for (let value of [ALIAS.substring(0, ALIAS.length - 1), ALIAS]) {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value,
+ selectionStart: value.length,
+ selectionEnd: value.length,
+ });
+
+ // Press Enter.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "keywordoffer",
+ });
+
+ await UrlbarTestUtils.exitSearchMode(window);
+ }
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Pressing Right on an @ alias autofill should enter search mode.
+add_task(async function rightEntersSearchMode() {
+ for (let value of [ALIAS.substring(0, ALIAS.length - 1), ALIAS]) {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value,
+ selectionStart: value.length,
+ selectionEnd: value.length,
+ });
+
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_ArrowRight");
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "typed",
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+ await UrlbarTestUtils.exitSearchMode(window);
+ }
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+// Pressing Tab when an @ alias is autofilled should enter search mode preview.
+add_task(async function rightEntersSearchMode() {
+ for (let value of [ALIAS.substring(0, ALIAS.length - 1), ALIAS]) {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value,
+ selectionStart: value.length,
+ selectionEnd: value.length,
+ });
+
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ -1,
+ "There is no selected result."
+ );
+
+ EventUtils.synthesizeKey("KEY_Tab");
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ 0,
+ "The first result is selected."
+ );
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "keywordoffer",
+ isPreview: true,
+ });
+ Assert.equal(gURLBar.value, "", "value should be empty");
+
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeKey("KEY_Enter");
+ await searchPromise;
+
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: TEST_ALIAS_ENGINE_NAME,
+ entry: "keywordoffer",
+ isPreview: false,
+ });
+ await UrlbarTestUtils.exitSearchMode(window);
+ }
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+});
+
+/**
+ * This test checks that if an engine is marked as hidden then
+ * it should not appear in the popup when using the "@" token alias in the search bar.
+ */
+add_task(async function hiddenEngine() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ fireInputEvent: true,
+ });
+
+ const defaultEngine = await Services.search.getDefault();
+
+ let foundDefaultEngineInPopup = false;
+
+ // Checks that the default engine appears in the urlbar's popup.
+ for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ if (defaultEngine.name == details.searchParams.engine) {
+ foundDefaultEngineInPopup = true;
+ break;
+ }
+ }
+ Assert.ok(foundDefaultEngineInPopup, "Default engine appears in the popup.");
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+
+ // Checks that a hidden default engine (i.e. an engine removed from
+ // a user's search settings) does not appear in the urlbar's popup.
+ defaultEngine.hidden = true;
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ fireInputEvent: true,
+ });
+ foundDefaultEngineInPopup = false;
+ for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ if (defaultEngine.name == details.searchParams.engine) {
+ foundDefaultEngineInPopup = true;
+ break;
+ }
+ }
+ Assert.ok(
+ !foundDefaultEngineInPopup,
+ "Hidden default engine does not appear in the popup"
+ );
+
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ EventUtils.synthesizeKey("KEY_Escape")
+ );
+
+ defaultEngine.hidden = false;
+});
+
+/**
+ * This test checks that if an engines alias is not prefixed with
+ * @ it still appears in the popup when using the "@" token
+ * alias in the search bar.
+ */
+add_task(async function nonPrefixedKeyword() {
+ let name = "Custom";
+ let alias = "customkeyword";
+ let extension = await SearchTestUtils.installSearchExtension(
+ {
+ name,
+ keyword: alias,
+ },
+ { skipUnload: true }
+ );
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ });
+
+ let foundEngineInPopup = false;
+
+ // Checks that the default engine appears in the urlbar's popup.
+ for (let i = 0; i < UrlbarTestUtils.getResultCount(window); i++) {
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ if (details.searchParams.engine === name) {
+ foundEngineInPopup = true;
+ break;
+ }
+ }
+ Assert.ok(foundEngineInPopup, "Custom engine appears in the popup.");
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@" + alias,
+ });
+
+ let keywordOfferResult = await UrlbarTestUtils.getDetailsOfResultAt(
+ window,
+ 0
+ );
+
+ Assert.equal(
+ keywordOfferResult.searchParams.engine,
+ name,
+ "The first result should be a keyword search result with the correct engine."
+ );
+
+ await extension.unload();
+});
+
+// Tests that we show all engines with a token alias that match the search
+// string.
+add_task(async function multipleMatchingEngines() {
+ let extension = await SearchTestUtils.installSearchExtension(
+ {
+ name: "TestFoo",
+ keyword: `${ALIAS}foo`,
+ },
+ { skipUnload: true }
+ );
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@te",
+ fireInputEvent: true,
+ });
+
+ Assert.equal(
+ UrlbarTestUtils.getResultCount(window),
+ 2,
+ "Two results are shown."
+ );
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ -1,
+ "Neither result is selected."
+ );
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.ok(result.autofill, "The first result is autofilling.");
+ Assert.equal(
+ result.searchParams.keyword,
+ ALIAS,
+ "The autofilled engine is shown first."
+ );
+
+ result = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+ Assert.equal(
+ result.searchParams.keyword,
+ `${ALIAS}foo`,
+ "The other engine is shown second."
+ );
+
+ EventUtils.synthesizeKey("KEY_Tab");
+ Assert.equal(UrlbarTestUtils.getSelectedRowIndex(window), 0);
+ Assert.equal(gURLBar.value, "", "Urlbar should be empty.");
+ EventUtils.synthesizeKey("KEY_Tab");
+ Assert.equal(UrlbarTestUtils.getSelectedRowIndex(window), 1);
+ Assert.equal(gURLBar.value, "", "Urlbar should be empty.");
+ EventUtils.synthesizeKey("KEY_Tab");
+ Assert.equal(
+ UrlbarTestUtils.getSelectedRowIndex(window),
+ -1,
+ "Tabbing all the way through the matching engines should return to the input."
+ );
+ Assert.equal(
+ gURLBar.value,
+ "@te",
+ "Urlbar should contain the search string."
+ );
+
+ await extension.unload();
+});
+
+// Tests that UrlbarProviderTokenAliasEngines is disabled in search mode.
+add_task(async function doNotShowInSearchMode() {
+ // Do a search for "@" to show all the @ aliases.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ });
+
+ // Find our test engine in the results. It's probably last, but for
+ // robustness don't assume it is.
+ let testEngineItem;
+ for (let i = 0; !testEngineItem; i++) {
+ let details = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ if (details.searchParams && details.searchParams.keyword == ALIAS) {
+ testEngineItem = await UrlbarTestUtils.waitForAutocompleteResultAt(
+ window,
+ i
+ );
+ }
+ }
+
+ Assert.equal(
+ testEngineItem.result.payload.keyword,
+ ALIAS,
+ "Sanity check: we found our engine."
+ );
+
+ // Click it.
+ let searchPromise = UrlbarTestUtils.promiseSearchComplete(window);
+ EventUtils.synthesizeMouseAtCenter(testEngineItem, {});
+ await searchPromise;
+ await UrlbarTestUtils.assertSearchMode(window, {
+ engineName: testEngineItem.result.payload.engine,
+ entry: "keywordoffer",
+ });
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "@",
+ fireInputEvent: true,
+ });
+
+ let resultCount = UrlbarTestUtils.getResultCount(window);
+ for (let i = 0; i < resultCount; i++) {
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ Assert.ok(
+ !result.searchParams.keyword,
+ `Result at index ${i} is not a keywordoffer.`
+ );
+ }
+});
+
+async function assertFirstResultIsAlias(isAlias, expectedAlias) {
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
+ Assert.equal(
+ result.type,
+ UrlbarUtils.RESULT_TYPE.SEARCH,
+ "Should have the correct type"
+ );
+
+ if (isAlias) {
+ Assert.equal(
+ result.searchParams.keyword,
+ expectedAlias,
+ "Payload keyword should be the alias"
+ );
+ } else {
+ Assert.notEqual(
+ result.searchParams.keyword,
+ expectedAlias,
+ "Payload keyword should be absent or not the alias"
+ );
+ }
+}
+
+function assertHighlighted(highlighted, expectedAlias) {
+ let selection = gURLBar.editor.selectionController.getSelection(
+ Ci.nsISelectionController.SELECTION_FIND
+ );
+ Assert.ok(selection);
+ if (!highlighted) {
+ Assert.equal(selection.rangeCount, 0);
+ return;
+ }
+ Assert.equal(selection.rangeCount, 1);
+ let index = gURLBar.value.indexOf(expectedAlias);
+ Assert.ok(
+ index >= 0,
+ `gURLBar.value="${gURLBar.value}" expectedAlias="${expectedAlias}"`
+ );
+ let range = selection.getRangeAt(0);
+ Assert.ok(range);
+ Assert.equal(range.startOffset, index);
+ Assert.equal(range.endOffset, index + expectedAlias.length);
+}
+
+/**
+ * Returns an array of code points in the given string. Each code point is
+ * returned as a hexidecimal string.
+ *
+ * @param {string} str
+ * The code points of this string will be returned.
+ * @returns {Array}
+ * Array of code points in the string, where each is a hexidecimal string.
+ */
+function codePoints(str) {
+ return str.split("").map(s => s.charCodeAt(0).toString(16));
+}