test_quicksuggest_topPicks.js (5542B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // Tests top pick quick suggest results. "Top picks" refers to two different 6 // concepts: 7 // 8 // (1) Any type of suggestion from Merino can have a boolean property called 9 // `is_top_pick`. When true, Firefox should show the suggestion using the 10 // "best match" UI treatment (labeled "top pick" in the UI) that makes a 11 // result's row larger than usual and sets `suggestedIndex` to 1. 12 // (2) There is a Merino provider called "top_picks" that returns a specific 13 // type of suggestion called "navigational suggestions". These suggestions 14 // also have `is_top_pick` set to true. 15 // 16 // This file tests aspects of both concepts. 17 18 "use strict"; 19 20 const SUGGESTION_SEARCH_STRING = "example"; 21 const SUGGESTION_URL = "http://example.com/"; 22 const SUGGESTION_URL_WWW = "http://www.example.com/"; 23 24 const MERINO_SUGGESTIONS = [ 25 { 26 is_top_pick: true, 27 provider: "top_picks", 28 url: SUGGESTION_URL, 29 title: "title", 30 icon: "icon", 31 is_sponsored: false, 32 score: 1, 33 }, 34 ]; 35 36 add_setup(async function init() { 37 // Disable search suggestions so we don't hit the network. 38 Services.prefs.setBoolPref("browser.search.suggest.enabled", false); 39 40 await QuickSuggestTestUtils.ensureQuickSuggestInit({ 41 merinoSuggestions: MERINO_SUGGESTIONS, 42 prefs: [["suggest.quicksuggest.all", true]], 43 }); 44 }); 45 46 // When `all` is disabled, navigational suggestions should be disabled. 47 add_task(async function allDisabled() { 48 // Disable sponsored suggestions. Navigational suggestions are non-sponsored, 49 // so doing this should not prevent them from being enabled. 50 UrlbarPrefs.set("suggest.quicksuggest.sponsored", false); 51 52 // First make sure the suggestion is added when non-sponsored suggestions are 53 // enabled. 54 await check_results({ 55 context: createContext(SUGGESTION_SEARCH_STRING, { 56 providers: [UrlbarProviderQuickSuggest.name], 57 isPrivate: false, 58 }), 59 matches: [ 60 makeExpectedResult({ 61 isBestMatch: true, 62 suggestedIndex: 1, 63 }), 64 ], 65 }); 66 67 // Now disable them. 68 UrlbarPrefs.set("suggest.quicksuggest.all", false); 69 await check_results({ 70 context: createContext(SUGGESTION_SEARCH_STRING, { 71 providers: [UrlbarProviderQuickSuggest.name], 72 isPrivate: false, 73 }), 74 matches: [], 75 }); 76 77 UrlbarPrefs.set("suggest.quicksuggest.all", true); 78 UrlbarPrefs.clear("suggest.quicksuggest.sponsored"); 79 }); 80 81 // Test that bestMatch navigational suggestion results are not shown when there 82 // is a heuristic result for the same domain. 83 add_task(async function heuristicDeduplication() { 84 let expectedNavSuggestResult = makeExpectedResult({ 85 isBestMatch: true, 86 suggestedIndex: 1, 87 dupedHeuristic: false, 88 }); 89 90 let scenarios = [ 91 [SUGGESTION_URL, false], 92 [SUGGESTION_URL_WWW, false], 93 ["http://exampledomain.com/", true], 94 ]; 95 let quickSuggestProviderInstance = UrlbarProvidersManager.getProvider( 96 UrlbarProviderQuickSuggest.name 97 ); 98 99 // Stub `UrlbarProviderQuickSuggest.startQuery()` so we can collect the 100 // results it adds for each query. 101 let addedResults = []; 102 let sandbox = sinon.createSandbox(); 103 let startQueryStub = sandbox.stub(quickSuggestProviderInstance, "startQuery"); 104 startQueryStub.callsFake((queryContext, add) => { 105 let fakeAdd = (provider, result) => { 106 addedResults.push(result); 107 add(provider, result); 108 }; 109 return startQueryStub.wrappedMethod.call( 110 quickSuggestProviderInstance, 111 queryContext, 112 fakeAdd 113 ); 114 }); 115 116 for (let [url, expectBestMatch] of scenarios) { 117 await PlacesTestUtils.addVisits(url); 118 119 // Do a search and check the results. 120 let context = createContext(SUGGESTION_SEARCH_STRING, { 121 providers: [UrlbarProviderQuickSuggest.name, UrlbarProviderAutofill.name], 122 isPrivate: false, 123 }); 124 const EXPECTED_AUTOFILL_RESULT = makeVisitResult(context, { 125 uri: url, 126 title: `test visit for ${url}`, 127 heuristic: true, 128 }); 129 await check_results({ 130 context, 131 matches: expectBestMatch 132 ? [EXPECTED_AUTOFILL_RESULT, expectedNavSuggestResult] 133 : [EXPECTED_AUTOFILL_RESULT], 134 }); 135 136 // Regardless of whether it was shown, one result should have been added and 137 // its `payload.dupedHeuristic` should be set properly. 138 Assert.equal( 139 addedResults.length, 140 1, 141 "The provider should have added one result" 142 ); 143 Assert.equal( 144 !addedResults[0].payload.dupedHeuristic, 145 expectBestMatch, 146 "dupedHeuristic should be the opposite of expectBestMatch" 147 ); 148 addedResults = []; 149 150 await PlacesUtils.history.clear(); 151 } 152 153 sandbox.restore(); 154 }); 155 156 function makeExpectedResult({ 157 isBestMatch, 158 suggestedIndex, 159 dupedHeuristic, 160 telemetryType = "top_picks", 161 }) { 162 let result = { 163 isBestMatch, 164 suggestedIndex, 165 type: UrlbarUtils.RESULT_TYPE.URL, 166 source: UrlbarUtils.RESULT_SOURCE.SEARCH, 167 heuristic: false, 168 payload: { 169 dupedHeuristic, 170 telemetryType, 171 title: "title", 172 url: SUGGESTION_URL, 173 icon: "icon", 174 isSponsored: false, 175 shouldShowUrl: true, 176 source: "merino", 177 provider: telemetryType, 178 isBlockable: true, 179 isManageable: true, 180 }, 181 }; 182 if (typeof dupedHeuristic == "boolean") { 183 result.payload.dupedHeuristic = dupedHeuristic; 184 } 185 return result; 186 }