Commit 28b1c7daab for woocommerce

commit 28b1c7daab083e7a325d8c9763ce095c8ee4c567
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date:   Thu Sep 18 14:49:58 2025 +0200

    Add to Cart + Options: don't disable optimistic updates when adding grouped product children to cart if only one fails (#60992)

    * Add changelog file

    * Add to Cart + Options: don't disable optimistic updates when adding grouped product children to cart if only one fails

    * CodeRabbit suggestions

    * Further cleanup

    * Add test

    * Typo

diff --git a/plugins/woocommerce/changelog/fix-60979-grouped-products-optimistic-updates-on-failure b/plugins/woocommerce/changelog/fix-60979-grouped-products-optimistic-updates-on-failure
new file mode 100644
index 0000000000..3e63952678
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-60979-grouped-products-optimistic-updates-on-failure
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Add to Cart + Options: don't disable optimistic updates when adding grouped product children to cart if only one fails
diff --git a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
index eebff41f79..f1ea3afad1 100644
--- a/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/base/stores/woocommerce/cart.ts
@@ -438,6 +438,8 @@ const { state, actions } = store< Store >(
 							existingItem.quantity = item.quantity;
 							if ( existingItem.key ) {
 								quantityChanges.cartItemsPendingQuantity = [
+									...( quantityChanges.cartItemsPendingQuantity ??
+										[] ),
 									existingItem.key,
 								];
 							}
@@ -493,6 +495,10 @@ const { state, actions } = store< Store >(

 					const json: BatchResponse = yield res.json();

+					// Checks if the response contains an error.
+					if ( isApiErrorResponse( res, json ) )
+						throw generateError( json );
+
 					const errorResponses = Array.isArray( json.responses )
 						? json.responses.filter(
 								( response ) =>
@@ -501,12 +507,6 @@ const { state, actions } = store< Store >(
 						  )
 						: [];

-					if ( errorResponses.length > 0 ) {
-						throw generateError(
-							errorResponses[ 0 ].body as ApiErrorResponse
-						);
-					}
-
 					const successfulResponses = Array.isArray( json.responses )
 						? json.responses.filter(
 								( response ) =>
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
index da758a47bd..3386e60320 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
@@ -370,6 +370,43 @@ test.describe( 'Add to Cart + Options Block', () => {

 			await expect( page.getByLabel( '3 items in cart' ) ).toBeVisible();
 		} );
+
+		await test.step( 'if one product succeeds and another fails, optimistic updates are applied and an error is displayed', async () => {
+			await page.reload();
+
+			// Try to add the individually sold product to cart again (it will fail).
+			const individuallySoldProductCheckbox = page.getByRole(
+				'checkbox',
+				{ name: 'Buy one of Hoodie with Logo' }
+			);
+			await individuallySoldProductCheckbox.click();
+
+			// Try to add another product to cart again (it will succeed).
+			const beanieIncreaseQuantityButton = page.getByLabel(
+				'Increase quantity of Beanie'
+			);
+			await beanieIncreaseQuantityButton.click();
+
+			await expect( addToCartButton ).not.toHaveClass( /\bdisabled\b/ );
+			await addToCartButton.click();
+
+			// Verify button updated successfully.
+			await expect(
+				page.getByRole( 'button', {
+					name: 'Added to cart',
+					exact: true,
+				} )
+			).toBeVisible();
+			// Verify error message is displayed.
+			await expect(
+				page.getByText(
+					'The quantity of "Hoodie with Logo" cannot be changed'
+				)
+			).toBeVisible();
+			// Verify optimistic updates were applied, so the product that was
+			// successfully added to cart is counted.
+			await expect( page.getByLabel( '4 items in cart' ) ).toBeVisible();
+		} );
 	} );

 	test( "doesn't allow selecting invalid variations in pills mode", async ( {