Commit 042ce639cbd for woocommerce
commit 042ce639cbd51639e1212ccbc2b6cd0862025285
Author: SH Sajal Chowdhury <72102985+shsajalchowdhury@users.noreply.github.com>
Date: Thu Oct 22 00:14:53 2026 +0600
Add woocommerce_checkout_after_cart_item_meta action hook (#64379)
* Add woocommerce_checkout_after_cart_item_meta action hook to checkout review-order template
Address review feedback:
- Rename the hook from woocommerce_checkout_cart_item_meta_after to
woocommerce_checkout_after_cart_item_meta to match the naming used by
sibling hooks in other templates.
- Document the hook inline and bump the template version to 11.3.0.
- Add unit tests covering the action firing for visible cart items and
not firing for items hidden via woocommerce_checkout_cart_item_visible.
* Tests: assert hook output position against the product cell and rendered item data
The first occurrence of class="product-name" in the template output is
the table header, so comparing against it does not prove the action
output is rendered inside the product cell. Compare against the td
element instead, add cart item data to the test product, and assert the
action output comes after the rendered item data.
Addresses CodeRabbit feedback.
* Tests: fix lint warnings and make item-data assertions meaningful
- Align assignment operators with surrounding statements.
- Use pre-increment for the standalone counter.
- Render the cart item data via the woocommerce_get_item_data filter so the
placement assertion against wc_get_formatted_cart_item_data() output is
meaningful (custom cart item data is not rendered by default).
- Assert both hook arguments (cart item key and cart item) and that the
action output stays inside the product-name cell boundary.
* Update checkout template version to 11.4.0
---------
Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/pr-64379 b/plugins/woocommerce/changelog/pr-64379
new file mode 100644
index 00000000000..d9e7bcb48dd
--- /dev/null
+++ b/plugins/woocommerce/changelog/pr-64379
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add woocommerce_checkout_after_cart_item_meta action hook to the checkout review-order template.
diff --git a/plugins/woocommerce/templates/checkout/review-order.php b/plugins/woocommerce/templates/checkout/review-order.php
index c297b8f1a1f..253496fd303 100644
--- a/plugins/woocommerce/templates/checkout/review-order.php
+++ b/plugins/woocommerce/templates/checkout/review-order.php
@@ -12,7 +12,7 @@
*
* @see https://woocommerce.com/document/template-structure/
* @package WooCommerce\Templates
- * @version 11.2.0
+ * @version 11.4.0
*/
defined( 'ABSPATH' ) || exit;
@@ -57,6 +57,16 @@ defined( 'ABSPATH' ) || exit;
<?php echo wp_kses_post( $product_name ) . ' '; ?>
<?php echo apply_filters( 'woocommerce_checkout_cart_item_quantity', ' <strong class="product-quantity">' . sprintf( '× %s', $cart_item['quantity'] ) . '</strong>', $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo wc_get_formatted_cart_item_data( $cart_item, false, $cart_item_name ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ <?php
+ /**
+ * Fires after the cart item data is rendered in the checkout review order table.
+ *
+ * @since 11.4.0
+ * @param array $cart_item The cart item data.
+ * @param string $cart_item_key The cart item key.
+ */
+ do_action( 'woocommerce_checkout_after_cart_item_meta', $cart_item, $cart_item_key );
+ ?>
</td>
<td class="product-total">
<?php echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
index e6b76fd5642..2d251848e52 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-checkout-test.php
@@ -930,6 +930,96 @@ class WC_Checkout_Test extends \WC_Unit_Test_Case {
$this->assertStringContainsString( 'woocommerce-form-login', $output );
}
+ /**
+ * @testdox The woocommerce_checkout_after_cart_item_meta action fires for visible cart items in the review order template.
+ */
+ public function test_checkout_after_cart_item_meta_action_fires_for_visible_items() {
+ $product = WC_Helper_Product::create_simple_product();
+ $cart_item_key = WC()->cart->add_to_cart(
+ $product->get_id(),
+ 1,
+ '',
+ '',
+ array(
+ 'test_meta' => 'TEST_ITEM_META_DATA',
+ )
+ );
+
+ $fired = array();
+ $callback = static function ( $cart_item, $cart_item_key ) use ( &$fired ) {
+ $fired[ $cart_item_key ] = $cart_item;
+ echo 'TEST_CHECKOUT_AFTER_CART_ITEM_META';
+ };
+ add_action( 'woocommerce_checkout_after_cart_item_meta', $callback, 10, 2 );
+
+ $get_item_data = static function () {
+ return array(
+ array(
+ 'key' => 'test_meta',
+ 'display' => 'TEST_ITEM_META_DATA',
+ ),
+ );
+ };
+ add_filter( 'woocommerce_get_item_data', $get_item_data );
+
+ $output = wc_get_template_html( 'checkout/review-order.php' );
+
+ remove_action( 'woocommerce_checkout_after_cart_item_meta', $callback, 10 );
+ remove_filter( 'woocommerce_get_item_data', $get_item_data );
+
+ $this->assertCount( 1, $fired, 'The action should fire once for the visible cart item.' );
+ $this->assertArrayHasKey( $cart_item_key, $fired, 'The action should receive the cart item key.' );
+ $this->assertSame( $product->get_id(), $fired[ $cart_item_key ]['product_id'], 'The action should receive the cart item.' );
+ $this->assertStringContainsString( 'TEST_ITEM_META_DATA', $output, 'The cart item data should be rendered for the test to be meaningful.' );
+ $this->assertStringContainsString( 'TEST_CHECKOUT_AFTER_CART_ITEM_META', $output );
+
+ $cell_start = strpos( $output, '<td class="product-name">' );
+ $cell_end = strpos( $output, '</td>', $cell_start );
+ $marker_pos = strpos( $output, 'TEST_CHECKOUT_AFTER_CART_ITEM_META' );
+ $meta_pos = strpos( $output, 'TEST_ITEM_META_DATA' );
+
+ $this->assertGreaterThan(
+ $cell_start,
+ $marker_pos,
+ 'The action output should be rendered inside the product name cell.'
+ );
+ $this->assertLessThan(
+ $cell_end,
+ $marker_pos,
+ 'The action output should be rendered inside the product name cell.'
+ );
+ $this->assertGreaterThan(
+ $meta_pos,
+ $marker_pos,
+ 'The action output should be rendered after the cart item data.'
+ );
+ }
+
+ /**
+ * @testdox The woocommerce_checkout_after_cart_item_meta action does not fire for items hidden by woocommerce_checkout_cart_item_visible.
+ */
+ public function test_checkout_after_cart_item_meta_action_does_not_fire_for_hidden_items() {
+ $product = WC_Helper_Product::create_simple_product();
+ WC()->cart->add_to_cart( $product->get_id() );
+
+ add_filter( 'woocommerce_checkout_cart_item_visible', '__return_false' );
+
+ $fired = 0;
+ $callback = static function () use ( &$fired ) {
+ ++$fired;
+ echo 'TEST_CHECKOUT_AFTER_CART_ITEM_META';
+ };
+ add_action( 'woocommerce_checkout_after_cart_item_meta', $callback );
+
+ $output = wc_get_template_html( 'checkout/review-order.php' );
+
+ remove_filter( 'woocommerce_checkout_cart_item_visible', '__return_false' );
+ remove_action( 'woocommerce_checkout_after_cart_item_meta', $callback );
+
+ $this->assertSame( 0, $fired, 'The action should not fire for hidden cart items.' );
+ $this->assertStringNotContainsString( 'TEST_CHECKOUT_AFTER_CART_ITEM_META', $output );
+ }
+
/**
* @testdox Returns WP_Error when line items fail to persist to the DB despite save() completing.
*/