Bug 1854501 - Add telemetry to count Fakespot exposures

Added a new probe `product_page_visits` which counts the number of
visits to a supported retailer product page.

(cherry picked from commit fbd860cb37334374806b835ad548b82e050c820a)
fenix/120.0
DreVla 7 months ago committed by mergify[bot]
parent 55eba1d604
commit 87bde8192c

@ -10773,6 +10773,26 @@ shopping:
metadata:
tags:
- Shopping
product_page_visits:
type: counter
description: |
Counts number of visits to a supported retailer product page
while enrolled in either the control or treatment branches
of the shopping experiment.
send_in_pings:
- metrics
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1854501
data_reviews:
- https://github.com/mozilla-mobile/firefox-android/pull/4120#issuecomment-1768423370
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: never
metadata:
tags:
- Shopping
shopping.settings:
component_opted_out:

@ -288,7 +288,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
shoppingExperienceFeature = DefaultShoppingExperienceFeature(
settings = requireContext().settings(),
),
onAvailabilityChange = {
onIconVisibilityChange = {
if (!reviewQualityCheckAvailable && it) {
Shopping.addressBarIconDisplayed.record()
}
@ -298,6 +298,9 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
onBottomSheetStateChange = {
reviewQualityCheck.setSelected(selected = it, notifyListener = false)
},
onProductPageDetected = {
Shopping.productPageVisits.add()
},
),
owner = this,
view = view,

@ -27,37 +27,41 @@ private const val DEBOUNCE_TIMEOUT_MILLIS = 200L
* @property appStore Reference to the application's [AppStore].
* @property browserStore Reference to the application's [BrowserStore].
* @property shoppingExperienceFeature Reference to the [ShoppingExperienceFeature].
* @property onAvailabilityChange Invoked when availability of this feature changes based on feature
* @property onIconVisibilityChange Invoked when shopping icon visibility changes based on feature
* flag and when the loaded page is a supported product page.
* @property onBottomSheetStateChange Invoked when the bottom sheet is collapsed or expanded.
* @property debounceTimeoutMillis Function that returns the debounce timeout in milliseconds. This
* make it possible to wait till [ContentState.isProductUrl] is stable before invoking
* [onAvailabilityChange].
* [onIconVisibilityChange].
* @property onProductPageDetected Invoked when a product page is detected and loaded. Used to
* detect when to send telemetry for shopping.product_page_visits.
*/
@OptIn(FlowPreview::class)
class ReviewQualityCheckFeature(
private val appStore: AppStore,
private val browserStore: BrowserStore,
private val shoppingExperienceFeature: ShoppingExperienceFeature,
private val onAvailabilityChange: (isAvailable: Boolean) -> Unit,
private val onIconVisibilityChange: (isAvailable: Boolean) -> Unit,
private val onBottomSheetStateChange: (isExpanded: Boolean) -> Unit,
private val debounceTimeoutMillis: (Boolean) -> Long = { if (it) DEBOUNCE_TIMEOUT_MILLIS else 0 },
private val onProductPageDetected: () -> Unit,
) : LifecycleAwareFeature {
private var scope: CoroutineScope? = null
private var appStoreScope: CoroutineScope? = null
override fun start() {
if (!shoppingExperienceFeature.isEnabled) {
onAvailabilityChange(false)
return
}
scope = browserStore.flowScoped { flow ->
flow.mapNotNull { it.selectedTab }
.map { it.content.isProductUrl && !it.content.loading }
.distinctUntilChanged()
.debounce(debounceTimeoutMillis)
.collect(onAvailabilityChange)
.collect {
if (it) {
onProductPageDetected()
}
onIconVisibilityChange(shoppingExperienceFeature.isEnabled && it)
}
}
appStoreScope = appStore.flowScoped { flow ->

@ -30,20 +30,34 @@ class ReviewQualityCheckFeatureTest {
val coroutinesTestRule = MainCoroutineRule()
@Test
fun `WHEN feature is not enabled THEN callback returns false`() {
fun `WHEN feature is not enabled THEN callback returns false`() = runTest {
var availability: Boolean? = null
val tab = createTab(
url = "https://www.mozilla.org",
id = "test-tab",
isProductUrl = true,
)
val browserState = BrowserState(
tabs = listOf(tab),
selectedTabId = tab.id,
)
val tested = ReviewQualityCheckFeature(
appStore = AppStore(),
browserStore = BrowserStore(),
browserStore = BrowserStore(
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(enabled = false),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
onProductPageDetected = {},
)
tested.start()
testScheduler.advanceTimeBy(250)
assertFalse(availability!!)
}
@ -66,10 +80,11 @@ class ReviewQualityCheckFeatureTest {
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
onProductPageDetected = {},
)
tested.start()
@ -99,10 +114,11 @@ class ReviewQualityCheckFeatureTest {
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
onProductPageDetected = {},
)
tested.start()
@ -129,11 +145,12 @@ class ReviewQualityCheckFeatureTest {
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {},
)
tested.start()
@ -165,11 +182,12 @@ class ReviewQualityCheckFeatureTest {
appStore = AppStore(),
browserStore = browserStore,
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {},
)
tested.start()
@ -204,11 +222,12 @@ class ReviewQualityCheckFeatureTest {
appStore = AppStore(),
browserStore = browserStore,
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {},
)
tested.start()
@ -238,10 +257,11 @@ class ReviewQualityCheckFeatureTest {
appStore = AppStore(),
browserStore = browserStore,
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability.add(it)
},
onBottomSheetStateChange = {},
onProductPageDetected = {},
)
tested.start()
@ -301,11 +321,12 @@ class ReviewQualityCheckFeatureTest {
appStore = AppStore(),
browserStore = browserStore,
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {
onIconVisibilityChange = {
availability = it
availabilityCount++
},
onBottomSheetStateChange = {},
onProductPageDetected = {},
)
tested.start()
@ -329,10 +350,11 @@ class ReviewQualityCheckFeatureTest {
appStore = appStore,
browserStore = BrowserStore(),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {},
onIconVisibilityChange = {},
onBottomSheetStateChange = {
isExpanded = it
},
onProductPageDetected = {},
)
tested.start()
@ -354,10 +376,11 @@ class ReviewQualityCheckFeatureTest {
appStore = appStore,
browserStore = BrowserStore(),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {},
onIconVisibilityChange = {},
onBottomSheetStateChange = {
isExpanded = it
},
onProductPageDetected = {},
)
tested.start()
@ -379,10 +402,11 @@ class ReviewQualityCheckFeatureTest {
appStore = appStore,
browserStore = BrowserStore(),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onAvailabilityChange = {},
onIconVisibilityChange = {},
onBottomSheetStateChange = {
isExpanded = it
},
onProductPageDetected = {},
)
tested.start()
@ -394,4 +418,140 @@ class ReviewQualityCheckFeatureTest {
tested.start()
assertFalse(isExpanded!!)
}
@Test
fun `GIVEN feature is enabled WHEN non product url accessed THEN callback not called`() {
runTest {
var invokedCounter = 0
val tab = createTab(
url = "https://www.mozilla.org",
id = "test-tab",
isProductUrl = false,
)
val browserState = BrowserState(
tabs = listOf(tab),
selectedTabId = tab.id,
)
val tested = ReviewQualityCheckFeature(
appStore = AppStore(),
browserStore = BrowserStore(
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onIconVisibilityChange = {},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {
invokedCounter++
},
)
tested.start()
assertEquals(invokedCounter, 0)
}
}
@Test
fun `GIVEN feature is enabled WHEN product url accessed THEN callback called`() {
runTest {
var invokedCounter = 0
val tab = createTab(
url = "https://www.shopping.org",
id = "test-tab",
isProductUrl = true,
).let {
it.copy(content = it.content.copy(loading = false))
}
val browserState = BrowserState(
tabs = listOf(tab),
selectedTabId = tab.id,
)
val tested = ReviewQualityCheckFeature(
appStore = AppStore(),
browserStore = BrowserStore(
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(),
onIconVisibilityChange = {},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {
invokedCounter++
},
)
tested.start()
assertEquals(invokedCounter, 1)
}
}
@Test
fun `GIVEN feature is disabled WHEN non product url accessed THEN callback not called`() {
runTest {
var invokedCounter = 0
val tab = createTab(
url = "https://www.mozilla.org",
id = "test-tab",
isProductUrl = false,
)
val browserState = BrowserState(
tabs = listOf(tab),
selectedTabId = tab.id,
)
val tested = ReviewQualityCheckFeature(
appStore = AppStore(),
browserStore = BrowserStore(
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(enabled = false),
onIconVisibilityChange = {},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {
invokedCounter++
},
)
tested.start()
assertEquals(invokedCounter, 0)
}
}
@Test
fun `GIVEN feature is disabled WHEN product url accessed THEN callback called`() {
runTest {
var invokedCounter = 0
val tab = createTab(
url = "https://www.mozilla.org",
id = "test-tab",
isProductUrl = true,
).let {
it.copy(content = it.content.copy(loading = false))
}
val browserState = BrowserState(
tabs = listOf(tab),
selectedTabId = tab.id,
)
val tested = ReviewQualityCheckFeature(
appStore = AppStore(),
browserStore = BrowserStore(
initialState = browserState,
),
shoppingExperienceFeature = FakeShoppingExperienceFeature(enabled = false),
onIconVisibilityChange = {},
onBottomSheetStateChange = {},
debounceTimeoutMillis = { 0 },
onProductPageDetected = {
invokedCounter++
},
)
tested.start()
assertEquals(invokedCounter, 1)
}
}
}

Loading…
Cancel
Save