Commit dac4645ca59 for woocommerce
commit dac4645ca59a5f607c060dd41a689aee0706991b
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date: Thu Nov 12 12:31:50 2026 +0200
Run the safe tests of 5 Blocks e2e template specs in parallel (#70886)
Tests that only read site-wide templates, or that delete what they
create, move to blocks-parallel. Tests that save site-wide templates,
the Cart and Checkout pages, or global options stay serial in new files.
diff --git a/plugins/woocommerce/changelog/dev-testops-309-blocks-e2e-split-template-specs b/plugins/woocommerce/changelog/dev-testops-309-blocks-e2e-split-template-specs
new file mode 100644
index 00000000000..513d8f25db4
--- /dev/null
+++ b/plugins/woocommerce/changelog/dev-testops-309-blocks-e2e-split-template-specs
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Split 5 Blocks e2e template specs and move their parallel-safe tests to the parallel pool; tests only.
diff --git a/plugins/woocommerce/tests/e2e/playwright.config.ts b/plugins/woocommerce/tests/e2e/playwright.config.ts
index 4ac7a3e02b1..8f44134614f 100644
--- a/plugins/woocommerce/tests/e2e/playwright.config.ts
+++ b/plugins/woocommerce/tests/e2e/playwright.config.ts
@@ -197,16 +197,15 @@ const blocksSerialSpecs = [
// `wc_blocks_use_blockified_product_grid_block_as_template` option. Every
// spec that visits those pages reads them.
'**/tests/blocks/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts',
- '**/tests/blocks/all-products/all-products.block_theme.spec.ts',
'**/tests/blocks/attributes-filter/attribute-filter.block_theme.spec.ts',
'**/tests/blocks/cart-store/cart-line-identity.block_theme.spec.ts',
'**/tests/blocks/checkout/checkout-block.merchant.block_theme.spec.ts',
- '**/tests/blocks/classic-template/classic-template.block_theme.spec.ts',
+ '**/tests/blocks/classic-template/classic-template-blockified-option.block_theme.spec.ts',
'**/tests/blocks/mini-cart/mini-cart-block.shopper.block_theme.spec.ts',
- '**/tests/blocks/mini-cart/mini-cart.block_theme.spec.ts',
- '**/tests/blocks/page-content-wrapper/page-content-wrapper.block_theme.spec.ts',
+ '**/tests/blocks/mini-cart/mini-cart-header-colors.block_theme.spec.ts',
+ '**/tests/blocks/page-content-wrapper/page-content-wrapper-page-edit.block_theme.spec.ts',
'**/tests/blocks/price-filter/price-filter.block_theme.spec.ts',
- '**/tests/blocks/product-collection/product-collection.block_theme.spec.ts',
+ '**/tests/blocks/product-collection/product-collection-site-templates.block_theme.spec.ts',
'**/tests/blocks/product-filters/active-filter-frontend.block_theme.spec.ts',
'**/tests/blocks/product-filters/attribute-filter-frontend.block_theme.spec.ts',
'**/tests/blocks/product-filters/product-filters-frontend.block_theme.spec.ts',
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/all-products/all-products.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/all-products/all-products.block_theme.spec.ts
index b3fc42ca95a..e8532f233b6 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/all-products/all-products.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/all-products/all-products.block_theme.spec.ts
@@ -1,8 +1,8 @@
/**
* External dependencies
*/
+import { randomUUID } from 'crypto';
import {
- BLOCK_THEME_SLUG,
expect,
getProductAttributeIds,
PostCompiler,
@@ -29,31 +29,56 @@ test.describe( `${ BLOCK_NAME } Block`, () => {
editor,
admin,
page,
+ requestUtils,
} ) => {
- const templatePath = 'header';
- await admin.visitSiteEditor( {
- postId: `${ BLOCK_THEME_SLUG }//${ templatePath }`,
- postType: 'wp_template_part',
- canvas: 'edit',
+ // WordPress uses `single-post-<slug>` for this post only. It has no
+ // header, so the page has no Mini-Cart block.
+ const postSlug = `all-products-no-header-${ randomUUID() }`;
+ const template = await requestUtils.createTemplate( 'wp_template', {
+ slug: `single-post-${ postSlug }`,
+ title: 'All Products without header',
+ content: '<!-- wp:post-content /-->',
} );
+ let postId: number | undefined;
- await editor.setContent( '' );
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
+ try {
+ const post = await requestUtils.rest< { id: number } >( {
+ method: 'POST',
+ path: 'wp/v2/posts',
+ data: {
+ title: 'All Products without Mini-Cart',
+ slug: postSlug,
+ status: 'draft',
+ },
+ } );
+ postId = post.id;
- await admin.createNewPost();
- await editor.insertBlock( { name: BLOCK_NAME } );
- await editor.publishAndVisitPost();
- await page.waitForResponse(
- ( response ) =>
- response.url().includes( 'wp-json/wc/store/v1/products' ) &&
- response.status() === 200
- );
+ await admin.editPost( post.id );
+ await editor.insertBlock( { name: BLOCK_NAME } );
+ await editor.publishAndVisitPost();
+ await page.waitForResponse(
+ ( response ) =>
+ response.url().includes( 'wp-json/wc/store/v1/products' ) &&
+ response.status() === 200
+ );
- await expect(
- page.locator( '.wc-block-grid__product.wc-block-layout' )
- ).toHaveCount( 9 );
+ await expect(
+ page.locator( '[data-block-name="woocommerce/mini-cart"]' )
+ ).toHaveCount( 0 );
+ await expect(
+ page.locator( '.wc-block-grid__product.wc-block-layout' )
+ ).toHaveCount( 9 );
+ } finally {
+ await Promise.allSettled( [
+ postId &&
+ requestUtils.rest( {
+ method: 'DELETE',
+ path: `wp/v2/posts/${ postId }`,
+ params: { force: true },
+ } ),
+ requestUtils.revertTemplate( 'wp_template', template.id ),
+ ] );
+ }
} );
// Regression coverage for legacy filter markup:
@@ -92,11 +117,21 @@ test.describe( `${ BLOCK_NAME } Block`, () => {
const outOfStockCheckbox = page.getByRole( 'checkbox', {
name: 'Out of Stock',
} );
+ await expect(
+ products.getByText( 'Album', { exact: true } )
+ ).toBeVisible();
await expect( outOfStockCheckbox ).toBeVisible();
await outOfStockCheckbox.click();
await expect( page ).toHaveURL( /filter_stock_status=outofstock/ );
- await expect( products ).toHaveCount( 1 );
+ // Other specs can add out-of-stock products, so check the sample one
+ // is listed and an in-stock one is gone instead of an exact count.
+ await expect(
+ products.getByText( 'T-Shirt with Logo', { exact: true } )
+ ).toBeVisible();
+ await expect(
+ products.getByText( 'Album', { exact: true } )
+ ).toHaveCount( 0 );
await expect(
page.getByRole( 'heading', { name: 'Active filters' } )
).toBeVisible();
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template-blockified-option.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template-blockified-option.block_theme.spec.ts
new file mode 100644
index 00000000000..fba19426da0
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template-blockified-option.block_theme.spec.ts
@@ -0,0 +1,29 @@
+/**
+ * External dependencies
+ */
+import { test, expect, wpCLI, BLOCK_THEME_SLUG } from '@woocommerce/e2e-utils';
+
+const BLOCK_NAME = 'woocommerce/legacy-template';
+
+test.describe( `${ BLOCK_NAME } Block `, () => {
+ test( `is visible as default block when wc_blocks_use_blockified_product_grid_block_as_template is false`, async ( {
+ admin,
+ editor,
+ page,
+ } ) => {
+ await wpCLI(
+ 'option update wc_blocks_use_blockified_product_grid_block_as_template false'
+ );
+ await admin.visitSiteEditor( {
+ postId: `${ BLOCK_THEME_SLUG }//archive-product`,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
+
+ await expect( await editor.getBlockByName( BLOCK_NAME ) ).toBeVisible();
+
+ await page.goto( '/shop/' );
+
+ await expect( page.locator( 'div[data-template]' ) ).toBeVisible();
+ } );
+} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template.block_theme.spec.ts
index 778d38de3d8..5e9a5adcde4 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/classic-template/classic-template.block_theme.spec.ts
@@ -4,16 +4,11 @@
import {
test,
expect,
- wpCLI,
BlockData,
Editor,
BLOCK_THEME_SLUG,
} from '@woocommerce/e2e-utils';
-/**
- * Internal dependencies
- */
-
const blockData: Partial< BlockData > = {
name: 'woocommerce/legacy-template',
};
@@ -70,27 +65,4 @@ test.describe( `${ blockData.name } Block `, () => {
expect( classicTemplateBlocks ).toHaveLength( 0 );
} );
-
- test( `is visible as default block when wc_blocks_use_blockified_product_grid_block_as_template is false`, async ( {
- admin,
- editor,
- page,
- } ) => {
- await wpCLI(
- 'option update wc_blocks_use_blockified_product_grid_block_as_template false'
- );
- await admin.visitSiteEditor( {
- postId: `${ BLOCK_THEME_SLUG }//archive-product`,
- postType: 'wp_template',
- canvas: 'edit',
- } );
-
- await expect(
- await editor.getBlockByName( blockData.name )
- ).toBeVisible();
-
- await page.goto( '/shop/' );
-
- await expect( page.locator( 'div[data-template]' ) ).toBeVisible();
- } );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart-header-colors.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart-header-colors.block_theme.spec.ts
new file mode 100644
index 00000000000..a6508ef9367
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart-header-colors.block_theme.spec.ts
@@ -0,0 +1,129 @@
+/**
+ * External dependencies
+ */
+import { test, expect } from '@woocommerce/e2e-utils';
+
+/**
+ * Internal dependencies
+ */
+import { REGULAR_PRICED_PRODUCT_NAME } from '../checkout/constants';
+
+test.describe( 'Mini-Cart Block (admin)', () => {
+ // eslint-disable-next-line playwright/no-skipped-test
+ test.skip( 'should update badge colors when header background changes', async ( {
+ page,
+ admin,
+ editor,
+ frontendUtils,
+ } ) => {
+ // First, change the header background color in the site editor.
+ await admin.visitSiteEditor( {
+ postId: 'twentytwentyfour//header',
+ postType: 'wp_template_part',
+ canvas: 'edit',
+ } );
+
+ // Select the mini-cart block to get access to its parent (the header row).
+ const miniCartBlock = editor.canvas.locator(
+ '[data-type="woocommerce/mini-cart"]'
+ );
+ await miniCartBlock.click();
+
+ // Select the parent Row block that contains the mini-cart.
+ // Use the block toolbar to select parent.
+ await editor.clickBlockToolbarButton( 'Select parent block: Row' );
+
+ // Now open the Styles panel and set background color.
+ await editor.openDocumentSettingsSidebar();
+
+ // Click on the Styles tab.
+ const stylesTab = page.getByRole( 'tab', { name: 'Styles' } );
+ if ( await stylesTab.isVisible() ) {
+ await stylesTab.click();
+ }
+
+ // Find and click the background color control.
+ const bgColorButton = page
+ .getByRole( 'button', { name: 'Background' } )
+ .first();
+ await bgColorButton.click();
+
+ // Select "Contrast" preset color (black).
+ await page
+ .getByRole( 'option', { name: 'Contrast', exact: true } )
+ .click();
+
+ // Extract the background color hex value from the editor UI.
+ const parentBgColorHex = await page
+ .locator( '.components-color-palette__custom-color-value' )
+ .textContent();
+
+ // Close the background color popover by clicking outside.
+ await stylesTab.click();
+
+ // Find and click the text color control.
+ const textColorButton = page
+ .getByRole( 'button', { name: 'Text' } )
+ .first();
+ await textColorButton.click();
+
+ // Select "Base" preset color (white).
+ await page.getByRole( 'option', { name: 'Base', exact: true } ).click();
+
+ // Extract the text color hex value from the editor UI.
+ const parentTextColorHex = await page
+ .locator( '.components-color-palette__custom-color-value' )
+ .textContent();
+
+ // Save the changes.
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ // Add an item to cart (use a product that's on the first page).
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+
+ // Verify color values were extracted from the editor.
+ expect( parentBgColorHex ).toBeTruthy();
+ expect( parentTextColorHex ).toBeTruthy();
+
+ // Helper to convert hex color to rgb format.
+ const hexToRgb = ( hex: string ) => {
+ const cleanHex = hex.replace( '#', '' );
+ const r = parseInt( cleanHex.slice( 0, 2 ), 16 );
+ const g = parseInt( cleanHex.slice( 2, 4 ), 16 );
+ const b = parseInt( cleanHex.slice( 4, 6 ), 16 );
+ return `rgb(${ r }, ${ g }, ${ b })`;
+ };
+
+ // Verify the badge has the correct colors:
+ // - Badge background = parent's text color (inverted)
+ // - Badge text = parent's background color (inverted)
+ const badge = page.locator( '.wc-block-mini-cart__badge' );
+ await expect( badge ).toHaveCSS(
+ 'background-color',
+ hexToRgb( parentTextColorHex as string )
+ );
+ await expect( badge ).toHaveCSS(
+ 'color',
+ hexToRgb( parentBgColorHex as string )
+ );
+
+ // Navigate to the next page using client-side navigation.
+ await page.getByRole( 'link', { name: 'Next Page' } ).click();
+
+ // Await for the navigation to happen.
+ await expect( page ).toHaveURL( /page\/2\/?$/ );
+
+ // Verify the badge colors persist after navigation.
+ await expect( badge ).toHaveCSS(
+ 'background-color',
+ hexToRgb( parentTextColorHex as string )
+ );
+ await expect( badge ).toHaveCSS(
+ 'color',
+ hexToRgb( parentBgColorHex as string )
+ );
+ } );
+} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart.block_theme.spec.ts
index b48b08352bf..7db273dc05a 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/mini-cart/mini-cart.block_theme.spec.ts
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
+import { randomUUID } from 'crypto';
import {
test,
expect,
@@ -403,126 +404,6 @@ test.describe( `${ blockData.name } Block`, () => {
} );
} );
-test.describe( `${ blockData.name } Block (admin)`, () => {
- // eslint-disable-next-line playwright/no-skipped-test
- test.skip( 'should update badge colors when header background changes', async ( {
- page,
- admin,
- editor,
- frontendUtils,
- } ) => {
- // First, change the header background color in the site editor.
- await admin.visitSiteEditor( {
- postId: 'twentytwentyfour//header',
- postType: 'wp_template_part',
- canvas: 'edit',
- } );
-
- // Select the mini-cart block to get access to its parent (the header row).
- const miniCartBlock = editor.canvas.locator(
- '[data-type="woocommerce/mini-cart"]'
- );
- await miniCartBlock.click();
-
- // Select the parent Row block that contains the mini-cart.
- // Use the block toolbar to select parent.
- await editor.clickBlockToolbarButton( 'Select parent block: Row' );
-
- // Now open the Styles panel and set background color.
- await editor.openDocumentSettingsSidebar();
-
- // Click on the Styles tab.
- const stylesTab = page.getByRole( 'tab', { name: 'Styles' } );
- if ( await stylesTab.isVisible() ) {
- await stylesTab.click();
- }
-
- // Find and click the background color control.
- const bgColorButton = page
- .getByRole( 'button', { name: 'Background' } )
- .first();
- await bgColorButton.click();
-
- // Select "Contrast" preset color (black).
- await page
- .getByRole( 'option', { name: 'Contrast', exact: true } )
- .click();
-
- // Extract the background color hex value from the editor UI.
- const parentBgColorHex = await page
- .locator( '.components-color-palette__custom-color-value' )
- .textContent();
-
- // Close the background color popover by clicking outside.
- await stylesTab.click();
-
- // Find and click the text color control.
- const textColorButton = page
- .getByRole( 'button', { name: 'Text' } )
- .first();
- await textColorButton.click();
-
- // Select "Base" preset color (white).
- await page.getByRole( 'option', { name: 'Base', exact: true } ).click();
-
- // Extract the text color hex value from the editor UI.
- const parentTextColorHex = await page
- .locator( '.components-color-palette__custom-color-value' )
- .textContent();
-
- // Save the changes.
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
-
- // Add an item to cart (use a product that's on the first page).
- await frontendUtils.goToShop();
- await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
-
- // Verify color values were extracted from the editor.
- expect( parentBgColorHex ).toBeTruthy();
- expect( parentTextColorHex ).toBeTruthy();
-
- // Helper to convert hex color to rgb format.
- const hexToRgb = ( hex: string ) => {
- const cleanHex = hex.replace( '#', '' );
- const r = parseInt( cleanHex.slice( 0, 2 ), 16 );
- const g = parseInt( cleanHex.slice( 2, 4 ), 16 );
- const b = parseInt( cleanHex.slice( 4, 6 ), 16 );
- return `rgb(${ r }, ${ g }, ${ b })`;
- };
-
- // Verify the badge has the correct colors:
- // - Badge background = parent's text color (inverted)
- // - Badge text = parent's background color (inverted)
- const badge = page.locator( '.wc-block-mini-cart__badge' );
- await expect( badge ).toHaveCSS(
- 'background-color',
- hexToRgb( parentTextColorHex as string )
- );
- await expect( badge ).toHaveCSS(
- 'color',
- hexToRgb( parentBgColorHex as string )
- );
-
- // Navigate to the next page using client-side navigation.
- await page.getByRole( 'link', { name: 'Next Page' } ).click();
-
- // Await for the navigation to happen.
- await expect( page ).toHaveURL( /page\/2\/?$/ );
-
- // Verify the badge colors persist after navigation.
- await expect( badge ).toHaveCSS(
- 'background-color',
- hexToRgb( parentTextColorHex as string )
- );
- await expect( badge ).toHaveCSS(
- 'color',
- hexToRgb( parentBgColorHex as string )
- );
- } );
-} );
-
test.describe( `${ blockData.name } Block (variation attributes)`, () => {
test.use( {
storageState: {
@@ -539,7 +420,7 @@ test.describe( `${ blockData.name } Block (variation attributes)`, () => {
// The API returns the ampersand as "&", so this also checks that
// the variation branch of the table decodes it.
const cliOutput = await wpCLI(
- `wc product create --user=1 --name="Test Variable" --type="variable" --attributes='${ JSON.stringify(
+ `wc product create --user=1 --name="Test Variable ${ randomUUID() }" --type="variable" --catalog_visibility="hidden" --attributes='${ JSON.stringify(
[
{
name: 'Shade',
@@ -553,29 +434,36 @@ test.describe( `${ blockData.name } Block (variation attributes)`, () => {
const productId = cliOutput.stdout.match(
/Created product (\d+)/
)?.[ 1 ];
- const variationOutput = await wpCLI(
- `wc product_variation create --user=1 "${ productId }" --regular_price="10" --attributes='${ JSON.stringify(
- [ { name: 'Shade', option: 'Red & Blue' } ]
- ) }'`
- );
- const variationId = variationOutput.stdout.match(
- /Created product_variation (\d+)/
- )?.[ 1 ];
-
- // Add the variation through the classic add-to-cart URL, since the
- // product page form is not what this test covers.
- await page.goto(
- `/?add-to-cart=${ productId }&variation_id=${ variationId }`
- );
- await frontendUtils.goToShop();
- await miniCartUtils.openMiniCart();
+ expect( productId ).toBeDefined();
+ try {
+ const variationOutput = await wpCLI(
+ `wc product_variation create --user=1 "${ productId }" --regular_price="10" --attributes='${ JSON.stringify(
+ [ { name: 'Shade', option: 'Red & Blue' } ]
+ ) }'`
+ );
+ const variationId = variationOutput.stdout.match(
+ /Created product_variation (\d+)/
+ )?.[ 1 ];
+
+ // Add the variation through the classic add-to-cart URL, since the
+ // product page form is not what this test covers.
+ await page.goto(
+ `/?add-to-cart=${ productId }&variation_id=${ variationId }`
+ );
+ await frontendUtils.goToShop();
+ await miniCartUtils.openMiniCart();
- const dialog = page.getByRole( 'dialog' );
- await expect(
- dialog.locator( '.wc-block-components-product-details__name' )
- ).toContainText( [ 'Shade' ] );
- await expect(
- dialog.locator( '.wc-block-components-product-details__value' )
- ).toContainText( [ 'Red & Blue' ] );
+ const dialog = page.getByRole( 'dialog' );
+ await expect(
+ dialog.locator( '.wc-block-components-product-details__name' )
+ ).toContainText( [ 'Shade' ] );
+ await expect(
+ dialog.locator( '.wc-block-components-product-details__value' )
+ ).toContainText( [ 'Red & Blue' ] );
+ } finally {
+ await wpCLI(
+ `wc product delete ${ productId } --force=true --user=1`
+ );
+ }
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper-page-edit.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper-page-edit.block_theme.spec.ts
new file mode 100644
index 00000000000..d43cd28fa48
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper-page-edit.block_theme.spec.ts
@@ -0,0 +1,73 @@
+/**
+ * External dependencies
+ */
+import { test, expect, FrontendUtils } from '@woocommerce/e2e-utils';
+
+// Instead of testing the block individually, we test the Cart and Checkout
+// templates, which make use of the block.
+const templates = [
+ {
+ title: 'Cart',
+ slug: 'cart',
+ blockClassName: '.wc-block-cart',
+ visitPage: async ( {
+ frontendUtils,
+ }: {
+ frontendUtils: FrontendUtils;
+ } ) => {
+ await frontendUtils.goToCart();
+ },
+ },
+ {
+ title: 'Checkout',
+ slug: 'checkout',
+ blockClassName: '.wc-block-checkout',
+ visitPage: async ( {
+ frontendUtils,
+ }: {
+ frontendUtils: FrontendUtils;
+ } ) => {
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart();
+ await frontendUtils.goToCheckout();
+ },
+ },
+];
+const userText = 'Hello World in the page';
+
+for ( const template of templates ) {
+ test.describe( 'Page Content Wrapper', () => {
+ test( `the content of the ${ template.title } page is correctly rendered in the ${ template.title } template`, async ( {
+ page,
+ admin,
+ editor,
+ frontendUtils,
+ requestUtils,
+ } ) => {
+ const pageData = await requestUtils.rest( {
+ path: 'wp/v2/pages?slug=' + template.slug,
+ } );
+ const pageId = pageData[ 0 ].id;
+
+ await admin.editPost( pageId );
+
+ await expect(
+ editor.canvas.locator( template.blockClassName )
+ ).toBeVisible();
+
+ // editor.isertBlock() doesn't work here.
+ await editor.insertBlockUsingGlobalInserter( 'Paragraph' );
+ await editor.canvas
+ .getByRole( 'document', { name: 'Empty block' } )
+ .fill( userText );
+
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ // Verify edits are in the template when viewed from the frontend.
+ await template.visitPage( { frontendUtils } );
+ await expect( page.getByText( userText ).first() ).toBeVisible();
+ } );
+ } );
+}
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper.block_theme.spec.ts
index c0b15d06662..e66ebcb1e55 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/page-content-wrapper/page-content-wrapper.block_theme.spec.ts
@@ -1,80 +1,23 @@
/**
* External dependencies
*/
-import {
- test,
- expect,
- FrontendUtils,
- BLOCK_THEME_SLUG,
-} from '@woocommerce/e2e-utils';
+import { test, expect, BLOCK_THEME_SLUG } from '@woocommerce/e2e-utils';
-// Instead of testing the block individually, we test the Cart and Checkout
-// templates, which make use of the block.
const templates = [
{
title: 'Cart',
slug: 'cart',
blockClassName: '.wc-block-cart',
- visitPage: async ( {
- frontendUtils,
- }: {
- frontendUtils: FrontendUtils;
- } ) => {
- await frontendUtils.goToCart();
- },
},
{
title: 'Checkout',
slug: 'checkout',
blockClassName: '.wc-block-checkout',
- visitPage: async ( {
- frontendUtils,
- }: {
- frontendUtils: FrontendUtils;
- } ) => {
- await frontendUtils.goToShop();
- await frontendUtils.addToCart();
- await frontendUtils.goToCheckout();
- },
},
];
-const userText = 'Hello World in the page';
for ( const template of templates ) {
test.describe( 'Page Content Wrapper', () => {
- test( `the content of the ${ template.title } page is correctly rendered in the ${ template.title } template`, async ( {
- page,
- admin,
- editor,
- frontendUtils,
- requestUtils,
- } ) => {
- const pageData = await requestUtils.rest( {
- path: 'wp/v2/pages?slug=' + template.slug,
- } );
- const pageId = pageData[ 0 ].id;
-
- await admin.editPost( pageId );
-
- await expect(
- editor.canvas.locator( template.blockClassName )
- ).toBeVisible();
-
- // editor.isertBlock() doesn't work here.
- await editor.insertBlockUsingGlobalInserter( 'Paragraph' );
- await editor.canvas
- .getByRole( 'document', { name: 'Empty block' } )
- .fill( userText );
-
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
-
- // Verify edits are in the template when viewed from the frontend.
- await template.visitPage( { frontendUtils } );
- await expect( page.getByText( userText ).first() ).toBeVisible();
- } );
-
// Regression test for #48936: opening a template that uses this block
// must not flag it as dirty (the block previously wrote postId/postType
// to attributes on mount, enabling the Save button with no edits).
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection-site-templates.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection-site-templates.block_theme.spec.ts
new file mode 100644
index 00000000000..8e650a742f1
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection-site-templates.block_theme.spec.ts
@@ -0,0 +1,152 @@
+/**
+ * External dependencies
+ */
+import { test as base, expect, wpCLI } from '@woocommerce/e2e-utils';
+
+/**
+ * Internal dependencies
+ */
+import ProductCollectionPage, { SELECTORS } from './product-collection.page';
+
+const test = base.extend< { pageObject: ProductCollectionPage } >( {
+ pageObject: async ( { page, admin, editor }, use ) => {
+ const pageObject = new ProductCollectionPage( {
+ page,
+ admin,
+ editor,
+ } );
+ await use( pageObject );
+ },
+} );
+
+test.describe( 'Product Collection', () => {
+ const templates = [
+ {
+ templateTitle: 'Product Category (all terms)',
+ slug: 'taxonomy-product_cat',
+ frontendPage: '/product-category/music/',
+ legacyBlockName: 'woocommerce/legacy-template',
+ },
+ {
+ templateTitle: 'Product Tag (all terms)',
+ slug: 'taxonomy-product_tag',
+ frontendPage: '/product-tag/recommended/',
+ legacyBlockName: 'woocommerce/legacy-template',
+ },
+ {
+ templateTitle: 'Product Catalog',
+ slug: 'archive-product',
+ frontendPage: '/shop/',
+ legacyBlockName: 'woocommerce/legacy-template',
+ },
+ {
+ templateTitle: 'Product Search Results',
+ slug: 'product-search-results',
+ frontendPage: '/?s=shirt&post_type=product',
+ legacyBlockName: 'woocommerce/legacy-template',
+ },
+ ];
+
+ templates.forEach(
+ ( { templateTitle, slug, frontendPage, legacyBlockName } ) => {
+ test.describe( `${ templateTitle } template`, () => {
+ test( 'Product Collection block matches with classic template block', async ( {
+ pageObject,
+ requestUtils,
+ admin,
+ editor,
+ page,
+ } ) => {
+ await pageObject.refreshLocators( 'frontend' );
+ await page.goto( frontendPage );
+
+ const productCollectionProductNames =
+ await pageObject.getProductNames();
+ expect(
+ productCollectionProductNames.length
+ ).toBeGreaterThan( 0 );
+
+ const template = await requestUtils.createTemplate(
+ 'wp_template',
+ {
+ slug,
+ title: 'classic template test',
+ content: 'placeholder',
+ }
+ );
+
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
+ await editor.insertBlock( { name: legacyBlockName } );
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ await page.goto( frontendPage );
+ const classicProducts = page.locator(
+ '.woocommerce-loop-product__title'
+ );
+ expect( await classicProducts.count() ).toBeGreaterThan(
+ 0
+ );
+ const classicProductsNames =
+ await classicProducts.allTextContents();
+
+ expect( classicProductsNames ).toEqual(
+ productCollectionProductNames
+ );
+ } );
+ } );
+ }
+ );
+
+ test.describe( 'default query can be modified', () => {
+ test( 'default query can be modified', async ( {
+ page,
+ pageObject,
+ editor,
+ } ) => {
+ await wpCLI(
+ 'option update woocommerce_default_catalog_orderby price'
+ );
+
+ await pageObject.goToEditorTemplate();
+
+ await pageObject.focusProductCollection();
+
+ // Verify the default order matches the option in the database.
+ const sidebarSettings = pageObject.locateSidebarSettings();
+ const orderBySelect = sidebarSettings.getByRole( 'combobox', {
+ name: 'Default sort by',
+ } );
+ const editorProductTitle = editor.canvas
+ .locator( SELECTORS.productTitle )
+ .first();
+
+ await expect( orderBySelect ).toHaveValue( 'price' );
+ await expect( editorProductTitle ).toHaveText( 'Single' );
+
+ await orderBySelect.selectOption( 'price-desc' );
+
+ await expect( editorProductTitle ).toHaveText( 'Sunglasses' );
+
+ await editor.saveSiteEditorEntities();
+ await pageObject.goToProductCatalogFrontend();
+
+ const frontendProductTitle = page
+ .locator( SELECTORS.productTitle )
+ .first();
+ await expect( frontendProductTitle ).toContainText( 'Sunglasses' );
+
+ await wpCLI(
+ 'option update woocommerce_default_catalog_orderby menu_order'
+ );
+ } );
+ } );
+} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection.block_theme.spec.ts
index 49c6015a217..37e6720ac81 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-collection/product-collection.block_theme.spec.ts
@@ -1,8 +1,14 @@
/**
* External dependencies
*/
+import { randomUUID } from 'crypto';
import { Request } from '@playwright/test';
-import { test as base, expect, wpCLI, BASE_URL } from '@woocommerce/e2e-utils';
+import {
+ test as base,
+ expect,
+ BASE_URL,
+ BLOCK_THEME_SLUG,
+} from '@woocommerce/e2e-utils';
/**
* Internal dependencies
@@ -268,6 +274,7 @@ test.describe( 'Product Collection', () => {
pageObject,
editor,
wpCoreVersion,
+ requestUtils,
} ) => {
const getLocationParams = ( request: Request ) =>
new URL( request.url() ).searchParams;
@@ -300,88 +307,112 @@ test.describe( 'Product Collection', () => {
);
};
- await admin.visitSiteEditor( { path: '/wp_template' } );
- await page
- .getByRole( 'button', {
- name:
- wpCoreVersion >= 6.8 ? 'Add Template' : 'Add New Template',
- } )
- .click();
- await page
- .getByRole( 'button', { name: 'Single Item: Product' } )
- .click();
- await page
- .getByRole( 'option', {
- name: `Cap ${ BASE_URL }/product/cap/`,
- } )
- .click();
- await page.getByRole( 'button', { name: 'Skip' } ).click();
- await editor.insertBlockUsingGlobalInserter( pageObject.BLOCK_NAME );
- await editor.closeGlobalBlockInserter();
-
- const productRequestPromise = page.waitForRequest(
- isSpecificProductRequest
- );
- await pageObject.chooseCollectionInTemplate( 'featured' );
- const productParams = getLocationParams( await productRequestPromise );
- expect(
- productParams.get(
- 'productCollectionLocation[sourceData][productId]'
- )
- ).toMatch( /^[1-9]\d*$/ );
- expect(
- productParams.get(
- 'productCollectionLocation[sourceData][taxonomy]'
- )
- ).toBeNull();
- expect(
- productParams.get( 'productCollectionLocation[sourceData][termId]' )
- ).toBeNull();
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
+ try {
+ await admin.visitSiteEditor( { path: '/wp_template' } );
+ await page
+ .getByRole( 'button', {
+ name:
+ wpCoreVersion >= 6.8
+ ? 'Add Template'
+ : 'Add New Template',
+ } )
+ .click();
+ await page
+ .getByRole( 'button', { name: 'Single Item: Product' } )
+ .click();
+ await page
+ .getByRole( 'option', {
+ name: `Cap ${ BASE_URL }/product/cap/`,
+ } )
+ .click();
+ await page.getByRole( 'button', { name: 'Skip' } ).click();
+ await editor.insertBlockUsingGlobalInserter(
+ pageObject.BLOCK_NAME
+ );
+ await editor.closeGlobalBlockInserter();
- await admin.visitSiteEditor( { path: '/wp_template' } );
- const categoriesLoaded = page.waitForResponse( ( response ) =>
- response.url().includes( 'wp-json/wp/v2/product_cat' )
- );
- await page
- .getByRole( 'button', {
- name:
- wpCoreVersion >= 6.8 ? 'Add Template' : 'Add New Template',
- } )
- .click();
- await categoriesLoaded;
- await page
- .getByRole( 'button', { name: 'Products by Category' } )
- .click();
- await page
- .getByRole( 'button', { name: 'For a specific item' } )
- .click();
- await page.getByRole( 'option', { name: 'Hoodies' } ).click();
-
- const categoryRequestPromise = page.waitForRequest(
- isSpecificCategoryRequest
- );
- await page.getByRole( 'option', { name: 'Fallback content' } ).click();
- const categoryParams = getLocationParams(
- await categoryRequestPromise
- );
- expect(
- categoryParams.get(
- 'productCollectionLocation[sourceData][termId]'
- )
- ).toMatch( /^[1-9]\d*$/ );
- expect(
- categoryParams.get(
- 'productCollectionLocation[sourceData][taxonomy]'
- )
- ).toBe( 'product_cat' );
- expect(
- categoryParams.get(
- 'productCollectionLocation[sourceData][productId]'
- )
- ).toBeNull();
+ const productRequestPromise = page.waitForRequest(
+ isSpecificProductRequest
+ );
+ await pageObject.chooseCollectionInTemplate( 'featured' );
+ const productParams = getLocationParams(
+ await productRequestPromise
+ );
+ expect(
+ productParams.get(
+ 'productCollectionLocation[sourceData][productId]'
+ )
+ ).toMatch( /^[1-9]\d*$/ );
+ expect(
+ productParams.get(
+ 'productCollectionLocation[sourceData][taxonomy]'
+ )
+ ).toBeNull();
+ expect(
+ productParams.get(
+ 'productCollectionLocation[sourceData][termId]'
+ )
+ ).toBeNull();
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ await admin.visitSiteEditor( { path: '/wp_template' } );
+ const categoriesLoaded = page.waitForResponse( ( response ) =>
+ response.url().includes( 'wp-json/wp/v2/product_cat' )
+ );
+ await page
+ .getByRole( 'button', {
+ name:
+ wpCoreVersion >= 6.8
+ ? 'Add Template'
+ : 'Add New Template',
+ } )
+ .click();
+ await categoriesLoaded;
+ await page
+ .getByRole( 'button', { name: 'Products by Category' } )
+ .click();
+ await page
+ .getByRole( 'button', { name: 'For a specific item' } )
+ .click();
+ await page.getByRole( 'option', { name: 'Hoodies' } ).click();
+
+ const categoryRequestPromise = page.waitForRequest(
+ isSpecificCategoryRequest
+ );
+ await page
+ .getByRole( 'option', { name: 'Fallback content' } )
+ .click();
+ const categoryParams = getLocationParams(
+ await categoryRequestPromise
+ );
+ expect(
+ categoryParams.get(
+ 'productCollectionLocation[sourceData][termId]'
+ )
+ ).toMatch( /^[1-9]\d*$/ );
+ expect(
+ categoryParams.get(
+ 'productCollectionLocation[sourceData][taxonomy]'
+ )
+ ).toBe( 'product_cat' );
+ expect(
+ categoryParams.get(
+ 'productCollectionLocation[sourceData][productId]'
+ )
+ ).toBeNull();
+ } finally {
+ await Promise.allSettled(
+ [ 'single-product-cap', 'taxonomy-product_cat-hoodies' ].map(
+ ( slug ) =>
+ requestUtils.revertTemplate(
+ 'wp_template',
+ `${ BLOCK_THEME_SLUG }//${ slug }`
+ )
+ )
+ );
+ }
} );
// Tests for regressions of https://github.com/woocommerce/woocommerce/pull/47994
@@ -420,23 +451,37 @@ test.describe( 'Product Collection', () => {
page,
pageObject,
editor,
+ requestUtils,
} ) => {
- await pageObject.goToEditorTemplate();
- await pageObject.insertProductCollection();
- await pageObject.chooseCollectionInTemplate( 'onSale' );
+ const template = await requestUtils.createTemplate( 'wp_template', {
+ slug: `product-collection-on-sale-${ randomUUID() }`,
+ title: 'Product Collection On Sale',
+ content: 'placeholder',
+ } );
- const productTemplate = editor.canvas.getByLabel(
- BLOCK_LABELS.productTemplate
- );
+ try {
+ await pageObject.goToEditorTemplate( template.id );
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
+ await pageObject.insertProductCollection();
+ await pageObject.chooseCollectionInTemplate( 'onSale' );
- await expect( productTemplate ).toHaveCount( 2 );
+ const productTemplate = editor.canvas.getByLabel(
+ BLOCK_LABELS.productTemplate
+ );
- // Refresh the template and verify "On Sale Products" collection is still visible
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
- await page.reload();
- await expect( productTemplate ).toHaveCount( 2 );
+ await expect( productTemplate ).toHaveCount( 1 );
+
+ // Refresh the template and verify "On Sale Products" collection is still visible
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+ await page.reload();
+ await expect( productTemplate ).toHaveCount( 1 );
+ } finally {
+ await requestUtils.revertTemplate( 'wp_template', template.id );
+ }
} );
test( 'On Sale Products collection should be visible after Refresh in a Post', async ( {
@@ -454,31 +499,20 @@ test.describe( 'Product Collection', () => {
} );
} );
+ // Specific-term templates, so only these test pages change.
const templates = [
{
templateTitle: 'Product Category',
- slug: 'taxonomy-product_cat',
+ slug: 'taxonomy-product_cat-music',
frontendPage: '/product-category/music/',
legacyBlockName: 'woocommerce/legacy-template',
},
{
templateTitle: 'Product Tag',
- slug: 'taxonomy-product_tag',
+ slug: 'taxonomy-product_tag-recommended',
frontendPage: '/product-tag/recommended/',
legacyBlockName: 'woocommerce/legacy-template',
},
- {
- templateTitle: 'Product Catalog',
- slug: 'archive-product',
- frontendPage: '/shop/',
- legacyBlockName: 'woocommerce/legacy-template',
- },
- {
- templateTitle: 'Product Search Results',
- slug: 'product-search-results',
- frontendPage: '/?s=shirt&post_type=product',
- legacyBlockName: 'woocommerce/legacy-template',
- },
];
templates.forEach(
@@ -509,78 +543,43 @@ test.describe( 'Product Collection', () => {
}
);
- await admin.visitSiteEditor( {
- postId: template.id,
- postType: 'wp_template',
- canvas: 'edit',
- } );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
- await editor.insertBlock( { name: legacyBlockName } );
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
-
- await page.goto( frontendPage );
- const classicProducts = page.locator(
- '.woocommerce-loop-product__title'
- );
- expect( await classicProducts.count() ).toBeGreaterThan(
- 0
- );
- const classicProductsNames =
- await classicProducts.allTextContents();
-
- expect( classicProductsNames ).toEqual(
- productCollectionProductNames
- );
+ try {
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
+ await expect(
+ editor.getCustomHtmlBlockContentLocator(
+ 'placeholder'
+ )
+ ).toBeVisible();
+ await editor.insertBlock( { name: legacyBlockName } );
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ await page.goto( frontendPage );
+ const classicProducts = page.locator(
+ '.woocommerce-loop-product__title'
+ );
+ expect( await classicProducts.count() ).toBeGreaterThan(
+ 0
+ );
+ const classicProductsNames =
+ await classicProducts.allTextContents();
+
+ expect( classicProductsNames ).toEqual(
+ productCollectionProductNames
+ );
+ } finally {
+ await requestUtils.revertTemplate(
+ 'wp_template',
+ template.id
+ );
+ }
} );
} );
}
);
-
- test.describe( 'default query can be modified', () => {
- test( 'default query can be modified', async ( {
- page,
- pageObject,
- editor,
- } ) => {
- await wpCLI(
- 'option update woocommerce_default_catalog_orderby price'
- );
-
- await pageObject.goToEditorTemplate();
-
- await pageObject.focusProductCollection();
-
- // Verify the default order matches the option in the database.
- const sidebarSettings = pageObject.locateSidebarSettings();
- const orderBySelect = sidebarSettings.getByRole( 'combobox', {
- name: 'Default sort by',
- } );
- const editorProductTitle = editor.canvas
- .locator( SELECTORS.productTitle )
- .first();
-
- await expect( orderBySelect ).toHaveValue( 'price' );
- await expect( editorProductTitle ).toHaveText( 'Single' );
-
- await orderBySelect.selectOption( 'price-desc' );
-
- await expect( editorProductTitle ).toHaveText( 'Sunglasses' );
-
- await editor.saveSiteEditorEntities();
- await pageObject.goToProductCatalogFrontend();
-
- const frontendProductTitle = page
- .locator( SELECTORS.productTitle )
- .first();
- await expect( frontendProductTitle ).toContainText( 'Sunglasses' );
-
- await wpCLI(
- 'option update woocommerce_default_catalog_orderby menu_order'
- );
- } );
- } );
} );