Commit 4c322802113 for woocommerce
commit 4c3228021130e04ee3ac49518f9661e4b7a52bc3
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Wed Sep 16 00:54:11 2026 +0300
[tests] Move grouped product cart removal to PHPUnit (#68653)
* test(product): Move grouped cart removal below E2E
The grouped products spec had two titles: one that adds grouped
children to the cart and one that removes them again. The removal
title drove the cart page to prove that clearing a grouped child's
quantity removes that line item. That is the update-cart form
handler's job, and WC_Cart_Test can ask it directly.
test_update_cart_action_removes_grouped_child_items posts a real
update-cart request through WC_Form_Handler and asserts the cart
contents afterwards. The add title stays in the browser: it is the
one that proves the grouped product form posts what the handler
expects.
The spec goes from 2 titles to 1.
This file is shared with another batch, and the sharing needed care.
class-wc-cart-test.php receives one method from this migration commit
and a different one from `760478af4c`, which belongs to the Add to
Cart options work and ships separately. Only this batch's method is
here; `test_add_to_cart_action_handles_grouped_product_quantities`
is deliberately absent.
Trunk had also moved on: #67527 added eleven methods to the same
class after this work branched. Those are all kept. The class goes
from trunk's 50 methods to 51, and the file's diff against trunk has
no deleted lines.
Carries the mega-branch commit:
- 8e1bd64631 test(product): Move grouped cart removal below E2E
Refs TESTOPS-288
Refs #68046
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cart): Stop the grouped cart test restoring base-class state
The finally block put back $_GET, $_POST and $_REQUEST, restored
woocommerce_cart_redirect_after_add, deleted three products, and
rewrote the cart contents and two session keys.
clean_up_global_scope() empties the three request globals before every
test and the rollback covers the option and the products. The cart and
session restores were doing less than nothing: WC_Unit_Test_Case::
tearDown() runs clear_wc_singleton_state() after this finally, which
empties the cart and clears the notices again, so the restored values
were overwritten a moment later.
$_SERVER stays, because the base class only resets it for core's own
suite and this test unsets HTTP_REFERER.
Refs TESTOPS-288
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(cart): Record why the referer unset cannot be removed
The unset of HTTP_REFERER reads like leftover request cleanup, and the
comment above it talked only about $_SERVER not being restored by the
base class. It is doing something else entirely: after removing a cart
item, WC_Form_Handler::update_cart_action() calls wp_safe_redirect()
followed by exit whenever wp_get_referer() returns a value. An exit
inside PHPUnit ends the whole run, not just this test.
Nothing in the diff shows that, and this campaign has been deleting
exactly this shape of line as duplicated teardown, so say what it is for.
Refs TESTOPS-288
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/testops-288-product-grouped b/plugins/woocommerce/changelog/testops-288-product-grouped
new file mode 100644
index 00000000000..217d5c56413
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-288-product-grouped
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Move the grouped product cart removal E2E title to PHPUnit; WC_Cart_Test now covers removing grouped children through the real update-cart form handler.
+
diff --git a/plugins/woocommerce/tests/e2e/tests/product/product-grouped.spec.ts b/plugins/woocommerce/tests/e2e/tests/product/product-grouped.spec.ts
index 7aabc327e0c..17da79d03d0 100644
--- a/plugins/woocommerce/tests/e2e/tests/product/product-grouped.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/product/product-grouped.spec.ts
@@ -120,36 +120,5 @@ test.describe(
0
);
} );
-
- test( 'should be able to remove grouped products from the cart', async ( {
- page,
- } ) => {
- await page.goto( `product/${ groupedProductSlug }` );
- await page.locator( 'div.quantity input.qty >> nth=0' ).fill( '1' );
- await page.locator( 'div.quantity input.qty >> nth=1' ).fill( '1' );
- await page
- .getByRole( 'button', { name: 'Add to cart', exact: true } )
- .click();
-
- await expect(
- page.getByText(
- new RegExp(
- `${ simpleProduct1.name }.*and.*${ simpleProduct2.name }.*have been added to your cart`
- )
- )
- ).toBeVisible();
-
- await page.goto( 'cart/' );
- await page
- .getByRole( 'button', { name: 'Remove' } )
- .first()
- .click();
- await page
- .getByRole( 'button', { name: 'Remove' } )
- .first()
- .click();
-
- await checkCartContent( false, page, [], 0 );
- } );
}
);
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
index d8f8c35fc24..6b8db71a3c9 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-cart-test.php
@@ -2339,4 +2339,99 @@ class WC_Cart_Test extends \WC_Unit_Test_Case {
$first_child->delete( true );
}
}
+
+ /**
+ * @testdox Should remove grouped child cart items through consecutive form requests.
+ */
+ public function test_update_cart_action_removes_grouped_child_items(): void {
+ // $_SERVER is the only request global the base class leaves alone; it resets
+ // the other three before every test.
+ $original_server = $GLOBALS['_SERVER'];
+
+ try {
+ // Do not remove this unset as redundant cleanup. It is what keeps the test
+ // alive: after removing an item, update_cart_action() calls wp_safe_redirect()
+ // followed by exit whenever wp_get_referer() is truthy
+ // (includes/class-wc-form-handler.php:818-821). exit inside PHPUnit kills the
+ // whole run, not just this test.
+ unset( $GLOBALS['_SERVER']['HTTP_REFERER'] );
+ update_option( 'woocommerce_cart_redirect_after_add', 'no' );
+ WC()->cart->empty_cart();
+ WC()->session->set( 'wc_notices', null );
+
+ $first_child = WC_Helper_Product::create_simple_product();
+ $first_child->set_regular_price( '10' );
+ $first_child->save();
+
+ $second_child = WC_Helper_Product::create_simple_product();
+ $second_child->set_regular_price( '20' );
+ $second_child->save();
+
+ $grouped_product = new WC_Product_Grouped();
+ $grouped_product->set_name( 'Grouped removal request product' );
+ $grouped_product->set_children(
+ array(
+ $first_child->get_id(),
+ $second_child->get_id(),
+ )
+ );
+ $grouped_product->save();
+
+ $grouped_quantities = array(
+ $first_child->get_id() => 1,
+ $second_child->get_id() => 1,
+ );
+ $GLOBALS['_REQUEST'] = array(
+ 'add-to-cart' => $grouped_product->get_id(),
+ 'quantity' => $grouped_quantities,
+ );
+ $GLOBALS['_POST'] = array(
+ 'quantity' => $grouped_quantities,
+ );
+
+ WC_Form_Handler::add_to_cart_action( false );
+
+ $cart_items = WC()->cart->get_cart();
+ $cart_item_keys_by_product = array();
+ foreach ( $cart_items as $cart_item_key => $cart_item ) {
+ $cart_item_keys_by_product[ $cart_item['product_id'] ] = $cart_item_key;
+ }
+ $expected_child_ids = array( $first_child->get_id(), $second_child->get_id() );
+ $actual_child_ids = array_map( 'intval', array_keys( $cart_item_keys_by_product ) );
+ sort( $expected_child_ids );
+ sort( $actual_child_ids );
+
+ $this->assertCount( 2, $cart_items, 'Both grouped children should be cart lines.' );
+ $this->assertSame(
+ $expected_child_ids,
+ $actual_child_ids,
+ 'Only the grouped children should be cart lines.'
+ );
+ $this->assertArrayNotHasKey( $grouped_product->get_id(), $cart_item_keys_by_product, 'The grouped parent should not become a cart line.' );
+
+ $cart_item_keys = array_values( $cart_item_keys_by_product );
+ foreach ( $cart_item_keys as $index => $cart_item_key ) {
+ $GLOBALS['_GET'] = array(
+ 'remove_item' => $cart_item_key,
+ );
+ $GLOBALS['_POST'] = array();
+ $GLOBALS['_REQUEST'] = array(
+ '_wpnonce' => wp_create_nonce( 'woocommerce-cart' ),
+ 'remove_item' => $cart_item_key,
+ );
+
+ WC_Form_Handler::update_cart_action();
+
+ $remaining_cart_items = WC()->cart->get_cart();
+ if ( 0 === $index ) {
+ $this->assertArrayNotHasKey( $cart_item_key, $remaining_cart_items, 'The first requested cart item should be removed.' );
+ $this->assertSame( array( $cart_item_keys[1] ), array_keys( $remaining_cart_items ), 'Only the other original cart item should remain.' );
+ } else {
+ $this->assertEmpty( $remaining_cart_items, 'The second removal request should empty the cart.' );
+ }
+ }
+ } finally {
+ $GLOBALS['_SERVER'] = $original_server;
+ }
+ }
}