Skip to content

Commit 4fdeb94

Browse files
authored
add search_suggestion_index pixel param to search suggestion click pixels (#6979)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671518894266/task/1211716661936840?focus=true ### Description Adds a parameters to search suggestion click pixels with the index of the clicked suggestion. The index ignores history, favorites, bookmarks, and other suggestion types which are not strictly a query suggestion or a website suggestion. For example, if the suggestions list includes: - (history) hey - (favorite) heeey.com/wave - hey.com - hey - hello - hello there and users clicks on 'hello', the `search_suggestion_index=2` param will be appended. ### Steps to test this PR - [x] Open the app, focus on the omnibar and type in a query. - [x] Verify that clicking on a search suggestion sends an `m_autocomplete_click_phrase` pixel with `search_suggestion_index` matching the position of the suggestion in the list. - [x] Verify that clicking on a website search suggestion sends an `m_autocomplete_click_website` pixel with `search_suggestion_index` matching the position of the suggestion in the list.
1 parent 5ad5811 commit 4fdeb94

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

app/src/main/java/com/duckduckgo/app/autocomplete/api/AutoComplete.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.core.net.toUri
2222
import com.duckduckgo.app.autocomplete.AutocompleteTabsFeature
2323
import com.duckduckgo.app.autocomplete.impl.AutoCompletePixelNames
2424
import com.duckduckgo.app.autocomplete.impl.AutoCompleteRepository
25+
import com.duckduckgo.app.autocomplete.impl.AutocompletePixelParams
2526
import com.duckduckgo.app.browser.UriString
2627
import com.duckduckgo.app.di.AppCoroutineScope
2728
import com.duckduckgo.app.onboarding.store.AppStage
@@ -292,7 +293,7 @@ class AutoCompleteApi constructor(
292293
val hasFavoriteResults = suggestions.any { it is AutoCompleteBookmarkSuggestion && it.isFavorite }
293294
val hasHistoryResults = suggestions.any { it is AutoCompleteHistorySuggestion || it is AutoCompleteHistorySearchSuggestion }
294295
val hasSwitchToTabResults = suggestions.any { it is AutoCompleteSwitchToTabSuggestion }
295-
val params = mapOf(
296+
val params = mutableMapOf(
296297
PixelParameter.SHOWED_BOOKMARKS to hasBookmarkResults.toString(),
297298
PixelParameter.SHOWED_FAVORITES to hasFavoriteResults.toString(),
298299
PixelParameter.BOOKMARK_CAPABLE to hasBookmarks.toString(),
@@ -333,6 +334,11 @@ class AutoCompleteApi constructor(
333334
else -> return
334335
}
335336

337+
if (suggestion is AutoCompleteSearchSuggestion) {
338+
val clickedSearchSuggestionIndex = suggestions.filter { it is AutoCompleteSearchSuggestion }.indexOf(suggestion)
339+
params[AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX] = clickedSearchSuggestionIndex.toString()
340+
}
341+
336342
pixel.fire(pixelName, params)
337343
}
338344

app/src/main/java/com/duckduckgo/app/autocomplete/impl/AutoCompletePixelNames.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,11 @@ class AutocompleteParamRemovalPlugin @Inject constructor() : PixelParamRemovalPl
4848
)
4949
}
5050
}
51+
52+
object AutocompletePixelParams {
53+
/**
54+
* Parameter to capture the index of the selected suggestion within the list of search suggestions
55+
* (either [AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_PHRASE_SELECTION] or [AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_WEBSITE_SELECTION]).
56+
*/
57+
const val PARAM_SEARCH_SUGGESTION_INDEX = "search_suggestion_index"
58+
}

app/src/test/java/com/duckduckgo/app/autocomplete/api/AutoCompleteApiTest.kt

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
2323
import com.duckduckgo.app.autocomplete.AutocompleteTabsFeature
2424
import com.duckduckgo.app.autocomplete.impl.AutoCompletePixelNames
2525
import com.duckduckgo.app.autocomplete.impl.AutoCompleteRepository
26+
import com.duckduckgo.app.autocomplete.impl.AutocompletePixelParams
2627
import com.duckduckgo.app.onboarding.store.AppStage
2728
import com.duckduckgo.app.onboarding.store.AppStage.NEW
2829
import com.duckduckgo.app.onboarding.store.UserStageStore
@@ -1881,6 +1882,120 @@ class AutoCompleteApiTest {
18811882
assertEquals("false", argumentCaptor.firstValue[PixelParameter.SWITCH_TO_TAB_CAPABLE])
18821883
}
18831884

1885+
@Test
1886+
fun `when search suggestion clicked then search suggestion index parameter is added`() = runTest {
1887+
whenever(mockSavedSitesRepository.hasBookmarks()).thenReturn(false)
1888+
whenever(mockNavigationHistory.hasHistory()).thenReturn(false)
1889+
whenever(mockHistory.hasHistory()).thenReturn(false)
1890+
tabsLiveData.value = listOf(TabEntity("1", "https://example.com", position = 0))
1891+
1892+
val suggestions = listOf(
1893+
AutoCompleteSearchSuggestion("first", isUrl = false, isAllowedInTopHits = false),
1894+
AutoCompleteSearchSuggestion("second", isUrl = false, isAllowedInTopHits = false),
1895+
AutoCompleteSearchSuggestion("third", isUrl = false, isAllowedInTopHits = false),
1896+
)
1897+
val clickedSuggestion = suggestions[1] // second suggestion (index 1)
1898+
1899+
testee.fireAutocompletePixel(suggestions, clickedSuggestion)
1900+
1901+
val argumentCaptor = argumentCaptor<Map<String, String>>()
1902+
Mockito.verify(mockPixel).fire(eq(AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_PHRASE_SELECTION), argumentCaptor.capture(), any(), any())
1903+
1904+
assertEquals("1", argumentCaptor.firstValue[AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX])
1905+
}
1906+
1907+
@Test
1908+
fun `when search website suggestion clicked then search suggestion index parameter is added`() = runTest {
1909+
whenever(mockSavedSitesRepository.hasBookmarks()).thenReturn(false)
1910+
whenever(mockNavigationHistory.hasHistory()).thenReturn(false)
1911+
whenever(mockHistory.hasHistory()).thenReturn(false)
1912+
tabsLiveData.value = listOf(TabEntity("1", "https://example.com", position = 0))
1913+
1914+
val suggestions = listOf(
1915+
AutoCompleteSearchSuggestion("first", isUrl = false, isAllowedInTopHits = false),
1916+
AutoCompleteSearchSuggestion("second", isUrl = false, isAllowedInTopHits = false),
1917+
AutoCompleteSearchSuggestion("third", isUrl = true, isAllowedInTopHits = false), // isUrl = true for website suggestion
1918+
)
1919+
val clickedSuggestion = suggestions[2] // third suggestion (index 2)
1920+
1921+
testee.fireAutocompletePixel(suggestions, clickedSuggestion)
1922+
1923+
val argumentCaptor = argumentCaptor<Map<String, String>>()
1924+
Mockito.verify(mockPixel).fire(eq(AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_WEBSITE_SELECTION), argumentCaptor.capture(), any(), any())
1925+
1926+
assertEquals("2", argumentCaptor.firstValue[AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX])
1927+
}
1928+
1929+
@Test
1930+
fun `when non search suggestion clicked then search suggestion index parameter is not added`() = runTest {
1931+
whenever(mockSavedSitesRepository.hasBookmarks()).thenReturn(true)
1932+
whenever(mockNavigationHistory.hasHistory()).thenReturn(false)
1933+
whenever(mockHistory.hasHistory()).thenReturn(false)
1934+
tabsLiveData.value = listOf(TabEntity("1", "https://example.com", position = 0))
1935+
1936+
val suggestions = listOf(
1937+
AutoCompleteSearchSuggestion("first", isUrl = false, isAllowedInTopHits = false),
1938+
AutoCompleteBookmarkSuggestion("bookmark", "title", "url"),
1939+
AutoCompleteSearchSuggestion("second", isUrl = false, isAllowedInTopHits = false),
1940+
)
1941+
val clickedSuggestion = AutoCompleteBookmarkSuggestion("bookmark", "title", "url")
1942+
1943+
testee.fireAutocompletePixel(suggestions, clickedSuggestion)
1944+
1945+
val argumentCaptor = argumentCaptor<Map<String, String>>()
1946+
Mockito.verify(mockPixel).fire(eq(AutoCompletePixelNames.AUTOCOMPLETE_BOOKMARK_SELECTION), argumentCaptor.capture(), any(), any())
1947+
1948+
assertFalse(argumentCaptor.firstValue.containsKey(AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX))
1949+
}
1950+
1951+
@Test
1952+
fun `when search suggestion clicked with mixed suggestions then correct index is calculated`() = runTest {
1953+
whenever(mockSavedSitesRepository.hasBookmarks()).thenReturn(true)
1954+
whenever(mockNavigationHistory.hasHistory()).thenReturn(false)
1955+
whenever(mockHistory.hasHistory()).thenReturn(false)
1956+
tabsLiveData.value = listOf(TabEntity("1", "https://example.com", position = 0))
1957+
1958+
val suggestions = listOf(
1959+
AutoCompleteBookmarkSuggestion("bookmark1", "title1", "url1"),
1960+
AutoCompleteSearchSuggestion("first", isUrl = false, isAllowedInTopHits = false),
1961+
AutoCompleteBookmarkSuggestion("bookmark2", "title2", "url2"),
1962+
AutoCompleteSearchSuggestion("second", isUrl = false, isAllowedInTopHits = false),
1963+
AutoCompleteSearchSuggestion("third", isUrl = false, isAllowedInTopHits = false),
1964+
)
1965+
val clickedSuggestion = suggestions[4] // third search suggestion (index 2 among search suggestions)
1966+
1967+
testee.fireAutocompletePixel(suggestions, clickedSuggestion)
1968+
1969+
val argumentCaptor = argumentCaptor<Map<String, String>>()
1970+
Mockito.verify(mockPixel).fire(eq(AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_PHRASE_SELECTION), argumentCaptor.capture(), any(), any())
1971+
1972+
assertEquals("2", argumentCaptor.firstValue[AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX])
1973+
}
1974+
1975+
@Test
1976+
fun `when search website suggestion clicked with mixed suggestions then correct index is calculated`() = runTest {
1977+
whenever(mockSavedSitesRepository.hasBookmarks()).thenReturn(true)
1978+
whenever(mockNavigationHistory.hasHistory()).thenReturn(false)
1979+
whenever(mockHistory.hasHistory()).thenReturn(false)
1980+
tabsLiveData.value = listOf(TabEntity("1", "https://example.com", position = 0))
1981+
1982+
val suggestions = listOf(
1983+
AutoCompleteBookmarkSuggestion("bookmark1", "title1", "url1"),
1984+
AutoCompleteSearchSuggestion("first", isUrl = true, isAllowedInTopHits = true), // isUrl = true for website suggestion
1985+
AutoCompleteBookmarkSuggestion("bookmark2", "title2", "url2"),
1986+
AutoCompleteSearchSuggestion("second", isUrl = true, isAllowedInTopHits = false), // isUrl = true for website suggestion
1987+
AutoCompleteSearchSuggestion("third", isUrl = false, isAllowedInTopHits = true),
1988+
)
1989+
val clickedSuggestion = suggestions[3] // second search suggestion (index 1 among search suggestions)
1990+
1991+
testee.fireAutocompletePixel(suggestions, clickedSuggestion)
1992+
1993+
val argumentCaptor = argumentCaptor<Map<String, String>>()
1994+
Mockito.verify(mockPixel).fire(eq(AutoCompletePixelNames.AUTOCOMPLETE_SEARCH_WEBSITE_SELECTION), argumentCaptor.capture(), any(), any())
1995+
1996+
assertEquals("1", argumentCaptor.firstValue[AutocompletePixelParams.PARAM_SEARCH_SUGGESTION_INDEX])
1997+
}
1998+
18841999
@Test
18852000
fun whenShowInstalledAppsDisabledThenNoDeviceAppResultsReturned() = runTest {
18862001
val testee = createTestee(AutoComplete.Config(showInstalledApps = false))

0 commit comments

Comments
 (0)