Bug 1843029 - Add preferences for shopping experience
parent
21068e8bb3
commit
e1a84285da
@ -0,0 +1,61 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.shopping.state
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* Interface to get and set preferences for the review quality check feature.
|
||||
*/
|
||||
interface ReviewQualityCheckPreferences {
|
||||
/**
|
||||
* Returns true if the user has opted in to the review quality check feature.
|
||||
*/
|
||||
suspend fun enabled(): Boolean
|
||||
|
||||
/**
|
||||
* Returns true if the user has enabled product recommendations.
|
||||
*/
|
||||
suspend fun productRecommendationsEnabled(): Boolean
|
||||
|
||||
/**
|
||||
* Sets whether the user has opted in to the review quality check feature.
|
||||
*/
|
||||
suspend fun setEnabled(isEnabled: Boolean)
|
||||
|
||||
/**
|
||||
* Sets whether the user has enabled product recommendations.
|
||||
*/
|
||||
suspend fun setProductRecommendationsEnabled(isEnabled: Boolean)
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of [ReviewQualityCheckPreferences] that uses [Settings] to store/fetch
|
||||
* preferences.
|
||||
*
|
||||
* @param settings The [Settings] instance to use.
|
||||
*/
|
||||
class ReviewQualityCheckPreferencesImpl(
|
||||
private val settings: Settings,
|
||||
) : ReviewQualityCheckPreferences {
|
||||
|
||||
override suspend fun enabled(): Boolean = withContext(Dispatchers.IO) {
|
||||
settings.isReviewQualityCheckEnabled
|
||||
}
|
||||
|
||||
override suspend fun productRecommendationsEnabled(): Boolean = withContext(Dispatchers.IO) {
|
||||
settings.isReviewQualityCheckProductRecommendationsEnabled
|
||||
}
|
||||
|
||||
override suspend fun setEnabled(isEnabled: Boolean) {
|
||||
settings.isReviewQualityCheckEnabled = isEnabled
|
||||
}
|
||||
|
||||
override suspend fun setProductRecommendationsEnabled(isEnabled: Boolean) {
|
||||
settings.isReviewQualityCheckProductRecommendationsEnabled = isEnabled
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.shopping.state
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.lib.state.Middleware
|
||||
import mozilla.components.lib.state.MiddlewareContext
|
||||
import mozilla.components.lib.state.Store
|
||||
|
||||
/**
|
||||
* Middleware for getting and setting review quality check user preferences.
|
||||
*
|
||||
* @param reviewQualityCheckPreferences The [ReviewQualityCheckPreferences] instance to use.
|
||||
* @param scope The [CoroutineScope] to use for launching coroutines.
|
||||
*/
|
||||
class ReviewQualityCheckPreferencesMiddleware(
|
||||
private val reviewQualityCheckPreferences: ReviewQualityCheckPreferences,
|
||||
private val scope: CoroutineScope,
|
||||
) : Middleware<ReviewQualityCheckState, ReviewQualityCheckAction> {
|
||||
|
||||
override fun invoke(
|
||||
context: MiddlewareContext<ReviewQualityCheckState, ReviewQualityCheckAction>,
|
||||
next: (ReviewQualityCheckAction) -> Unit,
|
||||
action: ReviewQualityCheckAction,
|
||||
) {
|
||||
when (action) {
|
||||
is ReviewQualityCheckAction.PreferencesMiddlewareAction -> {
|
||||
processAction(context.store, action)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
// Forward the actions
|
||||
next(action)
|
||||
}
|
||||
|
||||
private fun processAction(
|
||||
store: Store<ReviewQualityCheckState, ReviewQualityCheckAction>,
|
||||
action: ReviewQualityCheckAction.PreferencesMiddlewareAction,
|
||||
) {
|
||||
when (action) {
|
||||
is ReviewQualityCheckAction.Init -> {
|
||||
scope.launch {
|
||||
val hasUserOptedIn = reviewQualityCheckPreferences.enabled()
|
||||
val isProductRecommendationsEnabled =
|
||||
reviewQualityCheckPreferences.productRecommendationsEnabled()
|
||||
store.dispatch(
|
||||
ReviewQualityCheckAction.UpdateUserPreferences(
|
||||
hasUserOptedIn = hasUserOptedIn,
|
||||
isProductRecommendationsEnabled = isProductRecommendationsEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ReviewQualityCheckAction.OptIn -> {
|
||||
scope.launch {
|
||||
val isProductRecommendationsEnabled =
|
||||
reviewQualityCheckPreferences.productRecommendationsEnabled()
|
||||
store.dispatch(
|
||||
ReviewQualityCheckAction.UpdateUserPreferences(
|
||||
hasUserOptedIn = true,
|
||||
isProductRecommendationsEnabled = isProductRecommendationsEnabled,
|
||||
),
|
||||
)
|
||||
|
||||
// Update the preference
|
||||
reviewQualityCheckPreferences.setEnabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
ReviewQualityCheckAction.OptOut -> {
|
||||
scope.launch {
|
||||
// Update the preference
|
||||
reviewQualityCheckPreferences.setEnabled(false)
|
||||
}
|
||||
}
|
||||
|
||||
ReviewQualityCheckAction.ToggleProductRecommendation -> {
|
||||
scope.launch {
|
||||
reviewQualityCheckPreferences.setProductRecommendationsEnabled(
|
||||
!reviewQualityCheckPreferences.productRecommendationsEnabled(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/* 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/. */
|
||||
|
||||
package org.mozilla.fenix.shopping.state
|
||||
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import mozilla.components.support.test.ext.joinBlocking
|
||||
import mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class ReviewQualityCheckStoreTest {
|
||||
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule()
|
||||
private val dispatcher = coroutinesTestRule.testDispatcher
|
||||
private val scope = coroutinesTestRule.scope
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has not opted in the feature WHEN store is created THEN state should display not opted in UI`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(
|
||||
isEnabled = false,
|
||||
),
|
||||
scope = scope,
|
||||
)
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.NotOptedIn
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has not opted in the feature WHEN the user opts in THEN state should display opted in UI`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(
|
||||
isEnabled = false,
|
||||
isProductRecommendationsEnabled = false,
|
||||
),
|
||||
scope = scope,
|
||||
)
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
tested.dispatch(ReviewQualityCheckAction.OptIn).joinBlocking()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.OptedIn(productRecommendationsPreference = false)
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has opted in the feature WHEN the user opts out THEN state should display not opted in UI`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(
|
||||
isEnabled = true,
|
||||
isProductRecommendationsEnabled = true,
|
||||
),
|
||||
scope = scope,
|
||||
)
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
tested.dispatch(ReviewQualityCheckAction.OptOut).joinBlocking()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.NotOptedIn
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has opted in the feature and product recommendations are off WHEN the user turns on product recommendations THEN state should reflect that`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(
|
||||
isEnabled = true,
|
||||
isProductRecommendationsEnabled = false,
|
||||
),
|
||||
scope = scope,
|
||||
)
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
tested.dispatch(ReviewQualityCheckAction.ToggleProductRecommendation).joinBlocking()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.OptedIn(productRecommendationsPreference = true)
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has opted in the feature and product recommendations are on WHEN the user turns off product recommendations THEN state should reflect that`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(
|
||||
isEnabled = true,
|
||||
isProductRecommendationsEnabled = true,
|
||||
),
|
||||
scope = scope,
|
||||
)
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
tested.dispatch(ReviewQualityCheckAction.ToggleProductRecommendation).joinBlocking()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.OptedIn(productRecommendationsPreference = false)
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeReviewQualityCheckPreferences(
|
||||
private val isEnabled: Boolean = false,
|
||||
private val isProductRecommendationsEnabled: Boolean = false,
|
||||
) : ReviewQualityCheckPreferences {
|
||||
override suspend fun enabled(): Boolean = isEnabled
|
||||
|
||||
override suspend fun productRecommendationsEnabled(): Boolean = isProductRecommendationsEnabled
|
||||
|
||||
override suspend fun setEnabled(isEnabled: Boolean) {
|
||||
}
|
||||
|
||||
override suspend fun setProductRecommendationsEnabled(isEnabled: Boolean) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue