Commit 551b394c3eb for woocommerce

commit 551b394c3eb5ebe4076334b9af99fb16487dc412
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Mon Sep 14 17:21:46 2026 +0300

    [tests] Reduce Product Gallery E2E tests from 12 to 8 (#68598)

    test(blocks): Reduce Product Gallery E2E tests from 12 to 8

    The Product Gallery spec ran twelve browser titles. Seven of them
    checked one step each of a journey another title already walked: the
    first thumbnail, a thumbnail click, the pop-up setting's default, the
    pop-up opening, and the block's availability in three editor contexts.

    Fold those seven into three titles that walk each journey once, and
    move the block's registered defaults and its Single Product ancestry
    to Jest. The previous and next buttons and the pop-up title now check
    the active thumbnail too, and poll for the change instead of sleeping
    a fixed 400 ms.

    Consolidates the mega-branch slices:
    - Slice 057: test(blocks): Move Product Gallery behavior below E2E

    Refs TESTOPS-234
    Refs #68046

    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/testops-234-product-gallery b/plugins/woocommerce/changelog/testops-234-product-gallery
new file mode 100644
index 00000000000..2e23ca08f78
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-234-product-gallery
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Reduce Product Gallery E2E tests from 12 to 8; Jest owns the block's registered defaults and its Single Product ancestry.
+
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
index 9d59f3084ac..ee3ecf02e75 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/product-gallery/test/block.ts
@@ -3,7 +3,7 @@
  */
 import '@testing-library/jest-dom';
 import { screen, waitFor } from '@testing-library/react';
-import { createBlock } from '@wordpress/blocks';
+import { createBlock, getBlockType } from '@wordpress/blocks';
 import { http, HttpResponse } from 'msw';
 import { setupServer } from 'msw/node';

@@ -152,6 +152,21 @@ async function setup( attributes = {} ) {
 }

 describe( 'Product Gallery Block', () => {
+	it( 'uses the registered default gallery settings', () => {
+		const productGalleryBlock = createBlock( blockJson.name );
+
+		expect( productGalleryBlock.attributes ).toMatchObject( {
+			hoverZoom: true,
+			fullScreenOnClick: true,
+		} );
+	} );
+
+	it( 'restricts registration to Single Product descendants', () => {
+		expect( getBlockType( blockJson.name )?.ancestor ).toEqual( [
+			'woocommerce/single-product',
+		] );
+	} );
+
 	it( 'should render the block in the editor with correct structure', async () => {
 		await setup();

diff --git a/plugins/woocommerce/client/blocks/changelog/testops-234-product-gallery b/plugins/woocommerce/client/blocks/changelog/testops-234-product-gallery
new file mode 100644
index 00000000000..2e23ca08f78
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/changelog/testops-234-product-gallery
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Reduce Product Gallery E2E tests from 12 to 8; Jest owns the block's registered defaults and its Single Product ancestry.
+
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.block_theme.spec.ts
index 8b34ad7eb70..3329c3a3bc4 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.block_theme.spec.ts
@@ -86,7 +86,7 @@ test.describe( `${ blockData.name }`, () => {
 	} );

 	test.describe( 'with thumbnails', () => {
-		test( 'should have as first thumbnail, the same image that it is visible in the product block', async ( {
+		test( 'selects thumbnails and keeps the active viewer in sync', async ( {
 			page,
 			editor,
 			pageObject,
@@ -99,55 +99,39 @@ test.describe( `${ blockData.name }`, () => {

 			await page.goto( blockData.productPage );

-			const viewerImageId = await pageObject.getViewerImageId();
-
+			const thumbnailsBlock = await pageObject.getThumbnailsBlock( {
+				page: 'frontend',
+			} );
+			const initialViewerImageId = await pageObject.getViewerImageId();
 			const firstImageThumbnailId = await getThumbnailImageIdByNth(
 				0,
-				await pageObject.getThumbnailsBlock( {
-					page: 'frontend',
-				} )
+				thumbnailsBlock
 			);
-
-			expect( viewerImageId ).toBe( firstImageThumbnailId );
-		} );
-
-		test( 'should change the image when the user click on a thumbnail image', async ( {
-			page,
-			editor,
-			pageObject,
-		} ) => {
-			await pageObject.addProductGalleryBlock( { cleanContent: true } );
-
-			await editor.saveSiteEditorEntities( {
-				isOnlyCurrentEntityDirty: true,
-			} );
-
-			await page.goto( blockData.productPage );
-
-			const viewerImageId = await pageObject.getViewerImageId();
+			const initialActiveThumbnailId =
+				await pageObject.getActiveThumbnailImageId();

 			const secondImageThumbnailId = await getThumbnailImageIdByNth(
 				1,
-				await pageObject.getThumbnailsBlock( {
-					page: 'frontend',
-				} )
+				thumbnailsBlock
 			);

-			expect( viewerImageId ).not.toBe( secondImageThumbnailId );
+			expect( initialViewerImageId ).not.toBeNull();
+			expect( firstImageThumbnailId ).not.toBeNull();
+			expect( initialActiveThumbnailId ).not.toBeNull();
+			expect( initialViewerImageId ).toBe( firstImageThumbnailId );
+			expect( initialActiveThumbnailId ).toBe( firstImageThumbnailId );
+			expect( secondImageThumbnailId ).not.toBeNull();
+			expect( initialViewerImageId ).not.toBe( secondImageThumbnailId );

-			await (
-				await pageObject.getThumbnailsBlock( {
-					page: 'frontend',
-				} )
-			)
-				.locator( 'img' )
-				.nth( 1 )
-				.click();
+			await thumbnailsBlock.locator( 'img' ).nth( 1 ).click();

 			await expect( async () => {
 				const newViewerImageId = await pageObject.getViewerImageId();
+				const newActiveThumbnailId =
+					await pageObject.getActiveThumbnailImageId();

 				expect( newViewerImageId ).toBe( secondImageThumbnailId );
+				expect( newActiveThumbnailId ).toBe( secondImageThumbnailId );
 			} ).toPass( { timeout: 1_000 } );
 		} );
 	} );
@@ -175,19 +159,34 @@ test.describe( `${ blockData.name }`, () => {
 				} )
 			);

+			expect( initialViewerImageId ).not.toBeNull();
+			expect( secondImageThumbnailId ).not.toBeNull();
 			expect( initialViewerImageId ).not.toBe( secondImageThumbnailId );
+			expect( await pageObject.getActiveThumbnailImageId() ).toBe(
+				initialViewerImageId
+			);

 			await pageObject.clickNextButton();

-			const nextImageId = await pageObject.getViewerImageId();
-
-			expect( nextImageId ).toBe( secondImageThumbnailId );
+			await expect( async () => {
+				expect( await pageObject.getViewerImageId() ).toBe(
+					secondImageThumbnailId
+				);
+				expect( await pageObject.getActiveThumbnailImageId() ).toBe(
+					secondImageThumbnailId
+				);
+			} ).toPass( { timeout: 1_000 } );

 			await pageObject.clickPreviousButton();

-			const previousImageId = await pageObject.getViewerImageId();
-
-			expect( previousImageId ).toBe( initialViewerImageId );
+			await expect( async () => {
+				expect( await pageObject.getViewerImageId() ).toBe(
+					initialViewerImageId
+				);
+				expect( await pageObject.getActiveThumbnailImageId() ).toBe(
+					initialViewerImageId
+				);
+			} ).toPass( { timeout: 1_000 } );
 		} );
 	} );

@@ -216,13 +215,20 @@ test.describe( `${ blockData.name }`, () => {
 				} )
 			);

+			expect( initialViewerImageId ).not.toBeNull();
+			expect( secondImageThumbnailId ).not.toBeNull();
 			expect( initialViewerImageId ).not.toBe( secondImageThumbnailId );

 			await pageObject.clickNextButton();

-			const nextImageId = await pageObject.getViewerImageId();
-
-			expect( nextImageId ).toBe( secondImageThumbnailId );
+			await expect( async () => {
+				expect( await pageObject.getViewerImageId() ).toBe(
+					secondImageThumbnailId
+				);
+				expect( await pageObject.getActiveThumbnailImageId() ).toBe(
+					secondImageThumbnailId
+				);
+			} ).toPass( { timeout: 1_000 } );

 			const viewerBlock = await pageObject.getViewerBlock( {
 				page: 'frontend',
@@ -231,40 +237,35 @@ test.describe( `${ blockData.name }`, () => {

 			const dialogImage = page
 				.getByRole( 'dialog' )
-				.locator( `img[data-image-id='${ nextImageId }']` );
+				.locator( `img[data-image-id='${ secondImageThumbnailId }']` );

 			// The image should be in the viewport but it simply doesn't fit fully.
 			await expect( dialogImage ).toBeInViewport( { ratio: 0.7 } );

-			const closePopUpButton = page.locator(
-				'.wc-block-product-gallery-dialog__close-button'
-			);
-			await closePopUpButton.click();
-
-			const singleProductImageId = await pageObject.getViewerImageId();
+			await page.getByRole( 'button', { name: 'Close dialog' } ).click();

-			expect( singleProductImageId ).toBe( nextImageId );
+			await expect( async () => {
+				expect( await pageObject.getViewerImageId() ).toBe(
+					secondImageThumbnailId
+				);
+				expect( await pageObject.getActiveThumbnailImageId() ).toBe(
+					secondImageThumbnailId
+				);
+			} ).toPass( { timeout: 1_000 } );
 		} );
 	} );

 	test.describe( 'open pop-up when clicked option', () => {
-		test( 'should be enabled by default', async ( {
-			pageObject,
-			editor,
-		} ) => {
-			await pageObject.addProductGalleryBlock( { cleanContent: true } );
-			await editor.openDocumentSettingsSidebar();
-			const fullScreenOption = pageObject.getFullScreenOnClickSetting();
-
-			await expect( fullScreenOption ).toBeChecked();
-		} );
-
-		test( 'should open dialog on the frontend', async ( {
+		test( 'enables pop-up by default and opens it on the frontend', async ( {
 			pageObject,
 			page,
 			editor,
 		} ) => {
 			await pageObject.addProductGalleryBlock( { cleanContent: true } );
+			await editor.openDocumentSettingsSidebar();
+			await expect(
+				pageObject.getFullScreenOnClickSetting()
+			).toBeChecked();
 			await editor.saveSiteEditorEntities( {
 				isOnlyCurrentEntityDirty: true,
 			} );
@@ -309,38 +310,27 @@ test.describe( `${ blockData.name }`, () => {
 	} );

 	test.describe( 'block availability', () => {
-		test( 'should be available on the Single Product Template', async ( {
+		test( 'is available only in supported Single Product contexts', async ( {
+			admin,
 			page,
 			editor,
 		} ) => {
 			await editor.openGlobalBlockInserter();
 			await page.getByRole( 'tab', { name: 'Blocks' } ).click();
-			const productGalleryBlockOption = page
+			let productGalleryBlockOption = page
 				.getByRole( 'listbox', { name: 'WooCommerce' } )
 				.getByRole( 'option', { name: blockData.title } );

 			await expect( productGalleryBlockOption ).toBeVisible();
-		} );

-		test( 'should be hidden on the post editor globally', async ( {
-			admin,
-			page,
-			editor,
-		} ) => {
 			await admin.createNewPost();
 			await editor.openGlobalBlockInserter();
-			const productGalleryBlockOption = page
+			productGalleryBlockOption = page
 				.getByRole( 'listbox', { name: 'WooCommerce' } )
 				.getByRole( 'option', { name: blockData.title } );

 			await expect( productGalleryBlockOption ).toBeHidden();
-		} );

-		test( 'on the post editor, block should be in Single Product by default and is visible in inserter', async ( {
-			admin,
-			editor,
-		} ) => {
-			await admin.createNewPost();
 			await editor.insertBlockUsingGlobalInserter( 'Product' );
 			await editor.canvas.getByText( 'Album' ).click();
 			await editor.canvas.getByText( 'Done' ).click();
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.page.ts b/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.page.ts
index 19a62e97aeb..1ed73f9f035 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.page.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/product-gallery/product-gallery.page.ts
@@ -250,18 +250,12 @@ export class ProductGalleryPage {

 	async clickNextButton() {
 		await this.page.getByRole( 'button', { name: 'Next image' } ).click();
-		// Wait for the transition to change
-		// eslint-disable-next-line playwright/no-wait-for-timeout, no-restricted-syntax
-		await this.page.waitForTimeout( 400 );
 	}

 	async clickPreviousButton() {
 		await this.page
 			.getByRole( 'button', { name: 'Previous image' } )
 			.click();
-		// Wait for the transition to change
-		// eslint-disable-next-line playwright/no-wait-for-timeout, no-restricted-syntax
-		await this.page.waitForTimeout( 400 );
 	}

 	async getBlock( { page }: { page: 'frontend' | 'editor' } ) {