diff options
Diffstat (limited to 'browser/components/urlbar/tests/unit/test_muxer.js')
-rw-r--r-- | browser/components/urlbar/tests/unit/test_muxer.js | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/unit/test_muxer.js b/browser/components/urlbar/tests/unit/test_muxer.js new file mode 100644 index 0000000000..dcea820835 --- /dev/null +++ b/browser/components/urlbar/tests/unit/test_muxer.js @@ -0,0 +1,731 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + sinon: "resource://testing-common/Sinon.sys.mjs", +}); + +let sandbox; + +add_task(async function setup() { + sandbox = lazy.sinon.createSandbox(); + registerCleanupFunction(() => { + sandbox.restore(); + }); +}); + +add_task(async function test_muxer() { + Assert.throws( + () => UrlbarProvidersManager.registerMuxer(), + /invalid muxer/, + "Should throw with no arguments" + ); + Assert.throws( + () => UrlbarProvidersManager.registerMuxer({}), + /invalid muxer/, + "Should throw with empty object" + ); + Assert.throws( + () => + UrlbarProvidersManager.registerMuxer({ + name: "", + }), + /invalid muxer/, + "Should throw with empty name" + ); + Assert.throws( + () => + UrlbarProvidersManager.registerMuxer({ + name: "test", + sort: "no", + }), + /invalid muxer/, + "Should throw with invalid sort" + ); + + let matches = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.TAB_SWITCH, + UrlbarUtils.RESULT_SOURCE.TABS, + { url: "http://mozilla.org/tab/" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.BOOKMARKS, + { url: "http://mozilla.org/bookmark/" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/history/" } + ), + ]; + + let provider = registerBasicTestProvider(matches); + let context = createContext(undefined, { providers: [provider.name] }); + let controller = UrlbarTestUtils.newMockController(); + /** + * A test muxer. + */ + class TestMuxer extends UrlbarMuxer { + get name() { + return "TestMuxer"; + } + sort(queryContext, unsortedResults) { + queryContext.results = [...unsortedResults].sort((a, b) => { + if (b.source == UrlbarUtils.RESULT_SOURCE.TABS) { + return -1; + } + if (b.source == UrlbarUtils.RESULT_SOURCE.BOOKMARKS) { + return 1; + } + return a.source == UrlbarUtils.RESULT_SOURCE.BOOKMARKS ? -1 : 1; + }); + } + } + let muxer = new TestMuxer(); + + UrlbarProvidersManager.registerMuxer(muxer); + context.muxer = "TestMuxer"; + + info("Check results, the order should be: bookmark, history, tab"); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, [matches[1], matches[2], matches[0]]); + + // Sanity check, should not throw. + UrlbarProvidersManager.unregisterMuxer(muxer); + UrlbarProvidersManager.unregisterMuxer("TestMuxer"); // no-op. +}); + +add_task(async function test_preselectedHeuristic_singleProvider() { + let matches = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/a" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/b" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/c" } + ), + ]; + matches[1].heuristic = true; + + let provider = registerBasicTestProvider(matches); + let context = createContext(undefined, { + providers: [provider.name], + }); + let controller = UrlbarTestUtils.newMockController(); + + info("Check results, the order should be: b (heuristic), a, c"); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, [matches[1], matches[0], matches[2]]); +}); + +add_task(async function test_preselectedHeuristic_multiProviders() { + let matches1 = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/a" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/b" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/c" } + ), + ]; + + let matches2 = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/d" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/e" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/f" } + ), + ]; + matches2[1].heuristic = true; + + let provider1 = registerBasicTestProvider(matches1); + let provider2 = registerBasicTestProvider(matches2); + + let context = createContext(undefined, { + providers: [provider1.name, provider2.name], + }); + let controller = UrlbarTestUtils.newMockController(); + + info("Check results, the order should be: e (heuristic), a, b, c, d, f"); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, [ + matches2[1], + ...matches1, + matches2[0], + matches2[2], + ]); +}); + +add_task(async function test_suggestions() { + Services.prefs.setIntPref("browser.urlbar.maxHistoricalSearchSuggestions", 1); + + let matches = [ + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/a" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/b" } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.SEARCH, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { + engine: "mozSearch", + query: "moz", + suggestion: "mozzarella", + lowerCaseSuggestion: "mozzarella", + } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.SEARCH, + UrlbarUtils.RESULT_SOURCE.SEARCH, + { + engine: "mozSearch", + query: "moz", + suggestion: "mozilla", + lowerCaseSuggestion: "mozilla", + } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.SEARCH, + UrlbarUtils.RESULT_SOURCE.SEARCH, + { + engine: "mozSearch", + query: "moz", + providesSearchMode: true, + keyword: "@moz", + } + ), + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/c" } + ), + ]; + + let provider = registerBasicTestProvider(matches); + + let context = createContext(undefined, { + providers: [provider.name], + }); + let controller = UrlbarTestUtils.newMockController(); + + info("Check results, the order should be: mozzarella, moz, a, b, @moz, c"); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, [ + matches[2], + matches[3], + matches[0], + matches[1], + matches[4], + matches[5], + ]); + + Services.prefs.clearUserPref("browser.urlbar.maxHistoricalSearchSuggestions"); +}); + +add_task(async function test_deduplicate_for_unitConversion() { + const searchSuggestion = new UrlbarResult( + UrlbarUtils.RESULT_TYPE.SEARCH, + UrlbarUtils.RESULT_SOURCE.SEARCH, + { + engine: "Google", + query: "10cm to m", + suggestion: "= 0.1 meters", + } + ); + const searchProvider = registerBasicTestProvider( + [searchSuggestion], + null, + UrlbarUtils.PROVIDER_TYPE.PROFILE + ); + + const unitConversionSuggestion = new UrlbarResult( + UrlbarUtils.RESULT_TYPE.DYNAMIC, + UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL, + { + dynamicType: "unitConversion", + output: "0.1 m", + input: "10cm to m", + } + ); + unitConversionSuggestion.suggestedIndex = 1; + + const unitConversion = registerBasicTestProvider( + [unitConversionSuggestion], + null, + UrlbarUtils.PROVIDER_TYPE.PROFILE, + "UnitConversion" + ); + + const context = createContext(undefined, { + providers: [searchProvider.name, unitConversion.name], + }); + const controller = UrlbarTestUtils.newMockController(); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, [unitConversionSuggestion]); +}); + +// These results are used in the badHeuristicGroups tests below. The order of +// the results in the array isn't important because they all get added at the +// same time. It's the resultGroups in each test that is important. +const BAD_HEURISTIC_RESULTS = [ + // heuristic + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/heuristic-0" } + ), + { heuristic: true } + ), + // heuristic + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/heuristic-1" } + ), + { heuristic: true } + ), + // non-heuristic + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/non-heuristic-0" } + ), + // non-heuristic + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://mozilla.org/non-heuristic-1" } + ), +]; + +const BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC = BAD_HEURISTIC_RESULTS[0]; +const BAD_HEURISTIC_RESULTS_GENERAL = [ + BAD_HEURISTIC_RESULTS[2], + BAD_HEURISTIC_RESULTS[3], +]; + +add_task(async function test_badHeuristicGroups_multiple_0() { + await doBadHeuristicGroupsTest( + [ + // 2 heuristics with child groups + { + maxResultCount: 2, + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_1() { + await doBadHeuristicGroupsTest( + [ + // infinite heuristics with child groups + { + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_2() { + await doBadHeuristicGroupsTest( + [ + // 2 heuristics + { + maxResultCount: 2, + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_3() { + await doBadHeuristicGroupsTest( + [ + // infinite heuristics + { + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_4() { + await doBadHeuristicGroupsTest( + [ + // 1 heuristic with child groups + { + maxResultCount: 1, + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // 1 heuristic with child groups + { + maxResultCount: 1, + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_5() { + await doBadHeuristicGroupsTest( + [ + // infinite heuristics with child groups + { + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // infinite heuristics with child groups + { + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_6() { + await doBadHeuristicGroupsTest( + [ + // 1 heuristic + { + maxResultCount: 1, + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // 1 heuristic + { + maxResultCount: 1, + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicGroups_multiple_7() { + await doBadHeuristicGroupsTest( + [ + // infinite heuristics + { + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + // infinite general + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // infinite heuristics + { + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + ], + [BAD_HEURISTIC_RESULTS_FIRST_HEURISTIC, ...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicsGroups_notFirst_0() { + await doBadHeuristicGroupsTest( + [ + // infinite general first + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // 1 heuristic with child groups second + { + maxResultCount: 1, + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + ], + [...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicsGroups_notFirst_1() { + await doBadHeuristicGroupsTest( + [ + // infinite general first + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // infinite heuristics with child groups second + { + children: [{ group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST }], + }, + ], + [...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicsGroups_notFirst_2() { + await doBadHeuristicGroupsTest( + [ + // infinite general first + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // 1 heuristic second + { + maxResultCount: 1, + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + ], + [...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicsGroups_notFirst_3() { + await doBadHeuristicGroupsTest( + [ + // infinite general first + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // infinite heuristics second + { + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + ], + [...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +add_task(async function test_badHeuristicsGroups_notFirst_4() { + await doBadHeuristicGroupsTest( + [ + // 1 general first + { + maxResultCount: 1, + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + // infinite heuristics second + { + group: UrlbarUtils.RESULT_GROUP.HEURISTIC_TEST, + }, + // infinite general third + { + group: UrlbarUtils.RESULT_GROUP.GENERAL, + }, + ], + [...BAD_HEURISTIC_RESULTS_GENERAL] + ); +}); + +/** + * Sets the resultGroups pref, performs a search, and then checks the results. + * Regardless of the groups, the muxer should include at most one heuristic in + * its results and it should always be the first result. + * + * @param {Array} resultGroups + * The result groups. + * @param {Array} expectedResults + * The expected results. + */ +async function doBadHeuristicGroupsTest(resultGroups, expectedResults) { + sandbox.stub(UrlbarPrefs, "resultGroups").get(() => { + return { children: resultGroups }; + }); + + let provider = registerBasicTestProvider(BAD_HEURISTIC_RESULTS); + let context = createContext("foo", { providers: [provider.name] }); + let controller = UrlbarTestUtils.newMockController(); + await UrlbarProvidersManager.startQuery(context, controller); + Assert.deepEqual(context.results, expectedResults); + + sandbox.restore(); +} + +// When `maxRichResults` is positive and taken up by suggested-index result(s), +// both the heuristic and suggested-index results should be included because we +// (a) make room for the heuristic and (b) assume all suggested-index results +// should be included even if it means exceeding `maxRichResults`. The specified +// `maxRichResults` span will be exceeded in this case. +add_task(async function roomForHeuristic_suggestedIndex() { + let results = [ + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/heuristic" } + ), + { heuristic: true } + ), + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/suggestedIndex" } + ), + { suggestedIndex: 1 } + ), + ]; + + UrlbarPrefs.set("maxRichResults", 1); + + let provider = registerBasicTestProvider(results); + let context = createContext(undefined, { providers: [provider.name] }); + await check_results({ + context, + matches: results, + }); + + UrlbarPrefs.clear("maxRichResults"); +}); + +// When `maxRichResults` is positive but less than the heuristic's result span, +// the heuristic should be included because we make room for it even if it means +// exceeding `maxRichResults`. The specified `maxRichResults` span will be +// exceeded in this case. +add_task(async function roomForHeuristic_largeResultSpan() { + let results = [ + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/heuristic" } + ), + { heuristic: true, resultSpan: 2 } + ), + ]; + + UrlbarPrefs.set("maxRichResults", 1); + + let provider = registerBasicTestProvider(results); + let context = createContext(undefined, { providers: [provider.name] }); + await check_results({ + context, + matches: results, + }); + + UrlbarPrefs.clear("maxRichResults"); +}); + +// When `maxRichResults` is zero and there are no suggested-index results, the +// heuristic should not be included. +add_task(async function roomForHeuristic_maxRichResultsZero() { + let results = [ + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/heuristic" } + ), + { heuristic: true } + ), + ]; + + UrlbarPrefs.set("maxRichResults", 0); + + let provider = registerBasicTestProvider(results); + let context = createContext(undefined, { providers: [provider.name] }); + await check_results({ + context, + matches: [], + }); + + UrlbarPrefs.clear("maxRichResults"); +}); + +// When `maxRichResults` is zero and suggested-index results are present, +// neither the heuristic nor the suggested-index results should be included. +add_task(async function roomForHeuristic_maxRichResultsZero_suggestedIndex() { + let results = [ + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/heuristic" } + ), + { heuristic: true } + ), + Object.assign( + new UrlbarResult( + UrlbarUtils.RESULT_TYPE.URL, + UrlbarUtils.RESULT_SOURCE.HISTORY, + { url: "http://example.com/suggestedIndex" } + ), + { suggestedIndex: 1 } + ), + ]; + + UrlbarPrefs.set("maxRichResults", 0); + + let provider = registerBasicTestProvider(results); + let context = createContext(undefined, { providers: [provider.name] }); + await check_results({ + context, + matches: [], + }); + + UrlbarPrefs.clear("maxRichResults"); +}); |