Commit 35ed9c1cc00 for woocommerce
commit 35ed9c1cc0025c21facb6096aa376d06dbaa29fc
Author: Tung Du <dinhtungdu@gmail.com>
Date: Fri Sep 25 10:40:13 2026 +0700
Revert "[tests] Reduce Product Button E2E tests from 7 to 3" (#68595) (#69088)
Revert "[tests] Reduce Product Button E2E tests from 7 to 3 (#68595)"
This reverts commit ddf8dd862b6f064bed0adbe8ebacf61b3e2dc472.
The removed E2E test covered the add-to-cart text filter with AJAX add to
cart on, which nothing covers now. Conflict with #68747 resolved by taking
the pre-#68595 spec, since #68747 only edited a comment #68595 added.
Claude-Session: https://claude.ai/code/session_014r6PoAedUkWFLNpyerW69N
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/testops-234-product-button b/plugins/woocommerce/changelog/testops-234-product-button
deleted file mode 100644
index 750550983ba..00000000000
--- a/plugins/woocommerce/changelog/testops-234-product-button
+++ /dev/null
@@ -1,4 +0,0 @@
-Significance: patch
-Type: dev
-Comment: Reduce Product Button E2E tests from 7 to 3; PHPUnit owns the add-to-cart text filter and the block theme script dequeue.
-
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.block_theme.spec.ts
index 40c41714944..c1d36e06f0e 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.block_theme.spec.ts
@@ -13,6 +13,23 @@ test.describe( `${ blockData.name } Block`, () => {
await frontendUtils.goToShop();
} );
+ test( 'should be visible', async ( { frontendUtils } ) => {
+ const blocks = await frontendUtils.getBlockByName( blockData.slug );
+ await expect( blocks ).toHaveCount(
+ blockData.selectors.frontend.productsToDisplay
+ );
+ } );
+
+ test( 'should not enqueue add-to-cart-script', async ( { page } ) => {
+ let isScriptEnqueued = false;
+ page.on( 'request', ( request ) => {
+ if ( request.url().includes( 'add-to-cart.min.js' ) )
+ isScriptEnqueued = true;
+ } );
+ await page.reload();
+ expect( isScriptEnqueued ).toBe( false );
+ } );
+
test( 'should add product to the cart', async ( {
frontendUtils,
page,
@@ -50,47 +67,54 @@ test.describe( `${ blockData.name } Block`, () => {
page,
admin,
} ) => {
- // The setting is store-wide, but the Blocks `page` fixture restores the database
- // after every test, failed ones included, so the next spec starts with AJAX
- // enabled again. The `finally` below is only a safety net.
await handleAddToCartAjaxSetting( admin, page, {
isChecked: true,
} );
+ await frontendUtils.goToShop();
- try {
- await frontendUtils.goToShop();
+ const blocks = await frontendUtils.getBlockByName( blockData.slug );
+ const block = blocks.first();
+ const button = block.getByRole( 'link' );
- const blocks = await frontendUtils.getBlockByName( blockData.slug );
- const block = blocks.first();
- const button = block.getByRole( 'link' );
+ const productId = await button.getAttribute( 'data-product_id' );
- const productId = await button.getAttribute( 'data-product_id' );
+ const productNameLocator = page.locator( `li.post-${ productId } h2` );
+ await expect( productNameLocator ).not.toBeEmpty();
- const productNameLocator = page.locator(
- `li.post-${ productId } h2`
- );
- await expect( productNameLocator ).not.toBeEmpty();
+ const productName =
+ ( await productNameLocator.textContent() ) as string;
- const productName =
- ( await productNameLocator.textContent() ) as string;
+ await block.click();
- await block.click();
+ await expect(
+ page.locator( `a[href*="cart=${ productId }"]` )
+ ).toBeVisible();
- await expect(
- page.locator( `a[href*="cart=${ productId }"]` )
- ).toBeVisible();
+ await frontendUtils.goToCheckout();
- await frontendUtils.goToCheckout();
+ const productElement = page.getByText( productName, {
+ exact: true,
+ } );
- const productElement = page.getByText( productName, {
- exact: true,
- } );
+ await expect( productElement ).toBeVisible();
- await expect( productElement ).toBeVisible();
- } finally {
- await handleAddToCartAjaxSetting( admin, page, {
- isChecked: false,
- } );
- }
+ await handleAddToCartAjaxSetting( admin, page, {
+ isChecked: false,
+ } );
+ } );
+
+ test( 'the filter `woocommerce_product_add_to_cart_text` should be applied', async ( {
+ requestUtils,
+ frontendUtils,
+ } ) => {
+ await requestUtils.activatePlugin(
+ 'woocommerce-blocks-test-custom-add-to-cart-button-text'
+ );
+ await frontendUtils.goToShop();
+ const blocks = await frontendUtils.getBlockByName( blockData.slug );
+ const buttonWithNewText = blocks.getByText( 'Buy Now' );
+ await expect( buttonWithNewText ).toHaveCount(
+ blockData.selectors.frontend.productsToDisplay
+ );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.classic_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.classic_theme.spec.ts
index 74a5f8af618..9ecad5f986b 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.classic_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/product-button.classic_theme.spec.ts
@@ -1,22 +1,41 @@
/**
* External dependencies
*/
-import { expect, test, CLASSIC_THEME_SLUG } from '@woocommerce/e2e-utils';
+import {
+ expect,
+ test as base,
+ CLASSIC_THEME_SLUG,
+} from '@woocommerce/e2e-utils';
/**
* Internal dependencies
*/
import { blockData } from './utils';
+import ProductCollectionPage from '../product-collection/product-collection.page';
+const test = base.extend< { productCollectionPage: ProductCollectionPage } >( {
+ productCollectionPage: async ( { page, admin, editor }, use ) => {
+ const pageObject = new ProductCollectionPage( {
+ page,
+ admin,
+ editor,
+ } );
+ await use( pageObject );
+ },
+} );
test.describe( `${ blockData.name } Block`, () => {
test.beforeEach( async ( { page, requestUtils } ) => {
await requestUtils.activateTheme( CLASSIC_THEME_SLUG );
await page.goto( '/product-collection/' );
} );
- // The block's only classic-theme coverage. Here the legacy `add-to-cart.js`
- // loads next to the block's own script, and nothing at a lower layer
- // exercises the two together, so this journey stays in the browser.
+ test( 'should be visible', async ( { frontendUtils } ) => {
+ const blocks = await frontendUtils.getBlockByName( blockData.slug );
+ await expect( blocks ).toHaveCount(
+ blockData.selectors.frontend.productsToDisplay
+ );
+ } );
+
test( 'should add product to the cart', async ( {
frontendUtils,
page,
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/utils.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/utils.ts
index 0e576977d5c..6ed0388ef0b 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-button/utils.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-button/utils.ts
@@ -8,6 +8,12 @@ export const blockData = {
name: 'Product Button',
slug: 'woocommerce/product-button',
mainClass: '.wc-block-product-button',
+ selectors: {
+ frontend: {
+ productsToDisplay: 16,
+ },
+ editor: {},
+ },
};
export const handleAddToCartAjaxSetting = async (
diff --git a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductButton.php b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductButton.php
index 7d69343184c..870ea066977 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductButton.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/BlockTypes/ProductButton.php
@@ -4,7 +4,6 @@ declare( strict_types = 1 );
namespace Automattic\WooCommerce\Tests\Blocks\BlockTypes;
-use Automattic\WooCommerce\Blocks\BlockTypes\ProductButton as ProductButtonBlock;
use WC_Helper_Product;
/**
@@ -13,185 +12,49 @@ use WC_Helper_Product;
class ProductButton extends \WP_UnitTestCase {
/**
- * Set up test options.
+ * Previous WooCommerce options to restore after each test.
*
- * Both writes land inside the per-test transaction, which tear_down() rolls back.
+ * @var array<string, mixed>
*/
- protected function setUp(): void {
- parent::setUp();
-
- update_option( 'woocommerce_cart_redirect_after_add', 'no' );
- update_option( 'woocommerce_enable_ajax_add_to_cart', 'no' );
- }
+ private $previous_options = array();
/**
- * Render the Product Button block for a product.
- *
- * @param \WC_Product $product Product to render.
- * @return string Rendered block markup.
- */
- private function render_product_button( \WC_Product $product ): string {
- return do_blocks(
- '<!-- wp:woocommerce/single-product {"productId":' . $product->get_id() . '} --><!-- wp:woocommerce/product-button /--><!-- /wp:woocommerce/single-product -->'
- );
- }
-
- /**
- * @testdox Filtered add-to-cart text renders one visible Product Button for the expected product.
+ * Set up test options.
*/
- public function test_filtered_add_to_cart_text_renders_visible_button(): void {
- $had_product = array_key_exists( 'product', $GLOBALS );
- $previous_product = $GLOBALS['product'] ?? null;
- $product = WC_Helper_Product::create_simple_product(
- true,
- array(
- 'name' => 'Product Button add-to-cart text fixture',
- 'regular_price' => '10',
- )
- );
- $filter = static function (): string {
- return 'Buy Now';
- };
-
- add_filter( 'woocommerce_product_add_to_cart_text', $filter );
-
- try {
- $markup = $this->render_product_button( $product );
- $processor = new \WP_HTML_Tag_Processor( $markup );
- $wrappers = 0;
-
- while (
- $processor->next_tag(
- array(
- 'tag_name' => 'div',
- 'class_name' => 'wc-block-components-product-button',
- )
- )
- ) {
- ++$wrappers;
- }
+ protected function setUp(): void {
+ parent::setUp();
- $this->assertSame( 1, $wrappers, 'The registered composition should render exactly one Product Button wrapper.' );
-
- $processor = new \WP_HTML_Tag_Processor( $markup );
- $this->assertTrue(
- $processor->next_tag(
- array(
- 'tag_name' => 'a',
- 'class_name' => 'wc-block-components-product-button__button',
- )
- ),
- 'The non-AJAX registered Product Button should render a link.'
- );
- $this->assertSame( (string) $product->get_id(), $processor->get_attribute( 'data-product_id' ), 'The rendered button should identify the fixture product.' );
- $this->assertStringContainsString( '>Buy Now</span>', $markup, 'The real product text filter should reach escaped Product Button markup.' );
- } finally {
- // _restore_hooks() drops the filter and the rollback drops the product;
- // the product global is this test's own to put back.
- if ( $had_product ) {
- $GLOBALS['product'] = $previous_product; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore the exact pre-test product global.
- } else {
- unset( $GLOBALS['product'] );
- }
+ foreach ( array( 'woocommerce_cart_redirect_after_add', 'woocommerce_enable_ajax_add_to_cart' ) as $option_name ) {
+ $this->previous_options[ $option_name ] = get_option( $option_name, null );
+ update_option( $option_name, 'no' );
}
}
/**
- * @testdox A block-theme Product Button dequeues the legacy add-to-cart script through its registered frontend action.
+ * Restore test options.
*/
- public function test_block_theme_dequeues_legacy_add_to_cart_script(): void {
- global $wp_filter;
-
- $original_theme = get_stylesheet();
- $had_product = array_key_exists( 'product', $GLOBALS );
- $previous_product = $GLOBALS['product'] ?? null;
- $previous_cart = WC()->cart;
- $scripts = wp_scripts();
- $previous_queue = $scripts->queue;
- $previous_registered = $scripts->registered;
- $styles = wp_styles();
- $previous_styles = $styles->queue;
- $previous_style_reg = $styles->registered;
- $product = WC_Helper_Product::create_simple_product(
- true,
- array(
- 'name' => 'Product Button script policy fixture',
- 'regular_price' => '10',
- )
- );
-
- try {
- switch_theme( 'twentytwentyfour' );
- $this->assertTrue( wp_is_block_theme(), 'The script policy should run under a real block theme.' );
-
- // Let WooCommerce enqueue the handle rather than standing in for it. A test
- // that registers 'wc-add-to-cart' itself pins the same literal on both
- // sides, so core folding the legacy script into another handle would leave
- // dequeue_add_to_cart_scripts() a silent no-op with this test still green --
- // which is the regression the deleted E2E title used to catch.
- update_option( 'woocommerce_enable_ajax_add_to_cart', 'yes' );
- \WC_Frontend_Scripts::load_scripts();
- $this->assertTrue( wp_script_is( 'wc-add-to-cart', 'enqueued' ), 'WooCommerce should enqueue the legacy handle when AJAX add to cart is on.' );
-
- $markup = $this->render_product_button( $product );
- $callback = $this->get_dequeue_callback( $wp_filter['wp_enqueue_scripts'] ?? null );
- $this->assertNotSame( '', $markup, 'The registered Product Button should render before its frontend action runs.' );
- $this->assertNotNull( $callback, 'Rendering should queue the Product Button legacy-script callback.' );
-
- // Fire the hook rather than calling the callback, so the assertion covers the
- // whole stack: a later callback re-enqueueing the handle would fail here and
- // would not if the method were invoked on its own. `_restore_hooks()` rewinds
- // the `$wp_actions` count and `$wp_current_filter` that firing it leaves.
- // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- Firing core's own frontend action, not declaring one.
- do_action( 'wp_enqueue_scripts' );
- $this->assertFalse( wp_script_is( 'wc-add-to-cart', 'enqueued' ), 'The registered block-theme callback should dequeue the legacy handle.' );
- } finally {
- // _restore_hooks() rewinds wp_enqueue_scripts and the rollback takes the
- // product back. WP_Scripts, WC()->cart and the product global survive both:
- // this class extends WP_UnitTestCase, so there is no WooCommerce teardown.
- $scripts->queue = $previous_queue;
- $scripts->registered = $previous_registered;
- // Firing wp_enqueue_scripts writes to WP_Styles too -- WooCommerce's own
- // frontend styles, plus core attaching the theme's global stylesheet to the
- // shared 'global-styles' handle, which outlives the switch_theme() below.
- $styles->queue = $previous_styles;
- $styles->registered = $previous_style_reg;
- switch_theme( $original_theme );
- WC()->cart = $previous_cart;
-
- if ( $had_product ) {
- $GLOBALS['product'] = $previous_product; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Restore the exact pre-test product global.
+ protected function tearDown(): void {
+ foreach ( $this->previous_options as $option_name => $value ) {
+ if ( null === $value ) {
+ delete_option( $option_name );
} else {
- unset( $GLOBALS['product'] );
+ update_option( $option_name, $value );
}
}
+
+ parent::tearDown();
}
/**
- * Find the Product Button's registered dequeue callback.
+ * Render the Product Button block for a product.
*
- * @param \WP_Hook|null $hook Enqueue hook after rendering.
- * @return array{ProductButtonBlock, string}|null
+ * @param \WC_Product $product Product to render.
+ * @return string Rendered block markup.
*/
- private function get_dequeue_callback( ?\WP_Hook $hook ): ?array {
- if ( ! $hook ) {
- return null;
- }
-
- foreach ( $hook->callbacks as $callbacks ) {
- foreach ( $callbacks as $callback ) {
- $function = $callback['function'];
- if (
- is_array( $function ) &&
- $function[0] instanceof ProductButtonBlock &&
- 'dequeue_add_to_cart_scripts' === $function[1]
- ) {
- return $function;
- }
- }
- }
-
- return null;
+ private function render_product_button( \WC_Product $product ): string {
+ return do_blocks(
+ '<!-- wp:woocommerce/single-product {"productId":' . $product->get_id() . '} --><!-- wp:woocommerce/product-button /--><!-- /wp:woocommerce/single-product -->'
+ );
}
/**
@@ -217,7 +80,11 @@ class ProductButton extends \WP_UnitTestCase {
};
add_filter( 'woocommerce_loop_add_to_cart_args', $filter );
- $this->render_product_button( $product );
+ try {
+ $this->render_product_button( $product );
+ } finally {
+ remove_filter( 'woocommerce_loop_add_to_cart_args', $filter );
+ }
$this->assertIsArray( $filtered_args );
$this->assertArrayHasKey( 'rel', $filtered_args['attributes'] );