summaryrefslogtreecommitdiffstats
path: root/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js')
-rw-r--r--browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js531
1 files changed, 531 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js
new file mode 100644
index 0000000000..29133a8579
--- /dev/null
+++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_pocket.js
@@ -0,0 +1,531 @@
+/* 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 Pocket quick suggest results.
+
+"use strict";
+
+const LOW_KEYWORD = "low one two";
+const HIGH_KEYWORD = "high three";
+
+const REMOTE_SETTINGS_DATA = [
+ {
+ type: "pocket-suggestions",
+ attachment: [
+ {
+ url: "https://example.com/pocket-0",
+ title: "Pocket Suggestion 0",
+ description: "Pocket description 0",
+ lowConfidenceKeywords: [LOW_KEYWORD, "how to low"],
+ highConfidenceKeywords: [HIGH_KEYWORD],
+ score: 0.25,
+ },
+ {
+ url: "https://example.com/pocket-1",
+ title: "Pocket Suggestion 1",
+ description: "Pocket description 1",
+ lowConfidenceKeywords: ["other low"],
+ highConfidenceKeywords: ["another high"],
+ score: 0.25,
+ },
+ ],
+ },
+];
+
+add_setup(async () => {
+ // Disable search suggestions so we don't hit the network.
+ Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
+
+ await QuickSuggestTestUtils.ensureQuickSuggestInit({
+ remoteSettingsRecords: REMOTE_SETTINGS_DATA,
+ prefs: [
+ ["suggest.quicksuggest.nonsponsored", true],
+ ["pocket.featureGate", true],
+ ],
+ });
+});
+
+add_task(async function telemetryType() {
+ Assert.equal(
+ QuickSuggest.getFeature("PocketSuggestions").getSuggestionTelemetryType({}),
+ "pocket",
+ "Telemetry type should be 'pocket'"
+ );
+});
+
+// When non-sponsored suggestions are disabled, Pocket suggestions should be
+// disabled.
+add_tasks_with_rust(async function nonsponsoredDisabled() {
+ // Disable sponsored suggestions. Pocket suggestions are non-sponsored, so
+ // doing this should not prevent them from being enabled.
+ UrlbarPrefs.set("suggest.quicksuggest.sponsored", false);
+
+ // First make sure the suggestion is added when non-sponsored suggestions are
+ // enabled.
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [makeExpectedResult({ searchString: LOW_KEYWORD })],
+ });
+
+ // Now disable them.
+ UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", false);
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ UrlbarPrefs.set("suggest.quicksuggest.nonsponsored", true);
+ UrlbarPrefs.clear("suggest.quicksuggest.sponsored");
+ await QuickSuggestTestUtils.forceSync();
+});
+
+// When Pocket-specific preferences are disabled, suggestions should not be
+// added.
+add_tasks_with_rust(async function pocketSpecificPrefsDisabled() {
+ const prefs = ["suggest.pocket", "pocket.featureGate"];
+ for (const pref of prefs) {
+ // First make sure the suggestion is added.
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [makeExpectedResult({ searchString: LOW_KEYWORD })],
+ });
+
+ // Now disable the pref.
+ UrlbarPrefs.set(pref, false);
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ // Revert.
+ UrlbarPrefs.set(pref, true);
+ await QuickSuggestTestUtils.forceSync();
+ }
+});
+
+// Check wheather the Pocket suggestions will be shown by the setup of Nimbus
+// variable.
+add_tasks_with_rust(async function nimbus() {
+ // Disable the fature gate.
+ UrlbarPrefs.set("pocket.featureGate", false);
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ // Enable by Nimbus.
+ const cleanUpNimbusEnable = await UrlbarTestUtils.initNimbusFeature({
+ pocketFeatureGate: true,
+ });
+ await QuickSuggestTestUtils.forceSync();
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [makeExpectedResult({ searchString: LOW_KEYWORD })],
+ });
+ await cleanUpNimbusEnable();
+
+ // Enable locally.
+ UrlbarPrefs.set("pocket.featureGate", true);
+ await QuickSuggestTestUtils.forceSync();
+
+ // Disable by Nimbus.
+ const cleanUpNimbusDisable = await UrlbarTestUtils.initNimbusFeature({
+ pocketFeatureGate: false,
+ });
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+ await cleanUpNimbusDisable();
+
+ // Revert.
+ UrlbarPrefs.set("pocket.featureGate", true);
+ await QuickSuggestTestUtils.forceSync();
+});
+
+// The suggestion should be shown as a top pick when a high-confidence keyword
+// is matched.
+add_tasks_with_rust(async function topPick() {
+ await check_results({
+ context: createContext(HIGH_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [
+ makeExpectedResult({ searchString: HIGH_KEYWORD, isTopPick: true }),
+ ],
+ });
+});
+
+// Low-confidence keywords should do prefix matching starting at the first word.
+add_tasks_with_rust(async function lowPrefixes() {
+ // search string -> should match
+ let tests = {
+ l: false,
+ lo: false,
+ low: true,
+ "low ": true,
+ "low o": true,
+ "low on": true,
+ "low one": true,
+ "low one ": true,
+ "low one t": true,
+ "low one tw": true,
+ "low one two": true,
+ "low one two ": false,
+ };
+ for (let [searchString, shouldMatch] of Object.entries(tests)) {
+ info("Doing search: " + JSON.stringify({ searchString, shouldMatch }));
+ await check_results({
+ context: createContext(searchString, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: shouldMatch
+ ? [makeExpectedResult({ searchString, fullKeyword: LOW_KEYWORD })]
+ : [],
+ });
+ }
+});
+
+// Low-confidence keywords that start with "how to" should do prefix matching
+// starting at "how to" instead of the first word.
+//
+// Note: The Rust implementation doesn't support this.
+add_task(
+ {
+ skip_if: () => UrlbarPrefs.get("quickSuggestRustEnabled"),
+ },
+ async function lowPrefixes_howTo() {
+ // search string -> should match
+ let tests = {
+ h: false,
+ ho: false,
+ how: false,
+ "how ": false,
+ "how t": false,
+ "how to": true,
+ "how to ": true,
+ "how to l": true,
+ "how to lo": true,
+ "how to low": true,
+ };
+ for (let [searchString, shouldMatch] of Object.entries(tests)) {
+ info("Doing search: " + JSON.stringify({ searchString, shouldMatch }));
+ await check_results({
+ context: createContext(searchString, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: shouldMatch
+ ? [makeExpectedResult({ searchString, fullKeyword: "how to low" })]
+ : [],
+ });
+ }
+ }
+);
+
+// High-confidence keywords should not do prefix matching at all.
+add_tasks_with_rust(async function highPrefixes() {
+ // search string -> should match
+ let tests = {
+ h: false,
+ hi: false,
+ hig: false,
+ high: false,
+ "high ": false,
+ "high t": false,
+ "high th": false,
+ "high thr": false,
+ "high thre": false,
+ "high three": true,
+ "high three ": false,
+ };
+ for (let [searchString, shouldMatch] of Object.entries(tests)) {
+ info("Doing search: " + JSON.stringify({ searchString, shouldMatch }));
+ await check_results({
+ context: createContext(searchString, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: shouldMatch
+ ? [
+ makeExpectedResult({
+ searchString,
+ fullKeyword: HIGH_KEYWORD,
+ isTopPick: true,
+ }),
+ ]
+ : [],
+ });
+ }
+});
+
+// Keyword matching should be case insenstive.
+add_tasks_with_rust(async function uppercase() {
+ await check_results({
+ context: createContext(LOW_KEYWORD.toUpperCase(), {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [
+ makeExpectedResult({
+ searchString: LOW_KEYWORD.toUpperCase(),
+ fullKeyword: LOW_KEYWORD,
+ }),
+ ],
+ });
+ await check_results({
+ context: createContext(HIGH_KEYWORD.toUpperCase(), {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [
+ makeExpectedResult({
+ searchString: HIGH_KEYWORD.toUpperCase(),
+ fullKeyword: HIGH_KEYWORD,
+ isTopPick: true,
+ }),
+ ],
+ });
+});
+
+// Tests the "Not relevant" command: a dismissed suggestion shouldn't be added.
+add_tasks_with_rust(async function notRelevant() {
+ let result = makeExpectedResult({ searchString: LOW_KEYWORD });
+
+ info("Triggering the 'Not relevant' command");
+ QuickSuggest.getFeature("PocketSuggestions").handleCommand(
+ {
+ controller: { removeResult() {} },
+ },
+ result,
+ "not_relevant"
+ );
+ await QuickSuggest.blockedSuggestions._test_readyPromise;
+
+ Assert.ok(
+ await QuickSuggest.blockedSuggestions.has(result.payload.originalUrl),
+ "The result's URL should be blocked"
+ );
+
+ info("Doing search for blocked suggestion");
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ info("Doing search for blocked suggestion using high-confidence keyword");
+ await check_results({
+ context: createContext(HIGH_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ info("Doing search for a suggestion that wasn't blocked");
+ await check_results({
+ context: createContext("other low", {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [
+ makeExpectedResult({
+ searchString: "other low",
+ suggestion: REMOTE_SETTINGS_DATA[0].attachment[1],
+ }),
+ ],
+ });
+
+ info("Clearing blocked suggestions");
+ await QuickSuggest.blockedSuggestions.clear();
+
+ info("Doing search for unblocked suggestion");
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [result],
+ });
+});
+
+// Tests the "Not interested" command: all Pocket suggestions should be disabled
+// and not added anymore.
+add_tasks_with_rust(async function notInterested() {
+ let result = makeExpectedResult({ searchString: LOW_KEYWORD });
+
+ info("Triggering the 'Not interested' command");
+ QuickSuggest.getFeature("PocketSuggestions").handleCommand(
+ {
+ controller: { removeResult() {} },
+ },
+ result,
+ "not_interested"
+ );
+
+ Assert.ok(
+ !UrlbarPrefs.get("suggest.pocket"),
+ "Pocket suggestions should be disabled"
+ );
+
+ info("Doing search for the suggestion the command was used on");
+ await check_results({
+ context: createContext(LOW_KEYWORD, {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ info("Doing search for another Pocket suggestion");
+ await check_results({
+ context: createContext("other low", {
+ providers: [UrlbarProviderQuickSuggest.name],
+ isPrivate: false,
+ }),
+ matches: [],
+ });
+
+ UrlbarPrefs.clear("suggest.pocket");
+ await QuickSuggestTestUtils.forceSync();
+});
+
+// Tests the "show less frequently" behavior.
+add_tasks_with_rust(async function showLessFrequently() {
+ await doShowLessFrequentlyTests({
+ feature: QuickSuggest.getFeature("PocketSuggestions"),
+ showLessFrequentlyCountPref: "pocket.showLessFrequentlyCount",
+ nimbusCapVariable: "pocketShowLessFrequentlyCap",
+ expectedResult: searchString =>
+ makeExpectedResult({ searchString, fullKeyword: LOW_KEYWORD }),
+ keyword: LOW_KEYWORD,
+ });
+});
+
+// The `Pocket` Rust provider should be passed to the Rust component when
+// querying depending on whether Pocket suggestions are enabled.
+add_task(async function rustProviders() {
+ // TODO bug 1874074: The Rust component fetches Pocket suggestions when the
+ // AMO provider is specified regardless of whether the Pocket provider is
+ // specified. AMO suggestions are enabled by default, so disable them first so
+ // that the Rust backend does not pass in the AMO provider.
+ UrlbarPrefs.set("suggest.addons", false);
+
+ await doRustProvidersTests({
+ searchString: LOW_KEYWORD,
+ tests: [
+ {
+ prefs: {
+ "suggest.pocket": true,
+ },
+ expectedUrls: ["https://example.com/pocket-0"],
+ },
+ {
+ prefs: {
+ "suggest.pocket": false,
+ },
+ expectedUrls: [],
+ },
+ ],
+ });
+
+ UrlbarPrefs.clear("suggest.addons");
+ UrlbarPrefs.clear("suggest.pocket");
+ await QuickSuggestTestUtils.forceSync();
+});
+
+function makeExpectedResult({
+ searchString,
+ fullKeyword = searchString,
+ suggestion = REMOTE_SETTINGS_DATA[0].attachment[0],
+ source = "remote-settings",
+ isTopPick = false,
+} = {}) {
+ if (
+ source == "remote-settings" &&
+ UrlbarPrefs.get("quicksuggest.rustEnabled")
+ ) {
+ source = "rust";
+ }
+
+ let provider;
+ let keywordSubstringNotTyped = fullKeyword.substring(searchString.length);
+ let description = suggestion.description;
+ switch (source) {
+ case "remote-settings":
+ provider = "PocketSuggestions";
+ break;
+ case "rust":
+ provider = "Pocket";
+ // Rust suggestions currently do not include full keyword or description.
+ keywordSubstringNotTyped = "";
+ description = suggestion.title;
+ break;
+ case "merino":
+ provider = "pocket";
+ break;
+ }
+
+ let url = new URL(suggestion.url);
+ url.searchParams.set("utm_medium", "firefox-desktop");
+ url.searchParams.set("utm_source", "firefox-suggest");
+ url.searchParams.set("utm_campaign", "pocket-collections-in-the-address-bar");
+ url.searchParams.set("utm_content", "treatment");
+
+ return {
+ isBestMatch: isTopPick,
+ suggestedIndex: isTopPick ? 1 : -1,
+ type: UrlbarUtils.RESULT_TYPE.URL,
+ source: UrlbarUtils.RESULT_SOURCE.OTHER_NETWORK,
+ heuristic: false,
+ payload: {
+ source,
+ provider,
+ telemetryType: "pocket",
+ title: suggestion.title,
+ url: url.href,
+ displayUrl: url.href.replace(/^https:\/\//, ""),
+ originalUrl: suggestion.url,
+ description: isTopPick ? description : "",
+ icon: isTopPick
+ ? "chrome://global/skin/icons/pocket.svg"
+ : "chrome://global/skin/icons/pocket-favicon.ico",
+ helpUrl: QuickSuggest.HELP_URL,
+ shouldShowUrl: true,
+ bottomTextL10n: {
+ id: "firefox-suggest-pocket-bottom-text",
+ args: {
+ keywordSubstringTyped: searchString,
+ keywordSubstringNotTyped,
+ },
+ },
+ },
+ };
+}