/* 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 for AMP suggestions. "use strict"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { AmpMatchingStrategy: "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs", AmpSuggestions: "moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs", SuggestionProvider: "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustSuggest.sys.mjs", }); add_setup(async function init() { UrlbarPrefs.set("maxRichResults", 10); let engine = await addTestSuggestionsEngine(); await SearchService.setDefault(engine, SearchService.CHANGE_REASON.UNKNOWN); await QuickSuggestTestUtils.ensureQuickSuggestInit(); }); add_task(async function telemetryType() { await doTelemetryTypeTest({ feature: "AmpSuggestions", tests: [ { source: "rust", expected: "adm_sponsored", }, { source: "merino", expected: "adm_sponsored", }, ], }); }); add_task(async function prefs() { let context = createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }); await doResultCheckTest({ env: { remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { description: "Enable all and sponsored", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], context, expected: [QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 })], }, { description: "Enable all, disable sponsored", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", false], ], context, expected: [], }, { description: "Disable all, enable sponsored", prefs: [ ["suggest.quicksuggest.all", false], ["suggest.quicksuggest.sponsored", true], ], context, expected: [], }, { description: "Disable all and sponsored", prefs: [ ["suggest.quicksuggest.all", false], ["suggest.quicksuggest.sponsored", false], ], context, expected: [], }, { description: "browser.search.suggest.enabled is irrelevant", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["browser.search.suggest.enabled", false], ], context, expected: [QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 })], }, { description: "suggest.searches is irrelevant", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["suggest.searches", false], ], context, expected: [QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 })], }, { description: "Feature gate is off", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["amp.featureGate", false], ], context, expected: [], }, { description: "Local feature switch is off", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["suggest.amp", false], ], context, expected: [], }, ], }); }); add_task(async function keyword() { await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { description: "Case insentive", context: createContext("AMp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 })], }, { description: "Case insentive and leading spaces", context: createContext(" aMP", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 })], }, { description: "Empty string", context: createContext("", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, { description: "A space", context: createContext(" ", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, { description: "Some spaces", context: createContext(" ", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, ], }); }); add_task(async function privateContext() { await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { prefs: [["browser.search.suggest.enabled.private", true]], context: createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: true, }), expected: [], }, { prefs: [["browser.search.suggest.enabled.private", false]], context: createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: true, }), expected: [], }, ], }); }); add_task(async function showSearchSuggestionsFirst() { let context = createContext("amp", { isPrivate: false }); // Add some history that will match our query below. let histories = Array.from( { length: UrlbarPrefs.get("maxRichResults") }, (_, i) => `http://example.com/amp/${i}` ); let historyResults = histories .map(url => makeVisitResult(context, { uri: url, title: "test visit for " + url, }) ) .reverse() .slice(0, histories.length - 4); await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["browser.search.suggest.enabled", true], ["suggest.searches", true], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { description: "When search suggestions come before general results and the only general result is a quick suggest result, it should come last.", prefs: [["showSearchSuggestionsFirst", true]], context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), QuickSuggestTestUtils.ampResult(), ], }, { description: "When search suggestions come before general results and there are other general results besides quick suggest, the quick suggest result should come last.", prefs: [["showSearchSuggestionsFirst", true]], histories, context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), QuickSuggestTestUtils.ampResult(), ...historyResults, ], }, { description: "When general results come before search suggestions and the only general result is a quick suggest result, it should come before suggestions.", prefs: [["showSearchSuggestionsFirst", false]], context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), ], }, { description: "When general results come before search suggestions and there are other general results besides quick suggest, the quick suggest result should be the last general result.", prefs: [["showSearchSuggestionsFirst", false]], histories, context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), ...historyResults, QuickSuggestTestUtils.ampResult({ suggestedIndex: -1 }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), ], }, ], }); }); // Real quick suggest URLs include a timestamp template that // UrlbarProviderQuickSuggest fills in when it fetches suggestions. When the // user picks a quick suggest, its URL with its particular timestamp is added to // history. If the user triggers the quick suggest again later, its new // timestamp may be different from the one in the user's history. In that case, // the two URLs should be treated as dupes and only the quick suggest should be // shown, not the URL from history. add_task(async function dedupeAgainstURL_timestamps() { const { TIMESTAMP_TEMPLATE, TIMESTAMP_LENGTH } = lazy.AmpSuggestions; const TIMESTAMP_SUGGESTION_URL = `http://example.com/timestamp-${TIMESTAMP_TEMPLATE}`; const TIMESTAMP_SUGGESTION_CLICK_URL = `http://click.reporting.test.com/timestamp-${TIMESTAMP_TEMPLATE}-foo`; // Add a visit that will match the query below and dupe the quick suggest. let dupeURL = TIMESTAMP_SUGGESTION_URL.replace( TIMESTAMP_TEMPLATE, "2013051113" ); // Add other visits that will match the query and almost dupe the quick // suggest but not quite because they have invalid timestamps. let badTimestamps = [ // not numeric digits "x".repeat(TIMESTAMP_LENGTH), // too few digits "5".repeat(TIMESTAMP_LENGTH - 1), // empty string, too few digits "", ]; let badTimestampURLs = badTimestamps.map(str => TIMESTAMP_SUGGESTION_URL.replace(TIMESTAMP_TEMPLATE, str) ); let histories = [dupeURL, ...badTimestampURLs].map(uri => ({ uri, title: "amp", transition: PlacesUtils.history.TRANSITION_TYPED, })); let context = createContext("amp", { isPrivate: false }); let badTimestampResults = [...badTimestampURLs].reverse().map(uri => makeVisitResult(context, { uri, title: "amp", }) ); let ampResultIndex = badTimestampResults.length + 1; await doResultCheckTest({ env: { prefs: [ ["suggest.searches", false], ["quicksuggest.ampTopPickCharThreshold", 0], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings({ url: TIMESTAMP_SUGGESTION_URL, click_url: TIMESTAMP_SUGGESTION_CLICK_URL, }), ], }, ], }, tests: [ { description: "Timestamp with quicksuggest", prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], histories, context, conditionalPayloadProperties: { url: { custom: (index, result) => { if (index != ampResultIndex) { return false; } QuickSuggestTestUtils.assertTimestampsReplaced(result, { url: TIMESTAMP_SUGGESTION_URL, }); return true; }, }, sponsoredClickUrl: { custom: (index, result) => { if (index != ampResultIndex) { return false; } QuickSuggestTestUtils.assertTimestampsReplaced(result, { sponsoredClickUrl: TIMESTAMP_SUGGESTION_CLICK_URL, }); return true; }, }, }, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), ...badTimestampResults, QuickSuggestTestUtils.ampResult({ url: TIMESTAMP_SUGGESTION_URL, suggestedIndex: -1, }), ], }, { description: "Timestamp without quicksuggest", prefs: [ ["suggest.quicksuggest.all", false], ["suggest.quicksuggest.sponsored", false], ], histories, context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), ...badTimestampResults, makeVisitResult(context, { uri: dupeURL, title: "amp", }), ], }, ], }); }); add_task(async function showLessFrequently() { await doShowLessFrequentlyTest({ feature: "AmpSuggestions", showLessFrequentlyCountPref: "amp.showLessFrequentlyCount", minKeywordLengthPref: "amp.minKeywordLength", env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], // Make not top pick ["quicksuggest.ampTopPickCharThreshold", 100], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.OTHER, type: "configuration", configuration: { show_less_frequently_cap: 3, }, }, { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings({ keywords: [ "amp full key", "amp full keyw", "amp full keywo", "amp full keywor", "amp full keyword", ], full_keywords: [["amp full keyword", 5]], }), ], }, ], }, tests: [ { context: createContext("amp full key", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), // Index of result that will be executed the command. targetIndex: 0, // Before executing show_less_frequency command. before: { results: [ QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", suggestedIndex: -1, }), ], }, // After executing show_less_frequency command. after: { canShowLessFrequently: true, showLessFrequentlyCount: 1, minKeywordLength: 13, results: [], }, }, { context: createContext("amp full keywor", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), targetIndex: 0, before: { results: [ QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", suggestedIndex: -1, }), ], }, after: { canShowLessFrequently: true, showLessFrequentlyCount: 2, minKeywordLength: 16, results: [], }, }, { context: createContext("amp full keyword", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), targetIndex: 0, before: { results: [ QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", suggestedIndex: -1, }), ], }, after: { canShowLessFrequently: false, showLessFrequentlyCount: 3, minKeywordLength: 17, results: [], }, }, ], }); }); // Tests `UrlbarResult` dismissal. add_task(async function dismissResult() { const { TIMESTAMP_TEMPLATE } = lazy.AmpSuggestions; const TIMESTAMP_SUGGESTION_URL = `http://example.com/timestamp-${TIMESTAMP_TEMPLATE}`; const TIMESTAMP_SUGGESTION_CLICK_URL = `http://click.reporting.test.com/timestamp-${TIMESTAMP_TEMPLATE}-foo`; await doDismissTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], // Make not top pick ["quicksuggest.ampTopPickCharThreshold", 100], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings(), QuickSuggestTestUtils.ampRemoteSettings({ url: "http://example.com/", keywords: ["http page"], }), QuickSuggestTestUtils.ampRemoteSettings({ url: "https://example.com/", keywords: ["https page"], }), QuickSuggestTestUtils.ampRemoteSettings({ url: TIMESTAMP_SUGGESTION_URL, click_url: TIMESTAMP_SUGGESTION_CLICK_URL, keywords: ["timestamp"], }), ], }, ], }, tests: [ { context: createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), // Index of result that will be executed the command. targetIndex: 0, // Before dismissing. before: { results: [ QuickSuggestTestUtils.ampResult({ suggestedIndex: -1, }), ], }, // After dismissing. after: { results: [], }, }, { context: createContext("http page", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), targetIndex: 0, before: { results: [ QuickSuggestTestUtils.ampResult({ url: "http://example.com/", fullKeyword: "http page", suggestedIndex: -1, }), ], }, after: { results: [], }, }, { context: createContext("https page", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), targetIndex: 0, before: { results: [ QuickSuggestTestUtils.ampResult({ url: "https://example.com/", fullKeyword: "https page", suggestedIndex: -1, }), ], }, after: { results: [], }, }, { context: createContext("timestamp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), conditionalPayloadProperties: { url: { custom: (_index, result) => { QuickSuggestTestUtils.assertTimestampsReplaced(result, { url: TIMESTAMP_SUGGESTION_URL, }); return true; }, }, sponsoredClickUrl: { custom: (_index, result) => { QuickSuggestTestUtils.assertTimestampsReplaced(result, { sponsoredClickUrl: TIMESTAMP_SUGGESTION_CLICK_URL, }); return true; }, }, }, targetIndex: 0, before: { results: [ QuickSuggestTestUtils.ampResult({ url: TIMESTAMP_SUGGESTION_URL, fullKeyword: "timestamp", suggestedIndex: -1, }), ], }, after: { results: [], }, }, ], }); }); add_task(async function sponsoredPriority() { let context = createContext("amp", { isPrivate: false }); await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), QuickSuggestTestUtils.ampResult(), ], }, { nimbus: { quickSuggestSponsoredPriority: true, quickSuggestSponsoredIndex: 1, }, context, expected: [ makeSearchResult(context, { heuristic: true, query: "amp", engineName: SearchService.defaultEngine.name, }), QuickSuggestTestUtils.ampResult({ isBestMatch: true, suggestedIndex: 1, isSuggestedIndexRelativeToGroup: false, }), makeSearchResult(context, { query: "amp", suggestion: "amp foo", engineName: SearchService.defaultEngine.name, }), makeSearchResult(context, { query: "amp", suggestion: "amp bar", engineName: SearchService.defaultEngine.name, }), ], }, ], }); }); // The `Amp` Rust providers should be passed to the Rust component when querying // depending on whether sponsored and non-sponsored suggestions are enabled. add_task(async function rustProviders() { let result = QuickSuggestTestUtils.ampResult(); await doRustBackendTest({ env: { remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [QuickSuggestTestUtils.ampRemoteSettings()], }, ], }, tests: [ { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], input: "amp", expected: [result.payload.url], }, { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", false], ], input: "amp", expected: [], }, { prefs: [ ["suggest.quicksuggest.all", false], ["suggest.quicksuggest.sponsored", true], ], input: "amp", expected: [], }, { prefs: [ ["suggest.quicksuggest.all", false], ["suggest.quicksuggest.sponsored", false], ], input: "amp", expected: [], }, ], }); }); add_task(async function keywordLengthThreshold() { await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings({ keywords: ["x", "xx"], }), ], }, ], }, tests: [ { context: createContext("x", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, { context: createContext("x ", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, { context: createContext(" x", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [], }, { context: createContext("xx", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }), expected: [ QuickSuggestTestUtils.ampResult({ fullKeyword: "xx", suggestedIndex: -1, }), ], }, ], }); }); // AMP should be a top pick when `quicksuggest.ampTopPickCharThreshold` is // non-zero and the query length meets the threshold; otherwise it should not be // a top pick. It shouldn't matter whether the query is one of the suggestion's // full keywords. add_task(async function ampTopPickCharThreshold() { let key = input => createContext(input, { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }); let normal = QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", suggestedIndex: -1, }); let toppick = QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", isBestMatch: true, suggestedIndex: 1, isSuggestedIndexRelativeToGroup: false, }); let xyz = QuickSuggestTestUtils.ampResult({ fullKeyword: "xyz", suggestedIndex: -1, }); await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["quicksuggest.ampTopPickCharThreshold", "amp full keywo".length], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings({ keywords: [ "amp full key", "amp full keyw", "amp full keywo", "amp full keywor", "amp full keyword", "xyz", ], full_keywords: [ ["amp full keyword", 5], ["xyz", 1], ], }), ], }, ], }, tests: [ // No top pick: Matches an AMP suggestion but the query is shorter than the // threshold. { context: key("amp full key"), expected: [normal] }, { context: key("amp full keyw"), expected: [normal] }, { context: key(" amp full key"), expected: [normal] }, { context: key(" amp full keyw"), expected: [normal] }, // Top pick: Matches an AMP suggestion and the query meets the threshold. { context: key("amp full keywo"), expected: [toppick] }, { context: key("amp full keywor"), expected: [toppick] }, { context: key("amp full keyword"), expected: [toppick] }, { context: key("AmP FuLl KeYwOrD"), expected: [toppick] }, { context: key(" amp full keywo"), expected: [toppick] }, { context: key(" amp full keywor"), expected: [toppick] }, { context: key(" amp full keyword"), expected: [toppick] }, { context: key(" AmP FuLl KeYwOrD"), expected: [toppick] }, // No top pick: Matches an AMP suggestion but the query is shorter than the // threshold. It doesn't matter that the query is equal to the suggestion's // full keyword. { context: key("xyz"), expected: [xyz] }, { context: key("XyZ"), expected: [xyz] }, { context: key(" xyz"), expected: [xyz] }, { context: key(" XyZ"), expected: [xyz] }, // No match: These shouldn't match anything at all since they have extra // spaces at the end, but they're included for completeness. { context: key(" amp full key "), expected: [] }, { context: key(" amp full keyw "), expected: [] }, { context: key(" amp full keywo "), expected: [] }, { context: key(" amp full keywor "), expected: [] }, { context: key(" amp full keyword "), expected: [] }, { context: key(" AmP FuLl KeYwOrD "), expected: [] }, { context: key(" xyz "), expected: [] }, { context: key(" XyZ "), expected: [] }, ], }); }); // AMP should not be shown as a top pick when the threshold is zero. add_task(async function ampTopPickCharThreshold_zero() { let key = input => createContext(input, { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }); let normal = QuickSuggestTestUtils.ampResult({ fullKeyword: "amp full keyword", suggestedIndex: -1, }); let xyz = QuickSuggestTestUtils.ampResult({ fullKeyword: "xyz", suggestedIndex: -1, }); await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ["quicksuggest.ampTopPickCharThreshold", 0], ], remoteSettingRecords: [ { collection: QuickSuggestTestUtils.RS_COLLECTION.AMP, type: QuickSuggestTestUtils.RS_TYPE.AMP, attachment: [ QuickSuggestTestUtils.ampRemoteSettings({ keywords: [ "amp full key", "amp full keyw", "amp full keywo", "amp full keywor", "amp full keyword", "xyz", ], full_keywords: [ ["amp full keyword", 5], ["xyz", 1], ], }), ], }, ], }, tests: [ { context: key("amp full key"), expected: [normal] }, { context: key("amp full keyw"), expected: [normal] }, { context: key("amp full keywo"), expected: [normal] }, { context: key("amp full keywor"), expected: [normal] }, { context: key("amp full keyword"), expected: [normal] }, { context: key("AmP FuLl KeYwOrD"), expected: [normal] }, { context: key("xyz"), expected: [xyz] }, { context: key("XyZ"), expected: [xyz] }, ], }); }); // Tests `ampMatchingStrategy`. add_task(async function ampMatchingStrategy() { UrlbarPrefs.set("suggest.quicksuggest.all", true); UrlbarPrefs.set("suggest.quicksuggest.sponsored", true); await QuickSuggestTestUtils.forceSync(); // Test each strategy in `AmpMatchingStrategy`. There are only a few. for (let [key, value] of Object.entries(lazy.AmpMatchingStrategy)) { await doAmpMatchingStrategyTest({ key, value }); // Reset back to the default strategy just to make sure that works. await doAmpMatchingStrategyTest({ key: "(default)", value: 0, }); } // Test an invalid strategy integer value. The default strategy should // actually be used. First we need to set a valid non-default strategy. await doAmpMatchingStrategyTest({ key: "FTS_AGAINST_TITLE", value: lazy.AmpMatchingStrategy.FTS_AGAINST_TITLE, }); await doAmpMatchingStrategyTest({ key: "(invalid)", value: 99, expectedStrategy: 0, }); Services.prefs.clearUserPref( "browser.urlbar.quicksuggest.ampMatchingStrategy" ); await QuickSuggestTestUtils.forceSync(); }); async function doAmpMatchingStrategyTest({ key, value, expectedStrategy = value, }) { info("Doing ampMatchingStrategy test: " + JSON.stringify({ key, value })); let sandbox = sinon.createSandbox(); let ingestSpy = sandbox.spy(QuickSuggest.rustBackend._test_store, "ingest"); // Set the strategy. It should trigger ingest. (Assuming it's different from // the current strategy. If it's not, ingest won't happen.) Services.prefs.setIntPref( "browser.urlbar.quicksuggest.ampMatchingStrategy", value ); let ingestCall = await TestUtils.waitForCondition(() => { return ingestSpy.getCalls().find(call => { let ingestConstraints = call.args[0]; return ingestConstraints?.providers[0] == lazy.SuggestionProvider.AMP; }); }, "Waiting for ingest() to be called with Amp provider"); // Check the provider constraints in the ingest constraints. let { providerConstraints } = ingestCall.args[0]; if (!expectedStrategy) { Assert.ok( !providerConstraints, "ingest() should not have been called with provider constraints" ); } else { Assert.ok( providerConstraints, "ingest() should have been called with provider constraints" ); Assert.strictEqual( providerConstraints.ampAlternativeMatching, expectedStrategy, "ampAlternativeMatching should have been set" ); } // Now do a query to make sure it also uses the correct provider constraints. // No need to use `check_results()`. We only need to trigger a query, and // checking the right results unnecessarily complicates things. let querySpy = sandbox.spy( QuickSuggest.rustBackend._test_store, "queryWithMetrics" ); let controller = UrlbarTestUtils.newMockController(); await controller.startQuery( createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }) ); let queryCalls = querySpy.getCalls(); Assert.equal(queryCalls.length, 1, "query() should have been called once"); let query = queryCalls[0].args[0]; Assert.ok(query, "query() should have been called with a query object"); Assert.ok( query.providerConstraints, "query() should have been called with provider constraints" ); if (!expectedStrategy) { Assert.strictEqual( query.providerConstraints.ampAlternativeMatching, null, "ampAlternativeMatching should not have been set on query provider constraints" ); } else { Assert.strictEqual( query.providerConstraints.ampAlternativeMatching, expectedStrategy, "ampAlternativeMatching should have been set on query provider constraints" ); } sandbox.restore(); } add_task(async function online() { let context = createContext("amp", { providers: [UrlbarProviderQuickSuggest.name], isPrivate: false, }); await doResultCheckTest({ env: { prefs: [ ["suggest.quicksuggest.all", true], ["suggest.quicksuggest.sponsored", true], ], merinoSuggestions: [ { title: "Amp Suggestion", url: "https://example.com/amp", provider: "adm", is_sponsored: true, score: 0.31, icon: "https://example.com/amp-icon", iab_category: "22 - Shopping", block_id: 1, full_keyword: "amp", advertiser: "Amp", impression_url: "https://example.com/amp-impression", click_url: "https://example.com/amp-click", }, ], }, tests: [ { prefs: [ ["quicksuggest.online.available", true], ["quicksuggest.online.enabled", true], ], context, expected: [ QuickSuggestTestUtils.ampResult({ source: "merino", provider: "adm", icon: "https://example.com/amp-icon", iabCategory: "22 - Shopping", requestId: "request_id", suggestedIndex: -1, }), ], }, { prefs: [ ["quicksuggest.online.available", false], ["quicksuggest.online.enabled", true], ], context, expected: [], }, { prefs: [ ["quicksuggest.online.available", true], ["quicksuggest.online.enabled", false], ], context, expected: [], }, ], }); });