Commit 8f6bd619378 for woocommerce

commit 8f6bd619378fbeb667d399324769e3fb12f60907
Author: theAverageDev (Luca Tumedei) <luca.tumedei@automattic.com>
Date:   Tue Jun 9 22:22:14 2026 +0200

    Migrate Blocks e2e page-object utils into Core e2e suite (#65571)

    * Move Blocks e2e page-object utils into core e2e utils/blocks

    Pure git mv of the admin, editor, frontend, local-pickup, mini-cart,
    performance, and shipping page-object subtrees from
    client/blocks/tests/e2e/utils into tests/e2e-pw/utils/blocks/ so
    git rename detection preserves their history. Shims follow in the next commit.

    * Add re-export shims for moved Blocks e2e page-object utils

    Re-create the seven old client/blocks/tests/e2e/utils/<subtree>/index.ts paths
    as shims that re-export from tests/e2e-pw/utils/blocks/<subtree>, so the
    @woocommerce/e2e-utils barrel and any relative importers keep resolving until
    the barrel itself moves. Top-level utils/index.ts is left untouched.

    * Bump eslint-plugin-playwright to 1.6.0 in plugins/woocommerce

    Align the core e2e plugin with client/blocks (already on 1.6.0) so the
    playwright/no-hooks rule used by the QAO-405 utils/blocks eslint override
    resolves instead of erroring "rule not found" on the migrated page-object
    utils. Resolves this PR's changed-files lint without an eslintrc change.

    Note: 1.6.0's wider recommended set surfaces pre-existing violations in 4
    unrelated core specs (system-status-crud, launch-your-store, webhooks,
    shipping-classes); they are outside this PR's changed-files lint scope and
    left for a dedicated follow-up.

    * Fix saveAdminPage to actually wait for Save button to disable

    * Close widget editor welcome guide only when it appears

diff --git a/plugins/woocommerce/changelog/qao-402-blocks-e2e-page-object-utils b/plugins/woocommerce/changelog/qao-402-blocks-e2e-page-object-utils
new file mode 100644
index 00000000000..cf2b53363f9
--- /dev/null
+++ b/plugins/woocommerce/changelog/qao-402-blocks-e2e-page-object-utils
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Migrate Blocks e2e page-object utils (admin, editor, frontend, local-pickup, mini-cart, performance, shipping) into the Core e2e suite under utils/blocks/; test-only, no changes to shipped code.
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/admin/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/admin/index.ts
index febc25aabc5..ac4650be519 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/admin/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/admin/index.ts
@@ -1,96 +1,4 @@
-/**
- * External dependencies
- */
-import { Page } from '@playwright/test';
-import {
-	Admin as CoreAdmin,
-	PageUtils,
-	Editor,
-} from '@wordpress/e2e-test-utils-playwright';
-
-type AdminConstructorProps = {
-	page: Page;
-	pageUtils: PageUtils;
-	editor: Editor;
-	wpCoreVersion: number;
-};
-
-export class Admin extends CoreAdmin {
-	wpCoreVersion: number;
-
-	constructor( {
-		page,
-		pageUtils,
-		editor,
-		wpCoreVersion,
-	}: AdminConstructorProps ) {
-		super( { page, pageUtils, editor } );
-		this.wpCoreVersion = wpCoreVersion;
-	}
-
-	async visitWidgetEditor() {
-		await this.page.goto( '/wp-admin/widgets.php' );
-		await this.page
-			.getByRole( 'dialog', { name: 'Welcome to block Widgets' } )
-			.getByRole( 'button', { name: 'Close' } )
-			.click();
-	}
-
-	async createNewPattern( name: string, synced = true ) {
-		await this.page.goto( '/wp-admin/site-editor.php?postType=wp_block' );
-		await this.page.getByRole( 'button', { name: 'Patterns' } ).click();
-
-		await this.page
-			.getByLabel(
-				this.wpCoreVersion >= 6.8 ? 'Add Pattern' : 'Add New Pattern'
-			)
-			.click();
-
-		await this.page
-			.getByRole( 'menuitem', {
-				name:
-					this.wpCoreVersion >= 6.8
-						? 'Add Pattern'
-						: 'Add New Pattern',
-			} )
-			.click();
-
-		await this.page.getByLabel( 'Name' ).fill( name );
-
-		if ( ! synced ) {
-			// Synced toggle is enabled by default.
-			await this.page.getByLabel( 'Synced' ).click();
-		}
-
-		await this.page.getByRole( 'button', { name: 'Add' } ).click();
-
-		const welcomePopUp = async () => {
-			await this.page
-				.getByRole( 'button', {
-					name: 'Get started',
-				} )
-				.click();
-		};
-
-		const editorLoaded = async () => {
-			await this.page
-				.getByRole( 'heading', {
-					name: 'pattern',
-				} )
-				.waitFor();
-		};
-
-		await Promise.any( [ welcomePopUp(), editorLoaded() ] );
-	}
-
-	/**
-	 * Clicks the 'Save changes' button on an admin page and waits for it to become disabled to ensure the page is saved.
-	 */
-	async saveAdminPage() {
-		const saveButton = this.page.getByRole( 'button', {
-			name: 'Save changes',
-		} );
-		await saveButton.click();
-		await saveButton.isDisabled();
-	}
-}
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/admin. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/admin';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/editor/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/editor/index.ts
index 015f175c5c8..e74d4268a24 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/editor/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/editor/index.ts
@@ -1 +1,4 @@
-export * from './editor-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/editor. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/editor';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/index.ts
index a7b5d426561..448fcef6730 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/index.ts
@@ -1 +1,4 @@
-export * from './frontend-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/frontend. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/frontend';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/index.ts
index 9360f7ee4bf..19cc23fa11b 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/index.ts
@@ -1 +1,4 @@
-export * from './local-pickup-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/local-pickup. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/local-pickup';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/index.ts
index fe6274e8753..ccacc4e6f76 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/index.ts
@@ -1 +1,4 @@
-export * from './mini-cart-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/mini-cart. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/mini-cart';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/index.ts
index 91133f30afe..82cf25c5e1c 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/index.ts
@@ -1 +1,4 @@
-export * from './performance-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/performance. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/performance';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/index.ts b/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/index.ts
index bc956bae780..ebfa484378a 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/index.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/index.ts
@@ -1 +1,4 @@
-export * from './shipping-utils.page';
+// QAO-185 e2e merge shim: the implementation moved to
+// tests/e2e-pw/utils/blocks/shipping. Re-exported here so the
+// @woocommerce/e2e-utils barrel and relative imports keep resolving.
+export * from '../../../../../../tests/e2e-pw/utils/blocks/shipping';
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index b262aa3ddb2..8109efae023 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -945,7 +945,7 @@
 		"eslint": "^8.55.0",
 		"eslint-config-wpcalypso": "5.0.0",
 		"eslint-plugin-jest": "23.20.0",
-		"eslint-plugin-playwright": "0.22.1",
+		"eslint-plugin-playwright": "1.6.0",
 		"jest": "29.5.x",
 		"playwright-ctrf-json-reporter": "0.0.27",
 		"prettier": "npm:wp-prettier@^2.8.5",
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/admin/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/admin/index.ts
new file mode 100644
index 00000000000..c10470b96dd
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/admin/index.ts
@@ -0,0 +1,105 @@
+/**
+ * External dependencies
+ */
+import { Page } from '@playwright/test';
+import { expect } from '@woocommerce/e2e-utils';
+import {
+	Admin as CoreAdmin,
+	PageUtils,
+	Editor,
+} from '@wordpress/e2e-test-utils-playwright';
+
+type AdminConstructorProps = {
+	page: Page;
+	pageUtils: PageUtils;
+	editor: Editor;
+	wpCoreVersion: number;
+};
+
+export class Admin extends CoreAdmin {
+	wpCoreVersion: number;
+
+	constructor( {
+		page,
+		pageUtils,
+		editor,
+		wpCoreVersion,
+	}: AdminConstructorProps ) {
+		super( { page, pageUtils, editor } );
+		this.wpCoreVersion = wpCoreVersion;
+	}
+
+	async visitWidgetEditor() {
+		await this.page.goto( '/wp-admin/widgets.php' );
+		const closeWelcomeGuide = this.page
+			.getByRole( 'dialog', { name: 'Welcome to block Widgets' } )
+			.getByRole( 'button', { name: 'Close' } );
+		// The welcome guide only shows when it hasn't been dismissed before, so
+		// wait for it briefly and close it only when it actually appears.
+		const guideAppeared = await closeWelcomeGuide
+			.waitFor( { state: 'visible', timeout: 5000 } )
+			.then( () => true )
+			.catch( () => false );
+		if ( guideAppeared ) {
+			await closeWelcomeGuide.click();
+		}
+	}
+
+	async createNewPattern( name: string, synced = true ) {
+		await this.page.goto( '/wp-admin/site-editor.php?postType=wp_block' );
+		await this.page.getByRole( 'button', { name: 'Patterns' } ).click();
+
+		await this.page
+			.getByLabel(
+				this.wpCoreVersion >= 6.8 ? 'Add Pattern' : 'Add New Pattern'
+			)
+			.click();
+
+		await this.page
+			.getByRole( 'menuitem', {
+				name:
+					this.wpCoreVersion >= 6.8
+						? 'Add Pattern'
+						: 'Add New Pattern',
+			} )
+			.click();
+
+		await this.page.getByLabel( 'Name' ).fill( name );
+
+		if ( ! synced ) {
+			// Synced toggle is enabled by default.
+			await this.page.getByLabel( 'Synced' ).click();
+		}
+
+		await this.page.getByRole( 'button', { name: 'Add' } ).click();
+
+		const welcomePopUp = async () => {
+			await this.page
+				.getByRole( 'button', {
+					name: 'Get started',
+				} )
+				.click();
+		};
+
+		const editorLoaded = async () => {
+			await this.page
+				.getByRole( 'heading', {
+					name: 'pattern',
+				} )
+				.waitFor();
+		};
+
+		await Promise.any( [ welcomePopUp(), editorLoaded() ] );
+	}
+
+	/**
+	 * Clicks the 'Save changes' button on an admin page and waits for it to become disabled to ensure the page is saved.
+	 */
+	async saveAdminPage() {
+		const saveButton = this.page.getByRole( 'button', {
+			name: 'Save changes',
+		} );
+		await saveButton.click();
+		await expect( saveButton ).toBeDisabled();
+	}
+}
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/editor/editor-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/editor/editor-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/editor/editor-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/editor/editor-utils.page.ts
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/editor/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/editor/index.ts
new file mode 100644
index 00000000000..015f175c5c8
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/editor/index.ts
@@ -0,0 +1 @@
+export * from './editor-utils.page';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/frontend-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/frontend/frontend-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/frontend/frontend-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/frontend/frontend-utils.page.ts
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/frontend/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/frontend/index.ts
new file mode 100644
index 00000000000..a7b5d426561
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/frontend/index.ts
@@ -0,0 +1 @@
+export * from './frontend-utils.page';
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/local-pickup/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/local-pickup/index.ts
new file mode 100644
index 00000000000..9360f7ee4bf
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/local-pickup/index.ts
@@ -0,0 +1 @@
+export * from './local-pickup-utils.page';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/local-pickup-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/local-pickup/local-pickup-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/local-pickup/local-pickup-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/local-pickup/local-pickup-utils.page.ts
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/mini-cart/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/mini-cart/index.ts
new file mode 100644
index 00000000000..fe6274e8753
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/mini-cart/index.ts
@@ -0,0 +1 @@
+export * from './mini-cart-utils.page';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/mini-cart-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/mini-cart/mini-cart-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/mini-cart/mini-cart-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/mini-cart/mini-cart-utils.page.ts
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/index.ts
new file mode 100644
index 00000000000..91133f30afe
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/index.ts
@@ -0,0 +1 @@
+export * from './performance-utils.page';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/performance-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/performance-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/performance/performance-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/performance-utils.page.ts
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/performance/reports/e2e-cart-performance.json b/plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/reports/e2e-cart-performance.json
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/performance/reports/e2e-cart-performance.json
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/performance/reports/e2e-cart-performance.json
diff --git a/plugins/woocommerce/tests/e2e-pw/utils/blocks/shipping/index.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/shipping/index.ts
new file mode 100644
index 00000000000..bc956bae780
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e-pw/utils/blocks/shipping/index.ts
@@ -0,0 +1 @@
+export * from './shipping-utils.page';
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts b/plugins/woocommerce/tests/e2e-pw/utils/blocks/shipping/shipping-utils.page.ts
similarity index 100%
rename from plugins/woocommerce/client/blocks/tests/e2e/utils/shipping/shipping-utils.page.ts
rename to plugins/woocommerce/tests/e2e-pw/utils/blocks/shipping/shipping-utils.page.ts
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a4154b264a6..71e64ef0c1c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2647,8 +2647,8 @@ importers:
         specifier: 23.20.0
         version: 23.20.0(eslint@8.57.1)(typescript@5.7.3)
       eslint-plugin-playwright:
-        specifier: 0.22.1
-        version: 0.22.1(eslint-plugin-jest@23.20.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)
+        specifier: 1.6.0
+        version: 1.6.0(eslint-plugin-jest@23.20.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)
       jest:
         specifier: 29.5.x
         version: 29.5.0(@types/node@24.12.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.15.24)(@types/node@24.12.2)(typescript@5.7.3))
@@ -14652,15 +14652,6 @@ packages:
       eslint-plugin-jest:
         optional: true

-  eslint-plugin-playwright@0.22.1:
-    resolution: {integrity: sha512-xUQ9mJH+CjifLG6vMowl3r49G/8JvW4G10IqHjc1WO44fffdhLZF/i4Def+U3y6LqUEBp0JAMnWUhEck7ksqrw==}
-    peerDependencies:
-      eslint: '>=7'
-      eslint-plugin-jest: '>=25'
-    peerDependenciesMeta:
-      eslint-plugin-jest:
-        optional: true
-
   eslint-plugin-playwright@1.6.0:
     resolution: {integrity: sha512-tI1E/EDbHT4Fx5KvukUG3RTIT0gk44gvTP8bNwxLCFsUXVM98ZJG5zWU6Om5JOzH9FrmN4AhMu/UKyEsu0ZoDA==}
     engines: {node: '>=16.6.0'}
@@ -28357,7 +28348,7 @@ snapshots:
       '@remote-ui/rpc': 1.4.7
     optionalDependencies:
       '@babel/core': 7.25.7
-      webpack: 5.97.1(@swc/core@1.15.24)(webpack-cli@5.1.4)
+      webpack: 5.97.1(@swc/core@1.15.24)(uglify-js@3.19.3)(webpack-cli@4.10.0)
       webpack-virtual-modules: 0.6.2

   '@sideway/address@4.1.5':
@@ -42239,7 +42230,7 @@ snapshots:
     optionalDependencies:
       eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(jest@29.7.0(@types/node@24.12.2)(babel-plugin-macros@3.1.0)(node-notifier@8.0.2)(ts-node@10.9.2(@swc/core@1.15.24)(@types/node@24.12.2)(typescript@5.7.3)))(typescript@5.7.3)

-  eslint-plugin-playwright@0.22.1(eslint-plugin-jest@23.20.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1):
+  eslint-plugin-playwright@1.6.0(eslint-plugin-jest@23.20.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1):
     dependencies:
       eslint: 8.57.1
       globals: 13.24.0