From ea8281ef36b9a72d711f47e6e150d783466296d2 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:26:09 +0530 Subject: [PATCH 01/13] Add string resource for pos menu description for ineligible stores --- WooCommerce/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 3e1f5618560..bbc6476d261 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -3832,6 +3832,7 @@ Point of Sale Mode Accept payments at your physical store + Upgrade your site to the latest WooCommerce to use Point of Sale Customers From 3ef1d741d357d33a7290c3581004e21e9aa998cc Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:26:46 +0530 Subject: [PATCH 02/13] Add analytics key and values for POS ineligible menu description --- .../com/woocommerce/android/analytics/AnalyticsTracker.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt index 355ea09edf2..29ecddd2dd9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt @@ -479,6 +479,8 @@ class AnalyticsTracker private constructor( // We have to call in non consistent way to match the iOS naming const val VALUE_MORE_MENU_POS = "pointOfSale" + const val KEY_POS_NOT_ELIGIBLE = "not_eligible" + const val VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION = "outdated_woocommerce_version" const val VALUE_MORE_MENU_PAYMENTS_BADGE_VISIBLE = "badge_visible" From 75395fd497bf31746f4ecaec04081dacab4c421d Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:27:15 +0530 Subject: [PATCH 03/13] Add woo core update required event --- .../kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt index dddbb57449f..ad5fd3892ca 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt @@ -32,6 +32,7 @@ sealed class MoreMenuEvent : MultiLiveEvent.Event() { data object ViewCustomersEvent : MoreMenuEvent() data object NavigateToWooPosEvent : MoreMenuEvent() + data object ShowWooPosWooCoreUpdateRequiredEvent : MoreMenuEvent() } data class MoreMenuItemSection( From a159ac6d74f00c8a94123852c4ef2bc25ae024cc Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:27:34 +0530 Subject: [PATCH 04/13] Handle Woocommerce update required logic for POS --- .../android/ui/moremenu/MoreMenuFragment.kt | 6 ++ .../android/ui/moremenu/MoreMenuViewModel.kt | 73 ++++++++++++++++--- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuFragment.kt index 8f1cbe68e5e..c573bc72ae0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuFragment.kt @@ -28,6 +28,7 @@ import com.woocommerce.android.ui.moremenu.MoreMenuEvent.NavigateToSubscriptions import com.woocommerce.android.ui.moremenu.MoreMenuEvent.NavigateToWooPosEvent import com.woocommerce.android.ui.moremenu.MoreMenuEvent.OpenBlazeCampaignCreationEvent import com.woocommerce.android.ui.moremenu.MoreMenuEvent.OpenBlazeCampaignListEvent +import com.woocommerce.android.ui.moremenu.MoreMenuEvent.ShowWooPosWooCoreUpdateRequiredEvent import com.woocommerce.android.ui.moremenu.MoreMenuEvent.StartSitePickerEvent import com.woocommerce.android.ui.moremenu.MoreMenuEvent.ViewAdminEvent import com.woocommerce.android.ui.moremenu.MoreMenuEvent.ViewCouponsEvent @@ -42,6 +43,7 @@ import com.woocommerce.android.ui.woopos.root.WooPosActivity import com.woocommerce.android.util.ChromeCustomTabUtils import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import org.wordpress.android.util.ToastUtils import javax.inject.Inject @AndroidEntryPoint @@ -113,6 +115,10 @@ class MoreMenuFragment : TopLevelFragment() { is OpenBlazeCampaignCreationEvent -> openBlazeCreationFlow() is OpenBlazeCampaignListEvent -> openBlazeCampaignList() is NavigateToWooPosEvent -> openWooPos() + is ShowWooPosWooCoreUpdateRequiredEvent -> ToastUtils.showToast( + requireContext(), + R.string.more_menu_button_woo_pos_update_woocommerce_version_description + ) } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt index f5c697cc2a0..0d179e1d577 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt @@ -9,6 +9,7 @@ import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_CAMPAIGN_LIST_ENTR import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_ENTRY_POINT_DISPLAYED import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_OPTION +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_POS_NOT_ELIGIBLE import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_ADMIN_MENU import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_COUPONS import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_CUSTOMERS @@ -19,8 +20,10 @@ import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_M import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_REVIEWS import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_UPGRADES import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_VIEW_STORE +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.extensions.adminUrlOrDefault +import com.woocommerce.android.extensions.semverCompareTo import com.woocommerce.android.notifications.UnseenReviewsCountHandler import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.tools.SiteConnectionType @@ -35,6 +38,7 @@ import com.woocommerce.android.ui.payments.taptopay.isAvailable import com.woocommerce.android.ui.plans.domain.SitePlan import com.woocommerce.android.ui.plans.repository.SitePlanRepository import com.woocommerce.android.ui.woopos.WooPosIsEnabled +import com.woocommerce.android.util.GetWooCorePluginCachedVersion import com.woocommerce.android.util.WooLog import com.woocommerce.android.viewmodel.ResourceProvider import com.woocommerce.android.viewmodel.ScopedViewModel @@ -71,7 +75,8 @@ class MoreMenuViewModel @Inject constructor( private val isGoogleForWooEnabled: IsGoogleForWooEnabled, private val hasGoogleAdsCampaigns: HasGoogleAdsCampaigns, private val isWooPosEnabled: WooPosIsEnabled, - private val analyticsTrackerWrapper: AnalyticsTrackerWrapper + private val analyticsTrackerWrapper: AnalyticsTrackerWrapper, + private val getWooCoreVersion: GetWooCorePluginCachedVersion, ) : ScopedViewModel(savedState) { private var storeHasGoogleAdsCampaigns = false @@ -134,20 +139,41 @@ class MoreMenuViewModel @Inject constructor( } } + private fun isWooCoreVersionSupported(): Boolean { + val wooCoreVersion = getWooCoreVersion() ?: return false + return wooCoreVersion.semverCompareTo(WC_VERSION_SUPPORTS_POS_PRODUCT_FILTERING) >= 0 + } + private fun generatePOSSection(wooPosState: MoreMenuItemButton.State) = - MoreMenuItemSection( - title = null, - items = listOf( - MoreMenuItemButton( - title = R.string.more_menu_button_woo_pos, - description = R.string.more_menu_button_woo_pos_description, - icon = R.drawable.ic_more_menu_pos, - extraIcon = R.drawable.ic_more_menu_pos_extra, - state = wooPosState, - onClick = ::onWooPosButtonClick, + if (isWooCoreVersionSupported()) { + MoreMenuItemSection( + title = null, + items = listOf( + MoreMenuItemButton( + title = R.string.more_menu_button_woo_pos, + description = R.string.more_menu_button_woo_pos_description, + icon = R.drawable.ic_more_menu_pos, + extraIcon = R.drawable.ic_more_menu_pos_extra, + state = wooPosState, + onClick = ::onWooPosButtonClick, + ) ) ) - ) + } else { + MoreMenuItemSection( + title = null, + items = listOf( + MoreMenuItemButton( + title = R.string.more_menu_button_woo_pos, + description = R.string.more_menu_button_woo_pos_update_woocommerce_version_description, + icon = R.drawable.ic_more_menu_pos, + extraIcon = R.drawable.ic_more_menu_pos_extra, + state = wooPosState, + onClick = { onWooPOSNotEligibleButtonClick(WooPosNotEligible.OutdatedWooCommerceVersion) }, + ) + ) + ) + } @Suppress("LongMethod") private fun generateGeneralSection( @@ -400,6 +426,21 @@ class MoreMenuViewModel @Inject constructor( triggerEvent(MoreMenuEvent.NavigateToWooPosEvent) } + private fun onWooPOSNotEligibleButtonClick(reason: WooPosNotEligible) { + when (reason) { + WooPosNotEligible.OutdatedWooCommerceVersion -> { + trackMoreMenuOptionSelected( + VALUE_MORE_MENU_POS, + extraOptions = mapOf( + KEY_POS_NOT_ELIGIBLE to VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION + ) + ) + triggerEvent(MoreMenuEvent.ShowWooPosWooCoreUpdateRequiredEvent) + } + } + + } + private fun onViewAdminButtonClick() { trackMoreMenuOptionSelected(VALUE_MORE_MENU_ADMIN_MENU) triggerEvent(MoreMenuEvent.ViewAdminEvent(selectedSite.get().adminUrlOrDefault)) @@ -488,4 +529,12 @@ class MoreMenuViewModel @Inject constructor( private val SitePlan.formattedPlanName get() = generateFormattedPlanName(resourceProvider) + + sealed class WooPosNotEligible { + data object OutdatedWooCommerceVersion : WooPosNotEligible() + } + + private companion object { + const val WC_VERSION_SUPPORTS_POS_PRODUCT_FILTERING = "9.6.0" + } } From 9f22b35409cdd9af679b9cb42ca903ba83213e5c Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:27:42 +0530 Subject: [PATCH 05/13] Add tests --- .../ui/moremenu/MoreMenuViewModelTests.kt | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt index 41a0a0c802f..e524c444094 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt @@ -14,6 +14,7 @@ import com.woocommerce.android.ui.payments.taptopay.TapToPayAvailabilityStatus import com.woocommerce.android.ui.plans.domain.SitePlan import com.woocommerce.android.ui.plans.repository.SitePlanRepository import com.woocommerce.android.ui.woopos.WooPosIsEnabled +import com.woocommerce.android.util.GetWooCorePluginCachedVersion import com.woocommerce.android.util.captureValues import com.woocommerce.android.util.runAndCaptureValues import com.woocommerce.android.viewmodel.BaseUnitTest @@ -94,6 +95,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { private lateinit var viewModel: MoreMenuViewModel private val tapToPayAvailabilityStatus: TapToPayAvailabilityStatus = mock() + private val getWooCoreVersion: GetWooCorePluginCachedVersion = mock() suspend fun setup(setupMocks: suspend () -> Unit = {}) { setupMocks() @@ -113,6 +115,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { hasGoogleAdsCampaigns = hasGoogleAdsCampaigns, isWooPosEnabled = isWooPosEnabled, analyticsTrackerWrapper = analyticsTrackerWrapper, + getWooCoreVersion = getWooCoreVersion, ) } @@ -468,6 +471,54 @@ class MoreMenuViewModelTests : BaseUnitTest() { ).isEqualTo(MoreMenuItemButton.State.Visible) } + @Test + fun `given outdated WooCommerce version, when building state, then WooPOS section is displayed with upgrade woocommerce description`() = testBlocking { + // GIVEN + setup { + whenever(isWooPosEnabled.invoke()).thenReturn(true) + whenever(getWooCoreVersion()).thenReturn("3.0.0") + } + + // WHEN + val states = viewModel.moreMenuViewState.captureValues() + + // THEN + assertThat( + states.last().menuSections.flatMap { it.items } + .first { it.title == R.string.more_menu_button_woo_pos }.state + ).isEqualTo(MoreMenuItemButton.State.Visible) + assertThat( + states.last().menuSections.flatMap { it.items } + .first { + it.description == R.string.more_menu_button_woo_pos_update_woocommerce_version_description + }.state + ).isEqualTo(MoreMenuItemButton.State.Visible) + } + + @Test + fun `given eligible WooCommerce version, when building state, then WooPOS section is displayed with proper description`() = testBlocking { + // GIVEN + setup { + whenever(isWooPosEnabled.invoke()).thenReturn(true) + whenever(getWooCoreVersion()).thenReturn("11.0.0") + } + + // WHEN + val states = viewModel.moreMenuViewState.captureValues() + + // THEN + assertThat( + states.last().menuSections.flatMap { it.items } + .first { it.title == R.string.more_menu_button_woo_pos }.state + ).isEqualTo(MoreMenuItemButton.State.Visible) + assertThat( + states.last().menuSections.flatMap { it.items } + .first { + it.description == R.string.more_menu_button_woo_pos_description + }.state + ).isEqualTo(MoreMenuItemButton.State.Visible) + } + @Test fun `given isWooPosEnabled returns true, when building state, then 3 sections are shown without dividers at the end`() = testBlocking { @@ -527,6 +578,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { // GIVEN setup { whenever(isWooPosEnabled()).thenReturn(true) + whenever(getWooCoreVersion()).thenReturn("11.0.0") } // WHEN From bb2a5a49f920922f9428fc078ab7ab1a9440fc46 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 10:28:48 +0530 Subject: [PATCH 06/13] remove blank line --- .../com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt index 0d179e1d577..6e292a0149d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt @@ -438,7 +438,6 @@ class MoreMenuViewModel @Inject constructor( triggerEvent(MoreMenuEvent.ShowWooPosWooCoreUpdateRequiredEvent) } } - } private fun onViewAdminButtonClick() { From e90175c95e0e9124f275dee91e3778885090b966 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:21:44 +0530 Subject: [PATCH 07/13] Update KEY_POS_NOT_ELIGIBLE --- .../com/woocommerce/android/analytics/AnalyticsTracker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt index 29ecddd2dd9..771f586fb3d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/analytics/AnalyticsTracker.kt @@ -479,7 +479,7 @@ class AnalyticsTracker private constructor( // We have to call in non consistent way to match the iOS naming const val VALUE_MORE_MENU_POS = "pointOfSale" - const val KEY_POS_NOT_ELIGIBLE = "not_eligible" + const val KEY_POS_NOT_ELIGIBLE_REASON = "not_eligible_reason" const val VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION = "outdated_woocommerce_version" const val VALUE_MORE_MENU_PAYMENTS_BADGE_VISIBLE = "badge_visible" From 47e245d02a1d0e80f8fe5138013927abd5f0e895 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:22:04 +0530 Subject: [PATCH 08/13] Refactor MoreMenuState.kt --- .../woocommerce/android/ui/moremenu/MoreMenuState.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt index ad5fd3892ca..73b78a2c2cf 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuState.kt @@ -46,12 +46,17 @@ data class MoreMenuItemButton( @StringRes val description: Int, @DrawableRes val icon: Int, @DrawableRes val extraIcon: Int? = null, - val state: State = State.Visible, + val state: State = State.Visible.Enabled, val badgeState: BadgeState? = null, val onClick: () -> Unit = {}, ) { - enum class State { - Loading, Visible, Hidden, + sealed class State { + data object Loading : State() + sealed class Visible : State() { + data object Enabled : Visible() + data object WooCoreVersionNotSupported : Visible() + } + data object Hidden : State() } enum class Type { From 173df26fa809d26a112a3bfdb5ac86b01d8beca4 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:22:21 +0530 Subject: [PATCH 09/13] Refactor WooPosIsEnabled.kt --- .../android/ui/woopos/WooPosIsEnabled.kt | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt index 5fc34257a10..1d229b6f7b9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt @@ -19,19 +19,30 @@ class WooPosIsEnabled @Inject constructor( private val isRemoteFeatureFlagEnabled: IsRemoteFeatureFlagEnabled, ) { @Suppress("ReturnCount") - suspend operator fun invoke(): Boolean = coroutineScope { - val selectedSite = selectedSite.getOrNull() ?: return@coroutineScope false + suspend operator fun invoke(): Reason = coroutineScope { + val selectedSite = selectedSite.getOrNull() + ?: return@coroutineScope Reason.Disabled.InvalidSelectedSite - if (!isRemoteFeatureFlagEnabled(WOO_POS)) return@coroutineScope false - if (!isScreenSizeAllowed()) return@coroutineScope false - if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps()) return@coroutineScope false + if (!isRemoteFeatureFlagEnabled(WOO_POS)) return@coroutineScope Reason.Disabled.FeatureFlagDisabled + if (!isScreenSizeAllowed()) return@coroutineScope Reason.Disabled.ScreenSizeNotAllowed + if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps()) + return@coroutineScope Reason.Disabled.WooCoreVersionNotSupported - val siteSettings = wooCommerceStore.getSiteSettings(selectedSite) ?: return@coroutineScope false + val siteSettings = wooCommerceStore.getSiteSettings(selectedSite) + ?: return@coroutineScope Reason.Disabled.InvalidSiteSettings - return@coroutineScope isCountryAndCurrencySupported( - countryCode = siteSettings.countryCode, - currency = siteSettings.currencyCode - ) + return@coroutineScope if (isCountryAndCurrencySupported( + countryCode = siteSettings.countryCode, + currency = siteSettings.currencyCode + ) + ) { + Reason.Enabled + } else { + Reason.Disabled.CountryCurrencyNotSupported( + country = siteSettings.countryCode, + currency = siteSettings.currencyCode + ) + } } private fun isCountryAndCurrencySupported(countryCode: String, currency: String) = @@ -47,4 +58,16 @@ class WooPosIsEnabled @Inject constructor( val SUPPORTED_COUNTRY_CURRENCY_PAIRS = listOf("us" to "usd", "gb" to "gbp") } + + sealed class Reason { + data object Enabled : Reason() + sealed class Disabled : Reason() { + data object InvalidSelectedSite : Disabled() + data object InvalidSiteSettings : Disabled() + data object FeatureFlagDisabled : Disabled() + data object ScreenSizeNotAllowed : Disabled() + data class CountryCurrencyNotSupported(val country: String, val currency: String) : Disabled() + data object WooCoreVersionNotSupported : Disabled() + } + } } From fe0f35003c10187e15025176303c474d53a4db79 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:22:44 +0530 Subject: [PATCH 10/13] Refactor MoreMenuScreen and MoreMenuViewModel --- .../android/ui/moremenu/MoreMenuScreen.kt | 2 +- .../android/ui/moremenu/MoreMenuViewModel.kt | 147 +++++++++++------- 2 files changed, 96 insertions(+), 53 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuScreen.kt index b961ecd4634..0583f17dfd7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuScreen.kt @@ -294,7 +294,7 @@ private fun MoreMenuSection(section: MoreMenuItemSection) { section.items.forEach { item -> when (item.state) { MoreMenuItemButton.State.Loading -> MoreMenuLoading() - MoreMenuItemButton.State.Visible -> MoreMenuButton(item) + is MoreMenuItemButton.State.Visible -> MoreMenuButton(item) MoreMenuItemButton.State.Hidden -> Unit } Spacer(modifier = Modifier.height(8.dp)) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt index 6e292a0149d..6e4a1db7ff4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt @@ -9,7 +9,7 @@ import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_CAMPAIGN_LIST_ENTR import com.woocommerce.android.analytics.AnalyticsEvent.BLAZE_ENTRY_POINT_DISPLAYED import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_OPTION -import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_POS_NOT_ELIGIBLE +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_POS_NOT_ELIGIBLE_REASON import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_ADMIN_MENU import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_COUPONS import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_MENU_CUSTOMERS @@ -23,7 +23,6 @@ import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_MORE_M import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.extensions.adminUrlOrDefault -import com.woocommerce.android.extensions.semverCompareTo import com.woocommerce.android.notifications.UnseenReviewsCountHandler import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.tools.SiteConnectionType @@ -119,8 +118,8 @@ class MoreMenuViewModel @Inject constructor( buttonsStates: Map, count: Int, paymentsFeatureWasClicked: Boolean - ) = listOf( - generatePOSSection(buttonsStates[MoreMenuItemButton.Type.WooPos]!!), + ) = listOfNotNull( + generatePOSMenuButtons(buttonsStates), generateSettingsMenuButtons(buttonsStates[MoreMenuItemButton.Type.Settings]!!), generateGeneralSection( unseenReviewsCount = count, @@ -131,6 +130,32 @@ class MoreMenuViewModel @Inject constructor( ) ) + private fun generatePOSMenuButtons( + buttonsStates: Map + ): MoreMenuItemSection? { + return buttonsStates[MoreMenuItemButton.Type.WooPos]?.let { wooPosState -> + when (wooPosState) { + is MoreMenuItemButton.State.Loading, is MoreMenuItemButton.State.Visible.Enabled -> + generateWooPosSection( + wooPosState, + R.string.more_menu_button_woo_pos_description + ) { + onWooPosButtonClick() + } + + is MoreMenuItemButton.State.Visible.WooCoreVersionNotSupported -> generateWooPosSection( + wooPosState, + R.string.more_menu_button_woo_pos_update_woocommerce_version_description + ) { + onWooPOSNotEligibleButtonClick(WooPosNotEligible.OutdatedWooCommerceVersion) + } + + else -> null + } + } + } + fun onViewResumed() { moreMenuNewFeatureHandler.markNewFeatureAsSeen() launch { @@ -139,41 +164,25 @@ class MoreMenuViewModel @Inject constructor( } } - private fun isWooCoreVersionSupported(): Boolean { - val wooCoreVersion = getWooCoreVersion() ?: return false - return wooCoreVersion.semverCompareTo(WC_VERSION_SUPPORTS_POS_PRODUCT_FILTERING) >= 0 - } - - private fun generatePOSSection(wooPosState: MoreMenuItemButton.State) = - if (isWooCoreVersionSupported()) { - MoreMenuItemSection( - title = null, - items = listOf( - MoreMenuItemButton( - title = R.string.more_menu_button_woo_pos, - description = R.string.more_menu_button_woo_pos_description, - icon = R.drawable.ic_more_menu_pos, - extraIcon = R.drawable.ic_more_menu_pos_extra, - state = wooPosState, - onClick = ::onWooPosButtonClick, - ) - ) - ) - } else { - MoreMenuItemSection( - title = null, - items = listOf( - MoreMenuItemButton( - title = R.string.more_menu_button_woo_pos, - description = R.string.more_menu_button_woo_pos_update_woocommerce_version_description, - icon = R.drawable.ic_more_menu_pos, - extraIcon = R.drawable.ic_more_menu_pos_extra, - state = wooPosState, - onClick = { onWooPOSNotEligibleButtonClick(WooPosNotEligible.OutdatedWooCommerceVersion) }, - ) + private fun generateWooPosSection( + wooPosState: MoreMenuItemButton.State, + description: Int, + onPosButtonClick: () -> Unit + ): MoreMenuItemSection { + return MoreMenuItemSection( + title = null, + items = listOf( + MoreMenuItemButton( + title = R.string.more_menu_button_woo_pos, + description = description, + icon = R.drawable.ic_more_menu_pos, + extraIcon = R.drawable.ic_more_menu_pos_extra, + state = wooPosState, + onClick = onPosButtonClick, ) ) - } + ) + } @Suppress("LongMethod") private fun generateGeneralSection( @@ -432,7 +441,7 @@ class MoreMenuViewModel @Inject constructor( trackMoreMenuOptionSelected( VALUE_MORE_MENU_POS, extraOptions = mapOf( - KEY_POS_NOT_ELIGIBLE to VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION + KEY_POS_NOT_ELIGIBLE_REASON to VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION ) ) triggerEvent(MoreMenuEvent.ShowWooPosWooCoreUpdateRequiredEvent) @@ -500,16 +509,54 @@ class MoreMenuViewModel @Inject constructor( .onStart { emit("") } private fun checkFeaturesAvailability(): Flow> { - val initialState = MoreMenuItemButton.Type.entries.associateWith { - MoreMenuItemButton.State.Loading - }.toMutableMap() + val initialState = + MoreMenuItemButton.Type.entries.associateWith { + MoreMenuItemButton.State.Loading + }.toMutableMap() return listOf( - doCheckAvailability(MoreMenuItemButton.Type.Blaze) { isBlazeEnabled() }, - doCheckAvailability(MoreMenuItemButton.Type.GoogleForWoo) { isGoogleForWooEnabled() }, - doCheckAvailability(MoreMenuItemButton.Type.Inbox) { moreMenuRepository.isInboxEnabled() }, - doCheckAvailability(MoreMenuItemButton.Type.Settings) { moreMenuRepository.isUpgradesEnabled() }, - doCheckAvailability(MoreMenuItemButton.Type.WooPos) { isWooPosEnabled() } + doCheckAvailability(MoreMenuItemButton.Type.Blaze) { + if (isBlazeEnabled()) { + MoreMenuItemButton.State.Visible.Enabled + } else { + MoreMenuItemButton.State.Hidden + } + }, + doCheckAvailability(MoreMenuItemButton.Type.GoogleForWoo) { + if (isGoogleForWooEnabled()) { + MoreMenuItemButton.State.Visible.Enabled + } else { + MoreMenuItemButton.State.Hidden + } + }, + doCheckAvailability(MoreMenuItemButton.Type.Inbox) { + if (moreMenuRepository.isInboxEnabled()) { + MoreMenuItemButton.State.Visible.Enabled + } else { + MoreMenuItemButton.State.Hidden + } + }, + doCheckAvailability(MoreMenuItemButton.Type.Settings) { + if (moreMenuRepository.isUpgradesEnabled()) { + MoreMenuItemButton.State.Visible.Enabled + } else { + MoreMenuItemButton.State.Hidden + } + }, + doCheckAvailability(MoreMenuItemButton.Type.WooPos) { + when (isWooPosEnabled()) { + is WooPosIsEnabled.Reason.Disabled.CountryCurrencyNotSupported, + WooPosIsEnabled.Reason.Disabled.FeatureFlagDisabled, + WooPosIsEnabled.Reason.Disabled.InvalidSelectedSite, + WooPosIsEnabled.Reason.Disabled.InvalidSiteSettings, + WooPosIsEnabled.Reason.Disabled.ScreenSizeNotAllowed -> MoreMenuItemButton.State.Hidden + + WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported -> + MoreMenuItemButton.State.Visible.WooCoreVersionNotSupported + + WooPosIsEnabled.Reason.Enabled -> MoreMenuItemButton.State.Visible.Enabled + } + } ).merge() .map { update -> initialState[update.first] = update.second @@ -520,9 +567,9 @@ class MoreMenuViewModel @Inject constructor( private fun doCheckAvailability( type: MoreMenuItemButton.Type, - checker: suspend () -> Boolean + checker: suspend () -> MoreMenuItemButton.State ): Flow> = flow { - val state = if (checker()) MoreMenuItemButton.State.Visible else MoreMenuItemButton.State.Hidden + val state = checker() emit(type to state) } @@ -532,8 +579,4 @@ class MoreMenuViewModel @Inject constructor( sealed class WooPosNotEligible { data object OutdatedWooCommerceVersion : WooPosNotEligible() } - - private companion object { - const val WC_VERSION_SUPPORTS_POS_PRODUCT_FILTERING = "9.6.0" - } } From 0a5a10abd8481061f61c660eaadf1db9458d44a4 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:22:55 +0530 Subject: [PATCH 11/13] Make tests pass --- .../android/ui/woopos/WooPosIsEnabledTest.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt index dc82e60e703..6d8b9015ee1 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt @@ -6,6 +6,7 @@ import com.woocommerce.android.util.IsRemoteFeatureFlagEnabled import com.woocommerce.android.util.RemoteFeatureFlag.WOO_POS import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -53,72 +54,72 @@ class WooPosIsEnabledTest : BaseUnitTest() { whenever(isRemoteFeatureFlagEnabled(WOO_POS)).thenReturn(true) whenever(isScreenSizeAllowed()).thenReturn(true) - assertTrue(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Enabled::class.java) } @Test fun `given feature flag disabled, when invoked, then return false`() = testBlocking { whenever(isRemoteFeatureFlagEnabled.invoke(WOO_POS)).thenReturn(false) - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.FeatureFlagDisabled::class.java) } @Test fun `given unsupported country, when invoked, then return false`() = testBlocking { val result = buildSiteSettings(countryCode = "CA", currencyCode = "USD") whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(result) - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.CountryCurrencyNotSupported::class.java) } @Test fun `given unsupported currency, when invoked, then return false`() = testBlocking { val result = buildSiteSettings(currencyCode = "CAD", countryCode = "US") whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(result) - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.CountryCurrencyNotSupported::class.java) } @Test fun `given uk country and pounds, when invoked, then return true`() = testBlocking { val result = buildSiteSettings(countryCode = "GB", currencyCode = "GBP") whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(result) - assertTrue(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Enabled::class.java) } @Test fun `given uk country and usd, when invoked, then return false`() = testBlocking { val result = buildSiteSettings(countryCode = "GB", currencyCode = "USD") whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(result) - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.CountryCurrencyNotSupported::class.java) } @Test fun `given us country and pounds, when invoked, then return false`() = testBlocking { val result = buildSiteSettings(countryCode = "US", currencyCode = "GBP") whenever(wooCommerceStore.getSiteSettings(any())).thenReturn(result) - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.CountryCurrencyNotSupported::class.java) } @Test fun `given woo version 9_5_0, when invoked, then return false`() = testBlocking { whenever(getWooCoreVersion.invoke()).thenReturn("9.5.0") - assertFalse(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported::class.java) } @Test fun `given woo version 9_6_0, when invoked, then return true`() = testBlocking { whenever(getWooCoreVersion.invoke()).thenReturn("9.6.0") - assertTrue(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Enabled::class.java) } @Test fun `given woo version 9_6_0_1, when invoked, then return true`() = testBlocking { whenever(getWooCoreVersion.invoke()).thenReturn("9.6.0.1") - assertTrue(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Enabled::class.java) } @Test fun `given woo version 10_0_1, when invoked, then return true`() = testBlocking { whenever(getWooCoreVersion.invoke()).thenReturn("10.0.1") - assertTrue(sut()) + assertThat(sut()).isInstanceOf(WooPosIsEnabled.Reason.Enabled::class.java) } private fun buildSiteSettings( From f004e5c1da2b25b845d04527e9ebac090302a32c Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:23:13 +0530 Subject: [PATCH 12/13] Add tests to verify outdated woocommerce version is handled --- .../ui/moremenu/MoreMenuViewModelTests.kt | 90 +++++++++++++++---- 1 file changed, 74 insertions(+), 16 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt index e524c444094..4c6fa73e2a5 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModelTests.kt @@ -3,6 +3,8 @@ package com.woocommerce.android.ui.moremenu import androidx.lifecycle.SavedStateHandle import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.KEY_POS_NOT_ELIGIBLE_REASON +import com.woocommerce.android.analytics.AnalyticsTracker.Companion.VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION import com.woocommerce.android.analytics.AnalyticsTrackerWrapper import com.woocommerce.android.notifications.UnseenReviewsCountHandler import com.woocommerce.android.tools.SelectedSite @@ -86,7 +88,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { private val hasGoogleAdsCampaigns: HasGoogleAdsCampaigns = mock() private val isWooPosEnabled: WooPosIsEnabled = mock { - onBlocking { invoke() } doReturn true + onBlocking { invoke() } doReturn WooPosIsEnabled.Reason.Enabled } private val analyticsTrackerWrapper: AnalyticsTrackerWrapper = mock() @@ -439,7 +441,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { testBlocking { // GIVEN setup { - whenever(isWooPosEnabled.invoke()).thenReturn(false) + whenever(isWooPosEnabled.invoke()).thenReturn(WooPosIsEnabled.Reason.Disabled.InvalidSiteSettings) } // WHEN @@ -452,11 +454,32 @@ class MoreMenuViewModelTests : BaseUnitTest() { ).isNull() } + @Test + fun `given isWooPosEnabled returns outdated woocommerce version, when building state, then WooPOS section is displayed with appropriate description`() = + testBlocking { + // GIVEN + setup { + whenever(isWooPosEnabled.invoke()) + .thenReturn(WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported) + } + + // WHEN + val states = viewModel.moreMenuViewState.captureValues() + + // THEN + assertThat( + states.last().menuSections.flatMap { it.items } + .firstOrNull { + it.description == R.string.more_menu_button_woo_pos_update_woocommerce_version_description + }?.state + ).isEqualTo(MoreMenuItemButton.State.Visible.WooCoreVersionNotSupported) + } + @Test fun `given isWooPosEnabled returns true, when building state, then WooPOS section is displayed`() = testBlocking { // GIVEN setup { - whenever(isWooPosEnabled.invoke()).thenReturn(true) + whenever(isWooPosEnabled.invoke()).thenReturn(WooPosIsEnabled.Reason.Enabled) } // WHEN @@ -468,15 +491,14 @@ class MoreMenuViewModelTests : BaseUnitTest() { assertThat( states.last().menuSections.flatMap { it.items } .first { it.title == R.string.more_menu_button_woo_pos }.state - ).isEqualTo(MoreMenuItemButton.State.Visible) + ).isEqualTo(MoreMenuItemButton.State.Visible.Enabled) } @Test fun `given outdated WooCommerce version, when building state, then WooPOS section is displayed with upgrade woocommerce description`() = testBlocking { // GIVEN setup { - whenever(isWooPosEnabled.invoke()).thenReturn(true) - whenever(getWooCoreVersion()).thenReturn("3.0.0") + whenever(isWooPosEnabled.invoke()).thenReturn(WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported) } // WHEN @@ -486,21 +508,20 @@ class MoreMenuViewModelTests : BaseUnitTest() { assertThat( states.last().menuSections.flatMap { it.items } .first { it.title == R.string.more_menu_button_woo_pos }.state - ).isEqualTo(MoreMenuItemButton.State.Visible) + ).isEqualTo(MoreMenuItemButton.State.Visible.WooCoreVersionNotSupported) assertThat( states.last().menuSections.flatMap { it.items } .first { it.description == R.string.more_menu_button_woo_pos_update_woocommerce_version_description }.state - ).isEqualTo(MoreMenuItemButton.State.Visible) + ).isEqualTo(MoreMenuItemButton.State.Visible.WooCoreVersionNotSupported) } @Test fun `given eligible WooCommerce version, when building state, then WooPOS section is displayed with proper description`() = testBlocking { // GIVEN setup { - whenever(isWooPosEnabled.invoke()).thenReturn(true) - whenever(getWooCoreVersion()).thenReturn("11.0.0") + whenever(isWooPosEnabled.invoke()).thenReturn(WooPosIsEnabled.Reason.Enabled) } // WHEN @@ -510,13 +531,13 @@ class MoreMenuViewModelTests : BaseUnitTest() { assertThat( states.last().menuSections.flatMap { it.items } .first { it.title == R.string.more_menu_button_woo_pos }.state - ).isEqualTo(MoreMenuItemButton.State.Visible) + ).isEqualTo(MoreMenuItemButton.State.Visible.Enabled) assertThat( states.last().menuSections.flatMap { it.items } .first { it.description == R.string.more_menu_button_woo_pos_description }.state - ).isEqualTo(MoreMenuItemButton.State.Visible) + ).isEqualTo(MoreMenuItemButton.State.Visible.Enabled) } @Test @@ -524,7 +545,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { testBlocking { // GIVEN setup { - whenever(isWooPosEnabled.invoke()).thenReturn(true) + whenever(isWooPosEnabled.invoke()).thenReturn(WooPosIsEnabled.Reason.Enabled) whenever(moreMenuRepository.isUpgradesEnabled()).thenReturn(true) } @@ -551,7 +572,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { fun `when building state, then all optional buttons start with loading state`() = testBlocking { // GIVEN setup { - whenever(isWooPosEnabled()).thenReturn(true) + whenever(isWooPosEnabled()).thenReturn(WooPosIsEnabled.Reason.Enabled) whenever(isBlazeEnabled.invoke()).thenReturn(true) whenever(isGoogleForWooEnabled.invoke()).thenReturn(true) whenever(moreMenuRepository.isUpgradesEnabled()).thenReturn(true) @@ -577,8 +598,7 @@ class MoreMenuViewModelTests : BaseUnitTest() { fun `when WooPOS button clicked, then VALUE_MORE_MENU_POS tracking is triggered`() = testBlocking { // GIVEN setup { - whenever(isWooPosEnabled()).thenReturn(true) - whenever(getWooCoreVersion()).thenReturn("11.0.0") + whenever(isWooPosEnabled()).thenReturn(WooPosIsEnabled.Reason.Enabled) } // WHEN @@ -592,4 +612,42 @@ class MoreMenuViewModelTests : BaseUnitTest() { mapOf("option" to "pointOfSale") ) } + + @Test + fun `given outdated woocommerce version, when WooPOS button clicked, then VALUE_MORE_MENU_POS tracking is triggered with appropriate properties`() = testBlocking { + // GIVEN + setup { + whenever(isWooPosEnabled()).thenReturn(WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported) + } + + // WHEN + val state = viewModel.moreMenuViewState.captureValues().last() + val posButton = state.menuSections.flatMap { it.items }.first { it.title == R.string.more_menu_button_woo_pos } + posButton.onClick() + + // THEN + verify(analyticsTrackerWrapper).track( + AnalyticsEvent.HUB_MENU_OPTION_TAPPED, + mapOf( + "option" to "pointOfSale", + KEY_POS_NOT_ELIGIBLE_REASON to VALUE_POS_OUTDATED_WOOCOMMERCE_VERSION + ) + ) + } + + @Test + fun `given outdated woocommerce version, when WooPOS button clicked, then appropriate event is triggered`() = testBlocking { + // GIVEN + setup { + whenever(isWooPosEnabled()).thenReturn(WooPosIsEnabled.Reason.Disabled.WooCoreVersionNotSupported) + } + + // WHEN + val state = viewModel.moreMenuViewState.captureValues().last() + val posButton = state.menuSections.flatMap { it.items }.first { it.title == R.string.more_menu_button_woo_pos } + posButton.onClick() + + // THEN + assertThat(viewModel.event.value).isEqualTo(MoreMenuEvent.ShowWooPosWooCoreUpdateRequiredEvent) + } } From e8812ee67185af8394bb8bac0de6e2cb24137aa2 Mon Sep 17 00:00:00 2001 From: AnirudhBhat Date: Wed, 19 Mar 2025 18:25:33 +0530 Subject: [PATCH 13/13] Fix detekt errors --- .../com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt | 3 +-- .../com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt | 3 ++- .../com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt index 6e4a1db7ff4..b606253287b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/moremenu/MoreMenuViewModel.kt @@ -131,8 +131,7 @@ class MoreMenuViewModel @Inject constructor( ) private fun generatePOSMenuButtons( - buttonsStates: Map + buttonsStates: Map ): MoreMenuItemSection? { return buttonsStates[MoreMenuItemButton.Type.WooPos]?.let { wooPosState -> when (wooPosState) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt index 1d229b6f7b9..1328698aec0 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabled.kt @@ -25,8 +25,9 @@ class WooPosIsEnabled @Inject constructor( if (!isRemoteFeatureFlagEnabled(WOO_POS)) return@coroutineScope Reason.Disabled.FeatureFlagDisabled if (!isScreenSizeAllowed()) return@coroutineScope Reason.Disabled.ScreenSizeNotAllowed - if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps()) + if (!isWooCoreSupportsOrderAutoDraftsAndExtraPaymentsProps()) { return@coroutineScope Reason.Disabled.WooCoreVersionNotSupported + } val siteSettings = wooCommerceStore.getSiteSettings(selectedSite) ?: return@coroutineScope Reason.Disabled.InvalidSiteSettings diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt index 6d8b9015ee1..e10bae748f1 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/WooPosIsEnabledTest.kt @@ -15,8 +15,6 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.WCSettingsModel import org.wordpress.android.fluxc.store.WooCommerceStore import kotlin.test.Test -import kotlin.test.assertFalse -import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class WooPosIsEnabledTest : BaseUnitTest() {