Commit bba9f050469 for woocommerce

commit bba9f050469a57abf7d20a0fc9b4792e5bc7cc57
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Sat Nov 14 11:22:53 2026 +0200

    [tests] Fix three tests that only pass when another test sends an email first (#70943)

    test: Stop three tests depending on silent transactional emails

    WC_Emails::init_transactional_emails() hooks the email actions on
    init, before WordPress snapshots the hooks, but the listeners that
    send the emails are added when the mailer is first built. In the test
    process that happens in the first test that fires an email, after the
    snapshot, so the first restore removes them and no hook-driven email
    is sent for the rest of the run. Only that first test sends emails.

    Three tests passed only because an earlier test had used up that
    first slot. Run alone, each fails:

    - AbilitiesLoaderTest::test_order_send_email_refusals counted mails
      from before creating its processing order, whose pending to
      processing change sends the new order and processing emails. Create
      both orders first, then count.
    - WC_Tests_CRUD_Orders::test_stage_payment_complete_changes_the_
      order_in_memory_only counted every order note after saving; the new
      order email adds a note of its own. Count the payment note.
    - OrdersTableDataStoreTests::test_update_new_processing_order_
      correctly_triggers_new_order_hook got a second woocommerce_new_order
      from the new order email saving the order during the save. Turn
      that email off in the test.

    Building the mailer in the test bootstrap would make emails behave
    the same in every test, but adds about 27% to the suite's runtime, so
    the tests are fixed instead. With the mailer built in the bootstrap,
    these three were the only tests to fail.

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/test-email-hooks-after-first-test b/plugins/woocommerce/changelog/test-email-hooks-after-first-test
new file mode 100644
index 00000000000..c283e26ec99
--- /dev/null
+++ b/plugins/woocommerce/changelog/test-email-hooks-after-first-test
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Make three order and email tests pass whether or not transactional emails fire; no production change.
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
index f6d41879207..514ad0b92ee 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php
@@ -1196,7 +1196,12 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case {
 		$this->assertFalse( WC()->session->get( 'order_awaiting_payment' ) );
 		$this->assertSame( OrderStatus::COMPLETED, wc_get_order( $object->get_id() )->get_status() );
 		$this->assertSame( 1, did_action( 'woocommerce_payment_complete' ) - $fired );
-		$this->assertCount( $notes + 1, wc_get_order_notes( array( 'order_id' => $object->get_id() ) ) );
+		// Count the payment note only: the status change can also send emails, which add notes of their own.
+		$payment_notes = array_filter(
+			wc_get_order_notes( array( 'order_id' => $object->get_id() ) ),
+			fn( $note ) => 'Payment complete.' === $note->content
+		);
+		$this->assertCount( 1, $payment_notes );

 		$object->save();
 		$this->assertSame( 1, did_action( 'woocommerce_payment_complete' ) - $fired );
diff --git a/plugins/woocommerce/tests/php/src/Internal/Abilities/AbilitiesLoaderTest.php b/plugins/woocommerce/tests/php/src/Internal/Abilities/AbilitiesLoaderTest.php
index 42a00d39ba3..415a9c556df 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Abilities/AbilitiesLoaderTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Abilities/AbilitiesLoaderTest.php
@@ -5058,13 +5058,16 @@ class AbilitiesLoaderTest extends \WC_Unit_Test_Case {
 			++$mails;
 			return true;
 		};
+
+		$ability          = wp_get_ability( 'woocommerce/order-send-email' );
+		$pending_order    = $this->create_order_for_update( 'pending' );
+		$processing_order = $this->create_order_for_update( 'processing' );
+		// Count only what the ability sends: moving the second order to processing can send its own emails.
 		add_filter( 'pre_wp_mail', $capture );
-		$ability = wp_get_ability( 'woocommerce/order-send-email' );
-		$order   = $this->create_order_for_update( 'pending' );

 		$mismatch = $ability->execute(
 			array(
-				'id'    => $order->get_id(),
+				'id'    => $pending_order->get_id(),
 				'email' => 'completed',
 			)
 		);
@@ -5072,18 +5075,17 @@ class AbilitiesLoaderTest extends \WC_Unit_Test_Case {
 		add_filter( 'woocommerce_order_get_billing_email', '__return_empty_string' );
 		$no_recipient = $ability->execute(
 			array(
-				'id'    => $order->get_id(),
+				'id'    => $pending_order->get_id(),
 				'email' => 'invoice',
 			)
 		);

 		remove_filter( 'woocommerce_order_get_billing_email', '__return_empty_string' );

-		$order = $this->create_order_for_update( 'processing' );
 		add_filter( 'woocommerce_email_enabled_customer_processing_order', '__return_false' );
 		$disabled = $ability->execute(
 			array(
-				'id'    => $order->get_id(),
+				'id'    => $processing_order->get_id(),
 				'email' => 'processing',
 			)
 		);
@@ -5098,7 +5100,7 @@ class AbilitiesLoaderTest extends \WC_Unit_Test_Case {
 		add_filter( 'pre_wp_mail', '__return_false' );
 		$failed = $ability->execute(
 			array(
-				'id'    => $order->get_id(),
+				'id'    => $processing_order->get_id(),
 				'email' => 'invoice',
 			)
 		);
diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
index 318c308c0da..392b386a2bb 100644
--- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php
@@ -4410,6 +4410,8 @@ class OrdersTableDataStoreTests extends \HposTestCase {
 		};

 		add_action( 'woocommerce_new_order', $callback );
+		// The new order email saves the order again while it is being saved, which this test is not about.
+		add_filter( 'woocommerce_email_enabled_new_order', '__return_false' );

 		$order = new WC_Order();
 		$order->set_status( OrderStatus::PROCESSING );