From b9ed82452b0119c5fe8e0c8c16e0c12ea3dbff1e Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 09:57:15 +0200 Subject: [PATCH 1/7] Migrate WooPosCartViewModel --- .../woopos/home/cart/WooPosCartViewModel.kt | 10 +- .../home/cart/WooPosCartViewModelTest.kt | 279 ++++++++++-------- 2 files changed, 162 insertions(+), 127 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt index a8593490db11..5302441e0494 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.asLiveData import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import com.woocommerce.android.R -import com.woocommerce.android.model.Product import com.woocommerce.android.ui.woopos.common.composeui.modifier.BarcodeInputDetector import com.woocommerce.android.ui.woopos.common.data.WooPosGetCouponById import com.woocommerce.android.ui.woopos.common.data.WooPosGetProductById @@ -15,6 +14,7 @@ import com.woocommerce.android.ui.woopos.common.data.WooPosGetVariationById import com.woocommerce.android.ui.woopos.common.data.WooPosVariation import com.woocommerce.android.ui.woopos.common.data.WooPosVariationMapper import com.woocommerce.android.ui.woopos.common.data.getNameForPOS +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifier import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifierResult import com.woocommerce.android.ui.woopos.common.util.WooPosLogWrapper @@ -581,19 +581,19 @@ class WooPosCartViewModel @Inject constructor( } } - private suspend fun Product.toCartListItem(itemNumber: Int): WooPosCartItemViewState.Product.Simple = + private suspend fun WooPosProductModelVersion2.toCartListItem(itemNumber: Int): WooPosCartItemViewState.Product.Simple = WooPosCartItemViewState.Product.Simple( itemNumber = itemNumber, id = this.remoteId, name = name, description = null, - price = formatPrice(price), + price = formatPrice(pricing.displayPrice), imageUrl = firstImageUrl, ) private suspend fun WooPosVariation.toCartListItem( itemNumber: Int, - product: Product + product: WooPosProductModelVersion2 ): WooPosCartItemViewState.Product.Variation = WooPosCartItemViewState.Product.Variation( itemNumber = itemNumber, @@ -626,7 +626,7 @@ class WooPosCartViewModel @Inject constructor( id = product.remoteId, name = product.name, description = null, - price = formatPrice(product.price), + price = formatPrice(product.pricing.displayPrice), imageUrl = product.firstImageUrl ) } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt index 7bca931a9b23..e95960c09337 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt @@ -12,6 +12,7 @@ import com.woocommerce.android.ui.woopos.common.data.WooPosGetProductById import com.woocommerce.android.ui.woopos.common.data.WooPosGetVariationById import com.woocommerce.android.ui.woopos.common.data.WooPosVariation import com.woocommerce.android.ui.woopos.common.data.WooPosVariationMapper +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifier import com.woocommerce.android.ui.woopos.common.data.searchbyidentifier.WooPosSearchByIdentifierResult import com.woocommerce.android.ui.woopos.common.data.toWooPosVariation @@ -36,6 +37,7 @@ import com.woocommerce.android.ui.woopos.util.analytics.WooPosAnalyticsTrackingD import com.woocommerce.android.ui.woopos.util.analytics.WooPosBarcodeEventTracker import com.woocommerce.android.ui.woopos.util.format.WooPosCouponsFormatter import com.woocommerce.android.ui.woopos.util.format.WooPosFormatPrice +import com.woocommerce.android.ui.woopos.util.generateWooPosProduct import com.woocommerce.android.util.captureValues import com.woocommerce.android.viewmodel.ResourceProvider import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -72,6 +74,13 @@ class WooPosCartViewModelTest { private val parentToChildrenEventReceiver: WooPosParentToChildrenEventReceiver = mock { on { events }.thenReturn(parentToChildrenMutableSharedFlow) } + + private val mockedEventForTracking: WooPosAnalyticsEvent.Event.ItemAddedToCart = + WooPosAnalyticsEvent.Event.ItemAddedToCart( + item = WooPosItemsViewModel.ItemClickedData.Product.Simple(1L), + source = WooPosAnalyticsEventConstant.ItemsListSource.PRODUCT, + sourceType = WooPosAnalyticsEventConstant.ItemsListSourceType.LIST, + ) private val getProductById: WooPosGetProductById = mock() private val getCouponById: WooPosGetCouponById = mock { onBlocking { invoke(any()) }.thenReturn( @@ -80,7 +89,11 @@ class WooPosCartViewModelTest { "coupon_code", productIds = emptyList(), categoryIds = emptyList(), - restrictions = mock() + restrictions = Coupon.CouponRestrictions( + excludedProductIds = emptyList(), + excludedCategoryIds = emptyList(), + restrictedEmails = emptyList() + ) ) ) } @@ -142,11 +155,12 @@ class WooPosCartViewModelTest { @Test fun `given empty cart, when product clicked in product selector, then should add product to cart`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -158,7 +172,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -176,11 +190,12 @@ class WooPosCartViewModelTest { variationId = 24L, amount = "10.0" ).toWooPosVariation(variationMapper) - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever( getVariationsById(eq(variation.remoteProductId), eq(variation.remoteVariationId)) @@ -196,7 +211,7 @@ class WooPosCartViewModelTest { id = variation.remoteVariationId, productId = variation.remoteProductId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -225,11 +240,12 @@ class WooPosCartViewModelTest { fun `given product in cart, when product remove button clicked in cart, then should remove product from cart`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() whenever(parentToChildrenEventReceiver.events).thenReturn(parentToChildrenMutableSharedFlow) @@ -242,7 +258,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -297,11 +313,12 @@ class WooPosCartViewModelTest { @Test fun `given items in cart, when item remove button clicked in cart, then should track event`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -394,11 +411,12 @@ class WooPosCartViewModelTest { fun `given non empty cart in_progress, when vm created, then toolbar state should contain shopping cart itemsCart title and no clear all`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() whenever(parentToChildrenEventReceiver.events).thenReturn(parentToChildrenMutableSharedFlow) @@ -413,7 +431,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -444,21 +462,24 @@ class WooPosCartViewModelTest { fun `given non empty cart in process, when 2 items added and the first removed and third item added, then third will have item number 2`() = runTest { // GIVEN - val product1 = ProductTestUtils.generateProduct( + val product1 = generateWooPosProduct( productId = 1L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") - val product2 = ProductTestUtils.generateProduct( + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) + val product2 = generateWooPosProduct( productId = 2L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") - val product3 = ProductTestUtils.generateProduct( + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) + val product3 = generateWooPosProduct( productId = 3L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() whenever(parentToChildrenEventReceiver.events).thenReturn(parentToChildrenMutableSharedFlow) @@ -475,7 +496,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product1.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) parentToChildrenMutableSharedFlow.emit( @@ -483,7 +504,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product2.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -505,7 +526,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product3.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -531,11 +552,12 @@ class WooPosCartViewModelTest { @Test fun `given empty cart, when product tapped, then should track start of customer interaction event`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) createSut() @@ -546,7 +568,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -596,11 +618,12 @@ class WooPosCartViewModelTest { @Test fun `given non-empty cart, when all items removed individually, then state should be empty`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -611,7 +634,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -655,29 +678,29 @@ class WooPosCartViewModelTest { @Test fun `when item added to cart, then should track analytics event`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() sut.state.captureValues() // WHEN - val itemAddedToCartEvent = mock() parentToChildrenMutableSharedFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = itemAddedToCartEvent + eventForTracking = mockedEventForTracking ) ) // THEN - verify(analyticsTracker).track(itemAddedToCartEvent) + verify(analyticsTracker).track(mockedEventForTracking) } @Test @@ -690,7 +713,7 @@ class WooPosCartViewModelTest { parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = 1L, couponCode = ""), - eventForTracking = mock(), + eventForTracking = mockedEventForTracking, ) ) sut.onUIEvent(WooPosCartUIEvent.CheckoutClicked) @@ -715,26 +738,27 @@ class WooPosCartViewModelTest { parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = 1L, couponCode = ""), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = 2L, couponCode = ""), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Product.Simple(id = product.remoteId), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) sut.onUIEvent(WooPosCartUIEvent.CheckoutClicked) @@ -761,20 +785,21 @@ class WooPosCartViewModelTest { parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = 2L, couponCode = ""), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Product.Simple(id = product.remoteId), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -795,7 +820,7 @@ class WooPosCartViewModelTest { parentToChildrenEventsMutableFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = 1L, couponCode = ""), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) sut.onUIEvent(WooPosCartUIEvent.CheckoutClicked) @@ -838,11 +863,12 @@ class WooPosCartViewModelTest { @Test fun `given empty cart, when product added to cart, then checkout button should be visible`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() val states = sut.state.captureValues() @@ -853,7 +879,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -865,11 +891,12 @@ class WooPosCartViewModelTest { @Test fun `given empty cart, when coupon and product added to cart, then checkout button should be visible`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -883,7 +910,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -895,11 +922,12 @@ class WooPosCartViewModelTest { @Test fun `given cart with products and coupon, when products removed, then checkout button disappears`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -910,7 +938,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -1040,7 +1068,7 @@ class WooPosCartViewModelTest { ).doSuspendableAnswer { delay(1) WooPosSearchByIdentifierResult.Success( - ProductTestUtils.generateProduct( + generateWooPosProduct( amount = "10.0" ) ) @@ -1073,7 +1101,7 @@ class WooPosCartViewModelTest { ).doSuspendableAnswer { delay(1) WooPosSearchByIdentifierResult.Success( - ProductTestUtils.generateProduct( + generateWooPosProduct( amount = "10.0" ) ) @@ -1104,11 +1132,12 @@ class WooPosCartViewModelTest { fun `given empty cart, when barcode scanned and product found, then loading item is replaced with product`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "Scanned Product", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(searchByIdentifier(eq("123456789"))).thenReturn( WooPosSearchByIdentifierResult.Success(product) @@ -1141,11 +1170,12 @@ class WooPosCartViewModelTest { fun `when barcode scanned and product found, then track item added to cart event`() = runTest { // GIVEN - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "Scanned Product", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(searchByIdentifier(eq("123456789"))).thenReturn( WooPosSearchByIdentifierResult.Success(product) @@ -1249,17 +1279,19 @@ class WooPosCartViewModelTest { fun `given cart with items, when barcode scanned and product found, then product is added to existing items`() = runTest { // GIVEN - val existingProduct = ProductTestUtils.generateProduct( + val existingProduct = generateWooPosProduct( productId = 23L, productName = "Existing Product", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) - val scannedProduct = ProductTestUtils.generateProduct( + val scannedProduct = generateWooPosProduct( productId = 42L, productName = "Scanned Product", - amount = "10.0" - ).copy(firstImageUrl = "url2") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url2", name = "", alt = "")) + ) whenever(getProductById(eq(existingProduct.remoteId))).thenReturn(existingProduct) whenever(searchByIdentifier(eq("123456789"))).thenReturn( @@ -1272,7 +1304,7 @@ class WooPosCartViewModelTest { parentToChildrenMutableSharedFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( WooPosItemsViewModel.ItemClickedData.Product.Simple(id = existingProduct.remoteId), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) @@ -1379,7 +1411,7 @@ class WooPosCartViewModelTest { amount = "45.0" ).toWooPosVariation(variationMapper) - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = productId, productName = "Red Hoodie", amount = "45.0" @@ -1415,11 +1447,12 @@ class WooPosCartViewModelTest { } private suspend fun createSutWithItemsInCart(): Pair> { - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 23L, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -1429,7 +1462,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = product.remoteId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) return Pair(sut, states) @@ -1439,17 +1472,18 @@ class WooPosCartViewModelTest { parentToChildrenMutableSharedFlow.emit( ParentToChildrenEvent.ItemClickedInItemsList( itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = couponId, couponCode = ""), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ), ) } private suspend fun simulateProductClicked(productId: Long = 1L) { - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = productId, productName = "title", - amount = "10.0" - ).copy(firstImageUrl = "url") + amount = "10.0", + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -1458,7 +1492,7 @@ class WooPosCartViewModelTest { WooPosItemsViewModel.ItemClickedData.Product.Simple( id = productId ), - eventForTracking = mock() + eventForTracking = mockedEventForTracking ) ) } @@ -1520,35 +1554,36 @@ class WooPosCartViewModelTest { } @Test - fun `given barcode without terminator, when scanned, then error item added to cart with correct message`() = runTest { - // GIVEN - val barcodeWithoutTerminator = "1234567890" - val errorMessage = "Scanner did not send end-of-line character" - whenever(resourceProvider.getString(R.string.woopos_cart_barcode_scan_result_no_terminator)) - .thenReturn(errorMessage) + fun `given barcode without terminator, when scanned, then error item added to cart with correct message`() = + runTest { + // GIVEN + val barcodeWithoutTerminator = "1234567890" + val errorMessage = "Scanner did not send end-of-line character" + whenever(resourceProvider.getString(R.string.woopos_cart_barcode_scan_result_no_terminator)) + .thenReturn(errorMessage) - val sut = createSut() - val states = sut.state.captureValues() + val sut = createSut() + val states = sut.state.captureValues() - // WHEN - sut.onUIEvent( - WooPosCartUIEvent.OnBarcodeEvent( - BarcodeInputDetector.BarcodeResult.Error( - barcode = barcodeWithoutTerminator, - scanDurationMs = 100L, - failureReason = BarcodeInputDetector.FailureReason.NO_TERMINATOR + // WHEN + sut.onUIEvent( + WooPosCartUIEvent.OnBarcodeEvent( + BarcodeInputDetector.BarcodeResult.Error( + barcode = barcodeWithoutTerminator, + scanDurationMs = 100L, + failureReason = BarcodeInputDetector.FailureReason.NO_TERMINATOR + ) ) ) - ) - advanceUntilIdle() + advanceUntilIdle() - // THEN - val finalItemsInCart = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart - assertThat(finalItemsInCart).hasSize(1) - assertThat(finalItemsInCart.first()).isInstanceOf(WooPosCartItemViewState.Error::class.java) - val errorItem = finalItemsInCart.first() as WooPosCartItemViewState.Error - assertThat(errorItem.name).isEqualTo(barcodeWithoutTerminator) - assertThat(errorItem.message).isEqualTo(errorMessage) - verify(soundHelper).playBarcodeScanFailure() - } + // THEN + val finalItemsInCart = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart + assertThat(finalItemsInCart).hasSize(1) + assertThat(finalItemsInCart.first()).isInstanceOf(WooPosCartItemViewState.Error::class.java) + val errorItem = finalItemsInCart.first() as WooPosCartItemViewState.Error + assertThat(errorItem.name).isEqualTo(barcodeWithoutTerminator) + assertThat(errorItem.message).isEqualTo(errorMessage) + verify(soundHelper).playBarcodeScanFailure() + } } From 893a50c59b6ddca5ee2635fe3709b4790fa4a6f0 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 09:57:24 +0200 Subject: [PATCH 2/7] Migrate WooPosVariationMapper --- .../android/ui/woopos/common/data/WooPosVariationMapper.kt | 5 +++-- .../woopos/common/data/models/WooPosProductModelVersion2.kt | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/WooPosVariationMapper.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/WooPosVariationMapper.kt index 44e68e88a4ec..527b4551e967 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/WooPosVariationMapper.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/WooPosVariationMapper.kt @@ -6,6 +6,7 @@ import com.google.gson.reflect.TypeToken import com.woocommerce.android.R import com.woocommerce.android.model.Product import com.woocommerce.android.model.ProductVariation +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import com.woocommerce.android.util.WooLog import com.woocommerce.android.viewmodel.ResourceProvider import org.wordpress.android.fluxc.model.WCProductVariationModel @@ -91,7 +92,7 @@ class WooPosVariationMapper @Inject constructor( fun getNameForPOS( variation: WooPosVariation, - parentProduct: Product? = null, + parentProduct: WooPosProductModelVersion2? = null, resourceProvider: ResourceProvider, ): String { return parentProduct?.variationEnabledAttributes?.joinToString(", ") { attribute -> @@ -167,7 +168,7 @@ fun WCPosVariationModel.toWooPosVariation(mapper: WooPosVariationMapper): WooPos fun WooPosVariation.getNameForPOS( mapper: WooPosVariationMapper, - parentProduct: Product? = null, + parentProduct: WooPosProductModelVersion2? = null, resourceProvider: ResourceProvider, ): String = mapper.getNameForPOS(this, parentProduct, resourceProvider) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt index c04879aa9e99..cf75d18bd895 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt @@ -30,6 +30,9 @@ data class WooPosProductModelVersion2( val variationIds: List = emptyList(), ) : Parcelable { + val variationEnabledAttributes + get() = attributes.filter { it.isVariation } + sealed class WooPosPricing : Parcelable { @Parcelize data object NoPricing : WooPosPricing() From 3cea6b5f17a74a44340a3aa5245e89cda645709a Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 10:19:16 +0200 Subject: [PATCH 3/7] Migrate WooPosProductSearchPredicate --- .../home/items/search/WooPosProductSearchPredicate.kt | 8 ++++---- .../home/items/search/WooPosProductSearchPredicateTest.kt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicate.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicate.kt index 0cca6c0c001b..8890d4317368 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicate.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicate.kt @@ -1,7 +1,7 @@ package com.woocommerce.android.ui.woopos.home.items.search import com.woocommerce.android.extensions.semverCompareTo -import com.woocommerce.android.model.Product +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import com.woocommerce.android.util.GetWooCorePluginCachedVersion import javax.inject.Inject import javax.inject.Singleton @@ -13,14 +13,14 @@ class WooPosProductSearchPredicate @Inject constructor( private val whitespaceRegex = "\\s+".toRegex() private var cachedSupportsNameOrSkuSearch: Boolean? = null - operator fun invoke(query: String): (Product) -> Boolean = + operator fun invoke(query: String): (WooPosProductModelVersion2) -> Boolean = when { query.isBlank() -> { _ -> true } isWooCoreSupportsNameOrSkuSearch() -> tokenizedSkuOrNameSearchPredicate(query) else -> simpleSearchPredicate(query) } - private fun simpleSearchPredicate(query: String): (Product) -> Boolean { + private fun simpleSearchPredicate(query: String): (WooPosProductModelVersion2) -> Boolean { val terms: List = query.split(whitespaceRegex).filter { it.isNotBlank() }.map { it.lowercase() } return { product -> @@ -36,7 +36,7 @@ class WooPosProductSearchPredicate @Inject constructor( } } - private fun tokenizedSkuOrNameSearchPredicate(query: String): (Product) -> Boolean { + private fun tokenizedSkuOrNameSearchPredicate(query: String): (WooPosProductModelVersion2) -> Boolean { val tokens: List = query .trim() .split(whitespaceRegex) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicateTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicateTest.kt index 5b912a2f3990..d8b10ea870e0 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicateTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosProductSearchPredicateTest.kt @@ -1,6 +1,6 @@ package com.woocommerce.android.ui.woopos.home.items.search -import com.woocommerce.android.ui.products.ProductTestUtils +import com.woocommerce.android.ui.woopos.util.generateWooPosProduct import com.woocommerce.android.util.GetWooCorePluginCachedVersion import org.junit.Before import org.junit.Test @@ -15,7 +15,7 @@ class WooPosProductSearchPredicateTest { private lateinit var searchPredicate: WooPosProductSearchPredicate private val mockProduct by lazy { - val product = ProductTestUtils.generateProduct( + val product = generateWooPosProduct( productId = 1L, productName = "Test Product" ) From 50ce0d4042e285f351f39fb30dfa4106b393fcf5 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 10:20:25 +0200 Subject: [PATCH 4/7] Migrate WooPosSearchProductsDataSource --- .../search/WooPosSearchProductsDataSource.kt | 23 ++++++++++--------- .../WooPosSearchProductsDataSourceTest.kt | 13 +++++++---- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt index 22485ddcadde..782240d6d7dd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt @@ -1,11 +1,11 @@ package com.woocommerce.android.ui.woopos.home.items.search import com.woocommerce.android.WooException -import com.woocommerce.android.model.Product -import com.woocommerce.android.model.toAppModel import com.woocommerce.android.tools.SelectedSite import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache import com.woocommerce.android.ui.woopos.common.data.WooPosProductsTypesFilterConfig +import com.woocommerce.android.ui.woopos.common.data.models.WCProductToWooPosProductModelMapper +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import com.woocommerce.android.util.WooLog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -21,7 +21,8 @@ class WooPosSearchProductsDataSource @Inject constructor( private val productsCache: WooPosProductsCache, private val searchResultsIndex: WooPosSearchResultsIndex, private val searchPredicate: WooPosProductSearchPredicate, - private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig + private val productsTypesFilterConfig: WooPosProductsTypesFilterConfig, + private val posProductModelMapper: WCProductToWooPosProductModelMapper, ) { companion object { private const val PAGE_SIZE = 15 @@ -33,11 +34,11 @@ class WooPosSearchProductsDataSource @Inject constructor( val hasMorePages: Boolean get() = canLoadMore.get() - suspend fun searchLocalProducts(query: String): List = withContext(Dispatchers.IO) { + suspend fun searchLocalProducts(query: String): List = withContext(Dispatchers.IO) { sortProducts(productsCache.getAll().filter(searchPredicate(query))).take(PAGE_SIZE) } - suspend fun searchRemoteProducts(query: String): Result> = withContext(Dispatchers.IO) { + suspend fun searchRemoteProducts(query: String): Result> = withContext(Dispatchers.IO) { searchResultsIndex.clearCache() performRemoteSearch(query).fold( @@ -48,7 +49,7 @@ class WooPosSearchProductsDataSource @Inject constructor( ) } - suspend fun loadMore(query: String): Result> { + suspend fun loadMore(query: String): Result> { if (!canLoadMore.get()) { return Result.success(searchResultsIndex.getSearchResults(query)) } @@ -88,7 +89,7 @@ class WooPosSearchProductsDataSource @Inject constructor( } else { val searchResult = result.model!! val products = searchResult.products - .map { product -> product.toAppModel() } + .map { product -> posProductModelMapper.map(product) } .sortedBy { it.name.lowercase() } canLoadMore.set(searchResult.canLoadMore) @@ -106,17 +107,17 @@ class WooPosSearchProductsDataSource @Inject constructor( } } - private fun sortProducts(products: List): List { + private fun sortProducts(products: List): List { return products.sortedBy { it.name.lowercase() } } sealed class ProductsResult { - data class Cached(val products: List) : ProductsResult() - data class Remote(val productsResult: Result>) : ProductsResult() + data class Cached(val products: List) : ProductsResult() + data class Remote(val productsResult: Result>) : ProductsResult() } data class SearchResult( - val products: List, + val products: List, val canLoadMore: Boolean ) } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSourceTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSourceTest.kt index 0c277bd74fca..2b7c6550cfc9 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSourceTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSourceTest.kt @@ -1,10 +1,11 @@ package com.woocommerce.android.ui.woopos.home.items.search import com.woocommerce.android.tools.SelectedSite -import com.woocommerce.android.ui.products.ProductTestUtils import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache import com.woocommerce.android.ui.woopos.common.data.WooPosProductsTypesFilterConfig +import com.woocommerce.android.ui.woopos.common.data.models.WCProductToWooPosProductModelMapper import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule +import com.woocommerce.android.ui.woopos.util.generateWooPosProduct import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat @@ -36,12 +37,13 @@ class WooPosSearchProductsDataSourceTest { private val selectedSite: SelectedSite = mock() private val searchPredicate: WooPosProductSearchPredicate = mock() private val siteModel: SiteModel = mock() + private val posProductModelMapper: WCProductToWooPosProductModelMapper = mock() private lateinit var sut: WooPosSearchProductsDataSource - private val product1 = ProductTestUtils.generateProduct(productId = 1) - private val product2 = ProductTestUtils.generateProduct(productId = 2) - private val product3 = ProductTestUtils.generateProduct(productId = 3) + private val product1 = generateWooPosProduct(productId = 1) + private val product2 = generateWooPosProduct(productId = 2) + private val product3 = generateWooPosProduct(productId = 3) private val products = listOf(product1, product2, product3) private val productsTypesFilterConfig = WooPosProductsTypesFilterConfig() @@ -57,6 +59,7 @@ class WooPosSearchProductsDataSourceTest { searchResultsIndex = searchResultsIndex, searchPredicate = searchPredicate, productsTypesFilterConfig = productsTypesFilterConfig, + posProductModelMapper = posProductModelMapper, ) } @@ -78,7 +81,7 @@ class WooPosSearchProductsDataSourceTest { runTest { // GIVEN val query = "test" - val manyProducts = (1..20).map { ProductTestUtils.generateProduct(productId = it.toLong()) } + val manyProducts = (1..20).map { generateWooPosProduct(productId = it.toLong()) } whenever(wooPosProductsCache.getAll()).thenReturn(manyProducts) // WHEN From cd121007e8b25b9cd5739d86ed680bf9b259b114 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 10:21:05 +0200 Subject: [PATCH 5/7] Migrate WooPosSearchResultsIndex --- .../ui/woopos/home/items/search/WooPosSearchResultsIndex.kt | 4 ++-- .../woopos/home/items/search/WooPosSearchResultsIndexTest.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndex.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndex.kt index 49e7ecd3704e..6da62f5dc6b7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndex.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndex.kt @@ -1,7 +1,7 @@ package com.woocommerce.android.ui.woopos.home.items.search -import com.woocommerce.android.model.Product import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache +import com.woocommerce.android.ui.woopos.common.data.models.WooPosProductModelVersion2 import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import javax.inject.Inject @@ -22,7 +22,7 @@ class WooPosSearchResultsIndex @Inject constructor( paginatedResults = (paginatedResults + productIds).distinct() } - suspend fun getSearchResults(query: String): List = mutex.withLock { + suspend fun getSearchResults(query: String): List = mutex.withLock { if (currentSearchQuery != query.lowercase()) { return emptyList() } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndexTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndexTest.kt index 404818f9e2f7..b8555b29c44d 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndexTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchResultsIndexTest.kt @@ -1,7 +1,7 @@ package com.woocommerce.android.ui.woopos.home.items.search -import com.woocommerce.android.model.Product import com.woocommerce.android.ui.woopos.common.data.WooPosProductsCache +import com.woocommerce.android.ui.woopos.util.generateWooPosProduct import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Before @@ -17,7 +17,7 @@ class WooPosSearchResultsIndexTest { private lateinit var searchResultsIndex: WooPosSearchResultsIndex private val productsCache: WooPosProductsCache = mock() - private val mockProducts = listOf(mock(), mock(), mock()) + private val mockProducts = listOf(generateWooPosProduct(), generateWooPosProduct(), generateWooPosProduct()) @Before fun setUp() = runTest { From 658d0ab57af0413cf0207ddfce44c273a11b55b0 Mon Sep 17 00:00:00 2001 From: malinajirka Date: Mon, 8 Sep 2025 14:01:39 +0200 Subject: [PATCH 6/7] Fix detekt --- .../woopos/home/cart/WooPosCartViewModel.kt | 34 +++++++------- .../search/WooPosSearchProductsDataSource.kt | 19 ++++---- .../home/cart/WooPosCartViewModelTest.kt | 44 +++++++++---------- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt index 5302441e0494..752934abcdd4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt @@ -26,6 +26,7 @@ import com.woocommerce.android.ui.woopos.home.ParentToChildrenEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender import com.woocommerce.android.ui.woopos.home.WooPosParentToChildrenEventReceiver import com.woocommerce.android.ui.woopos.home.cart.WooPosCartItemViewState.Coupon.CouponValidationState +import com.woocommerce.android.ui.woopos.home.cart.WooPosCartItemViewState.Product import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.CHECKOUT import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.EDITABLE import com.woocommerce.android.ui.woopos.home.cart.WooPosCartStatus.EMPTY @@ -177,8 +178,8 @@ class WooPosCartViewModel @Inject constructor( private fun getCartItemsDataList(): List { val itemClickedDataList = (_state.value.body as WooPosCartState.Body.WithItems).itemsInCart.mapNotNull { when (it) { - is WooPosCartItemViewState.Product.Simple -> WooPosItemsViewModel.ItemClickedData.Product.Simple(it.id) - is WooPosCartItemViewState.Product.Variation -> WooPosItemsViewModel.ItemClickedData.Product.Variation( + is Product.Simple -> WooPosItemsViewModel.ItemClickedData.Product.Simple(it.id) + is Product.Variation -> WooPosItemsViewModel.ItemClickedData.Product.Variation( productId = it.id, id = it.variationId ) @@ -575,27 +576,26 @@ class WooPosCartViewModel @Inject constructor( .map { item -> when (item) { is WooPosCartItemViewState.Coupon -> item.copy(validationState = CouponValidationState.Unknown) - is WooPosCartItemViewState.Product -> item + is Product -> item is WooPosCartItemViewState.Error -> item is WooPosCartItemViewState.Loading -> item } } - private suspend fun WooPosProductModelVersion2.toCartListItem(itemNumber: Int): WooPosCartItemViewState.Product.Simple = - WooPosCartItemViewState.Product.Simple( - itemNumber = itemNumber, - id = this.remoteId, - name = name, - description = null, - price = formatPrice(pricing.displayPrice), - imageUrl = firstImageUrl, - ) + private suspend fun WooPosProductModelVersion2.toCartListItem(itemNumber: Int): Product.Simple = Product.Simple( + itemNumber = itemNumber, + id = this.remoteId, + name = name, + description = null, + price = formatPrice(pricing.displayPrice), + imageUrl = firstImageUrl, + ) private suspend fun WooPosVariation.toCartListItem( itemNumber: Int, product: WooPosProductModelVersion2 - ): WooPosCartItemViewState.Product.Variation = - WooPosCartItemViewState.Product.Variation( + ): Product.Variation = + Product.Variation( itemNumber = itemNumber, id = product.remoteId, variationId = this.remoteVariationId, @@ -609,7 +609,7 @@ class WooPosCartViewModel @Inject constructor( (_state.value.body as? WooPosCartState.Body.WithItems)?.itemsInCart?.maxOfOrNull { it.itemNumber } ?: 1 private fun cartContainsPurchasableItems(body: WooPosCartState.Body.WithItems) = - body.itemsInCart.filterIsInstance().isNotEmpty() + body.itemsInCart.filterIsInstance().isNotEmpty() private fun cartContainsLoadingOrErrorItems(body: WooPosCartState.Body.WithItems) = body.itemsInCart.any { it is WooPosCartItemViewState.Loading || it is WooPosCartItemViewState.Error } @@ -621,7 +621,7 @@ class WooPosCartViewModel @Inject constructor( return when (this) { is WooPosSearchByIdentifierResult.Success -> { val product = this.product - WooPosCartItemViewState.Product.Simple( + Product.Simple( itemNumber = itemNumber, id = product.remoteId, name = product.name, @@ -632,7 +632,7 @@ class WooPosCartViewModel @Inject constructor( } is WooPosSearchByIdentifierResult.VariationSuccess -> { - WooPosCartItemViewState.Product.Variation( + Product.Variation( itemNumber = itemNumber, id = variation.remoteProductId, variationId = variation.remoteVariationId, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt index 782240d6d7dd..a1d3200446c5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/items/search/WooPosSearchProductsDataSource.kt @@ -38,16 +38,17 @@ class WooPosSearchProductsDataSource @Inject constructor( sortProducts(productsCache.getAll().filter(searchPredicate(query))).take(PAGE_SIZE) } - suspend fun searchRemoteProducts(query: String): Result> = withContext(Dispatchers.IO) { - searchResultsIndex.clearCache() + suspend fun searchRemoteProducts(query: String): Result> = + withContext(Dispatchers.IO) { + searchResultsIndex.clearCache() - performRemoteSearch(query).fold( - onSuccess = { result -> - Result.success(result.products.sortedBy { it.name.lowercase() }) - }, - onFailure = { error -> Result.failure(error) } - ) - } + performRemoteSearch(query).fold( + onSuccess = { result -> + Result.success(result.products.sortedBy { it.name.lowercase() }) + }, + onFailure = { error -> Result.failure(error) } + ) + } suspend fun loadMore(query: String): Result> { if (!canLoadMore.get()) { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt index e95960c09337..f7288f47cac9 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt @@ -159,7 +159,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -194,7 +194,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever( @@ -244,7 +244,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() @@ -317,7 +317,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -415,7 +415,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() @@ -466,19 +466,19 @@ class WooPosCartViewModelTest { productId = 1L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val product2 = generateWooPosProduct( productId = 2L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val product3 = generateWooPosProduct( productId = 3L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val parentToChildrenMutableSharedFlow = MutableSharedFlow() @@ -556,7 +556,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -622,7 +622,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -682,7 +682,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -752,7 +752,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) parentToChildrenEventsMutableFlow.emit( @@ -793,7 +793,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) parentToChildrenEventsMutableFlow.emit( @@ -867,7 +867,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) val sut = createSut() @@ -895,7 +895,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -926,7 +926,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -1136,7 +1136,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "Scanned Product", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(searchByIdentifier(eq("123456789"))).thenReturn( @@ -1174,7 +1174,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "Scanned Product", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(searchByIdentifier(eq("123456789"))).thenReturn( @@ -1283,14 +1283,14 @@ class WooPosCartViewModelTest { productId = 23L, productName = "Existing Product", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) val scannedProduct = generateWooPosProduct( productId = 42L, productName = "Scanned Product", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url2", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url2", name = "", alt = "")) ) whenever(getProductById(eq(existingProduct.remoteId))).thenReturn(existingProduct) @@ -1451,7 +1451,7 @@ class WooPosCartViewModelTest { productId = 23L, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) @@ -1482,7 +1482,7 @@ class WooPosCartViewModelTest { productId = productId, productName = "title", amount = "10.0", - images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) + images = listOf(WooPosProductModelVersion2.WooPosProductImage(1L, url = "url", name = "", alt = "")) ) whenever(getProductById(eq(product.remoteId))).thenReturn(product) From 4bb389f4852243059ef5e859933399974b16843d Mon Sep 17 00:00:00 2001 From: malinajirka Date: Tue, 9 Sep 2025 08:38:16 +0200 Subject: [PATCH 7/7] Turn variationEnabledAttribute into field --- .../common/data/models/WooPosProductModelVersion2.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt index cf75d18bd895..c74f640bce85 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/data/models/WooPosProductModelVersion2.kt @@ -1,6 +1,7 @@ package com.woocommerce.android.ui.woopos.common.data.models import android.os.Parcelable +import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.wordpress.android.fluxc.network.rest.wpcom.wc.product.CoreProductStatus import java.math.BigDecimal @@ -30,8 +31,10 @@ data class WooPosProductModelVersion2( val variationIds: List = emptyList(), ) : Parcelable { - val variationEnabledAttributes - get() = attributes.filter { it.isVariation } + @IgnoredOnParcel + val variationEnabledAttributes by lazy { + attributes.filter { it.isVariation } + } sealed class WooPosPricing : Parcelable { @Parcelize