Repository: woocommerce/woocommerce-android
Pull request #14070: [WOOMOB-480] Order Items in POS Cart - coupons first
Author: login: malinajirka
Labels: name: feature: POS
Created: 2025-05-15T07:50:23Z | Merged: 2025-05-16T06:29:04Z

<!-- Remember about a good descriptive title. -->

Closes: #WOOMOB-480
<!-- Id number of the GitHub issue this PR addresses. -->

### Description
<!-- Take the time to write a good summary. Why is it needed? What does it do? When fixing bugs try to avoid just writing “See original issue” – clarify what the problem was and how you’ve fixed it. -->

This PR changes order of items in the list.
- Coupons are added to the very top
- Products are added after coupons (new products as close to the top of the list as possible, but after coupons)

It also prevents adding duplicated coupons to the cart - clicks are ignored, which isn't an optimal UX, but that's what we landed on after a discussion with the designer.


Note: When there are many coupons in the cart, added products might not be even visible as they are being added. Considering this is likely a temporary UX solution and it's very unlikely users would add so many coupons to the cart, we intentionally decided to not solve this edge-case.

### Testing information
<!-- This is your opportunity to break out individual scenarios that need testing (when necessary) and/or include a checklist for the reviewer to go through. Consider documenting the following from your own completed testing: devices used, alternate workflows, edge cases, affected areas, critical flows, areas not tested, and any remaining unknowns. Provide feedback on this new section of the PR template through Sept 30, 2024 to Apps Quality; additional context here: p91TBi-b8z-p2#comment-12036 -->


- Test adding and removing items from the cart - both coupons and products and ensure the above conditions are met at all times.

### The tests that have been performed
<!-- To give the reviewer an idea of what could be missed in terms of testing -->

I've tested the above + I tested checking out.

### Images/gif
<!-- Include before and after images or gifs when appropriate. -->


https://github.com/user-attachments/assets/43d37571-a1e4-4172-9c03-108ffc9fb6a1



- [x] I have considered if this change warrants release notes and have added them to `RELEASE-NOTES.txt` if necessary. Use the "[Internal]" label for non-user-facing changes.

## Reviewer (or Author, in the case of optional code reviews):

Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement:

- [ ] The PR is small and has a clear, single focus, or a valid explanation is provided in the description. If needed, please request to split it into smaller PRs.
- [ ] Ensure Adequate Unit Test Coverage: The changes are reasonably covered by unit tests or an explanation is provided in the PR description.
- [ ] Manual Testing: The author listed all the tests they ran, including smoke tests when needed (e.g., for refactorings). The reviewer confirmed that the PR works as expected on big (tablet) and small (phone) in case of UI changes, and no regressions are added.

<!-- Pull request guidelines: https://github.com/woocommerce/woocommerce-android/blob/develop/docs/pull-request-guidelines.md -->


Diff:
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 5ea069d985b3..5d00bb01b426 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
@@ -306,9 +306,33 @@ class WooPosCartViewModel @Inject constructor(
     private fun updateStateWithNewItem(newItem: WooPosCartItemViewState): WooPosCartState {
         return when (val currentState = _state.value.body) {
             is WooPosCartState.Body.Empty -> _state.value.copy(body = WooPosCartState.Body.WithItems(listOf(newItem)))
-            is WooPosCartState.Body.WithItems -> _state.value.copy(
-                body = currentState.copy(itemsInCart = listOf(newItem) + currentState.itemsInCart)
-            )
+            is WooPosCartState.Body.WithItems -> {
+                val updatedItemsList = placeItemInList(currentState, newItem)
+
+                _state.value.copy(body = currentState.copy(itemsInCart = updatedItemsList))
+            }
+        }
+    }
+
+    private fun placeItemInList(
+        currentState: WooPosCartState.Body.WithItems,
+        newItem: WooPosCartItemViewState
+    ): List<WooPosCartItemViewState> {
+        val (existingCoupons, existingProducts) = currentState.itemsInCart
+            .partition { it is WooPosCartItemViewState.Coupon }
+
+        return if (newItem is WooPosCartItemViewState.Coupon) {
+            val couponAlreadyInCart = existingCoupons.any {
+                (it as WooPosCartItemViewState.Coupon).id == newItem.id
+            }
+
+            if (couponAlreadyInCart) {
+                currentState.itemsInCart
+            } else {
+                listOf(newItem) + existingCoupons + existingProducts
+            }
+        } else {
+            existingCoupons + listOf(newItem) + existingProducts
         }
     }
 
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 4a8cd7b51b69..bbd69883a685 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
@@ -987,16 +987,6 @@ class WooPosCartViewModelTest {
         assertThat(finalState.isCheckoutButtonVisible).isFalse()
     }
 
-    private suspend fun simulateCouponClicked(couponId: Long = 1L) {
-        parentToChildrenMutableSharedFlow.emit(
-            ParentToChildrenEvent.ItemClickedInProductSelector(
-                itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = couponId, couponCode = ""),
-                source = WooPosItemSource.COUPON_LIST
-            ),
-
-        )
-    }
-
     @Test
     fun `given cart with products, when navigated to checkout, then checkout button not visible`() = runTest {
         // GIVEN
@@ -1026,6 +1016,85 @@ class WooPosCartViewModelTest {
         assertThat(finalState.isCheckoutButtonVisible).isTrue()
     }
 
+    @Test
+    fun `given cart with products, when coupon added, then coupon added to the beginning`() = runTest {
+        // GIVEN
+        val sut = createSut()
+        val states = sut.state.captureValues()
+        simulateProductClicked(23L)
+
+        // WHEN
+        simulateCouponClicked()
+
+        // THEN
+        val itemsInCart = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart
+        assertThat(itemsInCart).hasSize(2)
+        assertThat(itemsInCart[0]).isInstanceOf(WooPosCartItemViewState.Coupon::class.java)
+        assertThat(itemsInCart[1]).isInstanceOf(WooPosCartItemViewState.Product.Simple::class.java)
+    }
+
+    @Test
+    fun `given cart with coupons, when new coupon added, then new coupon added to the beginning`() =
+        runTest {
+            // GIVEN
+            val sut = createSut()
+            val states = sut.state.captureValues()
+
+            simulateCouponClicked(couponId = 1L)
+
+            // WHEN
+            simulateCouponClicked(couponId = 2L)
+
+            // THEN
+            val itemsInCart = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart
+            assertThat(itemsInCart).hasSize(2)
+            assertThat(itemsInCart[0]).isInstanceOf(WooPosCartItemViewState.Coupon::class.java)
+            assertThat(itemsInCart[1]).isInstanceOf(WooPosCartItemViewState.Coupon::class.java)
+            assertThat((itemsInCart[0] as WooPosCartItemViewState.Coupon).id).isEqualTo(2L)
+            assertThat((itemsInCart[1] as WooPosCartItemViewState.Coupon).id).isEqualTo(1L)
+        }
+
+    @Test
+    fun `given cart with coupons and products, when new product added, then added after coupons before products`() =
+        runTest {
+            // GIVEN
+            val sut = createSut()
+            val states = sut.state.captureValues()
+
+            simulateCouponClicked()
+            simulateProductClicked(999L)
+
+            // WHEN
+            simulateProductClicked(1L)
+
+            // THEN
+            val itemsInCart = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart
+            assertThat(itemsInCart).hasSize(3)
+            assertThat(itemsInCart[0]).isInstanceOf(WooPosCartItemViewState.Coupon::class.java)
+            assertThat(itemsInCart[1]).isInstanceOf(WooPosCartItemViewState.Product.Simple::class.java)
+            assertThat(itemsInCart[2]).isInstanceOf(WooPosCartItemViewState.Product.Simple::class.java)
+            assertThat((itemsInCart[1] as WooPosCartItemViewState.Product.Simple).id).isEqualTo(1L)
+            assertThat((itemsInCart[2] as WooPosCartItemViewState.Product.Simple).id).isEqualTo(999L)
+        }
+
+    @Test
+    fun `given coupon in cart, when same coupon clicked again, then coupon not duplicated`() = runTest {
+        // GIVEN
+        val sut = createSut()
+        val states = sut.state.captureValues()
+
+        simulateCouponClicked(couponId = 1L)
+
+        // WHEN
+        simulateCouponClicked(couponId = 1L)
+
+        // THEN
+        val itemsAfterSecondAdd = (states.last().body as WooPosCartState.Body.WithItems).itemsInCart
+        assertThat(itemsAfterSecondAdd).hasSize(1)
+        assertThat(itemsAfterSecondAdd[0]).isInstanceOf(WooPosCartItemViewState.Coupon::class.java)
+        assertThat((itemsAfterSecondAdd[0] as WooPosCartItemViewState.Coupon).id).isEqualTo(1L)
+    }
+
     private suspend fun createSutWithItemsInCart(): Pair<WooPosCartViewModel, List<WooPosCartState>> {
         val product = ProductTestUtils.generateProduct(
             productId = 23L,
@@ -1047,6 +1116,34 @@ class WooPosCartViewModelTest {
         return Pair(sut, states)
     }
 
+    private suspend fun simulateCouponClicked(couponId: Long = 1L) {
+        parentToChildrenMutableSharedFlow.emit(
+            ParentToChildrenEvent.ItemClickedInProductSelector(
+                itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = couponId, couponCode = ""),
+                source = WooPosItemSource.COUPON_LIST
+            ),
+        )
+    }
+
+    private suspend fun simulateProductClicked(productId: Long = 1L) {
+        val product = ProductTestUtils.generateProduct(
+            productId = productId,
+            productName = "title",
+            amount = "10.0"
+        ).copy(firstImageUrl = "url")
+
+        whenever(getProductById(eq(product.remoteId))).thenReturn(product)
+
+        parentToChildrenMutableSharedFlow.emit(
+            ParentToChildrenEvent.ItemClickedInProductSelector(
+                WooPosItemsViewModel.ItemClickedData.Product.Simple(
+                    id = productId
+                ),
+                source = WooPosItemSource.PRODUCT_LIST
+            )
+        )
+    }
+
     private fun createSut(): WooPosCartViewModel {
         return WooPosCartViewModel(
             childrenToParentEventSender,


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR updates the cart item insertion logic so that coupons are always placed at the top and products are inserted immediately after existing coupons. It refactors the viewmodel to use a new placement function and adds tests to verify the ordering behavior.

- Extracted `placeItemInList` in `WooPosCartViewModel` to handle coupon/product ordering
- Replaced simple prepending logic with the new placement function
- Expanded unit tests to cover coupon-first and products-after-coupons scenarios and reorganized helper methods

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

| File | Description |
| ---- | ----------- |
| WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt | Refactored `updateStateWithNewItem` to delegate ordering to `placeItemInList` |
| WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt | Added new tests for item order and moved helper methods to the bottom |


<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt:1019**
* Consider adding a test case for adding the same coupon ID twice to verify that duplicate coupons are either prevented or handled as intended.
```
@Test
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T07:53:08Z
user: login: samiuelson
body: The code looks good, @malinajirka 👍 

I have a UX concern. In a scenario of a cart with coupons and products, in particular where coupons take the visible height of cart (6 coupon items on my device), and at least the same number of products, when I scroll to the bottom and select a product, the list scrolls to the very top. This is misleading behavior, because the list animates to the top, not to the newly added item:

https://github.com/user-attachments/assets/7e092ab7-c1da-4271-a4bc-5c76c1187258

If feasible, I think it would be great to animate list scroll position to the newly added product instead of the top. Wdyt?
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T20:06:36Z
user: login: samiuelson
body: LGTM :shipit: 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T06:28:57Z

Review comments:
diff_hunk: @@ -306,10 +306,26 @@ class WooPosCartViewModel @Inject constructor(
     private fun updateStateWithNewItem(newItem: WooPosCartItemViewState): WooPosCartState {
         return when (val currentState = _state.value.body) {
             is WooPosCartState.Body.Empty -> _state.value.copy(body = WooPosCartState.Body.WithItems(listOf(newItem)))
-            is WooPosCartState.Body.WithItems -> _state.value.copy(
-                body = currentState.copy(itemsInCart = listOf(newItem) + currentState.itemsInCart)
-            )
+            is WooPosCartState.Body.WithItems -> {
+                val updatedItemsList = placeItemInList(currentState, newItem)
+
+                _state.value.copy(body = currentState.copy(itemsInCart = updatedItemsList))
+            }
+        }
+    }
+
+    private fun placeItemInList(
+        currentState: WooPosCartState.Body.WithItems,
+        newItem: WooPosCartItemViewState
+    ): List<WooPosCartItemViewState> {
+        val (existingCoupons, existingProducts) = currentState.itemsInCart.partition { it is WooPosCartItemViewState.Coupon }
+
+        val updatedItems = if (newItem is WooPosCartItemViewState.Coupon) {
+            listOf(newItem) + existingCoupons + existingProducts
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt
user: login: Copilot
body: The coupon branch doesn't filter out existing coupons with the same ID, allowing duplicates. Consider filtering existingCoupons by ID before prepending the new coupon.
```suggestion
            val filteredCoupons = existingCoupons.filterNot { it is WooPosCartItemViewState.Coupon && it.id == newItem.id }
            listOf(newItem) + filteredCoupons + existingProducts
```
created_at: 2025-05-15T07:53:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -306,10 +306,26 @@ class WooPosCartViewModel @Inject constructor(
     private fun updateStateWithNewItem(newItem: WooPosCartItemViewState): WooPosCartState {
         return when (val currentState = _state.value.body) {
             is WooPosCartState.Body.Empty -> _state.value.copy(body = WooPosCartState.Body.WithItems(listOf(newItem)))
-            is WooPosCartState.Body.WithItems -> _state.value.copy(
-                body = currentState.copy(itemsInCart = listOf(newItem) + currentState.itemsInCart)
-            )
+            is WooPosCartState.Body.WithItems -> {
+                val updatedItemsList = placeItemInList(currentState, newItem)
+
+                _state.value.copy(body = currentState.copy(itemsInCart = updatedItemsList))
+            }
+        }
+    }
+
+    private fun placeItemInList(
+        currentState: WooPosCartState.Body.WithItems,
+        newItem: WooPosCartItemViewState
+    ): List<WooPosCartItemViewState> {
+        val (existingCoupons, existingProducts) = currentState.itemsInCart.partition { it is WooPosCartItemViewState.Coupon }
+
+        val updatedItems = if (newItem is WooPosCartItemViewState.Coupon) {
+            listOf(newItem) + existingCoupons + existingProducts
+        } else {
+            existingCoupons + listOf(newItem) + existingProducts.filterNot { it == newItem }
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModel.kt
user: login: Copilot
body: [nitpick] Filtering products by object equality may be fragile if other fields differ. Consider filtering by product ID (e.g., `filterNot { (it as WooPosCartItemViewState.Product.Simple).id == newItem.id }`).
```suggestion
            existingCoupons + listOf(newItem) + existingProducts.filterNot { 
                (it as? WooPosCartItemViewState.Product.Simple)?.id == 
                (newItem as? WooPosCartItemViewState.Product.Simple)?.id 
            }
```
created_at: 2025-05-15T07:53:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1047,6 +1098,35 @@ class WooPosCartViewModelTest {
         return Pair(sut, states)
     }
 
+    private suspend fun simulateCouponClicked(couponId: Long = 1L) {
+        parentToChildrenMutableSharedFlow.emit(
+            ParentToChildrenEvent.ItemClickedInProductSelector(
+                itemData = WooPosItemsViewModel.ItemClickedData.Coupon(id = couponId),
+                source = WooPosItemSource.COUPON_LIST
+            ),
+
path: WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt
user: login: Copilot
body: [nitpick] There's a trailing comma and blank line before the closing parenthesis in `simulateCouponClicked`. Cleaning up the extra comma and whitespace would improve consistency.
```suggestion

```
created_at: 2025-05-15T07:53:08Z
author_association: CONTRIBUTOR

PR comments:
user: login: dangermattic
created_at: 2025-05-15T07:51:14Z
body: <!--
  0 Errors
  1 Warning: This PR is assigned to the mil...
  0 Messages
  0 Markdowns
-->
<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true" data-kind="Warning">
          1 Warning
      </th>
     </tr>
  </thead>
  <tbody>
    <tr>
      <td>:warning:</td>
      <td data-sticky="false">This PR is assigned to the milestone <a href="https://github.com/woocommerce/woocommerce-android/milestone/258">22.4</a>. This milestone is due in less than 2 days.<br />
Please make sure to get it merged by then or assign it to a milestone with a later deadline.</td>
    </tr>
  </tbody>
</table>

<p align="right" data-meta="generated_by_pr-check">
  Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
</p>

author_association: COLLABORATOR
user: login: wpmobilebot
created_at: 2025-05-15T07:57:18Z
body: <!-- REUSE_ID: WooCommerce-Wear-prototype-build-link -->

<details><summary>📲 You can test the changes from this Pull Request in <b>WooCommerce-Wear Android</b> by scanning the QR code below to install the corresponding build.</summary>
<table>
<tr>
  <td rowspan='6' width='260px'><img src='https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=https%3A%2F%2Fcdn.a8c-ci.services%2F70ad533e752cbd4dcd515ed6cfb9f823d6f9f819%2Fwoocommerce-wear-prototype-build-pr14070-ce35dc3.apk' width='250' height='250' /></td>
  <td><b>App Name</b></td><td> WooCommerce-Wear Android</td>
</tr>
<tr><td><b>Platform</b></td><td>⌚️ Wear OS</td></tr>
<tr><td><b>Flavor</b></td><td>Jalapeno</td></tr>
<tr><td><b>Build Type</b></td><td>Debug</td></tr>
<tr><td><b>Commit</b></td><td>ce35dc3daa8583db2645b853a5ba2b0f73ba6fc6</td></tr>
<tr><td><b>Direct Download</b></td><td><a href='https://cdn.a8c-ci.services/70ad533e752cbd4dcd515ed6cfb9f823d6f9f819/woocommerce-wear-prototype-build-pr14070-ce35dc3.apk'><code>woocommerce-wear-prototype-build-pr14070-ce35dc3.apk</code></a></td></tr>
</table>

</details>

author_association: COLLABORATOR
user: login: wpmobilebot
created_at: 2025-05-15T08:14:07Z
body: <!-- REUSE_ID: WooCommerce-prototype-build-link -->

<p>📲 You can test the changes from this Pull Request in <b>WooCommerce Android</b> by scanning the QR code below to install the corresponding build.</p>
<table>
<tr>
  <td rowspan='6' width='260px'><img src='https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=https%3A%2F%2Fcdn.a8c-ci.services%2Fa8b6e90d9d969f1b573b3c0dd090796761ee106e%2Fwoocommerce-prototype-build-pr14070-ce35dc3.apk' width='250' height='250' /></td>
  <td><b>App Name</b></td><td> WooCommerce Android</td>
</tr>
<tr><td><b>Platform</b></td><td>📱 Mobile</td></tr>
<tr><td><b>Flavor</b></td><td>Jalapeno</td></tr>
<tr><td><b>Build Type</b></td><td>Debug</td></tr>
<tr><td><b>Commit</b></td><td>ce35dc3daa8583db2645b853a5ba2b0f73ba6fc6</td></tr>
<tr><td><b>Direct Download</b></td><td><a href='https://cdn.a8c-ci.services/a8b6e90d9d969f1b573b3c0dd090796761ee106e/woocommerce-prototype-build-pr14070-ce35dc3.apk'><code>woocommerce-prototype-build-pr14070-ce35dc3.apk</code></a></td></tr>
</table>


author_association: COLLABORATOR
user: login: codecov-commenter
created_at: 2025-05-15T08:46:38Z
body: ## [Codecov](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/14070?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 38.43%. Comparing base [(`12a201e`)](https://app.codecov.io/gh/woocommerce/woocommerce-android/commit/12a201ebbae26a6ea7f77127fd3733c9980a562c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) to head [(`ce35dc3`)](https://app.codecov.io/gh/woocommerce/woocommerce-android/commit/ce35dc3daa8583db2645b853a5ba2b0f73ba6fc6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##              trunk   #14070   +/-   ##
=========================================
  Coverage     38.43%   38.43%           
- Complexity     9606     9609    +3     
=========================================
  Files          2126     2126           
  Lines        116952   116962   +10     
  Branches      15029    15032    +3     
=========================================
+ Hits          44949    44958    +9     
  Misses        67885    67885           
- Partials       4118     4119    +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/14070?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: malinajirka
created_at: 2025-05-16T04:11:34Z
body: Thank you Sam for the review! The situation you are describing is mentioned in the description:

`Note: When there are many coupons in the cart, added products might not be even visible as they are being added. Considering this is likely a temporary UX solution and it's very unlikely users would add so many coupons to the cart, we intentionally decided to not solve this edge-case.`

Unless you feel it's a high risk, I'd skip fixing it to avoid any unnecessary complexity. Wdyt?


author_association: CONTRIBUTOR