Skip to content

Commit

Permalink
Merge pull request #10624 from woocommerce/fix/widget-woo-express-stores
Browse files Browse the repository at this point in the history
Fix widget for woo express stores
  • Loading branch information
atorresveiga authored Feb 2, 2024
2 parents 57f48eb + 0e73991 commit 0681588
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class GetWidgetStats @Inject constructor(
// If siteModel is null, exit the function with WidgetStatsFailure
siteModel == null -> WidgetStatsResult.WidgetStatsFailure("No site selected")
else -> {
// Fetch stats, always force to refresh data
val areVisitorStatsSupported = siteModel.connectionType == SiteConnectionType.Jetpack

// Fetch stats, always force to refresh data.
val fetchedStats = statsRepository.fetchStats(
granularity = granularity,
forced = true,
Expand All @@ -47,7 +47,7 @@ class GetWidgetStats @Inject constructor(
if (fetchedStats.isError) {
WidgetStatsResult.WidgetStatsFailure(fetchedStats.error.message)
} else {
WidgetStatsResult.WidgetStats(fetchedStats.model!!, areVisitorStatsSupported)
WidgetStatsResult.WidgetStats(fetchedStats.model!!)
}
}
}
Expand All @@ -62,15 +62,13 @@ class GetWidgetStats @Inject constructor(
data class WidgetStats(
private val revenueModel: WCRevenueStatsModel?,
private val visitorsMap: Map<String, Int>?,
val currencyCode: String,
val areVisitorStatsSupported: Boolean
val currencyCode: String
) : WidgetStatsResult() {
constructor(
stats: StatsRepository.SiteStats,
areVisitorStatsSupported: Boolean
) : this(stats.revenue, stats.visitors, stats.currencyCode, areVisitorStatsSupported)
) : this(stats.revenue, stats.visitors, stats.currencyCode)

val visitorsTotal: Int
val visitorsTotal: Int?
val ordersTotal: Int
val revenueGross: Double

Expand All @@ -82,7 +80,7 @@ class GetWidgetStats @Inject constructor(
orderCount = total.ordersCount ?: 0
}

visitorsTotal = visitorsMap?.values?.sum() ?: 0
visitorsTotal = visitorsMap?.values?.sum()
ordersTotal = orderCount
revenueGross = grossRevenue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ class TodayStatsWidgetUIHelper @Inject constructor(

remoteViews.setTextViewText(R.id.widget_revenue_value, revenue)
remoteViews.setTextViewText(R.id.widget_orders_value, stats.ordersTotal.toString())
remoteViews.setTextViewText(R.id.widget_visitors_value, stats.visitorsTotal.toString())

stats.visitorsTotal?.let {
remoteViews.setTextViewText(R.id.widget_visitors_value, it.toString())
}
remoteViews.setViewVisibility(R.id.widget_revenue_value, View.VISIBLE)
remoteViews.setViewVisibility(R.id.widget_revenue_skeleton, View.INVISIBLE)

Expand All @@ -80,11 +81,11 @@ class TodayStatsWidgetUIHelper @Inject constructor(

remoteViews.setViewVisibility(
R.id.widget_visitors_title,
if (stats.areVisitorStatsSupported) View.VISIBLE else View.GONE
if (stats.visitorsTotal != null) View.VISIBLE else View.GONE
)
remoteViews.setViewVisibility(
R.id.widget_visitors_value,
if (stats.areVisitorStatsSupported) View.VISIBLE else View.GONE
if (stats.visitorsTotal != null) View.VISIBLE else View.GONE
)

remoteViews.setTextViewText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ class StatsRepository @Inject constructor(
}
}

/**
* This function will return the site stats optional including visitor stats.
* Even if the includeVisitorStats flag is set to true, errors fetching visitor
* will be handled as null and only errors fetching the revenue stats will be processed.
*/
suspend fun fetchStats(
granularity: StatsGranularity,
forced: Boolean,
Expand Down Expand Up @@ -314,7 +319,10 @@ class StatsRepository @Inject constructor(
val revenueStats = fetchRevenueStats.await()
val siteCurrencyCode = wooCommerceStore.getSiteSettings(site)?.currencyCode.orEmpty()

return@coroutineScope if (visitorStats.isError || revenueStats.isError) {
// If there was an error fetching the visitor stats chances are that is because
// jetpack is not properly configure to return stats. So we take into account
// only revenue stats to return process the error response.
return@coroutineScope if (revenueStats.isError) {
val error = WooError(
type = WooErrorType.GENERIC_ERROR,
original = BaseRequest.GenericErrorType.UNKNOWN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class GetWidgetStatsTest : BaseUnitTest() {
}

@Test
fun `when fetchStats succeed then get stats respond with WidgetStatsFailure`() =
fun `when fetchStats succeed then get stats respond with WidgetStats`() =
testBlocking {
// Given the user is logged, v4 stats is supported and network is working fine
whenever(accountRepository.isUserLoggedIn()).thenReturn(true)
Expand All @@ -146,7 +146,7 @@ class GetWidgetStatsTest : BaseUnitTest() {

// When GetWidgetStats is invoked
val result = sut.invoke(defaultGranularity, defaultSiteModel)
val expected = GetWidgetStats.WidgetStatsResult.WidgetStats(defaultResponse, true)
val expected = GetWidgetStats.WidgetStatsResult.WidgetStats(defaultResponse)

// Then the result is WidgetStatsFailure
assertThat(result).isEqualTo(expected)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.woocommerce.android.ui.mystore

import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.ui.mystore.data.StatsRepository
import com.woocommerce.android.viewmodel.BaseUnitTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.assertj.core.api.Assertions.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.wordpress.android.fluxc.action.WCStatsAction
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.model.WCRevenueStatsModel
import org.wordpress.android.fluxc.store.WCLeaderboardsStore
import org.wordpress.android.fluxc.store.WCOrderStore
import org.wordpress.android.fluxc.store.WCStatsStore
import org.wordpress.android.fluxc.store.WooCommerceStore

@OptIn(ExperimentalCoroutinesApi::class)
class StatsRepositoryTests : BaseUnitTest() {

private val selectedSite: SelectedSite = mock()
private val wcStatsStore: WCStatsStore = mock()
private val wcOrderStore: WCOrderStore = mock()
private val wcLeaderboardsStore: WCLeaderboardsStore = mock()
private val wooCommerceStore: WooCommerceStore = mock()

private lateinit var sut: StatsRepository

private val defaultSiteModel = SiteModel()

@Before
fun setup() {
sut = StatsRepository(
selectedSite = selectedSite,
wcStatsStore = wcStatsStore,
wcOrderStore = wcOrderStore,
wcLeaderboardsStore = wcLeaderboardsStore,
wooCommerceStore = wooCommerceStore
)
}

@Test
fun `when visitors and revenue requests succeed then a success response is returned containing both value`() = testBlocking {
val granularity = WCStatsStore.StatsGranularity.DAYS
val startDate = "2024-01-25 00:00:00"
val endDate = "2024-01-25 23:59:59"
val visitorStatsResponse = WCStatsStore.OnWCStatsChanged(
rowsAffected = 2,
granularity = granularity,
quantity = "5",
date = startDate
)

val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged(
rowsAffected = 2,
granularity = granularity,
startDate = startDate,
endDate = endDate
)

whenever(selectedSite.get()).thenReturn(defaultSiteModel)
whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null)
whenever(wcStatsStore.fetchNewVisitorStats(any())).thenReturn(visitorStatsResponse)
whenever(wcStatsStore.fetchRevenueStats(any())).thenReturn(revenueStatsResponse)
whenever(wcStatsStore.getRawRevenueStats(eq(defaultSiteModel), eq(granularity), eq(startDate), eq(endDate)))
.thenReturn(WCRevenueStatsModel())

val result = sut.fetchStats(
granularity = WCStatsStore.StatsGranularity.DAYS,
forced = true,
includeVisitorStats = true
)

assertThat(result.isError).isEqualTo(false)
assertThat(result.model).isNotNull
assertThat(result.model!!.revenue).isNotNull
assertThat(result.model!!.visitors).isNotNull
}

@Test
fun `when visitors requests fails then a success response is returned with visitors null`() = testBlocking {
val granularity = WCStatsStore.StatsGranularity.DAYS
val startDate = "2024-01-25 00:00:00"
val endDate = "2024-01-25 23:59:59"
val visitorStatsResponse = WCStatsStore.OnWCStatsChanged(0, granularity).also {
it.error = WCStatsStore.OrderStatsError()
it.causeOfChange = WCStatsAction.FETCH_NEW_VISITOR_STATS
}

val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged(
rowsAffected = 2,
granularity = granularity,
startDate = startDate,
endDate = endDate
)

whenever(selectedSite.get()).thenReturn(defaultSiteModel)
whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null)
whenever(wcStatsStore.fetchNewVisitorStats(any())).thenReturn(visitorStatsResponse)
whenever(wcStatsStore.fetchRevenueStats(any())).thenReturn(revenueStatsResponse)
whenever(wcStatsStore.getRawRevenueStats(eq(defaultSiteModel), eq(granularity), eq(startDate), eq(endDate)))
.thenReturn(WCRevenueStatsModel())

val result = sut.fetchStats(
granularity = WCStatsStore.StatsGranularity.DAYS,
forced = true,
includeVisitorStats = true
)

assertThat(result.isError).isEqualTo(false)
assertThat(result.model).isNotNull
assertThat(result.model!!.revenue).isNotNull
assertThat(result.model!!.visitors).isNull()
}

@Test
fun `when revenue requests fails then an error is returned`() = testBlocking {
val granularity = WCStatsStore.StatsGranularity.DAYS
val startDate = "2024-01-25 00:00:00"
val visitorStatsResponse = WCStatsStore.OnWCStatsChanged(
rowsAffected = 2,
granularity = granularity,
quantity = "5",
date = startDate
)

val revenueStatsResponse = WCStatsStore.OnWCRevenueStatsChanged(0, granularity)
.also { it.error = WCStatsStore.OrderStatsError() }

whenever(selectedSite.get()).thenReturn(defaultSiteModel)
whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(null)
whenever(wcStatsStore.fetchNewVisitorStats(any())).thenReturn(visitorStatsResponse)
whenever(wcStatsStore.fetchRevenueStats(any())).thenReturn(revenueStatsResponse)

val result = sut.fetchStats(
granularity = WCStatsStore.StatsGranularity.DAYS,
forced = true,
includeVisitorStats = true
)

assertThat(result.isError).isEqualTo(true)
assertThat(result.model).isNull()
}
}

0 comments on commit 0681588

Please sign in to comment.