Commit 04ae865ba6e for woocommerce
commit 04ae865ba6eaba16bad4496d6cff1a14bbca7f16
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date: Thu Nov 12 14:54:31 2026 +0200
Run Blocks e2e parallel test cleanup in fixture teardown (#70896)
* Run Blocks e2e parallel test cleanup in fixture teardown
After a test timeout, a finally block in the test body may not run.
The test data then stays on the shared site for the retry and for
other workers. A new cleanup fixture runs the cleanup in teardown,
which runs after a timeout too.
* Run every cleanup step when one throws at once
A callback that throws before it returns a promise stopped the map,
so the steps after it never ran. Start each step in a promise chain
so Promise.allSettled catches that error too.
* Add changelog entry
diff --git a/plugins/woocommerce/changelog/dev-e2e-blocks-parallel-cleanup-in-fixtures b/plugins/woocommerce/changelog/dev-e2e-blocks-parallel-cleanup-in-fixtures
new file mode 100644
index 00000000000..c7e4e515b27
--- /dev/null
+++ b/plugins/woocommerce/changelog/dev-e2e-blocks-parallel-cleanup-in-fixtures
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Run Blocks e2e parallel test cleanup in fixture teardown so it also runs after a timeout; tests only.
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/add-to-cart-form/add-to-cart-form.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/add-to-cart-form/add-to-cart-form.block_theme.spec.ts
index a08a4649bd1..39d57f1c2cb 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/add-to-cart-form/add-to-cart-form.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/add-to-cart-form/add-to-cart-form.block_theme.spec.ts
@@ -145,6 +145,7 @@ test.describe( `${ blockData.name } Block`, () => {
admin,
editor,
requestUtils,
+ cleanup,
blockUtils,
} ) => {
// Add to Cart with Options in the Site Editor is only available as
@@ -155,35 +156,34 @@ test.describe( `${ blockData.name } Block`, () => {
title: `Add to Cart Form Test ${ test.info().repeatEachIndex }`,
content: 'placeholder',
} );
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- try {
- await admin.visitSiteEditor( {
- postId: template.id,
- postType: 'wp_template',
- canvas: 'edit',
- } );
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
- await editor.insertBlock( { name: 'woocommerce/single-product' } );
+ await editor.insertBlock( { name: 'woocommerce/single-product' } );
- await blockUtils.configureSingleProductBlock();
+ await blockUtils.configureSingleProductBlock();
- await expect(
- await editor.getBlockByName( blockData.slug )
- ).toBeVisible();
-
- // When the block is registered as ancestor, the function doesn't throw an error, but the block is not added.
- // So we check that only one instance of the block is present.
- await editor.insertBlock( { name: blockData.slug } );
- await expect(
- await editor.getBlockByName( blockData.slug )
- ).toBeVisible();
- } finally {
- await requestUtils.revertTemplate( 'wp_template', template.id );
- }
+ await expect(
+ await editor.getBlockByName( blockData.slug )
+ ).toBeVisible();
+
+ // When the block is registered as ancestor, the function doesn't throw an error, but the block is not added.
+ // So we check that only one instance of the block is present.
+ await editor.insertBlock( { name: blockData.slug } );
+ await expect(
+ await editor.getBlockByName( blockData.slug )
+ ).toBeVisible();
} );
test( 'can be added in the Post Editor - Single Product Template', async ( {
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 e8532f233b6..cc4f3171006 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
@@ -30,6 +30,7 @@ test.describe( `${ BLOCK_NAME } Block`, () => {
admin,
page,
requestUtils,
+ cleanup,
} ) => {
// WordPress uses `single-post-<slug>` for this post only. It has no
// header, so the page has no Mini-Cart block.
@@ -39,46 +40,42 @@ test.describe( `${ BLOCK_NAME } Block`, () => {
title: 'All Products without header',
content: '<!-- wp:post-content /-->',
} );
- let postId: number | undefined;
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- 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;
+ const post = await requestUtils.rest< { id: number } >( {
+ method: 'POST',
+ path: 'wp/v2/posts',
+ data: {
+ title: 'All Products without Mini-Cart',
+ slug: postSlug,
+ status: 'draft',
+ },
+ } );
+ cleanup( () =>
+ requestUtils.rest( {
+ method: 'DELETE',
+ path: `wp/v2/posts/${ post.id }`,
+ params: { force: true },
+ } )
+ );
- 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 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( '[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 ),
- ] );
- }
+ 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 );
} );
// Regression coverage for legacy filter markup:
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/breadcrumbs/breadcrumbs.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/breadcrumbs/breadcrumbs.block_theme.spec.ts
index a7286de9564..231a28b9e10 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/breadcrumbs/breadcrumbs.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/breadcrumbs/breadcrumbs.block_theme.spec.ts
@@ -12,6 +12,7 @@ test.describe( `${ blockData.slug } Block`, () => {
test( 'block can be inserted in the Site Editor', async ( {
admin,
requestUtils,
+ cleanup,
editor,
} ) => {
const template = await requestUtils.createTemplate( 'wp_template', {
@@ -19,29 +20,26 @@ test.describe( `${ blockData.slug } Block`, () => {
title: `Breadcrumbs Test ${ test.info().repeatEachIndex }`,
content: 'placeholder',
} );
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- try {
- await admin.visitSiteEditor( {
- postId: template.id,
- postType: 'wp_template',
- canvas: 'edit',
- } );
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
- await editor.insertBlock( {
- name: blockData.slug,
- } );
+ await editor.insertBlock( {
+ name: blockData.slug,
+ } );
- const block = await editor.getBlockByName( blockData.slug );
+ const block = await editor.getBlockByName( blockData.slug );
- await expect( block ).toHaveText(
- 'Breadcrumbs / Navigation / Path'
- );
- } finally {
- await requestUtils.revertTemplate( 'wp_template', template.id );
- }
+ await expect( block ).toHaveText( 'Breadcrumbs / Navigation / Path' );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/cart-store/parent-item-key.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/cart-store/parent-item-key.block_theme.spec.ts
index be4ee49d40e..b931b5b4191 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/cart-store/parent-item-key.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/cart-store/parent-item-key.block_theme.spec.ts
@@ -142,6 +142,7 @@ test.describe( 'ProductButton in-cart count', () => {
test( 'does not count a child cart line in the add to cart button', async ( {
page,
requestUtils,
+ cleanup,
addToCartWithOptionsPage,
} ) => {
await addToCartWithOptionsPage.createPostWithProductBlock(
@@ -155,75 +156,72 @@ test.describe( 'ProductButton in-cart count', () => {
)?.[ 1 ]
);
expect( postId ).toBeGreaterThan( 0 );
+ cleanup( async () => {
+ await requestUtils.rest( {
+ method: 'DELETE',
+ path: `/wp/v2/posts/${ postId }`,
+ params: { force: true },
+ } );
+ } );
// All workers share the admin cart, so the cart steps run as a guest.
await page.context().clearCookies();
- try {
- const variationId = await getSinglePostId(
- 'post list --post_type=product_variation --field=ID --name="Hoodie - Blue, Yes" --format=ids'
- );
+ const variationId = await getSinglePostId(
+ 'post list --post_type=product_variation --field=ID --name="Hoodie - Blue, Yes" --format=ids'
+ );
- const capId = await getProductIdBySlug( 'cap' );
- await page.goto( `/?add-to-cart=${ capId }` );
- const parentCartItems = await readCartItems( page );
- expect( parentCartItems ).toHaveLength( 1 );
- const parentLine = parentCartItems.find(
- ( item ) => item.id === capId
+ const capId = await getProductIdBySlug( 'cap' );
+ await page.goto( `/?add-to-cart=${ capId }` );
+ const parentCartItems = await readCartItems( page );
+ expect( parentCartItems ).toHaveLength( 1 );
+ const parentLine = parentCartItems.find(
+ ( item ) => item.id === capId
+ );
+ expect( parentLine ).toBeDefined();
+ const parentItemKey = ( parentLine as StoreApiCartItem ).key;
+
+ await seedDeclaredChildLine( page, variationId, parentItemKey );
+ const cartItems = await readCartItems( page );
+ expect( cartItems ).toHaveLength( 2 );
+ expect(
+ cartItems.find( ( item ) => item.id === variationId )
+ ?.parent_item_key
+ ).toBe( parentItemKey );
+ expect(
+ cartItems.find( ( item ) => item.key === parentItemKey )
+ ?.parent_item_key
+ ).toBeNull();
+
+ // Reads the server-rendered HTML without running scripts.
+ const readServerRenderedButtonText = async () => {
+ const response = await page.request.get( postUrl );
+ const button = ( await response.text() ).match(
+ /<button\b[^>]*class="[^"]*\bsingle_add_to_cart_button\b[^"]*"[^>]*>([\s\S]*?)<\/button>/
);
- expect( parentLine ).toBeDefined();
- const parentItemKey = ( parentLine as StoreApiCartItem ).key;
+ expect( button ).not.toBeNull();
- await seedDeclaredChildLine( page, variationId, parentItemKey );
- const cartItems = await readCartItems( page );
- expect( cartItems ).toHaveLength( 2 );
- expect(
- cartItems.find( ( item ) => item.id === variationId )
- ?.parent_item_key
- ).toBe( parentItemKey );
- expect(
- cartItems.find( ( item ) => item.key === parentItemKey )
- ?.parent_item_key
- ).toBeNull();
+ return button?.[ 1 ]?.replace( /<[^>]*>/g, '' ).trim();
+ };
- // Reads the server-rendered HTML without running scripts.
- const readServerRenderedButtonText = async () => {
- const response = await page.request.get( postUrl );
- const button = ( await response.text() ).match(
- /<button\b[^>]*class="[^"]*\bsingle_add_to_cart_button\b[^"]*"[^>]*>([\s\S]*?)<\/button>/
- );
- expect( button ).not.toBeNull();
+ const addToCartButton = page
+ .locator( '.wp-block-add-to-cart-with-options' )
+ .locator( '.single_add_to_cart_button' );
- return button?.[ 1 ]?.replace( /<[^>]*>/g, '' ).trim();
- };
+ expect( await readServerRenderedButtonText() ).toBe( 'Add to cart' );
+ await page.goto( postUrl );
+ // The server renders the button hidden and the client store reveals it, so
+ // waiting for it to be visible makes the next text check read the hydrated count.
+ await expect( addToCartButton ).toBeVisible();
+ await expect( addToCartButton ).toHaveText( 'Add to cart' );
- const addToCartButton = page
- .locator( '.wp-block-add-to-cart-with-options' )
- .locator( '.single_add_to_cart_button' );
+ await addToCartButton.click();
+ await expect( addToCartButton ).toHaveText( '1 in cart' );
- expect( await readServerRenderedButtonText() ).toBe(
- 'Add to cart'
- );
- await page.goto( postUrl );
- // The server renders the button hidden and the client store reveals it, so
- // waiting for it to be visible makes the next text check read the hydrated count.
- await expect( addToCartButton ).toBeVisible();
- await expect( addToCartButton ).toHaveText( 'Add to cart' );
-
- await addToCartButton.click();
- await expect( addToCartButton ).toHaveText( '1 in cart' );
-
- expect( await readServerRenderedButtonText() ).toBe( '1 in cart' );
- await page.reload();
- await expect( addToCartButton ).toBeVisible();
- await expect( addToCartButton ).toHaveText( '1 in cart' );
- } finally {
- await requestUtils.rest( {
- method: 'DELETE',
- path: `/wp/v2/posts/${ postId }`,
- params: { force: true },
- } );
- }
+ expect( await readServerRenderedButtonText() ).toBe( '1 in cart' );
+ await page.reload();
+ await expect( addToCartButton ).toBeVisible();
+ await expect( addToCartButton ).toHaveText( '1 in cart' );
} );
test.describe( 'as a guest', () => {
@@ -299,6 +297,7 @@ test.describe( 'ProductButton in-cart count', () => {
test( 'counts a child when its declared parent product is drafted', async ( {
page,
requestUtils,
+ cleanup,
} ) => {
// Other specs read the sample catalog, so the parent is a product
// of its own. Hidden and dated in the past, it stays out of the
@@ -316,41 +315,38 @@ test.describe( 'ProductButton in-cart count', () => {
},
} );
- try {
- await seedBeanieAsChildOf( page, parentId );
- await wpCLI( `post update ${ parentId } --post_status=draft` );
-
- const items = await readCartItems( page );
- expect(
- items.find( ( item ) => item.id === parentId )
- ).toBeUndefined();
- const childLine = items.find(
- ( item ) => item.id === PRODUCT_X.id
- );
- expect( childLine ).toBeDefined();
- expect( childLine?.parent_item_key ).toBeNull();
-
- expect(
- await readServerRenderedProductButtonText(
- page,
- PRODUCT_X.id
- )
- ).toBe( '1 in cart' );
-
- await page.goto( '/shop/' );
- await expectHydratedProductButton(
- page,
- PRODUCT_X.id,
- '1 in cart',
- '2 in cart'
- );
- } finally {
+ cleanup( async () => {
await requestUtils.rest( {
method: 'DELETE',
path: `wc/v3/products/${ parentId }`,
params: { force: true },
} );
- }
+ } );
+
+ await seedBeanieAsChildOf( page, parentId );
+ await wpCLI( `post update ${ parentId } --post_status=draft` );
+
+ const items = await readCartItems( page );
+ expect(
+ items.find( ( item ) => item.id === parentId )
+ ).toBeUndefined();
+ const childLine = items.find(
+ ( item ) => item.id === PRODUCT_X.id
+ );
+ expect( childLine ).toBeDefined();
+ expect( childLine?.parent_item_key ).toBeNull();
+
+ expect(
+ await readServerRenderedProductButtonText( page, PRODUCT_X.id )
+ ).toBe( '1 in cart' );
+
+ await page.goto( '/shop/' );
+ await expectHydratedProductButton(
+ page,
+ PRODUCT_X.id,
+ '1 in cart',
+ '2 in cart'
+ );
} );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/catalog-sorting/catalog-sorting.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/catalog-sorting/catalog-sorting.block_theme.spec.ts
index c831e7008ab..95d88c1ed78 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/catalog-sorting/catalog-sorting.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/catalog-sorting/catalog-sorting.block_theme.spec.ts
@@ -13,6 +13,7 @@ test.describe( `${ blockData.slug } Block`, () => {
test( 'block can be inserted in the Site Editor', async ( {
admin,
requestUtils,
+ cleanup,
editor,
} ) => {
const template = await requestUtils.createTemplate( 'wp_template', {
@@ -20,26 +21,25 @@ test.describe( `${ blockData.slug } Block`, () => {
title: `Catalog Sorting Test ${ test.info().repeatEachIndex }`,
content: 'placeholder',
} );
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- try {
- await admin.visitSiteEditor( {
- postId: template.id,
- postType: 'wp_template',
- canvas: 'edit',
- } );
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
- await editor.insertBlock( {
- name: blockData.slug,
- } );
+ await editor.insertBlock( {
+ name: blockData.slug,
+ } );
- const block = await editor.getBlockByName( blockData.slug );
- await expect( block ).toHaveText( 'Default sorting' );
- } finally {
- await requestUtils.revertTemplate( 'wp_template', template.id );
- }
+ const block = await editor.getBlockByName( blockData.slug );
+ await expect( block ).toHaveText( 'Default sorting' );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/featured-category/featured-category.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/featured-category/featured-category.block_theme.spec.ts
index 0abbb398edf..c627273006b 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/featured-category/featured-category.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/featured-category/featured-category.block_theme.spec.ts
@@ -33,68 +33,64 @@ test.describe( `${ blockData.slug } Block`, () => {
).toBeVisible();
} );
- test( 'image can be edited', async ( { editor, admin, requestUtils } ) => {
- let editedMediaId: number | undefined;
+ test( 'image can be edited', async ( {
+ editor,
+ admin,
+ requestUtils,
+ cleanup,
+ } ) => {
const media = await requestUtils.uploadMedia(
path.resolve( __dirname, '../../../test-data/images/image-01.png' )
);
- try {
- const cliOutput = await wpCLI(
- 'term list product_cat --slug=music --field=term_id'
- );
- const categoryId = Number(
- cliOutput.stdout.match( /^[1-9]\d*$/m )?.[ 0 ]
- );
- if ( ! categoryId ) {
- throw new Error(
- `Failed to find the Music category: ${ cliOutput.stdout }`
- );
- }
+ cleanup( () => requestUtils.deleteMedia( media.id ) );
- // The block's own image overrides the category image, so the test
- // does not change any category that other specs read.
- await admin.createNewPost();
- await editor.insertBlock( {
- name: blockData.slug,
- attributes: {
- categoryId,
- mediaId: media.id,
- mediaSrc: media.source_url,
- },
- } );
- await editor.clickBlockToolbarButton( 'Edit category image' );
- await editor.clickBlockToolbarButton( 'Rotate' );
- const editImageResponse = editor.page.waitForResponse(
- ( response ) =>
- response.request().method() === 'POST' &&
- new URL( response.url() ).pathname ===
- `/wp-json/wp/v2/media/${ media.id }/edit`
+ const cliOutput = await wpCLI(
+ 'term list product_cat --slug=music --field=term_id'
+ );
+ const categoryId = Number(
+ cliOutput.stdout.match( /^[1-9]\d*$/m )?.[ 0 ]
+ );
+ if ( ! categoryId ) {
+ throw new Error(
+ `Failed to find the Music category: ${ cliOutput.stdout }`
);
- await editor.page
- .getByRole( 'button', { name: 'Apply', exact: true } )
- .click();
- const editResponse = await editImageResponse;
- expect( editResponse.status() ).toBe( 201 );
- const editedMedia = await editResponse.json();
- if (
- typeof editedMedia.id !== 'number' ||
- ! Number.isInteger( editedMedia.id )
- ) {
- throw new Error( 'The image edit did not return a media ID.' );
- }
- editedMediaId = editedMedia.id;
- await expect(
- editor.canvas.locator( 'img[alt="Music"][src*="-edited"]' )
- ).toBeVisible();
- } finally {
- try {
- if ( editedMediaId !== undefined ) {
- await requestUtils.deleteMedia( editedMediaId );
- }
- } finally {
- await requestUtils.deleteMedia( media.id );
- }
}
+
+ // The block's own image overrides the category image, so the test
+ // does not change any category that other specs read.
+ await admin.createNewPost();
+ await editor.insertBlock( {
+ name: blockData.slug,
+ attributes: {
+ categoryId,
+ mediaId: media.id,
+ mediaSrc: media.source_url,
+ },
+ } );
+ await editor.clickBlockToolbarButton( 'Edit category image' );
+ await editor.clickBlockToolbarButton( 'Rotate' );
+ const editImageResponse = editor.page.waitForResponse(
+ ( response ) =>
+ response.request().method() === 'POST' &&
+ new URL( response.url() ).pathname ===
+ `/wp-json/wp/v2/media/${ media.id }/edit`
+ );
+ await editor.page
+ .getByRole( 'button', { name: 'Apply', exact: true } )
+ .click();
+ const editResponse = await editImageResponse;
+ expect( editResponse.status() ).toBe( 201 );
+ const editedMedia = await editResponse.json();
+ if (
+ typeof editedMedia.id !== 'number' ||
+ ! Number.isInteger( editedMedia.id )
+ ) {
+ throw new Error( 'The image edit did not return a media ID.' );
+ }
+ cleanup( () => requestUtils.deleteMedia( editedMedia.id ) );
+ await expect(
+ editor.canvas.locator( 'img[alt="Music"][src*="-edited"]' )
+ ).toBeVisible();
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/featured-product/featured-product.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/featured-product/featured-product.block_theme.spec.ts
index 1ac838495f2..b1c6ea78059 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/featured-product/featured-product.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/featured-product/featured-product.block_theme.spec.ts
@@ -33,68 +33,64 @@ test.describe( `${ blockData.slug } Block`, () => {
).toBeVisible();
} );
- test( 'image can be edited', async ( { editor, admin, requestUtils } ) => {
- let editedMediaId: number | undefined;
+ test( 'image can be edited', async ( {
+ editor,
+ admin,
+ requestUtils,
+ cleanup,
+ } ) => {
const media = await requestUtils.uploadMedia(
path.resolve( __dirname, '../../../test-data/images/image-01.png' )
);
- try {
- const cliOutput = await wpCLI(
- 'post list --post_type=product --title=Album --field=ID'
- );
- const productId = Number(
- cliOutput.stdout.match( /^[1-9]\d*$/m )?.[ 0 ]
- );
- if ( ! productId ) {
- throw new Error(
- `Failed to find the Album product: ${ cliOutput.stdout }`
- );
- }
+ cleanup( () => requestUtils.deleteMedia( media.id ) );
- // The block's own image overrides the product image, so the test
- // does not change any product that other specs read.
- await admin.createNewPost();
- await editor.insertBlock( {
- name: blockData.slug,
- attributes: {
- productId,
- mediaId: media.id,
- mediaSrc: media.source_url,
- },
- } );
- await editor.clickBlockToolbarButton( 'Edit product image' );
- await editor.clickBlockToolbarButton( 'Rotate' );
- const editImageResponse = editor.page.waitForResponse(
- ( response ) =>
- response.request().method() === 'POST' &&
- new URL( response.url() ).pathname ===
- `/wp-json/wp/v2/media/${ media.id }/edit`
+ const cliOutput = await wpCLI(
+ 'post list --post_type=product --title=Album --field=ID'
+ );
+ const productId = Number(
+ cliOutput.stdout.match( /^[1-9]\d*$/m )?.[ 0 ]
+ );
+ if ( ! productId ) {
+ throw new Error(
+ `Failed to find the Album product: ${ cliOutput.stdout }`
);
- await editor.page
- .getByRole( 'button', { name: 'Apply', exact: true } )
- .click();
- const editResponse = await editImageResponse;
- expect( editResponse.status() ).toBe( 201 );
- const editedMedia = await editResponse.json();
- if (
- typeof editedMedia.id !== 'number' ||
- ! Number.isInteger( editedMedia.id )
- ) {
- throw new Error( 'The image edit did not return a media ID.' );
- }
- editedMediaId = editedMedia.id;
- await expect(
- editor.canvas.locator( 'img[alt="Album"][src*="-edited"]' )
- ).toBeVisible();
- } finally {
- try {
- if ( editedMediaId !== undefined ) {
- await requestUtils.deleteMedia( editedMediaId );
- }
- } finally {
- await requestUtils.deleteMedia( media.id );
- }
}
+
+ // The block's own image overrides the product image, so the test
+ // does not change any product that other specs read.
+ await admin.createNewPost();
+ await editor.insertBlock( {
+ name: blockData.slug,
+ attributes: {
+ productId,
+ mediaId: media.id,
+ mediaSrc: media.source_url,
+ },
+ } );
+ await editor.clickBlockToolbarButton( 'Edit product image' );
+ await editor.clickBlockToolbarButton( 'Rotate' );
+ const editImageResponse = editor.page.waitForResponse(
+ ( response ) =>
+ response.request().method() === 'POST' &&
+ new URL( response.url() ).pathname ===
+ `/wp-json/wp/v2/media/${ media.id }/edit`
+ );
+ await editor.page
+ .getByRole( 'button', { name: 'Apply', exact: true } )
+ .click();
+ const editResponse = await editImageResponse;
+ expect( editResponse.status() ).toBe( 201 );
+ const editedMedia = await editResponse.json();
+ if (
+ typeof editedMedia.id !== 'number' ||
+ ! Number.isInteger( editedMedia.id )
+ ) {
+ throw new Error( 'The image edit did not return a media ID.' );
+ }
+ cleanup( () => requestUtils.deleteMedia( editedMedia.id ) );
+ await expect(
+ editor.canvas.locator( 'img[alt="Album"][src*="-edited"]' )
+ ).toBeVisible();
} );
} );
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 7db273dc05a..43de9afefb1 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
@@ -416,6 +416,7 @@ test.describe( `${ blockData.name } Block (variation attributes)`, () => {
page,
frontendUtils,
miniCartUtils,
+ cleanup,
} ) => {
// The API returns the ampersand as "&", so this also checks that
// the variation branch of the table decodes it.
@@ -435,35 +436,35 @@ test.describe( `${ blockData.name } Block (variation attributes)`, () => {
/Created product (\d+)/
)?.[ 1 ];
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' ] );
- } finally {
+ cleanup( async () => {
await wpCLI(
`wc product delete ${ productId } --force=true --user=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();
+
+ 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' ] );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/patterns/add-new-pattern.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/patterns/add-new-pattern.block_theme.spec.ts
index d8a56869a8a..26f9b4efdc9 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/patterns/add-new-pattern.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/patterns/add-new-pattern.block_theme.spec.ts
@@ -13,19 +13,13 @@ test.describe( 'Patterns in block theme', () => {
admin,
editor,
requestUtils,
+ cleanup,
} ) => {
const patternName = `Woo Blocks Synced Pattern ${
test.info().repeatEachIndex
}`;
- try {
- await admin.createNewPattern( patternName );
- const { productTitles, productPrices } =
- await addTestingBlocks( editor );
-
- await expect( productTitles ).toHaveText( expectedTitles );
- await expect( productPrices ).toHaveText( expectedPrices );
- } finally {
+ cleanup( async () => {
const patterns: { id: number; title: { raw: string } }[] =
await requestUtils.rest( {
path: '/wp/v2/blocks',
@@ -45,6 +39,13 @@ test.describe( 'Patterns in block theme', () => {
params: { force: true },
} );
}
- }
+ } );
+
+ await admin.createNewPattern( patternName );
+ const { productTitles, productPrices } =
+ await addTestingBlocks( editor );
+
+ await expect( productTitles ).toHaveText( expectedTitles );
+ await expect( productPrices ).toHaveText( expectedPrices );
} );
} );
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 37e6720ac81..e24faefc42f 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
@@ -275,6 +275,7 @@ test.describe( 'Product Collection', () => {
editor,
wpCoreVersion,
requestUtils,
+ cleanup,
} ) => {
const getLocationParams = ( request: Request ) =>
new URL( request.url() ).searchParams;
@@ -307,102 +308,7 @@ test.describe( 'Product Collection', () => {
);
};
- 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();
-
- 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 {
+ cleanup( async () => {
await Promise.allSettled(
[ 'single-product-cap', 'taxonomy-product_cat-hoodies' ].map(
( slug ) =>
@@ -412,7 +318,90 @@ 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,
+ } );
+
+ 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();
} );
// Tests for regressions of https://github.com/woocommerce/woocommerce/pull/47994
@@ -452,6 +441,7 @@ test.describe( 'Product Collection', () => {
pageObject,
editor,
requestUtils,
+ cleanup,
} ) => {
const template = await requestUtils.createTemplate( 'wp_template', {
slug: `product-collection-on-sale-${ randomUUID() }`,
@@ -459,29 +449,29 @@ test.describe( 'Product Collection', () => {
content: 'placeholder',
} );
- try {
- await pageObject.goToEditorTemplate( template.id );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
- await pageObject.insertProductCollection();
- await pageObject.chooseCollectionInTemplate( 'onSale' );
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- const productTemplate = editor.canvas.getByLabel(
- BLOCK_LABELS.productTemplate
- );
+ await pageObject.goToEditorTemplate( template.id );
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
+ await pageObject.insertProductCollection();
+ await pageObject.chooseCollectionInTemplate( 'onSale' );
- await expect( productTemplate ).toHaveCount( 1 );
+ 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( 1 );
- } finally {
- await requestUtils.revertTemplate( 'wp_template', template.id );
- }
+ 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 );
} );
test( 'On Sale Products collection should be visible after Refresh in a Post', async ( {
@@ -521,6 +511,7 @@ test.describe( 'Product Collection', () => {
test( 'Product Collection block matches with classic template block', async ( {
pageObject,
requestUtils,
+ cleanup,
admin,
editor,
page,
@@ -543,41 +534,39 @@ test.describe( 'Product Collection', () => {
}
);
- 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(
+ cleanup( () =>
+ requestUtils.revertTemplate(
'wp_template',
template.id
- );
- }
+ )
+ );
+
+ 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
+ );
} );
} );
}
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-results-count/product-results-count.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-results-count/product-results-count.block_theme.spec.ts
index 1818fbcf55b..86239a06832 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-results-count/product-results-count.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-results-count/product-results-count.block_theme.spec.ts
@@ -13,6 +13,7 @@ test.describe( `${ blockData.slug } Block`, () => {
test( 'block can be inserted in the Site Editor', async ( {
admin,
requestUtils,
+ cleanup,
editor,
} ) => {
const template = await requestUtils.createTemplate( 'wp_template', {
@@ -22,27 +23,26 @@ test.describe( `${ blockData.slug } Block`, () => {
}`,
content: 'placeholder',
} );
+ cleanup( () =>
+ requestUtils.revertTemplate( 'wp_template', template.id )
+ );
- try {
- await admin.visitSiteEditor( {
- postId: template.id,
- postType: 'wp_template',
- canvas: 'edit',
- } );
+ await admin.visitSiteEditor( {
+ postId: template.id,
+ postType: 'wp_template',
+ canvas: 'edit',
+ } );
- await expect(
- editor.getCustomHtmlBlockContentLocator( 'placeholder' )
- ).toBeVisible();
- await editor.insertBlock( {
- name: blockData.slug,
- } );
+ await expect(
+ editor.getCustomHtmlBlockContentLocator( 'placeholder' )
+ ).toBeVisible();
+ await editor.insertBlock( {
+ name: blockData.slug,
+ } );
- const block = await editor.getBlockByName( blockData.slug );
+ const block = await editor.getBlockByName( blockData.slug );
- await expect( block ).toHaveText( 'Showing 1-X of X results' );
- } finally {
- await requestUtils.revertTemplate( 'wp_template', template.id );
- }
+ await expect( block ).toHaveText( 'Showing 1-X of X results' );
} );
test( 'announces the new count to screen readers after sorting', async ( {
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/templates/single-product-template.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/templates/single-product-template.block_theme.spec.ts
index 3c9415c1b88..1e712eb7b12 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/templates/single-product-template.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/templates/single-product-template.block_theme.spec.ts
@@ -9,6 +9,7 @@ test.describe( 'Single Product template', () => {
editor,
page,
requestUtils,
+ cleanup,
wpCoreVersion,
} ) => {
const testData = {
@@ -20,60 +21,7 @@ test.describe( 'Single Product template', () => {
};
const userText = 'Hello World in the Belt template';
- try {
- // Create the specific product template.
- await admin.visitSiteEditor( {
- path: `/${ testData.templateType }`,
- } );
-
- 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
- .getByPlaceholder( 'Search products' )
- .fill( testData.productName );
- await page
- .getByRole( 'option', { name: testData.productName } )
- .click();
- await page.getByLabel( 'Close', { exact: true } ).click();
-
- await editor.canvas.locator( 'body' ).waitFor( { timeout: 20000 } );
-
- // Edit the template.
- await editor.insertBlock( {
- name: 'core/paragraph',
- attributes: { content: userText },
- } );
- await editor.saveSiteEditorEntities( {
- isOnlyCurrentEntityDirty: true,
- } );
-
- // Verify edits are visible.
- await page.goto( testData.permalink );
- await expect( page.getByText( userText ).first() ).toBeVisible();
-
- // Revert edition.
- await admin.visitSiteEditor( {
- postType: testData.templateType,
- } );
- await editor.revertTemplate( {
- templateName: testData.templateName,
- } );
- await page.goto( testData.permalink );
-
- // Verify the edits are no longer visible.
- await expect( page.getByText( userText ) ).toHaveCount( 0 );
- } finally {
+ cleanup( async () => {
const templates = (
await requestUtils.getTemplates( 'wp_template' )
).filter(
@@ -87,6 +35,57 @@ test.describe( 'Single Product template', () => {
params: { force: true },
} );
}
- }
+ } );
+
+ // Create the specific product template.
+ await admin.visitSiteEditor( {
+ path: `/${ testData.templateType }`,
+ } );
+
+ 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
+ .getByPlaceholder( 'Search products' )
+ .fill( testData.productName );
+ await page
+ .getByRole( 'option', { name: testData.productName } )
+ .click();
+ await page.getByLabel( 'Close', { exact: true } ).click();
+
+ await editor.canvas.locator( 'body' ).waitFor( { timeout: 20000 } );
+
+ // Edit the template.
+ await editor.insertBlock( {
+ name: 'core/paragraph',
+ attributes: { content: userText },
+ } );
+ await editor.saveSiteEditorEntities( {
+ isOnlyCurrentEntityDirty: true,
+ } );
+
+ // Verify edits are visible.
+ await page.goto( testData.permalink );
+ await expect( page.getByText( userText ).first() ).toBeVisible();
+
+ // Revert edition.
+ await admin.visitSiteEditor( {
+ postType: testData.templateType,
+ } );
+ await editor.revertTemplate( {
+ templateName: testData.templateName,
+ } );
+ await page.goto( testData.permalink );
+
+ // Verify the edits are no longer visible.
+ await expect( page.getByText( userText ) ).toHaveCount( 0 );
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/utils/blocks/test.ts b/plugins/woocommerce/tests/e2e/utils/blocks/test.ts
index 9c16b364215..79d9f3f947d 100644
--- a/plugins/woocommerce/tests/e2e/utils/blocks/test.ts
+++ b/plugins/woocommerce/tests/e2e/utils/blocks/test.ts
@@ -134,6 +134,7 @@ const test = base.extend<
localPickupUtils: LocalPickupUtils;
miniCartUtils: MiniCartUtils;
resetDatabaseAfterEachTest: boolean;
+ cleanup: ( callback: () => Promise< unknown > ) => void;
createProductTemplate: (
sourceProductSlug: string,
options?: { content?: string }
@@ -211,6 +212,26 @@ const test = base.extend<
miniCartUtils: async ( { page, frontendUtils }, use ) => {
await use( new MiniCartUtils( page, frontendUtils ) );
},
+ // Registers a callback that runs after the test, even after a timeout,
+ // when code after the timed-out step in the test body does not run.
+ // The page may already be closed then, so use `requestUtils` or `wpCLI`.
+ cleanup: async ( {}, use ) => {
+ const callbacks: ( () => Promise< unknown > )[] = [];
+
+ await use( ( callback ) => {
+ callbacks.push( callback );
+ } );
+
+ const results = await Promise.allSettled(
+ callbacks.map( ( callback ) => Promise.resolve().then( callback ) )
+ );
+ for ( const result of results ) {
+ if ( result.status === 'rejected' ) {
+ // eslint-disable-next-line no-console
+ console.warn( 'Test cleanup failed:', result.reason );
+ }
+ }
+ },
// Copies a sample product and creates a `single-product-<slug>` template
// for the copy, so a spec can save a Single Product template without
// changing the pages other workers visit. The copy is hidden from the