mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-19 09:25:34 +00:00
Bug 1853299 - Part 3: Update review checker error states
This commit is contained in:
parent
5a390424c7
commit
43a6c7992b
@ -35,6 +35,7 @@ class ReviewQualityCheckFragment : BottomSheetDialogFragment() {
|
||||
middleware = ReviewQualityCheckMiddlewareProvider.provideMiddleware(
|
||||
settings = requireComponents.settings,
|
||||
browserStore = requireComponents.core.store,
|
||||
context = requireContext(),
|
||||
openLink = { link, shouldOpenInNewTab ->
|
||||
(requireActivity() as HomeActivity).openToBrowserAndLoad(
|
||||
searchTermOrURL = link,
|
||||
|
@ -4,8 +4,10 @@
|
||||
|
||||
package org.mozilla.fenix.shopping.di
|
||||
|
||||
import android.content.Context
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import org.mozilla.fenix.shopping.middleware.NetworkCheckerImpl
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckNavigationMiddleware
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckNetworkMiddleware
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckPreferencesImpl
|
||||
@ -24,6 +26,7 @@ object ReviewQualityCheckMiddlewareProvider {
|
||||
*
|
||||
* @param settings The [Settings] instance to use.
|
||||
* @param browserStore The [BrowserStore] instance to access state.
|
||||
* @param context The [Context] instance to use.
|
||||
* @param openLink Opens a link. The callback is invoked with the URL [String] parameter and
|
||||
* whether or not it should open in a new or the currently selected tab [Boolean] parameter.
|
||||
* @param scope The [CoroutineScope] to use for launching coroutines.
|
||||
@ -31,12 +34,13 @@ object ReviewQualityCheckMiddlewareProvider {
|
||||
fun provideMiddleware(
|
||||
settings: Settings,
|
||||
browserStore: BrowserStore,
|
||||
context: Context,
|
||||
openLink: (String, Boolean) -> Unit,
|
||||
scope: CoroutineScope,
|
||||
): List<ReviewQualityCheckMiddleware> =
|
||||
listOf(
|
||||
providePreferencesMiddleware(settings, scope),
|
||||
provideNetworkMiddleware(browserStore, scope),
|
||||
provideNetworkMiddleware(browserStore, context, scope),
|
||||
provideNavigationMiddleware(openLink, scope),
|
||||
)
|
||||
|
||||
@ -50,9 +54,11 @@ object ReviewQualityCheckMiddlewareProvider {
|
||||
|
||||
private fun provideNetworkMiddleware(
|
||||
browserStore: BrowserStore,
|
||||
context: Context,
|
||||
scope: CoroutineScope,
|
||||
) = ReviewQualityCheckNetworkMiddleware(
|
||||
reviewQualityCheckService = ReviewQualityCheckServiceImpl(browserStore),
|
||||
networkChecker = NetworkCheckerImpl(context),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* 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.middleware
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import androidx.core.content.getSystemService
|
||||
import org.mozilla.fenix.ext.isOnline
|
||||
|
||||
/**
|
||||
* Checks if the device is connected to the internet.
|
||||
*/
|
||||
interface NetworkChecker {
|
||||
|
||||
/**
|
||||
* @return true if the device is connected to the internet, false otherwise.
|
||||
*/
|
||||
fun isConnected(): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [NetworkChecker].
|
||||
*/
|
||||
class NetworkCheckerImpl(private val context: Context) : NetworkChecker {
|
||||
|
||||
private val connectivityManager by lazy { context.getSystemService<ConnectivityManager>() }
|
||||
|
||||
override fun isConnected(): Boolean {
|
||||
return connectivityManager?.isOnline() ?: false
|
||||
}
|
||||
}
|
@ -17,17 +17,17 @@ import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn.ProductR
|
||||
*/
|
||||
fun ProductAnalysis?.toProductReviewState(): ProductReviewState =
|
||||
if (this == null) {
|
||||
ProductReviewState.Error
|
||||
ProductReviewState.Error.GenericError
|
||||
} else {
|
||||
when (this) {
|
||||
is GeckoProductAnalysis -> toProductReview()
|
||||
else -> ProductReviewState.Error
|
||||
else -> ProductReviewState.Error.GenericError
|
||||
}
|
||||
}
|
||||
|
||||
private fun GeckoProductAnalysis.toProductReview(): ProductReviewState =
|
||||
if (productId == null) {
|
||||
ProductReviewState.Error
|
||||
ProductReviewState.NoAnalysisPresent()
|
||||
} else {
|
||||
val mappedRating = adjustedRating.toFloatOrNull()
|
||||
val mappedGrade = grade?.toGrade()
|
||||
|
@ -13,15 +13,18 @@ import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction
|
||||
import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction.FetchProductAnalysis
|
||||
import org.mozilla.fenix.shopping.store.ReviewQualityCheckAction.RetryProductAnalysis
|
||||
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState
|
||||
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn.ProductReviewState
|
||||
|
||||
/**
|
||||
* Middleware that handles network requests for the review quality check feature.
|
||||
*
|
||||
* @property reviewQualityCheckService The service that handles the network requests.
|
||||
* @property networkChecker The [NetworkChecker] instance to check the network status.
|
||||
* @property scope The [CoroutineScope] that will be used to launch coroutines.
|
||||
*/
|
||||
class ReviewQualityCheckNetworkMiddleware(
|
||||
private val reviewQualityCheckService: ReviewQualityCheckService,
|
||||
private val networkChecker: NetworkChecker,
|
||||
private val scope: CoroutineScope,
|
||||
) : Middleware<ReviewQualityCheckState, ReviewQualityCheckAction> {
|
||||
|
||||
@ -46,8 +49,11 @@ class ReviewQualityCheckNetworkMiddleware(
|
||||
when (action) {
|
||||
FetchProductAnalysis, RetryProductAnalysis -> {
|
||||
scope.launch {
|
||||
val analysis = reviewQualityCheckService.fetchProductReview()
|
||||
val productReviewState = analysis.toProductReviewState()
|
||||
val productReviewState = if (networkChecker.isConnected()) {
|
||||
reviewQualityCheckService.fetchProductReview().toProductReviewState()
|
||||
} else {
|
||||
ProductReviewState.Error.NetworkError
|
||||
}
|
||||
store.dispatch(ReviewQualityCheckAction.UpdateProductReview(productReviewState))
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,22 @@ sealed interface ReviewQualityCheckState : State {
|
||||
/**
|
||||
* Denotes an error has occurred.
|
||||
*/
|
||||
object Error : ProductReviewState
|
||||
sealed interface Error : ProductReviewState {
|
||||
/**
|
||||
* Denotes a network error has occurred.
|
||||
*/
|
||||
object NetworkError : Error
|
||||
|
||||
/**
|
||||
* Denotes a product is not supported.
|
||||
*/
|
||||
object UnsupportedProductTypeError : Error
|
||||
|
||||
/**
|
||||
* Denotes a generic error has occurred.
|
||||
*/
|
||||
object GenericError : Error
|
||||
}
|
||||
|
||||
/**
|
||||
* Denotes no analysis is present for the product the user is browsing.
|
||||
@ -92,6 +107,11 @@ sealed interface ReviewQualityCheckState : State {
|
||||
highlights != null && showMoreButtonVisible &&
|
||||
highlights.forCompactMode().entries.first().value.size > 1
|
||||
|
||||
val notEnoughReviewsCardVisible: Boolean =
|
||||
(reviewGrade == null || adjustedRating == null) &&
|
||||
analysisStatus != AnalysisStatus.NEEDS_ANALYSIS &&
|
||||
analysisStatus != AnalysisStatus.REANALYZING
|
||||
|
||||
/**
|
||||
* The status of the product analysis.
|
||||
*/
|
||||
|
@ -107,16 +107,16 @@ class ProductAnalysisMapperTest {
|
||||
@Test
|
||||
fun `WHEN product analysis is null THEN it is mapped to Error`() {
|
||||
val actual = null.toProductReviewState()
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.Error
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.Error.GenericError
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN product id is null THEN it is mapped to Error`() {
|
||||
fun `WHEN product id is null THEN it is mapped to no analysis present`() {
|
||||
val actual =
|
||||
ProductAnalysisTestData.productAnalysis(productId = null).toProductReviewState()
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.Error
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.NoAnalysisPresent()
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
@ -141,7 +141,7 @@ class ProductAnalysisMapperTest {
|
||||
}
|
||||
|
||||
val actual = randomAnalysis.toProductReviewState()
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.Error
|
||||
val expected = ReviewQualityCheckState.OptedIn.ProductReviewState.Error.GenericError
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import org.junit.Assert.assertThrows
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.shopping.ProductAnalysisTestData
|
||||
import org.mozilla.fenix.shopping.store.ReviewQualityCheckState.OptedIn.ProductReviewState.AnalysisPresent.AnalysisStatus
|
||||
|
||||
class ReviewQualityCheckStateTest {
|
||||
|
||||
@ -221,4 +222,51 @@ class ReviewQualityCheckStateTest {
|
||||
assertTrue(analysis.showMoreButtonVisible)
|
||||
assertFalse(analysis.highlightsFadeVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN AnalysisPresent is created with grade or rating as null THEN not enough reviews card is visible`() {
|
||||
val analysisWithoutGrade = ProductAnalysisTestData.analysisPresent(
|
||||
reviewGrade = null,
|
||||
adjustedRating = 3.2f,
|
||||
analysisStatus = AnalysisStatus.COMPLETED,
|
||||
)
|
||||
|
||||
val analysisWithoutRatings = ProductAnalysisTestData.analysisPresent(
|
||||
reviewGrade = ReviewQualityCheckState.Grade.A,
|
||||
adjustedRating = null,
|
||||
analysisStatus = AnalysisStatus.UP_TO_DATE,
|
||||
)
|
||||
|
||||
assertTrue(analysisWithoutGrade.notEnoughReviewsCardVisible)
|
||||
assertTrue(analysisWithoutRatings.notEnoughReviewsCardVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN AnalysisPresent is created with grade or rating as null and analysis status is needs analysis or reanalyzing THEN not enough reviews card is not visible`() {
|
||||
val analysisWithoutGrade = ProductAnalysisTestData.analysisPresent(
|
||||
reviewGrade = null,
|
||||
adjustedRating = 3.2f,
|
||||
analysisStatus = AnalysisStatus.NEEDS_ANALYSIS,
|
||||
)
|
||||
|
||||
val analysisWithoutRatings = ProductAnalysisTestData.analysisPresent(
|
||||
reviewGrade = ReviewQualityCheckState.Grade.A,
|
||||
adjustedRating = null,
|
||||
analysisStatus = AnalysisStatus.REANALYZING,
|
||||
)
|
||||
|
||||
assertFalse(analysisWithoutGrade.notEnoughReviewsCardVisible)
|
||||
assertFalse(analysisWithoutRatings.notEnoughReviewsCardVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN AnalysisPresent is created with both grade and rating THEN not enough reviews card is not visible`() {
|
||||
val analysis = ProductAnalysisTestData.analysisPresent(
|
||||
reviewGrade = ReviewQualityCheckState.Grade.A,
|
||||
adjustedRating = 3.2f,
|
||||
analysisStatus = AnalysisStatus.UP_TO_DATE,
|
||||
)
|
||||
|
||||
assertFalse(analysis.notEnoughReviewsCardVisible)
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import mozilla.components.support.test.whenever
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.shopping.ProductAnalysisTestData
|
||||
import org.mozilla.fenix.shopping.middleware.NetworkChecker
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckNetworkMiddleware
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckPreferences
|
||||
import org.mozilla.fenix.shopping.middleware.ReviewQualityCheckPreferencesMiddleware
|
||||
@ -167,6 +168,7 @@ class ReviewQualityCheckStoreTest {
|
||||
middleware = provideReviewQualityCheckMiddleware(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(isEnabled = true),
|
||||
reviewQualityCheckService = reviewQualityCheckService,
|
||||
networkChecker = FakeNetworkChecker(isConnected = true),
|
||||
),
|
||||
)
|
||||
tested.waitUntilIdle()
|
||||
@ -184,7 +186,7 @@ class ReviewQualityCheckStoreTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the user opts in the feature THEN update the preferences`() =
|
||||
fun `GIVEN the user has opted in the feature WHEN the a product analysis returns an error THEN state should reflect that`() =
|
||||
runTest {
|
||||
val reviewQualityCheckService = mock<ReviewQualityCheckService>()
|
||||
whenever(reviewQualityCheckService.fetchProductReview()).thenReturn(null)
|
||||
@ -193,6 +195,7 @@ class ReviewQualityCheckStoreTest {
|
||||
middleware = provideReviewQualityCheckMiddleware(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(isEnabled = true),
|
||||
reviewQualityCheckService = reviewQualityCheckService,
|
||||
networkChecker = FakeNetworkChecker(isConnected = true),
|
||||
),
|
||||
)
|
||||
tested.waitUntilIdle()
|
||||
@ -203,7 +206,31 @@ class ReviewQualityCheckStoreTest {
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.OptedIn(
|
||||
productReviewState = ReviewQualityCheckState.OptedIn.ProductReviewState.Error,
|
||||
productReviewState = ReviewQualityCheckState.OptedIn.ProductReviewState.Error.GenericError,
|
||||
productRecommendationsPreference = false,
|
||||
)
|
||||
assertEquals(expected, tested.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the user has opted in the feature WHEN device is not connected to the internet THEN state should reflect that`() =
|
||||
runTest {
|
||||
val tested = ReviewQualityCheckStore(
|
||||
middleware = provideReviewQualityCheckMiddleware(
|
||||
reviewQualityCheckPreferences = FakeReviewQualityCheckPreferences(isEnabled = true),
|
||||
reviewQualityCheckService = mock(),
|
||||
networkChecker = FakeNetworkChecker(isConnected = false),
|
||||
),
|
||||
)
|
||||
tested.waitUntilIdle()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
tested.waitUntilIdle()
|
||||
tested.dispatch(ReviewQualityCheckAction.FetchProductAnalysis).joinBlocking()
|
||||
tested.waitUntilIdle()
|
||||
dispatcher.scheduler.advanceUntilIdle()
|
||||
|
||||
val expected = ReviewQualityCheckState.OptedIn(
|
||||
productReviewState = ReviewQualityCheckState.OptedIn.ProductReviewState.Error.NetworkError,
|
||||
productRecommendationsPreference = false,
|
||||
)
|
||||
assertEquals(expected, tested.state)
|
||||
@ -212,8 +239,9 @@ class ReviewQualityCheckStoreTest {
|
||||
private fun provideReviewQualityCheckMiddleware(
|
||||
reviewQualityCheckPreferences: ReviewQualityCheckPreferences,
|
||||
reviewQualityCheckService: ReviewQualityCheckService? = null,
|
||||
networkChecker: NetworkChecker? = null,
|
||||
): List<ReviewQualityCheckMiddleware> {
|
||||
return if (reviewQualityCheckService != null) {
|
||||
return if (reviewQualityCheckService != null && networkChecker != null) {
|
||||
listOf(
|
||||
ReviewQualityCheckPreferencesMiddleware(
|
||||
reviewQualityCheckPreferences = reviewQualityCheckPreferences,
|
||||
@ -221,6 +249,7 @@ class ReviewQualityCheckStoreTest {
|
||||
),
|
||||
ReviewQualityCheckNetworkMiddleware(
|
||||
reviewQualityCheckService = reviewQualityCheckService,
|
||||
networkChecker = networkChecker,
|
||||
scope = this.scope,
|
||||
),
|
||||
)
|
||||
@ -254,3 +283,9 @@ private class FakeReviewQualityCheckPreferences(
|
||||
updateCFRCallback()
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeNetworkChecker(
|
||||
private val isConnected: Boolean,
|
||||
) : NetworkChecker {
|
||||
override fun isConnected(): Boolean = isConnected
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user