Bug 1836582 - Update the matching behavior of addon suggestions. r=daisuke

This implements the new required matching behavior, which isn't based on min
keyword length anymore. This is how it works:

* Use the full keywords in remote settings to generate keywords that contain the
  first word plus each possible substring after the first word. For example if a
  full keyword is "video download", then generate these keywords: "video",
  "video ", "video d", "video do", etc. If a full keyword is only one word, then
  use it as is. The keywords never change even when the user clicks "Show less
  frequently". This is implemented in `onRemoteSettingsSync()`, and I modified
  `SuggestionsMap.add()` to make it easy to generate new keywords from the
  strings in `suggestion.keywords`.
* Keep track of the number of times the user clicked "Show less frequently" in
  `showLessFrequentlyCount`.
* When a suggestion is fetched from the suggestions map, filter it out if the
  search string isn't long enough given the `showLessFrequentlyCount`. This is
  done in `makeResult()`.

Other changes:

* I made some of the private properties in `AddonSuggestions` public so that the
  xpcshell test can easily use them. I think it's OK for them to be public.
* Added `show_less_frequently_cap` to the RS config object so that we can
  specify a cap in RS as well as Nimbus.
* mv'ed test_quicksuggest_addResults.js to test_suggestionsMap.js, since I
  modified this file. I should have done that back when I replaced `addResults()`
  with `SuggestionsMap`.
* Fixed a bug in `SuggestionsMap.add()` where the same suggestion could be added
  multiple times to the array stored in the map, if it had duplicate keywords.

Differential Revision: https://phabricator.services.mozilla.com/D179867
This commit is contained in:
Drew Willcoxon
2023-06-05 20:49:21 +00:00
parent 7e3cdd320a
commit 5299a47663
8 changed files with 509 additions and 146 deletions

View File

@@ -35,9 +35,9 @@ const PREF_URLBAR_DEFAULTS = new Map([
// Feature gate pref for addon suggestions in the urlbar.
["addons.featureGate", false],
// The minimum prefix length of addons keyword the user must type to trigger
// the suggestion. 0 means the min length should be taken from Nimbus.
["addons.minKeywordLength", 0],
// The number of times the user has clicked the "Show less frequently" command
// for addon suggestions.
["addons.showLessFrequentlyCount", 0],
// "Autofill" is the name of the feature that automatically completes domains
// and URLs that the user has visited as the user is typing them in the urlbar
@@ -453,8 +453,7 @@ const PREF_OTHER_DEFAULTS = new Map([
// Variables with fallback prefs do not need to be defined here because their
// defaults are the values of their fallbacks.
const NIMBUS_DEFAULTS = {
addonsKeywordsMinimumLength: 0,
addonsKeywordsMinimumLengthCap: 0,
addonsShowLessFrequentlyCap: 0,
addonsUITreatment: "a",
experimentType: "",
isBestMatchExperiment: false,

View File

@@ -175,7 +175,20 @@ export class AddonSuggestions extends BaseFeature {
}
const results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
await suggestionsMap.add(results);
// The keywords in remote settings are full keywords. Map each one to an
// array containing the full keyword's first word plus every subsequent
// prefix of the full keyword.
await suggestionsMap.add(results, fullKeyword => {
let keywords = [fullKeyword];
let spaceIndex = fullKeyword.search(/\s/);
if (spaceIndex >= 0) {
for (let i = spaceIndex; i < fullKeyword.length; i++) {
keywords.push(fullKeyword.substring(0, i));
}
}
return keywords;
});
if (rs != lazy.QuickSuggestRemoteSettings.rs) {
return;
}
@@ -185,10 +198,26 @@ export class AddonSuggestions extends BaseFeature {
}
async makeResult(queryContext, suggestion, searchString) {
if (!this.isEnabled || searchString.length < this.#minKeywordLength) {
if (!this.isEnabled) {
// The feature is disabled on the client, but Merino may still return
// addon suggestions anyway, and we filter them out here.
return null;
}
// If the user hasn't clicked the "Show less frequently" command, the
// suggestion can be shown. Otherwise, the suggestion can be shown if the
// user typed more than one word with at least `showLessFrequentlyCount`
// characters after the first word, including spaces.
if (this.showLessFrequentlyCount) {
let spaceIndex = searchString.search(/\s/);
if (
spaceIndex < 0 ||
searchString.length - spaceIndex < this.showLessFrequentlyCount
) {
return null;
}
}
// If is_top_pick is not specified, handle it as top pick suggestion.
suggestion.is_top_pick = suggestion.is_top_pick ?? true;
@@ -294,7 +323,7 @@ export class AddonSuggestions extends BaseFeature {
getResultCommands(result) {
const commands = [];
if (this.#canIncrementMinKeywordLength) {
if (this.canShowLessFrequently) {
commands.push({
name: RESULT_MENU_COMMAND.SHOW_LESS_FREQUENTLY,
l10n: {
@@ -349,7 +378,7 @@ export class AddonSuggestions extends BaseFeature {
break;
case RESULT_MENU_COMMAND.SHOW_LESS_FREQUENTLY:
queryContext.view.acknowledgeFeedback(result);
this.#incrementMinKeywordLength();
this.incrementShowLessFrequentlyCount();
break;
}
}
@@ -369,26 +398,26 @@ export class AddonSuggestions extends BaseFeature {
return "full";
}
#incrementMinKeywordLength() {
if (this.#canIncrementMinKeywordLength) {
incrementShowLessFrequentlyCount() {
if (this.canShowLessFrequently) {
lazy.UrlbarPrefs.set(
"addons.minKeywordLength",
this.#minKeywordLength + 1
"addons.showLessFrequentlyCount",
this.showLessFrequentlyCount + 1
);
}
}
get #minKeywordLength() {
const minLength =
lazy.UrlbarPrefs.get("addons.minKeywordLength") ||
lazy.UrlbarPrefs.get("addonsKeywordsMinimumLength") ||
0;
return Math.max(minLength, 0);
get showLessFrequentlyCount() {
const count = lazy.UrlbarPrefs.get("addons.showLessFrequentlyCount") || 0;
return Math.max(count, 0);
}
get #canIncrementMinKeywordLength() {
const cap = lazy.UrlbarPrefs.get("addonsKeywordsMinimumLengthCap") || 0;
return !cap || this.#minKeywordLength < cap;
get canShowLessFrequently() {
const cap =
lazy.UrlbarPrefs.get("addonsShowLessFrequentlyCap") ||
lazy.QuickSuggestRemoteSettings.config.show_less_frequently_cap ||
0;
return !cap || this.showLessFrequentlyCount < cap;
}
#suggestionsMap = null;

View File

@@ -81,6 +81,7 @@ class _QuickSuggestRemoteSettings {
* ],
* },
* },
* show_less_frequently_cap,
* }
*/
get config() {
@@ -303,12 +304,21 @@ export class SuggestionsMap {
/**
* Adds a list of suggestion objects to the results map. Each suggestion must
* have a `keywords` property.
* have a `keywords` property whose value is an array of keyword strings. The
* suggestion's keywords will be taken from this array either exactly as they
* are specified or by generating new keywords from them; see `mapKeyword`.
*
* @param {Array} suggestions
* Array of suggestion objects.
* @param {Function} mapKeyword
* If null, each suggestion's keywords will be taken from its `keywords`
* array exactly as they are specified. Otherwise, as each suggestion is
* processed, this function will be called for each string in its `keywords`
* array. The function should return an array of strings. The suggestion's
* final list of keywords will be all the keywords returned by this function
* as it is called for each string in `keywords`.
*/
async add(suggestions) {
async add(suggestions, mapKeyword = null) {
// There can be many suggestions, and each suggestion can have many
// keywords. To avoid blocking the main thread for too long, update the map
// in chunks, and to avoid blocking the UI and other higher priority work,
@@ -329,22 +339,32 @@ export class SuggestionsMap {
) {
let suggestion = suggestions[suggestionIndex];
if (keywordIndex == suggestion.keywords.length) {
// We've added entries for all keywords of the current suggestion.
// Move on to the next suggestion.
suggestionIndex++;
keywordIndex = 0;
continue;
}
// If the keyword's only suggestion is `suggestion`, store it
// directly as the value. Otherwise store an array of suggestions.
// For details, see the `#suggestionsByKeyword` comment.
let keyword = suggestion.keywords[keywordIndex];
let object = this.#suggestionsByKeyword.get(keyword);
if (!object) {
this.#suggestionsByKeyword.set(keyword, suggestion);
} else if (!Array.isArray(object)) {
this.#suggestionsByKeyword.set(keyword, [object, suggestion]);
} else {
object.push(suggestion);
let originalKeyword = suggestion.keywords[keywordIndex];
let keywords = mapKeyword?.(originalKeyword) ?? [originalKeyword];
for (let keyword of keywords) {
// If the keyword's only suggestion is `suggestion`, store it
// directly as the value. Otherwise store an array of unique
// suggestions. See the `#suggestionsByKeyword` comment.
let object = this.#suggestionsByKeyword.get(keyword);
if (!object) {
this.#suggestionsByKeyword.set(keyword, suggestion);
} else {
let isArray = Array.isArray(object);
if (!isArray && object != suggestion) {
this.#suggestionsByKeyword.set(keyword, [object, suggestion]);
} else if (isArray && !object.includes(suggestion)) {
object.push(suggestion);
}
}
}
keywordIndex++;
indexInChunk++;
}

View File

@@ -251,111 +251,60 @@ add_task(async function resultMenu_showLessFrequently() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.addons.featureGate", true],
["browser.urlbar.addons.minKeywordLength", 0],
["browser.urlbar.addons.showLessFrequentlyCount", 0],
],
});
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
addonsKeywordsMinimumLengthCap: 3,
addonsShowLessFrequentlyCap: 3,
});
// Sanity check.
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLengthCap"), 3);
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLength"), 0);
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 0);
Assert.equal(UrlbarPrefs.get("addonsShowLessFrequentlyCap"), 3);
Assert.equal(UrlbarPrefs.get("addons.showLessFrequentlyCount"), 0);
await doShowLessFrequently({
input: "12",
input: "aaa b",
expected: {
isSuggestionShown: true,
isMenuItemShown: true,
},
});
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 1);
Assert.equal(UrlbarPrefs.get("addons.showLessFrequentlyCount"), 1);
await doShowLessFrequently({
input: "12",
input: "aaa b",
expected: {
isSuggestionShown: true,
isMenuItemShown: true,
},
});
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 2);
Assert.equal(UrlbarPrefs.get("addons.showLessFrequentlyCount"), 2);
await doShowLessFrequently({
input: "12",
input: "aaa b",
expected: {
isSuggestionShown: true,
isMenuItemShown: true,
},
});
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 3);
Assert.equal(UrlbarPrefs.get("addons.showLessFrequentlyCount"), 3);
await doShowLessFrequently({
input: "12",
input: "aaa b",
expected: {
// The suggestion should not display since addons.minKeywordLength is 3
// and the text length is 2,
// The suggestion should not display since addons.showLessFrequentlyCount
// is 3 and the substring (" b") after the first word ("aaa") is 2 chars
// long.
isSuggestionShown: false,
},
});
await doShowLessFrequently({
input: "123",
input: "aaa bb",
expected: {
// The suggestion should display, but item should not shown since the
// addons.minKeywordLength reached to addonsKeywordsMinimumLengthCap
// already.
isSuggestionShown: true,
isMenuItemShown: false,
},
});
await cleanUpNimbus();
await SpecialPowers.popPrefEnv();
});
add_task(async function resultMenu_showLessFrequentlyWithNimbusMinimumLength() {
await SpecialPowers.pushPrefEnv({
set: [
["browser.urlbar.addons.featureGate", true],
["browser.urlbar.addons.minKeywordLength", 0],
],
});
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
addonsKeywordsMinimumLengthCap: 3,
addonsKeywordsMinimumLength: 2,
});
// Sanity check.
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLengthCap"), 3);
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLength"), 2);
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 0);
await doShowLessFrequently({
input: "12",
expected: {
isSuggestionShown: true,
isMenuItemShown: true,
},
});
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 3);
await doShowLessFrequently({
input: "12",
expected: {
// The suggestion should not display since addons.minKeywordLength is 3
// and the text length is 2,
isSuggestionShown: false,
},
});
await doShowLessFrequently({
input: "123",
expected: {
// The suggestion should display, but item should not shown since the
// addons.minKeywordLength reached to addonsKeywordsMinimumLengthCap
// addons.showLessFrequentlyCount reached to addonsShowLessFrequentlyCap
// already.
isSuggestionShown: true,
isMenuItemShown: false,
@@ -524,16 +473,6 @@ async function doDismissTest(command) {
set: [["browser.urlbar.addons.featureGate", true]],
});
const cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({
addonsKeywordsMinimumLengthCap: 0,
addonsKeywordsMinimumLength: 0,
});
// Sanity check.
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLengthCap"), 0);
Assert.equal(UrlbarPrefs.get("addonsKeywordsMinimumLength"), 0);
Assert.equal(UrlbarPrefs.get("addons.minKeywordLength"), 0);
await UrlbarTestUtils.promiseAutocompleteResultPopup({
window,
value: "123",
@@ -616,7 +555,6 @@ async function doDismissTest(command) {
await UrlbarTestUtils.promisePopupClose(window);
await cleanUpNimbus();
await SpecialPowers.popPrefEnv();
UrlbarPrefs.clear("suggest.addons");
}

View File

@@ -39,7 +39,7 @@ const REMOTE_SETTINGS_RESULTS = [
icon: "https://example.com/first-addon.svg",
title: "First Addon",
rating: "4.7",
keywords: ["first", "1st"],
keywords: ["first", "1st", "two words", "a b c"],
description: "Description for the First Addon",
number_of_ratings: 1256,
is_top_pick: true,
@@ -249,6 +249,22 @@ add_task(async function hideIfAlreadyInstalled() {
add_task(async function remoteSettings() {
const testCases = [
{
input: "f",
expected: null,
},
{
input: "fi",
expected: null,
},
{
input: "fir",
expected: null,
},
{
input: "firs",
expected: null,
},
{
input: "first",
expected: makeExpectedResult({
@@ -265,6 +281,110 @@ add_task(async function remoteSettings() {
isTopPick: true,
}),
},
{
input: "t",
expected: null,
},
{
input: "tw",
expected: null,
},
{
input: "two",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two ",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two w",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two wo",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two wor",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two word",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "two words",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "a",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "a ",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "a b",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "a b ",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "a b c",
expected: makeExpectedResult({
suggestion: REMOTE_SETTINGS_RESULTS[0].attachment[0],
source: "remote-settings",
isTopPick: true,
}),
},
{
input: "second",
expected: makeExpectedResult({
@@ -297,26 +417,22 @@ add_task(async function remoteSettings() {
isTopPick: true,
}),
},
{
// Merino result.
input: "not rs",
expected: makeExpectedResult({
suggestion: MERINO_SUGGESTIONS[0],
source: "merino",
isTopPick: true,
}),
},
];
// Disable Merino so we trigger only remote settings suggestions.
UrlbarPrefs.set("quicksuggest.dataCollection.enabled", false);
for (const { input, expected } of testCases) {
await check_results({
context: createContext(input, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [expected],
matches: expected ? [expected] : [],
});
}
UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
});
add_task(async function merinoIsTopPick() {
@@ -357,6 +473,226 @@ add_task(async function merinoIsTopPick() {
});
});
// Tests "show less frequently" with the cap set in remote settings.
add_task(async function showLessFrequently_rs() {
await doShowLessFrequentlyTest({
rs: {
show_less_frequently_cap: 3,
},
tests: [
{
showLessFrequentlyCount: 0,
canShowLessFrequently: true,
searches: {
f: false,
fi: false,
fir: false,
firs: false,
first: true,
t: false,
tw: false,
two: true,
"two ": true,
"two w": true,
"two wo": true,
"two wor": true,
"two word": true,
"two words": true,
a: true,
"a ": true,
"a b": true,
"a b ": true,
"a b c": true,
},
},
{
showLessFrequentlyCount: 1,
canShowLessFrequently: true,
searches: {
first: false,
two: false,
a: false,
},
},
{
showLessFrequentlyCount: 2,
canShowLessFrequently: true,
searches: {
"two ": false,
"a ": false,
},
},
{
showLessFrequentlyCount: 3,
canShowLessFrequently: false,
searches: {
"two w": false,
"a b": false,
},
},
{
showLessFrequentlyCount: 3,
canShowLessFrequently: false,
searches: {},
},
],
});
});
// Tests "show less frequently" with the cap set in both Nimbus and remote
// settings. Nimbus should override remote settings.
add_task(async function showLessFrequently_nimbus() {
await doShowLessFrequentlyTest({
nimbus: {
addonsShowLessFrequentlyCap: 3,
},
rs: {
show_less_frequently_cap: 10,
},
tests: [
{
showLessFrequentlyCount: 0,
canShowLessFrequently: true,
searches: {
a: true,
"a ": true,
"a b": true,
"a b ": true,
"a b c": true,
},
},
{
showLessFrequentlyCount: 1,
canShowLessFrequently: true,
searches: {
a: false,
},
},
{
showLessFrequentlyCount: 2,
canShowLessFrequently: true,
searches: {
"a ": false,
},
},
{
showLessFrequentlyCount: 3,
canShowLessFrequently: false,
searches: {
"a b": false,
},
},
{
showLessFrequentlyCount: 3,
canShowLessFrequently: false,
searches: {},
},
],
});
});
/**
* Does a group of searches, increments the `showLessFrequentlyCount`, and
* repeats until all groups are done. The cap can be set by remote settings
* config and/or Nimbus.
*
* @param {object} options
* Options object.
* @param {object} options.tests
* An array where each item describes a group of searches to perform and
* expected state. Each item should look like this:
* `{ showLessFrequentlyCount, canShowLessFrequently, searches }`
*
* {number} showLessFrequentlyCount
* The expected value of `showLessFrequentlyCount` before the group of
* searches is performed.
* {boolean} canShowLessFrequently
* The expected value of `canShowLessFrequently` before the group of
* searches is performed.
* {object} searches
* An object that maps each search string to a boolean that indicates
* whether the first remote settings suggestion should be triggered by the
* search string. `searches` objects are cumulative: The intended use is to
* pass a large initial group of searches in the first search group, and
* then each following `searches` is a diff against the previous.
* @param {object} options.rs
* The remote settings config to set.
* @param {object} options.nimbus
* The Nimbus variables to set.
*/
async function doShowLessFrequentlyTest({ tests, rs = {}, nimbus = {} }) {
// Disable Merino so we trigger only remote settings suggestions.
UrlbarPrefs.set("quicksuggest.dataCollection.enabled", false);
// We'll be testing with the first remote settings suggestion.
let suggestion = REMOTE_SETTINGS_RESULTS[0].attachment[0];
let addonSuggestions = QuickSuggest.getFeature("AddonSuggestions");
// Set Nimbus variables and RS config.
let cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature(nimbus);
await QuickSuggestTestUtils.withConfig({
config: rs,
callback: async () => {
let cumulativeSearches = {};
for (let {
showLessFrequentlyCount,
canShowLessFrequently,
searches,
} of tests) {
Assert.equal(
addonSuggestions.showLessFrequentlyCount,
showLessFrequentlyCount,
"showLessFrequentlyCount should be correct initially"
);
Assert.equal(
UrlbarPrefs.get("addons.showLessFrequentlyCount"),
showLessFrequentlyCount,
"Pref should be correct initially"
);
Assert.equal(
addonSuggestions.canShowLessFrequently,
canShowLessFrequently,
"canShowLessFrequently should be correct initially"
);
// Merge the current `searches` object into the cumulative object.
cumulativeSearches = {
...cumulativeSearches,
...searches,
};
for (let [searchString, isExpected] of Object.entries(
cumulativeSearches
)) {
await check_results({
context: createContext(searchString, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: !isExpected
? []
: [
makeExpectedResult({
suggestion,
source: "remote-settings",
isTopPick: true,
}),
],
});
}
addonSuggestions.incrementShowLessFrequentlyCount();
}
},
});
await cleanUpNimbus();
UrlbarPrefs.clear("addons.showLessFrequentlyCount");
UrlbarPrefs.set("quicksuggest.dataCollection.enabled", true);
}
function makeExpectedResult({ suggestion, source, isTopPick }) {
let rating;
let number_of_ratings;

View File

@@ -125,7 +125,7 @@ add_task(async function duplicateKeywords() {
let suggestions = [
{
title: "suggestion 0",
keywords: ["a", "b", "c"],
keywords: ["a", "a", "a", "b", "b", "c"],
},
{
title: "suggestion 1",
@@ -137,7 +137,7 @@ add_task(async function duplicateKeywords() {
},
{
title: "suggestion 3",
keywords: ["f"],
keywords: ["f", "f"],
},
];
@@ -161,3 +161,57 @@ add_task(async function duplicateKeywords() {
);
}
});
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, 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
);
}
});

View File

@@ -7,7 +7,6 @@ firefox-appdir = browser
[test_merinoClient_sessions.js]
[test_quicksuggest.js]
[test_quicksuggest_addons.js]
[test_quicksuggest_addResults.js]
[test_quicksuggest_bestMatch.js]
[test_quicksuggest_dynamicWikipedia.js]
[test_quicksuggest_impressionCaps.js]
@@ -19,5 +18,6 @@ firefox-appdir = browser
[test_quicksuggest_offlineDefault.js]
[test_quicksuggest_positionInSuggestions.js]
[test_quicksuggest_topPicks.js]
[test_suggestionsMap.js]
[test_weather.js]
[test_weather_keywords.js]

View File

@@ -146,27 +146,14 @@ urlbar:
fallbackPref: browser.urlbar.addons.featureGate
description: >-
Feature gate that controls whether all aspects of the addons suggestion
feature are exposed to the user. See also `addonsKeywordsMinimumLength`
and `addonsKeywordsMinimumLengthCap`.
addonsKeywordsMinimumLength:
feature are exposed to the user.
addonsShowLessFrequentlyCap:
type: int
description: >-
If defined and non-zero, the addons suggestion will be triggered only by
the query that has number of chars more than this variable.
However, if there is a value in browser.urlbar.addons.minKeywordLength,
it will take priority.
addonsKeywordsMinimumLengthCap:
type: int
description: >-
If defined and non-zero, the user will not be able to increment the
minimum keyword length beyond this value. e.g., if this value is 6, the
current minimum length is 5, and the user clicks "Show less frequently",
then the minimum length will be incremented to 6, the "Show less
frequently" command will be hidden, and the user can continue to trigger
the weather suggestion by typing 6 characters, but they will not be able
to increment the minimum length any further.
If not defines a cap, no cap will be used, and the user will be able to
increment the minimum length without any limit.
If defined and non-zero, this is the maximum number of times the user
will be able to click the "Show less frequently" command for addon
suggestions. If undefined or zero, the user will be able to click the
command without any limit.
addonsUITreatment:
type: string
enum: