Commit c082aba805b for woocommerce
commit c082aba805bf26807507e81250f90241bfa7c50e
Author: Faisal Ahammad <faisalahammad24@gmail.com>
Date: Wed Aug 5 17:22:37 2026 +0600
Tests: Add end-to-end routing test for order reviews AJAX submission (#64408)
* test: add coverage for OrderReviews\SubmissionHandler AJAX endpoint
Adds unit tests driving the existing woocommerce_submit_order_reviews
AJAX action. Trunk already registers the handler via
OrderReviews\SubmissionHandler; the original PR added a duplicate
WC_AJAX::submit_order_reviews() that shadowed the registered one with
a different payload contract.
Refs #64310.
* fix: address 7 review issues in order reviews test coverage
- Fix add_product() returns int not object (line 59)
- Enable customer_review_request feature flag in set_up()
- Clean up feature flag in tear_down()
- Remove data.code assertions (handler sets no code key)
- Change changelog type from add to dev for test-only work
Addresses PR #64408 review feedback.
* fix: resolve 12 PHPUnit test failures and lint warnings
Wire SubmissionHandler AJAX hooks in setUp() (feature flag disabled
at WP bootstrap, so maybe_init_order_reviews() skips init()).
Replace bogus data.code assertions with data.message checks
(handler never sets 'code' in JSON response body).
Fix 4 PHPCS alignment warnings (auto-fixed by phpcbf).
Errors fixed:
- All 12 WC_AJAX_Submit_Order_Reviews_Test failures: handler not registered
- data.code check: tests nothing (handler uses data.message, not data.code)
- PHPCS alignment: 4 spacing warnings in test file
Refs #64408
* test: replace legacy submit order reviews AJAX test with routing test
SubmissionHandlerRoutingTest replaces class-wc-ajax-submit-order-reviews-test.php,
proving the registered nopriv admin-ajax action routes guest requests through
SubmissionHandler::handle() and inserts a review end to end.
* fix(tests): address Ferdev routing test feedback
- setUp() re-runs SubmissionHandler::init() so the AJAX actions
re-register after tearDown() removes them.
- dispatch() captures the JSON envelope via output buffering, matching
the pattern SubmissionHandlerTest uses for wp_send_json output.
Tests: 2/2 pass. Lint clean. CodeRabbit clean.
* fix(tests): import WPAjaxDieContinueException per Ferdev review
The unqualified class reference in the catch block resolves to the test
namespace, not the global WP test-suite exception. Adding the import
matches the sibling SubmissionHandlerTest convention.
Addresses PR #64408 review feedback.
* test(order-reviews): clean output buffer on non-WP exception escape
Move ob_get_clean into a finally block so the test process does not leak a
stray output buffer when do_action raises an exception other than
WPAjaxDieContinueException.
Addresses PR #64408 review feedback from Ferdev (thread #3647175364).
* test(order-reviews): cover resolver path and wp_ajax_ hook
- setUp uses WC()->maybe_init_order_reviews() to exercise production wiring,
then handler->init() for repeatability across tests
- test_action_hooks_are_registered covers both wp_ajax_ and wp_ajax_nopriv_
hooks per issue #64310 scope
Refs #64408
* test(order-reviews): reset container cache per test, drop redundant init()
Adds wc_get_container()->reset_all_resolved() at the top of tearDown so
the resolved-instance cache does not stream across tests. The cached
SubmissionHandler instance skips auto-init() on the next resolve, which
would silently regress test_action_hooks_are_registered.
Removes the explicit $handler->init() call from setUp so the resolver
path (WC()->maybe_init_order_reviews) is the sole source of the
wp_ajax_* hook wiring. If the resolver ever stops resolving
the handler, this test will now fail instead of staying green via the
manual fallback.
Drops the two remove_action('wp_ajax_*') calls in tearDown that were
only compensating for the cached-instance problem.
Addresses PR feedback from Ferdev.
diff --git a/plugins/woocommerce/changelog/64310-add-ajax-submit-order-reviews b/plugins/woocommerce/changelog/64310-add-ajax-submit-order-reviews
new file mode 100644
index 00000000000..c900b33948e
--- /dev/null
+++ b/plugins/woocommerce/changelog/64310-add-ajax-submit-order-reviews
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Add test coverage for the existing `woocommerce_submit_order_reviews` AJAX endpoint handled by `OrderReviews\SubmissionHandler`.
diff --git a/plugins/woocommerce/tests/php/src/Internal/OrderReviews/SubmissionHandlerRoutingTest.php b/plugins/woocommerce/tests/php/src/Internal/OrderReviews/SubmissionHandlerRoutingTest.php
new file mode 100644
index 00000000000..158c348fd08
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/OrderReviews/SubmissionHandlerRoutingTest.php
@@ -0,0 +1,144 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\OrderReviews;
+
+use Automattic\WooCommerce\Enums\OrderStatus;
+use Automattic\WooCommerce\Internal\OrderReviews\ItemEligibility;
+use Automattic\WooCommerce\Internal\OrderReviews\SubmissionHandler;
+use WC_Helper_Product;
+use WC_Unit_Test_Case;
+use WPAjaxDieContinueException;
+
+/**
+ * End-to-end wiring test for the submit_order_reviews AJAX action.
+ *
+ * Complements SubmissionHandlerTest (which calls handle() directly) by proving
+ * the registered admin-ajax action actually routes to the handler for guests.
+ */
+class SubmissionHandlerRoutingTest extends WC_Unit_Test_Case {
+
+ /**
+ * Set up test fixtures.
+ */
+ public function setUp(): void {
+ parent::setUp();
+ update_option( 'woocommerce_feature_customer_review_request_enabled', 'yes' );
+ // Drive the production registration path: the flag was off at bootstrap,
+ // so re-run the init-hooked resolver now that it's on. tearDown() drops the
+ // container's resolved-instance cache, so each test re-resolves a fresh
+ // SubmissionHandler whose auto-init() registers both wp_ajax_* hooks.
+ WC()->maybe_init_order_reviews();
+ update_option( 'comment_moderation', '0' );
+ wp_set_current_user( 0 );
+ }
+
+ /**
+ * Tear down test fixtures.
+ */
+ public function tearDown(): void {
+ // Drop the container's resolved-instance cache so the next setUp()
+ // re-resolves a fresh SubmissionHandler. Without this, the cached instance
+ // would skip its auto-init() on the next resolve and the wp_ajax_* hooks
+ // would never register, regressing test_action_hooks_are_registered.
+ wc_get_container()->reset_all_resolved();
+ delete_option( 'woocommerce_feature_customer_review_request_enabled' );
+ delete_option( 'comment_moderation' );
+ $_POST = array();
+ ItemEligibility::reset_cache();
+ remove_all_filters( 'wp_die_ajax_handler' );
+ remove_all_filters( 'wp_doing_ajax' );
+ remove_all_filters( 'wp_send_json_handler' );
+ parent::tearDown();
+ }
+
+ /**
+ * @testdox Both the authenticated and nopriv AJAX actions are wired through the production init path.
+ */
+ public function test_action_hooks_are_registered(): void {
+ $handler = wc_get_container()->get( SubmissionHandler::class );
+
+ $this->assertNotFalse(
+ has_action( 'wp_ajax_' . SubmissionHandler::ACTION, array( $handler, 'handle' ) ),
+ 'Authenticated submit_order_reviews action should be wired when the feature is enabled.'
+ );
+ $this->assertNotFalse(
+ has_action( 'wp_ajax_nopriv_' . SubmissionHandler::ACTION, array( $handler, 'handle' ) ),
+ 'Guest submit_order_reviews action should be wired when the feature is enabled.'
+ );
+ }
+
+ /**
+ * @testdox A guest request routes through admin-ajax to the handler and inserts a review.
+ */
+ public function test_guest_submission_routes_end_to_end(): void {
+ $order = wc_create_order( array( 'status' => OrderStatus::COMPLETED ) );
+ $order->set_billing_first_name( 'John' );
+ $order->set_billing_email( 'john@example.com' );
+ $product = WC_Helper_Product::create_simple_product();
+ $order->add_product( $product, 1 );
+ $order->save();
+
+ $item_id = 0;
+ foreach ( $order->get_items() as $item ) {
+ $item_id = $item->get_id();
+ }
+
+ $_POST['_wcnonce'] = wp_create_nonce( SubmissionHandler::ACTION );
+ $_POST['order_id'] = $order->get_id();
+ $_POST['key'] = $order->get_order_key();
+ $_POST['reviews'] = array(
+ array(
+ 'order_item_id' => $item_id,
+ 'product_id' => $product->get_id(),
+ 'rating' => 5,
+ 'text' => 'Great product!',
+ ),
+ );
+
+ $response = $this->dispatch();
+
+ $this->assertTrue( $response['success'] );
+ $this->assertSame( 'ok', $response['data']['results'][0]['status'] );
+
+ $comment = get_comment( $response['data']['results'][0]['comment_id'] );
+ $this->assertSame( 'review', $comment->comment_type );
+ $this->assertSame( $product->get_id(), (int) $comment->comment_post_ID );
+ }
+
+ /**
+ * Fire the registered nopriv action and capture the JSON envelope it emits.
+ *
+ * @return array{success:bool,data:mixed}
+ */
+ private function dispatch(): array {
+ // Same capture pattern SubmissionHandlerTest uses: wp_send_json writes
+ // to the output buffer via wp_die, so an ob_start/ob_get_clean pair
+ // collects the rendered JSON. wp_send_json itself fires no filter.
+ add_filter( 'wp_die_ajax_handler', static fn() => static fn() => null );
+ add_filter( 'wp_doing_ajax', '__return_true' );
+
+ ob_start();
+ try {
+ do_action( 'wp_ajax_nopriv_' . SubmissionHandler::ACTION ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
+ } catch ( WPAjaxDieContinueException $e ) {
+ // Expected: wp_send_json_* always calls wp_die().
+ unset( $e );
+ } finally {
+ // Clean the buffer even if a non-WPAjax exception escaped, so the test
+ // process does not leak a stray buffer to subsequent tests.
+ $body = (string) ob_get_clean();
+ }
+
+ $decoded = json_decode( $body, true );
+ $response = array(
+ 'success' => false,
+ 'data' => null,
+ );
+ if ( is_array( $decoded ) ) {
+ $response['success'] = ! empty( $decoded['success'] );
+ $response['data'] = $decoded['data'] ?? null;
+ }
+ return $response;
+ }
+}