summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/browser/browser_groupLabels.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/browser/browser_groupLabels.js')
-rw-r--r--browser/components/urlbar/tests/browser/browser_groupLabels.js629
1 files changed, 629 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/browser/browser_groupLabels.js b/browser/components/urlbar/tests/browser/browser_groupLabels.js
new file mode 100644
index 0000000000..2b43990b77
--- /dev/null
+++ b/browser/components/urlbar/tests/browser/browser_groupLabels.js
@@ -0,0 +1,629 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests group labels in the view.
+
+"use strict";
+
+const SUGGESTIONS_FIRST_PREF = "browser.urlbar.showSearchSuggestionsFirst";
+const SUGGESTIONS_PREF = "browser.urlbar.suggest.searches";
+
+const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
+const TEST_ENGINE_2_BASENAME = "searchSuggestionEngine2.xml";
+const MAX_RESULTS = UrlbarPrefs.get("maxRichResults");
+
+const TOP_SITES = [
+ "http://example-1.com/",
+ "http://example-2.com/",
+ "http://example-3.com/",
+];
+
+const FIREFOX_SUGGEST_LABEL = "Firefox Suggest";
+
+// %s is replaced with the engine name.
+const ENGINE_SUGGESTIONS_LABEL = "%s suggestions";
+
+// Allow more time for Mac machines so they don't time out in verify mode.
+if (AppConstants.platform == "macosx") {
+ requestLongerTimeout(3);
+}
+
+add_setup(async function () {
+ Assert.ok(
+ UrlbarPrefs.get("showSearchSuggestionsFirst"),
+ "Precondition: Search suggestions shown first by default"
+ );
+
+ // Add some history.
+ await PlacesUtils.history.clear();
+ await PlacesUtils.bookmarks.eraseEverything();
+ await UrlbarTestUtils.formHistory.clear();
+ await addHistory();
+
+ // Make sure we have some top sites.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.urlbar.suggest.topsites", true],
+ ["browser.newtabpage.activity-stream.default.sites", TOP_SITES.join(",")],
+ ],
+ });
+ // Waiting for all top sites to be added intermittently times out, so just
+ // wait for any to be added. We're not testing top sites here; we only need
+ // the view to open in top-sites mode.
+ await updateTopSites(sites => sites && sites.length);
+
+ // Add a mock engine so we don't hit the network.
+ await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
+
+ registerCleanupFunction(async () => {
+ await PlacesUtils.history.clear();
+ });
+});
+
+// The Firefox Suggest label should not appear when the labels pref is disabled.
+add_task(async function prefDisabled() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.urlbar.groupLabels.enabled", false]],
+ });
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {});
+ await UrlbarTestUtils.promisePopupClose(window);
+ await SpecialPowers.popPrefEnv();
+});
+
+// The Firefox Suggest label should not appear when the view shows top sites.
+add_task(async function topSites() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "",
+ });
+ await checkLabels(-1, {});
+ await UrlbarTestUtils.promisePopupClose(window);
+});
+
+// The Firefox Suggest label should appear when the search string is non-empty
+// and there are only general results.
+add_task(async function general() {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+});
+
+// The Firefox Suggest label should appear when the search string is non-empty
+// and there are suggestions followed by general results.
+add_task(async function suggestionsBeforeGeneral() {
+ await withSuggestions(async () => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {
+ 3: FIREFOX_SUGGEST_LABEL,
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+});
+
+// Both the Firefox Suggest and Suggestions labels should appear when the search
+// string is non-empty, general results are shown before suggestions, and there
+// are general and suggestion results.
+add_task(async function generalBeforeSuggestions() {
+ await withSuggestions(async engine => {
+ Assert.ok(engine.name, "Engine name is non-empty");
+ await SpecialPowers.pushPrefEnv({
+ set: [[SUGGESTIONS_FIRST_PREF, false]],
+ });
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ [MAX_RESULTS - 2]: engineSuggestionsLabel(engine.name),
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+});
+
+// Neither the Firefox Suggest nor Suggestions label should appear when the
+// search string is non-empty, general results are shown before suggestions, and
+// there are only suggestion results.
+add_task(async function generalBeforeSuggestions_suggestionsOnly() {
+ await PlacesUtils.history.clear();
+
+ await withSuggestions(async engine => {
+ await SpecialPowers.pushPrefEnv({
+ set: [[SUGGESTIONS_FIRST_PREF, false]],
+ });
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(3, {});
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+
+ // Add back history so subsequent tasks run with this test's initial state.
+ await addHistory();
+});
+
+// The Suggestions label should be updated when the default engine changes.
+add_task(async function generalBeforeSuggestions_defaultChanged() {
+ // Install both test engines, one after the other. Engine 2 will be the final
+ // default engine.
+ await withSuggestions(async engine1 => {
+ await withSuggestions(async engine2 => {
+ Assert.ok(engine2.name, "Engine 2 name is non-empty");
+ Assert.notEqual(engine1.name, engine2.name, "Engine names are different");
+ Assert.equal(
+ Services.search.defaultEngine.name,
+ engine2.name,
+ "Engine 2 is default"
+ );
+ await SpecialPowers.pushPrefEnv({
+ set: [[SUGGESTIONS_FIRST_PREF, false]],
+ });
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ [MAX_RESULTS - 2]: engineSuggestionsLabel(engine2.name),
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+ }, TEST_ENGINE_2_BASENAME);
+ });
+});
+
+// The Firefox Suggest label should appear above a suggested-index result when
+// the result is the only result with that label.
+add_task(async function suggestedIndex_only() {
+ // Clear history, add a provider that returns a result with suggestedIndex =
+ // -1, set up an engine with suggestions, and start a query. The suggested-
+ // index result will be the only result with a label.
+ await PlacesUtils.history.clear();
+
+ let index = -1;
+ let provider = new SuggestedIndexProvider(index);
+ UrlbarProvidersManager.registerProvider(provider);
+
+ await withSuggestions(async engine => {
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 3);
+ Assert.equal(
+ result.element.row.result.suggestedIndex,
+ index,
+ "Sanity check: Our suggested-index result is present"
+ );
+ await checkLabels(4, {
+ 3: FIREFOX_SUGGEST_LABEL,
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+ });
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+
+ // Add back history so subsequent tasks run with this test's initial state.
+ await addHistory();
+});
+
+// The Firefox Suggest label should appear above a suggested-index result when
+// the result is the first but not the only result with that label.
+add_task(async function suggestedIndex_first() {
+ let index = 1;
+ let provider = new SuggestedIndexProvider(index);
+ UrlbarProvidersManager.registerProvider(provider);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, index);
+ Assert.equal(
+ result.element.row.result.suggestedIndex,
+ index,
+ "Sanity check: Our suggested-index result is present"
+ );
+ await checkLabels(MAX_RESULTS, {
+ [index]: FIREFOX_SUGGEST_LABEL,
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+// The Firefox Suggest label should not appear above a suggested-index result
+// when the result is not the first with that label.
+add_task(async function suggestedIndex_notFirst() {
+ let index = -1;
+ let provider = new SuggestedIndexProvider(index);
+ UrlbarProvidersManager.registerProvider(provider);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(
+ window,
+ MAX_RESULTS + index
+ );
+ Assert.equal(
+ result.element.row.result.suggestedIndex,
+ index,
+ "Sanity check: Our suggested-index result is present"
+ );
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+// Labels that appear multiple times but not consecutively should be shown.
+add_task(async function repeatLabels() {
+ let engineName = Services.search.defaultEngine.name;
+ let results = [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/1" }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.SEARCH,
+ UrlbarUtils.RESULT_SOURCE.SEARCH,
+ { suggestion: "test1", engine: engineName }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/2" }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.SEARCH,
+ UrlbarUtils.RESULT_SOURCE.SEARCH,
+ { suggestion: "test2", engine: engineName }
+ ),
+ ];
+
+ for (let i = 0; i < results.length; i++) {
+ results[i].suggestedIndex = i;
+ }
+
+ let provider = new UrlbarTestUtils.TestProvider({
+ results,
+ priority: Infinity,
+ });
+ UrlbarProvidersManager.registerProvider(provider);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(results.length, {
+ 0: FIREFOX_SUGGEST_LABEL,
+ 1: engineSuggestionsLabel(engineName),
+ 2: FIREFOX_SUGGEST_LABEL,
+ 3: engineSuggestionsLabel(engineName),
+ });
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+// Clicking a row label shouldn't do anything.
+add_task(async function clickLabel() {
+ await BrowserTestUtils.withNewTab("about:blank", async () => {
+ // Do a search. The mock history added in init() should appear with the
+ // Firefox Suggest label at index 1.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ });
+
+ // Check the result at index 2.
+ let result2 = await UrlbarTestUtils.getDetailsOfResultAt(window, 2);
+ Assert.ok(result2.url, "Result at index 2 has a URL");
+ let url2 = result2.url;
+ Assert.ok(
+ url2.startsWith("http://example.com/"),
+ "Result at index 2 is one of our mock history results"
+ );
+
+ // Get the row at index 3 and click above it. The click should hit the row
+ // at index 2 and load its URL. We do this to make sure our click code
+ // here in the test works properly and that performing a similar click
+ // relative to index 1 (see below) would hit the row at index 0 if not for
+ // the label at index 1.
+ let result3 = await UrlbarTestUtils.getDetailsOfResultAt(window, 3);
+ let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+ info("Performing click relative to index 3");
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ click(result3.element.row, { y: -2 })
+ );
+ info("Waiting for load after performing click relative to index 3");
+ await loadPromise;
+ Assert.equal(gBrowser.currentURI.spec, url2, "Loaded URL at index 2");
+ // Now do the search again.
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+
+ await checkLabels(MAX_RESULTS, {
+ 1: FIREFOX_SUGGEST_LABEL,
+ });
+
+ // Check the result at index 1, the one with the label.
+ let result1 = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
+ Assert.ok(result1.url, "Result at index 1 has a URL");
+ let url1 = result1.url;
+ Assert.ok(
+ url1.startsWith("http://example.com/"),
+ "Result at index 1 is one of our mock history results"
+ );
+ Assert.notEqual(url1, url2, "URLs at indexes 1 and 2 are different");
+
+ // Do a click on the row at index 1 in the same way as before. This time
+ // nothing should happen because the click should hit the label, not the
+ // row at index 0.
+ info("Clicking row label at index 1");
+ click(result1.element.row, { y: -2 });
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 500));
+ Assert.ok(UrlbarTestUtils.isPopupOpen(window), "View remains open");
+ Assert.equal(
+ gBrowser.currentURI.spec,
+ url2,
+ "Current URL is still URL from index 2"
+ );
+
+ // Now click the main part of the row at index 1. Its URL should load.
+ loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ let { height } = result1.element.row.getBoundingClientRect();
+ info(`Clicking main part of the row at index 1, height=${height}`);
+ await UrlbarTestUtils.promisePopupClose(window, () =>
+ click(result1.element.row)
+ );
+ info("Waiting for load after clicking row at index 1");
+ await loadPromise;
+ Assert.equal(gBrowser.currentURI.spec, url1, "Loaded URL at index 1");
+ });
+});
+
+add_task(async function ariaLabel() {
+ const helpUrl = "http://example.com/help";
+ const results = [
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/1", helpUrl }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/2", helpUrl }
+ ),
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/3" }
+ ),
+ ];
+
+ for (let i = 0; i < results.length; i++) {
+ results[i].suggestedIndex = i;
+ }
+
+ const provider = new UrlbarTestUtils.TestProvider({
+ results,
+ priority: Infinity,
+ });
+ UrlbarProvidersManager.registerProvider(provider);
+
+ await UrlbarTestUtils.promiseAutocompleteResultPopup({
+ window,
+ value: "test",
+ });
+ await checkLabels(results.length, {
+ 0: FIREFOX_SUGGEST_LABEL,
+ });
+
+ const expectedRows = [
+ { hasGroupAriaLabel: true, ariaLabel: FIREFOX_SUGGEST_LABEL },
+ { hasGroupAriaLabel: false },
+ { hasGroupAriaLabel: false },
+ ];
+ await checkGroupAriaLabels(expectedRows);
+
+ await UrlbarTestUtils.promisePopupClose(window);
+
+ UrlbarProvidersManager.unregisterProvider(provider);
+});
+
+/**
+ * Provider that returns a suggested-index result.
+ */
+class SuggestedIndexProvider extends UrlbarTestUtils.TestProvider {
+ constructor(suggestedIndex) {
+ super({
+ results: [
+ Object.assign(
+ new UrlbarResult(
+ UrlbarUtils.RESULT_TYPE.URL,
+ UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL,
+ { url: "http://example.com/" }
+ ),
+ { suggestedIndex }
+ ),
+ ],
+ });
+ }
+}
+
+async function addHistory() {
+ for (let i = 0; i < MAX_RESULTS; i++) {
+ await PlacesTestUtils.addVisits("http://example.com/" + i);
+ }
+}
+
+/**
+ * Asserts that each result in the view does or doesn't have a label, depending
+ * on `labelsByIndex`.
+ *
+ * @param {number} resultCount
+ * The expected number of results. Pass -1 to use the max index in
+ * `labelsByIndex` or the actual result count if `labelsByIndex` is empty.
+ * @param {object} labelsByIndex
+ * A mapping from indexes to expected labels.
+ */
+async function checkLabels(resultCount, labelsByIndex) {
+ if (resultCount >= 0) {
+ Assert.equal(
+ UrlbarTestUtils.getResultCount(window),
+ resultCount,
+ "Expected result count"
+ );
+ } else {
+ // This `else` branch is only necessary because waiting for all top sites to
+ // be added intermittently times out. Don't let the test fail for such a
+ // dumb reason.
+ let indexes = Object.keys(labelsByIndex);
+ if (indexes.length) {
+ resultCount = indexes.sort((a, b) => b - a)[0] + 1;
+ } else {
+ resultCount = UrlbarTestUtils.getResultCount(window);
+ Assert.greater(resultCount, 0, "Actual result count is > 0");
+ }
+ }
+ for (let i = 0; i < resultCount; i++) {
+ let result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ let { row } = result.element;
+ let before = getComputedStyle(row, "::before");
+ if (labelsByIndex.hasOwnProperty(i)) {
+ Assert.equal(
+ before.content,
+ "attr(label)",
+ `::before.content is correct at index ${i}`
+ );
+ Assert.equal(
+ row.getAttribute("label"),
+ labelsByIndex[i],
+ `Row has correct label at index ${i}`
+ );
+ } else {
+ Assert.equal(
+ before.content,
+ "none",
+ `::before.content is 'none' at index ${i}`
+ );
+ Assert.ok(
+ !row.hasAttribute("label"),
+ `Row does not have label attribute at index ${i}`
+ );
+ }
+ }
+}
+
+/**
+ * Asserts that an element for group aria label.
+ *
+ * @param {Array} expectedRows The expected rows.
+ */
+async function checkGroupAriaLabels(expectedRows) {
+ Assert.equal(
+ UrlbarTestUtils.getResultCount(window),
+ expectedRows.length,
+ "Expected result count"
+ );
+
+ for (let i = 0; i < expectedRows.length; i++) {
+ const result = await UrlbarTestUtils.getDetailsOfResultAt(window, i);
+ const { row } = result.element;
+ const groupAriaLabel = row.querySelector(".urlbarView-group-aria-label");
+
+ const expected = expectedRows[i];
+
+ Assert.equal(
+ !!groupAriaLabel,
+ expected.hasGroupAriaLabel,
+ `Group aria label exists as expected in the results[${i}]`
+ );
+
+ if (expected.hasGroupAriaLabel) {
+ Assert.equal(
+ groupAriaLabel.getAttribute("aria-label"),
+ expected.ariaLabel,
+ `Content of aria-label attribute in the element for group aria label in the results[${i}] is correct`
+ );
+ }
+ }
+}
+
+function engineSuggestionsLabel(engineName) {
+ return ENGINE_SUGGESTIONS_LABEL.replace("%s", engineName);
+}
+
+/**
+ * Adds a search engine that provides suggestions, calls your callback, and then
+ * remove the engine.
+ *
+ * @param {Function} callback
+ * Your callback function.
+ * @param {string} [engineBasename]
+ * The basename of the engine file.
+ */
+async function withSuggestions(
+ callback,
+ engineBasename = TEST_ENGINE_BASENAME
+) {
+ await SpecialPowers.pushPrefEnv({
+ set: [[SUGGESTIONS_PREF, true]],
+ });
+ let engine = await SearchTestUtils.promiseNewSearchEngine({
+ url: getRootDirectory(gTestPath) + engineBasename,
+ });
+ let oldDefaultEngine = await Services.search.getDefault();
+ await Services.search.setDefault(
+ engine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ try {
+ await callback(engine);
+ } finally {
+ await Services.search.setDefault(
+ oldDefaultEngine,
+ Ci.nsISearchService.CHANGE_REASON_UNKNOWN
+ );
+ await Services.search.removeEngine(engine);
+ await SpecialPowers.popPrefEnv();
+ }
+}
+
+function click(element, { x = undefined, y = undefined } = {}) {
+ let { width, height } = element.getBoundingClientRect();
+ if (typeof x != "number") {
+ x = width / 2;
+ }
+ if (typeof y != "number") {
+ y = height / 2;
+ }
+ EventUtils.synthesizeMouse(element, x, y, { type: "mousedown" });
+ EventUtils.synthesizeMouse(element, x, y, { type: "mouseup" });
+}