summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js')
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js293
1 files changed, 293 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js b/browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js
new file mode 100644
index 0000000000..f50fe32dd3
--- /dev/null
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_suggestionsMap.js
@@ -0,0 +1,293 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests `SuggestionsMap`.
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ ObjectUtils: "resource://gre/modules/ObjectUtils.sys.mjs",
+ SuggestionsMap: "resource:///modules/urlbar/private/SuggestBackendJs.sys.mjs",
+});
+
+// This overrides `SuggestionsMap.chunkSize`. Testing the actual value can make
+// the test run too long. This is OK because the correctness of the chunking
+// behavior doesn't depend on the chunk size.
+const TEST_CHUNK_SIZE = 100;
+
+add_setup(async () => {
+ // Sanity check the actual `chunkSize` value.
+ Assert.equal(
+ typeof SuggestionsMap.chunkSize,
+ "number",
+ "Sanity check: chunkSize is a number"
+ );
+ Assert.greater(SuggestionsMap.chunkSize, 0, "Sanity check: chunkSize > 0");
+
+ // Set our test value.
+ SuggestionsMap.chunkSize = TEST_CHUNK_SIZE;
+});
+
+// Tests many suggestions with one keyword each.
+add_task(async function chunking_singleKeyword() {
+ let suggestionCounts = [
+ 1 * SuggestionsMap.chunkSize - 1,
+ 1 * SuggestionsMap.chunkSize,
+ 1 * SuggestionsMap.chunkSize + 1,
+ 2 * SuggestionsMap.chunkSize - 1,
+ 2 * SuggestionsMap.chunkSize,
+ 2 * SuggestionsMap.chunkSize + 1,
+ 3 * SuggestionsMap.chunkSize - 1,
+ 3 * SuggestionsMap.chunkSize,
+ 3 * SuggestionsMap.chunkSize + 1,
+ ];
+ for (let count of suggestionCounts) {
+ await doChunkingTest(count, 1);
+ }
+});
+
+// Tests a small number of suggestions with many keywords each.
+add_task(async function chunking_manyKeywords() {
+ let keywordCounts = [
+ 1 * SuggestionsMap.chunkSize - 1,
+ 1 * SuggestionsMap.chunkSize,
+ 1 * SuggestionsMap.chunkSize + 1,
+ 2 * SuggestionsMap.chunkSize - 1,
+ 2 * SuggestionsMap.chunkSize,
+ 2 * SuggestionsMap.chunkSize + 1,
+ 3 * SuggestionsMap.chunkSize - 1,
+ 3 * SuggestionsMap.chunkSize,
+ 3 * SuggestionsMap.chunkSize + 1,
+ ];
+ for (let suggestionCount = 1; suggestionCount <= 3; suggestionCount++) {
+ for (let keywordCount of keywordCounts) {
+ await doChunkingTest(suggestionCount, keywordCount);
+ }
+ }
+});
+
+async function doChunkingTest(suggestionCount, keywordCountPerSuggestion) {
+ info(
+ "Running chunking test: " +
+ JSON.stringify({ suggestionCount, keywordCountPerSuggestion })
+ );
+
+ // Create `suggestionCount` suggestions, each with `keywordCountPerSuggestion`
+ // keywords.
+ let suggestions = [];
+ for (let i = 0; i < suggestionCount; i++) {
+ let keywords = [];
+ for (let k = 0; k < keywordCountPerSuggestion; k++) {
+ keywords.push(`keyword-${i}-${k}`);
+ }
+ suggestions.push({
+ keywords,
+ id: i,
+ url: "http://example.com/" + i,
+ title: "Suggestion " + i,
+ click_url: "http://example.com/click",
+ impression_url: "http://example.com/impression",
+ advertiser: "TestAdvertiser",
+ iab_category: "22 - Shopping",
+ });
+ }
+
+ // Add the suggestions.
+ let map = new SuggestionsMap();
+ await map.add(suggestions);
+
+ // Make sure all keyword-suggestion pairs have been added.
+ for (let i = 0; i < suggestionCount; i++) {
+ for (let k = 0; k < keywordCountPerSuggestion; k++) {
+ let keyword = `keyword-${i}-${k}`;
+
+ // Check the map. Logging all assertions takes a ton of time and makes the
+ // test run much longer than it otherwise would, especially if `chunkSize`
+ // is large, so only log failing assertions.
+ let actualSuggestions = map.get(keyword);
+ if (!ObjectUtils.deepEqual(actualSuggestions, [suggestions[i]])) {
+ Assert.deepEqual(
+ actualSuggestions,
+ [suggestions[i]],
+ `Suggestion ${i} is present for keyword ${keyword}`
+ );
+ }
+ }
+ }
+}
+
+add_task(async function duplicateKeywords() {
+ let suggestions = [
+ {
+ title: "suggestion 0",
+ keywords: ["a", "a", "a", "b", "b", "c"],
+ },
+ {
+ title: "suggestion 1",
+ keywords: ["b", "c", "d"],
+ },
+ {
+ title: "suggestion 2",
+ keywords: ["c", "d", "e"],
+ },
+ {
+ title: "suggestion 3",
+ keywords: ["f", "f"],
+ },
+ ];
+
+ let expectedIndexesByKeyword = {
+ a: [0],
+ b: [0, 1],
+ c: [0, 1, 2],
+ d: [1, 2],
+ e: [2],
+ f: [3],
+ };
+
+ let map = new SuggestionsMap();
+ await map.add(suggestions);
+
+ for (let [keyword, indexes] of Object.entries(expectedIndexesByKeyword)) {
+ Assert.deepEqual(
+ map.get(keyword),
+ indexes.map(i => suggestions[i]),
+ "get() with keyword: " + keyword
+ );
+ }
+});
+
+add_task(async function mapKeywords() {
+ let suggestions = [
+ {
+ title: "suggestion 0",
+ keywords: ["a", "a", "a", "b", "b", "c"],
+ },
+ {
+ title: "suggestion 1",
+ keywords: ["b", "c", "d"],
+ },
+ {
+ title: "suggestion 2",
+ keywords: ["c", "d", "e"],
+ },
+ {
+ title: "suggestion 3",
+ keywords: ["f", "f"],
+ },
+ ];
+
+ let expectedIndexesByKeyword = {
+ a: [],
+ b: [],
+ c: [],
+ d: [],
+ e: [],
+ f: [],
+ ax: [0],
+ bx: [0, 1],
+ cx: [0, 1, 2],
+ dx: [1, 2],
+ ex: [2],
+ fx: [3],
+ fy: [3],
+ fz: [3],
+ };
+
+ let map = new SuggestionsMap();
+ await map.add(suggestions, {
+ mapKeyword: keyword => {
+ if (keyword == "f") {
+ return [keyword + "x", keyword + "y", keyword + "z"];
+ }
+ return [keyword + "x"];
+ },
+ });
+
+ for (let [keyword, indexes] of Object.entries(expectedIndexesByKeyword)) {
+ Assert.deepEqual(
+ map.get(keyword),
+ indexes.map(i => suggestions[i]),
+ "get() with keyword: " + keyword
+ );
+ }
+});
+
+// Tests `keywordsProperty`.
+add_task(async function keywordsProperty() {
+ let suggestion = {
+ title: "suggestion",
+ keywords: ["should be ignored"],
+ foo: ["hello"],
+ };
+
+ let map = new SuggestionsMap();
+ await map.add([suggestion], {
+ keywordsProperty: "foo",
+ });
+
+ Assert.deepEqual(
+ map.get("hello"),
+ [suggestion],
+ "Keyword in `foo` should match"
+ );
+ Assert.deepEqual(
+ map.get("should be ignored"),
+ [],
+ "Keyword in `keywords` should not match"
+ );
+});
+
+// Tests `MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD`.
+add_task(async function prefixesStartingAtFirstWord() {
+ let suggestion = {
+ title: "suggestion",
+ keywords: ["one two three", "four five six"],
+ };
+
+ // keyword passed to `get()` -> should match
+ let tests = {
+ o: false,
+ on: false,
+ one: true,
+ "one ": true,
+ "one t": true,
+ "one tw": true,
+ "one two": true,
+ "one two ": true,
+ "one two t": true,
+ "one two th": true,
+ "one two thr": true,
+ "one two thre": true,
+ "one two three": true,
+ "one two three ": false,
+ f: false,
+ fo: false,
+ fou: false,
+ four: true,
+ "four ": true,
+ "four f": true,
+ "four fi": true,
+ "four fiv": true,
+ "four five": true,
+ "four five ": true,
+ "four five s": true,
+ "four five si": true,
+ "four five six": true,
+ "four five six ": false,
+ };
+
+ let map = new SuggestionsMap();
+ await map.add([suggestion], {
+ mapKeyword: SuggestionsMap.MAP_KEYWORD_PREFIXES_STARTING_AT_FIRST_WORD,
+ });
+
+ for (let [keyword, shouldMatch] of Object.entries(tests)) {
+ Assert.deepEqual(
+ map.get(keyword),
+ shouldMatch ? [suggestion] : [],
+ "get() with keyword: " + keyword
+ );
+ }
+});