Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test runs only with pattern: os != 'android'
- Manifest: browser/components/urlbar/tests/quicksuggest/unit/xpcshell.toml
/* 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 Merino integration with UrlbarProviderQuickSuggest.
"use strict";
ChromeUtils.defineESModuleGetters(this, {
AmpSuggestions:
"moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs",
});
const SEARCH_STRING = "frab";
const { DEFAULT_SUGGESTION_SCORE } = UrlbarProviderQuickSuggest;
const { TIMESTAMP_TEMPLATE } = AmpSuggestions;
const REMOTE_SETTINGS_RESULTS = [
QuickSuggestTestUtils.ampRemoteSettings({
keywords: [SEARCH_STRING],
}),
];
const EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT = QuickSuggestTestUtils.ampResult({
keyword: SEARCH_STRING,
suggestedIndex: -1,
});
const EXPECTED_MERINO_URLBAR_RESULT = QuickSuggestTestUtils.ampResult({
source: "merino",
provider: "adm",
requestId: "request_id",
suggestedIndex: -1,
});
add_setup(async () => {
await MerinoTestUtils.server.start();
// Set up the remote settings client with the test data.
await QuickSuggestTestUtils.ensureQuickSuggestInit({
prefs: [
["suggest.quicksuggest.all", true],
["suggest.quicksuggest.sponsored", true],
["quicksuggest.ampTopPickCharThreshold", 0],
],
});
await resetRemoteSettingsData();
Assert.equal(
typeof DEFAULT_SUGGESTION_SCORE,
"number",
"Sanity check: DEFAULT_SUGGESTION_SCORE is defined"
);
});
// Tests with the Merino endpoint URL set to an empty string, which disables
// fetching from Merino.
add_task(async function merinoDisabled() {
let mockEndpointUrl = UrlbarPrefs.get("merino.endpointURL");
UrlbarPrefs.set("merino.endpointURL", "");
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// Clear the remote settings suggestions so that if Merino is actually queried
// -- which would be a bug -- we don't accidentally mask the Merino suggestion
// by also matching an RS suggestion with the same or higher score.
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [],
});
UrlbarPrefs.set("merino.endpointURL", mockEndpointUrl);
await resetRemoteSettingsData();
});
// Results should be fetched from Merino only when online is both available and
// enabled.
add_task(async function onlineAvailableAndEnabled() {
// Clear the remote settings suggestions so that if Merino is actually queried
// -- which would be a bug -- we don't accidentally mask the Merino suggestion
// by also matching an RS suggestion with the same or higher score.
await QuickSuggestTestUtils.setRemoteSettingsRecords([]);
for (let onlineAvailable of [false, true]) {
for (let onlineEnabled of [false, true]) {
UrlbarPrefs.set("quicksuggest.online.available", onlineAvailable);
UrlbarPrefs.set("quicksuggest.online.enabled", onlineEnabled);
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches:
onlineAvailable && onlineEnabled
? [EXPECTED_MERINO_URLBAR_RESULT]
: [],
});
}
}
await resetRemoteSettingsData();
});
// When the Merino suggestion has a higher score than the remote settings
// suggestion, the Merino suggestion should be used.
add_task(async function higherScore() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions[0].score =
2 * DEFAULT_SUGGESTION_SCORE;
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [EXPECTED_MERINO_URLBAR_RESULT],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// When the Merino suggestion has a lower score than the remote settings
// suggestion, the remote settings suggestion should be used.
add_task(async function lowerScore() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions[0].score =
DEFAULT_SUGGESTION_SCORE / 2;
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// When remote settings doesn't return a suggestion but Merino does, the Merino
// suggestion should be used.
add_task(async function noSuggestion_remoteSettings() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
let context = createContext("this doesn't match remote settings", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [EXPECTED_MERINO_URLBAR_RESULT],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// When Merino doesn't return a suggestion but remote settings does, the remote
// settings suggestion should be used.
add_task(async function noSuggestion_merino() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions = [];
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// When Merino returns multiple suggestions, the one with the largest score
// should be used.
add_task(async function multipleMerinoSuggestions() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions = [
{
provider: "adm",
full_keyword: "multipleMerinoSuggestions 0 full_keyword",
title: "multipleMerinoSuggestions 0 title",
url: "multipleMerinoSuggestions 0 url",
icon: "multipleMerinoSuggestions 0 icon",
impression_url: "multipleMerinoSuggestions 0 impression_url",
click_url: "multipleMerinoSuggestions 0 click_url",
block_id: 0,
advertiser: "multipleMerinoSuggestions 0 advertiser",
iab_category: "22 - Shopping",
is_sponsored: true,
score: 0.1,
},
{
provider: "adm",
full_keyword: "multipleMerinoSuggestions 1 full_keyword",
title: "multipleMerinoSuggestions 1 title",
url: "multipleMerinoSuggestions 1 url",
icon: "multipleMerinoSuggestions 1 icon",
impression_url: "multipleMerinoSuggestions 1 impression_url",
click_url: "multipleMerinoSuggestions 1 click_url",
block_id: 1,
advertiser: "multipleMerinoSuggestions 1 advertiser",
iab_category: "22 - Shopping",
is_sponsored: true,
score: 1,
},
{
provider: "adm",
full_keyword: "multipleMerinoSuggestions 2 full_keyword",
title: "multipleMerinoSuggestions 2 title",
url: "multipleMerinoSuggestions 2 url",
icon: "multipleMerinoSuggestions 2 icon",
impression_url: "multipleMerinoSuggestions 2 impression_url",
click_url: "multipleMerinoSuggestions 2 click_url",
block_id: 2,
advertiser: "multipleMerinoSuggestions 2 advertiser",
iab_category: "22 - Shopping",
is_sponsored: true,
score: 0.2,
},
];
let context = createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [
QuickSuggestTestUtils.ampResult({
keyword: "multipleMerinoSuggestions 1 full_keyword",
title: "multipleMerinoSuggestions 1 title",
url: "multipleMerinoSuggestions 1 url",
originalUrl: "multipleMerinoSuggestions 1 url",
icon: "multipleMerinoSuggestions 1 icon",
impressionUrl: "multipleMerinoSuggestions 1 impression_url",
clickUrl: "multipleMerinoSuggestions 1 click_url",
blockId: 1,
advertiser: "multipleMerinoSuggestions 1 advertiser",
requestId: "request_id",
source: "merino",
provider: "adm",
suggestedIndex: -1,
}),
],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Timestamp templates in URLs should be replaced with real timestamps.
add_task(async function timestamps() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// Set up the Merino response with template URLs.
let suggestion = MerinoTestUtils.server.response.body.suggestions[0];
suggestion.url = `http://example.com/time-${TIMESTAMP_TEMPLATE}`;
suggestion.click_url = `http://example.com/time-${TIMESTAMP_TEMPLATE}-foo`;
// Do a search.
let context = createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
let controller = UrlbarTestUtils.newMockController({
input: {
isPrivate: context.isPrivate,
onFirstResult() {
return false;
},
getSearchSource() {
return "dummy-search-source";
},
window: {
location: {
href: AppConstants.BROWSER_CHROME_URL,
},
},
},
});
await controller.startQuery(context);
// Should be one quick suggest result.
Assert.equal(context.results.length, 1, "One result returned");
let result = context.results[0];
QuickSuggestTestUtils.assertTimestampsReplaced(result, {
url: suggestion.click_url,
sponsoredClickUrl: suggestion.click_url,
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests dismissals of managed Merino suggestions (suggestions that are managed
// by a `SuggestFeature`).
add_task(async function dismissals_managed() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// Set up a single Merino AMP suggestion with a unique URL.
let url = "https://example.com/merino-amp-url";
MerinoTestUtils.server.response =
MerinoTestUtils.server.makeDefaultResponse();
MerinoTestUtils.server.response.body.suggestions[0].url = url;
let expectedMerinoResult = QuickSuggestTestUtils.ampResult({
url,
source: "merino",
provider: "adm",
requestId: "request_id",
suggestedIndex: -1,
});
// Do a search. The Merino suggestion should be matched.
const context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [expectedMerinoResult],
});
let result = context.results[0];
Assert.ok(
QuickSuggest.getFeatureByResult(result),
"Sanity check: The actual result should be managed by a feature"
);
// Dismiss the Merino result.
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result"
);
// Do another search. The remote settings suggestion should now be matched.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT],
});
// Clear dismissals.
await QuickSuggest.clearDismissedSuggestions();
Assert.ok(
!(await QuickSuggest.isResultDismissed(result)),
"isResultDismissed should return false after clearing dismissals"
);
// The Merino suggestion should be matched again.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [expectedMerinoResult],
});
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests dismissals of Merino AMP suggestions, which have special handling
// around their dismissal keys.
add_task(async function dismissals_amp() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
UrlbarPrefs.set("suggest.quicksuggest.all", true);
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
await QuickSuggestTestUtils.forceSync();
let tests = [
{
suggestion: {
url: "https://example.com/0",
},
},
},
{
suggestion: {
url: `https://example.com/1-${TIMESTAMP_TEMPLATE}`,
},
expected: {
// dismissal key should be the original `url` value with the timestamp
// template
dismissalKey: `https://example.com/1-${TIMESTAMP_TEMPLATE}`,
},
},
{
},
expected: {
// dismissal key should be the `url` value
dismissalKey: "https://example.com/2",
notDismissalKeys: ["full keyword 2"],
},
},
{
},
expected: {
// dismissal key should be the `url` value
dismissalKey: `https://example.com/3-${TIMESTAMP_TEMPLATE}`,
notDismissalKeys: ["full keyword 3"],
},
},
{
},
expected: {
// dismissal key should be the `dismissal_key` value
dismissalKey: "4-dismissal-key",
notDismissalKeys: ["https://example.com/4"],
},
},
{
},
expected: {
// dismissal key should be the `dismissal_key` value
dismissalKey: "5-dismissal-key",
notDismissalKeys: [`https://example.com/5-${TIMESTAMP_TEMPLATE}`],
},
},
{
suggestion: {
url: "https://example.com/6",
full_keyword: "full keyword 6",
dismissal_key: "6-dismissal-key",
},
expected: {
// dismissal key should be the `dismissal_key` value
dismissalKey: "6-dismissal-key",
notDismissalKeys: ["full keyword 6", "https://example.com/6"],
},
},
{
suggestion: {
url: `https://example.com/7-${TIMESTAMP_TEMPLATE}`,
full_keyword: "full keyword 7",
dismissal_key: "7-dismissal-key",
},
expected: {
// dismissal key should be the `dismissal_key` value
dismissalKey: "7-dismissal-key",
notDismissalKeys: [
"full keyword 7",
`https://example.com/7-${TIMESTAMP_TEMPLATE}`,
],
},
},
];
for (let test of tests) {
info("Doing subtest: " + JSON.stringify(test));
let { suggestion, expected } = test;
suggestion = {
provider: "adm",
title: "title",
icon: null,
impression_url: "https://example.com/impression",
click_url: "https://example.com/click",
block_id: 1,
advertiser: "advertiser",
iab_category: "22 - Shopping",
is_sponsored: true,
request_id: "request_id",
score: 1,
...suggestion,
};
MerinoTestUtils.server.response =
MerinoTestUtils.server.makeDefaultResponse();
MerinoTestUtils.server.response.body.suggestions = [suggestion];
let expectedResult = QuickSuggestTestUtils.ampResult({
suggestedIndex: -1,
provider: suggestion.provider,
title: suggestion.title,
fullKeyword: suggestion.full_keyword,
url: suggestion.url,
originalUrl: suggestion.original_url || suggestion.url,
dismissalKey: suggestion.dismissal_key,
requestId: suggestion.request_id,
impressionUrl: suggestion.impression_url,
clickUrl: suggestion.click_url,
blockId: suggestion.block_id,
advertiser: suggestion.advertiser,
iabCategory: suggestion.iab_category,
source: "merino",
});
if (!suggestion.full_keyword) {
delete expectedResult.payload.title;
}
// Do a search. The Merino suggestion should be matched.
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [expectedResult],
// Ignore values related to the timestamp template. They're not important
// for this test.
conditionalPayloadProperties: {
url: { ignore: true },
urlTimestampIndex: { ignore: true },
},
});
let result = context.results[0];
Assert.equal(
QuickSuggest.getFeatureByResult(result)?.name,
"AmpSuggestions",
"Sanity check: The actual result should be managed by AmpSuggestions"
);
// Dismiss the Merino result.
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result"
);
Assert.ok(
await QuickSuggest.rustBackend.isDismissedByKey(expected.dismissalKey),
"isDismissedByKey should return true after dismissing result"
);
if (expected.notDismissalKeys) {
for (let value of expected.notDismissalKeys) {
Assert.ok(
!(await QuickSuggest.rustBackend.isDismissedByKey(value)),
"isDismissedByKey should return false for notDismissalKey: " + value
);
}
}
// Do another search. The remote settings suggestion should now be matched.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT],
});
// Clear dismissals.
await QuickSuggest.clearDismissedSuggestions();
Assert.ok(
!(await QuickSuggest.isResultDismissed(result)),
"isResultDismissed should return false after clearing dismissals"
);
// The Merino suggestion should be matched again.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [expectedResult],
conditionalPayloadProperties: {
url: { ignore: true },
urlTimestampIndex: { ignore: true },
},
});
}
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests dismissals of unmanaged Merino suggestions (suggestions that are not
// managed by a `SuggestFeature`).
add_task(async function dismissals_unmanaged_1() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// The "top_picks" provider is the only supported unmanaged suggestion.
let provider = "top_picks";
let tests = [
{
},
},
},
{
suggestion: {
provider,
url: "https://example.com/1",
original_url: "https://example.com/1-original-url",
score: 1,
},
expected: {
// dismissal key should be the `original_url` value
dismissalKey: "https://example.com/1-original-url",
notDismissalKeys: ["https://example.com/1"],
},
},
{
suggestion: {
provider,
url: "https://example.com/2",
original_url: "https://example.com/2-original-url",
dismissal_key: "2-dismissal-key",
score: 1,
},
expected: {
// dismissal key should be the `dismissal_key` value
dismissalKey: "2-dismissal-key",
notDismissalKeys: [
],
},
},
];
for (let test of tests) {
info("Doing subtest: " + JSON.stringify(test));
let { suggestion, expected } = test;
MerinoTestUtils.server.response =
MerinoTestUtils.server.makeDefaultResponse();
MerinoTestUtils.server.response.body.suggestions = [suggestion];
let expectedResult = {
type: UrlbarUtils.RESULT_TYPE.URL,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
heuristic: false,
payload: {
provider,
url: suggestion.url,
originalUrl: suggestion.original_url,
dismissalKey: suggestion.dismissal_key,
source: "merino",
isSponsored: false,
shouldShowUrl: true,
isBlockable: true,
isManageable: true,
telemetryType: provider,
},
};
// Do a search. The Merino suggestion should be matched.
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [expectedResult],
});
let result = context.results[0];
Assert.ok(
!QuickSuggest.getFeatureByResult(result),
"Sanity check: The actual result should not be managed by a feature"
);
// Dismiss the Merino result.
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result"
);
Assert.ok(
await QuickSuggest.rustBackend.isDismissedByKey(expected.dismissalKey),
"isDismissedByKey should return true after dismissing result"
);
if (expected.notDismissalKeys) {
for (let value of expected.notDismissalKeys) {
Assert.ok(
!(await QuickSuggest.rustBackend.isDismissedByKey(value)),
"isDismissedByKey should return false for notDismissalKey: " + value
);
}
}
// Do another search. The remote settings suggestion should now be matched.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [EXPECTED_REMOTE_SETTINGS_URLBAR_RESULT],
});
// Clear dismissals.
await QuickSuggest.clearDismissedSuggestions();
Assert.ok(
!(await QuickSuggest.isResultDismissed(result)),
"isResultDismissed should return false after clearing dismissals"
);
// The Merino suggestion should be matched again.
await check_results({
context: createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [expectedResult],
});
}
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests dismissals of unmanaged Merino suggestions (suggestions that are not
// managed by a `SuggestFeature`) that all have the same URL but different
// original URLs and dismissal keys.
add_task(async function dismissals_unmanaged_2() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// The "top_picks" provider is the only supported unmanaged suggestion.
let provider = "top_picks";
MerinoTestUtils.server.response =
MerinoTestUtils.server.makeDefaultResponse();
MerinoTestUtils.server.response.body.suggestions = [
// all three: url, original_url, dismissal_key
{
provider,
url: "https://example.com/url",
original_url: "https://example.com/original_url",
dismissal_key: "dismissal-key",
score: 1.0,
},
// two: url, original_url
{
provider,
url: "https://example.com/url",
original_url: "https://example.com/original_url",
score: 0.9,
},
// only one: url
{
provider,
url: "https://example.com/url",
score: 0.8,
},
];
let expectedBaseResult = {
type: UrlbarUtils.RESULT_TYPE.URL,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
heuristic: false,
payload: {
provider,
url: "https://example.com/url",
source: "merino",
isSponsored: false,
shouldShowUrl: true,
isBlockable: true,
isManageable: true,
telemetryType: provider,
},
};
// Do a search. The first Merino suggestion should be matched.
info("Doing search 1");
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [
{
...expectedBaseResult,
payload: {
...expectedBaseResult.payload,
originalUrl: "https://example.com/original_url",
dismissalKey: "dismissal-key",
},
},
],
});
let result = context.results[0];
Assert.ok(
!QuickSuggest.getFeatureByResult(result),
"Sanity check: The actual result should not be managed by a feature"
);
// Dismiss it.
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result 1"
);
Assert.ok(
await QuickSuggest.rustBackend.isDismissedByKey("dismissal-key"),
"isDismissedByKey should return true after dismissing suggestion 1"
);
for (let value of [
]) {
Assert.ok(
!(await QuickSuggest.rustBackend.isDismissedByKey(value)),
"isDismissedByKey should return false after dismissing suggestion 1: " +
value
);
}
// Do another search. The second suggestion should be matched.
info("Doing search 2");
context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [
{
...expectedBaseResult,
payload: {
...expectedBaseResult.payload,
originalUrl: "https://example.com/original_url",
// no dismissal_key
},
},
],
});
// Dismiss it.
result = context.results[0];
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result 2"
);
for (let value of ["dismissal-key", "https://example.com/original_url"]) {
Assert.ok(
await QuickSuggest.rustBackend.isDismissedByKey(value),
"isDismissedByKey should return true after dismissing suggestion 2: " +
value
);
}
Assert.ok(
!(await QuickSuggest.rustBackend.isDismissedByKey(
)),
"isDismissedByKey should return false after dismissing suggestion 2"
);
// Do another search. The third suggestion should be matched.
info("Doing search 3");
context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [
// no dismissal_key or original_url
expectedBaseResult,
],
});
// Dismiss it.
result = context.results[0];
await QuickSuggest.dismissResult(result);
Assert.ok(
await QuickSuggest.isResultDismissed(result),
"isResultDismissed should return true after dismissing result 3"
);
for (let value of [
"dismissal-key",
]) {
Assert.ok(
await QuickSuggest.rustBackend.isDismissedByKey(value),
"isDismissedByKey should return true after dismissing suggestion 3: " +
value
);
}
await QuickSuggest.clearDismissedSuggestions();
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests a Merino suggestion that is a top pick/best match.
add_task(async function bestMatch() {
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
// Set up a suggestion with `is_top_pick` and the "top_picks" provider so that
// UrlbarProviderQuickSuggest will make a default result for it.
let provider = "top_picks";
MerinoTestUtils.server.response.body.suggestions = [
{
is_top_pick: true,
provider,
full_keyword: "full_keyword",
title: "title",
url: "url",
icon: null,
score: 1,
},
];
let context = createContext(SEARCH_STRING, {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [
{
isBestMatch: true,
type: UrlbarUtils.RESULT_TYPE.URL,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
heuristic: false,
payload: {
telemetryType: provider,
title: "full_keyword — title",
url: "url",
icon: null,
isSponsored: false,
isBlockable: true,
isManageable: true,
source: "merino",
provider,
},
},
],
});
// This isn't necessary since `check_results()` checks `isBestMatch`, but
// check it here explicitly for good measure.
Assert.ok(context.results[0].isBestMatch, "Result is a best match");
MerinoTestUtils.server.reset();
merinoClient().resetSession();
});
// Tests a sponsored suggestion that isn't managed by a feature. When the `all`
// pref is disabled, a result for the suggestion should not be added.
add_task(async function unmanaged_sponsored_allDisabled() {
await doUnmanagedTest({
pref: "suggest.quicksuggest.all",
suggestion: {
title: "Sponsored without feature",
is_sponsored: true,
},
shouldBeAdded: false,
});
});
// Tests a sponsored suggestion that isn't managed by a feature. When the
// sponsored pref is disabled, a result for the suggestion should not be added.
add_task(async function unmanaged_sponsored_sponsoredDisabled() {
await doUnmanagedTest({
pref: "suggest.quicksuggest.sponsored",
suggestion: {
title: "Sponsored without feature",
is_sponsored: true,
},
shouldBeAdded: false,
});
});
// Tests a nonsponsored suggestion that isn't managed by a feature. When the
// `all` pref is disabled, a result for the suggestion should not be added.
add_task(async function unmanaged_nonsponsored_allDisabled() {
await doUnmanagedTest({
pref: "suggest.quicksuggest.all",
suggestion: {
title: "Nonsponsored without feature",
// no is_sponsored
},
shouldBeAdded: false,
});
});
// Tests a nonsponsored suggestion that isn't managed by a feature. When the
// `all` pref is enabled and the sponsored pref is disabled, a result for the
// suggestion should be added.
add_task(async function unmanaged_nonsponsored_sponsoredDisabled() {
await doUnmanagedTest({
pref: "suggest.quicksuggest.sponsored",
suggestion: {
title: "Nonsponsored without feature",
// no is_sponsored
},
shouldBeAdded: true,
});
});
async function doUnmanagedTest({ pref, suggestion, shouldBeAdded }) {
// The "top_picks" provider is the only supported unmanaged suggestion.
suggestion.provider = "top_picks";
UrlbarPrefs.set("suggest.quicksuggest.all", true);
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
await QuickSuggestTestUtils.forceSync();
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions = [suggestion];
let expectedResult = {
type: UrlbarUtils.RESULT_TYPE.URL,
source: UrlbarUtils.RESULT_SOURCE.SEARCH,
heuristic: false,
payload: {
title: suggestion.title,
url: suggestion.url,
provider: suggestion.provider,
telemetryType: suggestion.provider,
isSponsored: !!suggestion.is_sponsored,
source: "merino",
isBlockable: true,
isManageable: true,
shouldShowUrl: true,
},
};
// Do an initial search. The `all` pref and sponsored suggestions are both
// enabled, so the suggestion should be matched.
info("Doing search 1");
await check_results({
context: createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [expectedResult],
});
// Set the passed-in pref to false and do another search. The suggestion
// should be matched as expected.
UrlbarPrefs.set(pref, false);
await QuickSuggestTestUtils.forceSync();
info("Doing search 2");
await check_results({
context: createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: shouldBeAdded ? [expectedResult] : [],
});
// Flip the pref back to true and do a third search.
UrlbarPrefs.set(pref, true);
await QuickSuggestTestUtils.forceSync();
info("Doing search 3");
let context = createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
});
await check_results({
context,
matches: [expectedResult],
});
// Trigger the dismiss command on the result.
let dismissalPromise = TestUtils.topicObserved(
"quicksuggest-dismissals-changed"
);
let providersManager = ProvidersManager.getInstanceForSap("urlbar");
triggerCommand({
feature: providersManager.getProvider(UrlbarProviderQuickSuggest.name),
command: "dismiss",
result: context.results[0],
expectedCountsByCall: {
removeResult: 1,
},
});
await dismissalPromise;
Assert.ok(
await QuickSuggest.isResultDismissed(context.results[0]),
"The result should be dismissed"
);
await QuickSuggest.clearDismissedSuggestions();
MerinoTestUtils.server.reset();
merinoClient().resetSession();
}
// An unmanaged suggestion with an unrecognized Merino provider (i.e., not
// "top_picks") should not be added.
add_task(async function unmanaged_unrecognized() {
UrlbarPrefs.set("suggest.quicksuggest.all", true);
UrlbarPrefs.set("suggest.quicksuggest.sponsored", true);
await QuickSuggestTestUtils.forceSync();
UrlbarPrefs.set("quicksuggest.online.available", true);
UrlbarPrefs.set("quicksuggest.online.enabled", true);
MerinoTestUtils.server.response.body.suggestions = [
{
title: "Some unrecognized suggestion",
provider: "unmanaged-unrecognized-provider",
},
];
await check_results({
context: createContext("test", {
providers: [UrlbarProviderQuickSuggest.name],
isPrivate: false,
}),
matches: [],
});
});
function merinoClient() {
return QuickSuggest.getFeature("SuggestBackendMerino")?.client;
}
async function resetRemoteSettingsData() {
await QuickSuggestTestUtils.setRemoteSettingsRecords([
{
collection: QuickSuggestTestUtils.RS_COLLECTION.AMP,
type: QuickSuggestTestUtils.RS_TYPE.AMP,
attachment: REMOTE_SETTINGS_RESULTS,
},
]);
}