diff options
Diffstat (limited to '')
-rw-r--r-- | browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js new file mode 100644 index 0000000000..224dd6cb22 --- /dev/null +++ b/browser/components/urlbar/tests/quicksuggest/unit/test_quicksuggest_scoreMap.js @@ -0,0 +1,670 @@ +/* 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 the `quickSuggestScoreMap` Nimbus variable that assigns scores to +// specified types of quick suggest suggestions. The scores in the map should +// override the scores in the individual suggestion objects so that experiments +// can fully control the relative ranking of suggestions. + +"use strict"; + +const { DEFAULT_SUGGESTION_SCORE } = UrlbarProviderQuickSuggest; + +const REMOTE_SETTINGS_RECORDS = [ + { + type: "data", + attachment: [ + // sponsored without score + QuickSuggestTestUtils.ampRemoteSettings({ + score: undefined, + keywords: [ + "sponsored without score", + "sponsored without score, nonsponsored without score", + "sponsored without score, nonsponsored with score", + "sponsored without score, addon without score", + ], + url: "https://example.com/sponsored-without-score", + title: "Sponsored without score", + }), + // sponsored with score + QuickSuggestTestUtils.ampRemoteSettings({ + score: 2 * DEFAULT_SUGGESTION_SCORE, + keywords: [ + "sponsored with score", + "sponsored with score, nonsponsored without score", + "sponsored with score, nonsponsored with score", + "sponsored with score, addon with score", + ], + url: "https://example.com/sponsored-with-score", + title: "Sponsored with score", + }), + // nonsponsored without score + QuickSuggestTestUtils.wikipediaRemoteSettings({ + score: undefined, + keywords: [ + "nonsponsored without score", + "sponsored without score, nonsponsored without score", + "sponsored with score, nonsponsored without score", + ], + url: "https://example.com/nonsponsored-without-score", + title: "Nonsponsored without score", + }), + // nonsponsored with score + QuickSuggestTestUtils.wikipediaRemoteSettings({ + score: 2 * DEFAULT_SUGGESTION_SCORE, + keywords: [ + "nonsponsored with score", + "sponsored without score, nonsponsored with score", + "sponsored with score, nonsponsored with score", + ], + url: "https://example.com/nonsponsored-with-score", + title: "Nonsponsored with score", + }), + ], + }, + { + type: "amo-suggestions", + attachment: [ + // addon with score + QuickSuggestTestUtils.amoRemoteSettings({ + score: 2 * DEFAULT_SUGGESTION_SCORE, + keywords: [ + "addon with score", + "sponsored with score, addon with score", + ], + url: "https://example.com/addon-with-score", + title: "Addon with score", + }), + ], + }, +]; + +const ADM_RECORD = REMOTE_SETTINGS_RECORDS[0]; +const SPONSORED_WITHOUT_SCORE = ADM_RECORD.attachment[0]; +const SPONSORED_WITH_SCORE = ADM_RECORD.attachment[1]; +const NONSPONSORED_WITHOUT_SCORE = ADM_RECORD.attachment[2]; +const NONSPONSORED_WITH_SCORE = ADM_RECORD.attachment[3]; + +const ADDON_RECORD = REMOTE_SETTINGS_RECORDS[1]; +const ADDON_WITH_SCORE = ADDON_RECORD.attachment[0]; + +const MERINO_SPONSORED_SUGGESTION = { + provider: "adm", + score: DEFAULT_SUGGESTION_SCORE, + iab_category: "22 - Shopping", + is_sponsored: true, + keywords: ["test"], + full_keyword: "test", + block_id: 1, + url: "https://example.com/merino-sponsored", + title: "Merino sponsored", + click_url: "https://example.com/click", + impression_url: "https://example.com/impression", + advertiser: "TestAdvertiser", + icon: "1234", +}; + +const MERINO_ADDON_SUGGESTION = { + provider: "amo", + score: DEFAULT_SUGGESTION_SCORE, + keywords: ["test"], + icon: "https://example.com/addon.svg", + url: "https://example.com/merino-addon", + title: "Merino addon", + description: "Merino addon", + custom_details: { + amo: { + guid: "merino-addon@example.com", + rating: "4.7", + number_of_ratings: "1256", + }, + }, +}; + +const MERINO_UNKNOWN_SUGGESTION = { + provider: "some_unknown_provider", + score: DEFAULT_SUGGESTION_SCORE, + keywords: ["test"], + url: "https://example.com/merino-unknown", + title: "Merino unknown", +}; + +add_setup(async function init() { + // Disable search suggestions so we don't hit the network. + Services.prefs.setBoolPref("browser.search.suggest.enabled", false); + + await QuickSuggestTestUtils.ensureQuickSuggestInit({ + remoteSettingsRecords: REMOTE_SETTINGS_RECORDS, + merinoSuggestions: [], + prefs: [ + ["suggest.quicksuggest.sponsored", true], + ["suggest.quicksuggest.nonsponsored", true], + ], + }); +}); + +add_task(async function sponsoredWithout_nonsponsoredWithout_sponsoredWins() { + let keyword = "sponsored without score, nonsponsored without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITHOUT_SCORE, + }), + }); +}); + +add_task( + async function sponsoredWithout_nonsponsoredWithout_nonsponsoredWins() { + let keyword = "sponsored without score, nonsponsored without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_nonsponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedWikipediaResult({ + keyword, + suggestion: NONSPONSORED_WITHOUT_SCORE, + }), + }); + } +); + +add_task( + async function sponsoredWithout_nonsponsoredWithout_sponsoredWins_both() { + let keyword = "sponsored without score, nonsponsored without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + adm_nonsponsored: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITHOUT_SCORE, + }), + }); + } +); + +add_task( + async function sponsoredWithout_nonsponsoredWithout_nonsponsoredWins_both() { + let keyword = "sponsored without score, nonsponsored without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_nonsponsored: score, + adm_sponsored: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedWikipediaResult({ + keyword, + suggestion: NONSPONSORED_WITHOUT_SCORE, + }), + }); + } +); + +add_task(async function sponsoredWith_nonsponsoredWith_sponsoredWins() { + let keyword = "sponsored with score, nonsponsored with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_nonsponsoredWith_nonsponsoredWins() { + let keyword = "sponsored with score, nonsponsored with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_nonsponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedWikipediaResult({ + keyword, + suggestion: NONSPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_nonsponsoredWith_sponsoredWins_both() { + let keyword = "sponsored with score, nonsponsored with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + adm_nonsponsored: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_nonsponsoredWith_nonsponsoredWins_both() { + let keyword = "sponsored with score, nonsponsored with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_nonsponsored: score, + adm_sponsored: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedWikipediaResult({ + keyword, + suggestion: NONSPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWithout_addonWithout_sponsoredWins() { + let keyword = "sponsored without score, addon without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITHOUT_SCORE, + }), + }); +}); + +add_task(async function sponsoredWithout_addonWithout_sponsoredWins_both() { + let keyword = "sponsored without score, addon without score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + amo: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITHOUT_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_addonWith_sponsoredWins() { + let keyword = "sponsored with score, addon with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_addonWith_addonWins() { + let keyword = "sponsored with score, addon with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + amo: score, + }, + expectedFeatureName: "AddonSuggestions", + expectedScore: score, + expectedResult: makeExpectedAddonResult({ + suggestion: ADDON_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_addonWith_sponsoredWins_both() { + let keyword = "sponsored with score, addon with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: score, + amo: score / 2, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITH_SCORE, + }), + }); +}); + +add_task(async function sponsoredWith_addonWith_addonWins_both() { + let keyword = "sponsored with score, addon with score"; + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword, + scoreMap: { + amo: score, + adm_sponsored: score / 2, + }, + expectedFeatureName: "AddonSuggestions", + expectedScore: score, + expectedResult: makeExpectedAddonResult({ + suggestion: ADDON_WITH_SCORE, + }), + }); +}); + +add_task(async function merino_sponsored_addon_sponsoredWins() { + await QuickSuggestTestUtils.setRemoteSettingsRecords([]); + + MerinoTestUtils.server.response.body.suggestions = [ + MERINO_SPONSORED_SUGGESTION, + MERINO_ADDON_SUGGESTION, + ]; + + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword: "test", + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword: "test", + suggestion: MERINO_SPONSORED_SUGGESTION, + source: "merino", + provider: "adm", + requestId: MerinoTestUtils.server.response.body.request_id, + }), + }); + + await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS); +}); + +add_task(async function merino_sponsored_addon_addonWins() { + await QuickSuggestTestUtils.setRemoteSettingsRecords([]); + + MerinoTestUtils.server.response.body.suggestions = [ + MERINO_SPONSORED_SUGGESTION, + MERINO_ADDON_SUGGESTION, + ]; + + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword: "test", + scoreMap: { + amo: score, + }, + expectedFeatureName: "AddonSuggestions", + expectedScore: score, + expectedResult: makeExpectedAddonResult({ + suggestion: MERINO_ADDON_SUGGESTION, + source: "merino", + provider: "amo", + requestId: MerinoTestUtils.server.response.body.request_id, + }), + }); + + await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS); +}); + +add_task(async function merino_sponsored_unknown_sponsoredWins() { + await QuickSuggestTestUtils.setRemoteSettingsRecords([]); + + MerinoTestUtils.server.response.body.suggestions = [ + MERINO_SPONSORED_SUGGESTION, + MERINO_UNKNOWN_SUGGESTION, + ]; + + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword: "test", + scoreMap: { + adm_sponsored: score, + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: score, + expectedResult: makeExpectedAdmResult({ + keyword: "test", + suggestion: MERINO_SPONSORED_SUGGESTION, + source: "merino", + provider: "adm", + requestId: MerinoTestUtils.server.response.body.request_id, + }), + }); + + await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS); +}); + +add_task(async function merino_sponsored_unknown_unknownWins() { + await QuickSuggestTestUtils.setRemoteSettingsRecords([]); + + MerinoTestUtils.server.response.body.suggestions = [ + MERINO_SPONSORED_SUGGESTION, + MERINO_UNKNOWN_SUGGESTION, + ]; + + let score = 10 * DEFAULT_SUGGESTION_SCORE; + await doTest({ + keyword: "test", + scoreMap: { + [MERINO_UNKNOWN_SUGGESTION.provider]: score, + }, + expectedFeatureName: null, + expectedScore: score, + expectedResult: makeExpectedDefaultResult({ + suggestion: MERINO_UNKNOWN_SUGGESTION, + }), + }); + + await QuickSuggestTestUtils.setRemoteSettingsRecords(REMOTE_SETTINGS_RECORDS); +}); + +add_task(async function stringValue() { + let keyword = "sponsored with score, nonsponsored with score"; + await doTest({ + keyword, + scoreMap: { + adm_sponsored: "123.456", + }, + expectedFeatureName: "AdmWikipedia", + expectedScore: 123.456, + expectedResult: makeExpectedAdmResult({ + keyword, + suggestion: SPONSORED_WITH_SCORE, + }), + }); +}); + +/** + * Sets up Nimbus with a `quickSuggestScoreMap` variable value, does a search, + * and makes sure the expected result is shown and the expected score is set on + * the suggestion. + * + * @param {object} options + * Options object. + * @param {string} options.keyword + * The search string. This should be equal to a keyword from one or more + * suggestions. + * @param {object} options.scoreMap + * The value to set for the `quickSuggestScoreMap` variable. + * @param {string} options.expectedFeatureName + * The name of the `BaseFeature` instance that is expected to create the + * `UrlbarResult` that's shown. If the suggestion is intentionally from an + * unknown Merino provider and therefore the quick suggest provider is + * expected to create a default result for it, set this to null. + * @param {UrlbarResultstring} options.expectedResult + * The `UrlbarResult` that's expected to be shown. + * @param {number} options.expectedScore + * The final `score` value that's expected to be defined on the suggestion + * object. + */ +async function doTest({ + keyword, + scoreMap, + expectedFeatureName, + expectedResult, + expectedScore, +}) { + let cleanUpNimbus = await UrlbarTestUtils.initNimbusFeature({ + quickSuggestScoreMap: scoreMap, + }); + + // Stub the expected feature's `makeResult()` so we can see the value of the + // passed-in suggestion's score. If the suggestion's type is in the score map, + // the provider will set its score before calling `makeResult()`. + let actualScore; + let sandbox; + if (expectedFeatureName) { + sandbox = sinon.createSandbox(); + let feature = QuickSuggest.getFeature(expectedFeatureName); + let stub = sandbox + .stub(feature, "makeResult") + .callsFake((queryContext, suggestion, searchString) => { + actualScore = suggestion.score; + return stub.wrappedMethod.call( + feature, + queryContext, + suggestion, + searchString + ); + }); + } + + await check_results({ + context: createContext(keyword, { + providers: [UrlbarProviderQuickSuggest.name], + isPrivate: false, + }), + matches: [expectedResult], + }); + + if (expectedFeatureName) { + Assert.equal( + actualScore, + expectedScore, + "Suggestion score should be set correctly" + ); + sandbox.restore(); + } + + await cleanUpNimbus(); +} + +function makeExpectedAdmResult({ + suggestion, + keyword, + source, + provider, + requestId, +}) { + return makeAmpResult({ + keyword, + source, + provider, + requestId, + title: suggestion.title, + url: suggestion.url, + originalUrl: suggestion.url, + impressionUrl: suggestion.impression_url, + clickUrl: suggestion.click_url, + blockId: suggestion.id, + advertiser: suggestion.advertiser, + icon: suggestion.icon, + }); +} + +function makeExpectedWikipediaResult({ suggestion, keyword, source }) { + return makeWikipediaResult({ + keyword, + source, + title: suggestion.title, + url: suggestion.url, + originalUrl: suggestion.url, + impressionUrl: suggestion.impression_url, + clickUrl: suggestion.click_url, + blockId: suggestion.id, + }); +} + +function makeExpectedAddonResult({ suggestion, source, provider }) { + return makeAmoResult({ + source, + provider, + title: suggestion.title, + description: suggestion.description, + url: suggestion.url, + originalUrl: suggestion.url, + icon: suggestion.icon, + }); +} + +function makeExpectedDefaultResult({ suggestion }) { + return { + type: UrlbarUtils.RESULT_TYPE.URL, + source: UrlbarUtils.RESULT_SOURCE.SEARCH, + heuristic: false, + payload: { + source: "merino", + provider: suggestion.provider, + telemetryType: suggestion.provider, + isSponsored: suggestion.is_sponsored, + title: suggestion.title, + url: suggestion.url, + displayUrl: suggestion.url.replace(/^https:\/\//, ""), + icon: suggestion.icon, + descriptionL10n: suggestion.is_sponsored + ? { id: "urlbar-result-action-sponsored" } + : undefined, + shouldShowUrl: true, + helpUrl: QuickSuggest.HELP_URL, + helpL10n: { + id: "urlbar-result-menu-learn-more-about-firefox-suggest", + }, + isBlockable: true, + blockL10n: { + id: "urlbar-result-menu-dismiss-firefox-suggest", + }, + }, + }; +} |