Commit 5e9c4f38151 for woocommerce

commit 5e9c4f38151e732b8745223cd688339b6a41d589
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date:   Tue Jul 7 23:54:15 2026 +0300

    e2e tests: reorganize analytics specs across serial and parallel pools (#66388)

diff --git a/plugins/woocommerce/changelog/dev-testops-210-analytics-e2e-pools b/plugins/woocommerce/changelog/dev-testops-210-analytics-e2e-pools
new file mode 100644
index 00000000000..a502afc30e3
--- /dev/null
+++ b/plugins/woocommerce/changelog/dev-testops-210-analytics-e2e-pools
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+E2E: reorganize the analytics specs across the core-serial/core-parallel pools — move analytics (with the Overview manual-import-trigger tests folded in) and analytics-overview to core-parallel, keep only analytics-settings serial, and drop the redundant analytics-access smoke test.
diff --git a/plugins/woocommerce/tests/e2e/playwright.config.ts b/plugins/woocommerce/tests/e2e/playwright.config.ts
index 20344f92adf..70f35dce557 100644
--- a/plugins/woocommerce/tests/e2e/playwright.config.ts
+++ b/plugins/woocommerce/tests/e2e/playwright.config.ts
@@ -120,19 +120,17 @@ export const setupProjects = [
  * `core-parallel` by default, except the other-project folders in `nonCoreSpecs`.
  */
 const serialRunSpecs = [
-	// Forces immediate analytics-import mode by flipping the global
-	// `woocommerce_analytics_scheduled_import` option, and needs it to stay
-	// immediate while it creates orders and drains their imports. In
-	// `core-parallel` that would race `analytics-settings.spec.ts` — which runs in
-	// that pool and toggles the same option — over the shared store and Action
-	// Scheduler queue. Its numeric assertions are scoped to its own products, so
-	// they no longer depend on store-wide totals.
-	'**/tests/analytics/analytics-data.spec.ts',
-	// Asserts store-wide `$0.00 / Orders 0`, polluted by concurrent orders.
-	'**/tests/analytics/analytics-access.spec.ts',
-	// Mutates the shared admin's `woocommerce_meta.dashboard_sections` and flips the
-	// global `woocommerce_analytics_scheduled_import` option (racing analytics-settings).
-	'**/tests/analytics/analytics-overview.spec.ts',
+	// Toggles the global `woocommerce_analytics_scheduled_import` option to
+	// exercise the Settings-page scheduled/immediate switch. Kept serial because,
+	// as a standalone file, running it in `core-parallel` would race
+	// `analytics.spec.ts` — which also toggles that option — across workers.
+	// (The other analytics specs are parallel: `analytics` owns its own
+	// import-mode toggles plus the Overview manual-trigger tests in one file, so
+	// those serialise within a single worker; `analytics-overview` only mutates
+	// the admin's own `dashboard_sections` meta. No other parallel spec depends on
+	// the order-import mode, and this serial job never runs concurrently with the
+	// parallel one.)
+	'**/tests/analytics/analytics-settings.spec.ts',
 	// Flips the global `woocommerce_default_customer_address` (geolocation) and
 	// `woocommerce_enable_ajax_add_to_cart` settings, which change add-to-cart
 	// behavior for every other worker. (`cart.spec.ts` runs in core-parallel — it
diff --git a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-access.spec.ts b/plugins/woocommerce/tests/e2e/tests/analytics/analytics-access.spec.ts
deleted file mode 100644
index 2121bd6efde..00000000000
--- a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-access.spec.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * External dependencies
- */
-import { test, expect } from '@playwright/test';
-
-/**
- * Internal dependencies
- */
-import { tags } from '../../fixtures/fixtures';
-import { ADMIN_STATE_PATH } from '../../playwright.config';
-
-test.describe( 'WooCommerce Home', () => {
-	test.use( { storageState: ADMIN_STATE_PATH } );
-
-	test(
-		'Can access Analytics Reports from Stats Overview',
-		{ tag: [ tags.NOT_E2E ] },
-		async ( { page } ) => {
-			await test.step( 'Navigate to the WooCommerce Home page', async () => {
-				await page.goto( 'wp-admin/admin.php?page=wc-admin' );
-				await expect(
-					page.getByText( 'Stats overview' )
-				).toBeVisible();
-				await expect(
-					page.getByRole( 'menuitem', {
-						name: 'Total sales $0.00 No change',
-					} )
-				).toBeVisible();
-				await expect(
-					page.getByRole( 'link', { name: 'View detailed stats' } )
-				).toBeVisible();
-			} );
-
-			await test.step( 'Navigate to Revenue Report', async () => {
-				await page
-					.getByRole( 'menuitem', {
-						name: 'Total sales $0.00 No change',
-					} )
-					.click();
-				await expect(
-					page.getByRole( 'heading', { name: 'Revenue' } ).first()
-				).toBeVisible();
-				await expect( page.url() ).toContain(
-					'admin.php?page=wc-admin&chart=total_sales&period=today&compare=previous_period&path=%2Fanalytics%2Frevenue'
-				);
-			} );
-
-			await test.step( 'Navigate to Orders Report', async () => {
-				await page.goto( 'wp-admin/admin.php?page=wc-admin' );
-				await expect(
-					page.getByRole( 'menuitem', {
-						name: 'Orders 0 No change from',
-					} )
-				).toBeVisible();
-				await page
-					.getByRole( 'menuitem', {
-						name: 'Orders 0 No change from',
-					} )
-					.click();
-				await expect(
-					page.getByRole( 'heading', { name: 'Orders' } ).first()
-				).toBeVisible();
-				await expect( page.url() ).toContain(
-					'admin.php?page=wc-admin&chart=orders_count&period=today&compare=previous_period&path=%2Fanalytics%2Forders'
-				);
-			} );
-
-			await test.step( 'Navigate to Analytics Overview', async () => {
-				await page.goto( 'wp-admin/admin.php?page=wc-admin' );
-				await expect(
-					page.getByRole( 'link', { name: 'View detailed stats' } )
-				).toBeVisible();
-				await page
-					.getByRole( 'link', { name: 'View detailed stats' } )
-					.click();
-				await expect(
-					page.getByRole( 'heading', { name: 'Overview' } ).first()
-				).toBeVisible();
-				await expect( page.url() ).toContain(
-					'admin.php?page=wc-admin&path=%2Fanalytics%2Foverview'
-				);
-			} );
-		}
-	);
-} );
diff --git a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-overview.spec.ts b/plugins/woocommerce/tests/e2e/tests/analytics/analytics-overview.spec.ts
index 7d72cf41c36..e49a6ea617b 100644
--- a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-overview.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/analytics/analytics-overview.spec.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { test, expect, request } from '@playwright/test';
+import { test, expect } from '@playwright/test';
 import type { Page, Locator } from '@playwright/test';

 /**
@@ -10,7 +10,6 @@ import type { Page, Locator } from '@playwright/test';
 import { admin } from '../../test-data/data';
 import { tags } from '../../fixtures/fixtures';
 import { ADMIN_STATE_PATH } from '../../playwright.config';
-import { setOption } from '../../utils/options';

 const EXPECTED_SECTION_HEADERS = [ 'Performance', 'Charts', 'Leaderboards' ];

@@ -303,89 +302,3 @@ test.describe(
 		} );
 	}
 );
-
-test.describe(
-	'Analytics Overview - Manual Import Trigger',
-	{ tag: [ tags.PAYMENTS, tags.SERVICES ] },
-	() => {
-		test.use( { storageState: ADMIN_STATE_PATH } );
-
-		test.beforeAll( async ( { browser } ) => {
-			page = await browser.newPage();
-		} );
-
-		test.beforeEach( async () => {
-			await test.step( `Go to Analytics > Overview`, async () => {
-				await page.goto(
-					'wp-admin/admin.php?page=wc-admin&path=%2Fanalytics%2Foverview'
-				);
-			} );
-		} );
-
-		test.afterAll( async () => {
-			await page.close();
-		} );
-
-		test( 'should show manual update trigger in scheduled mode', async ( {
-			baseURL,
-		} ) => {
-			// Set to scheduled mode
-			await setOption(
-				request,
-				baseURL,
-				'woocommerce_analytics_scheduled_import',
-				'yes'
-			);
-
-			// Reload the page
-			await page.reload();
-
-			// Verify import status bar is visible
-			await expect( page.getByText( 'Data status' ) ).toBeVisible();
-			// Verify "Update now" button is visible
-			const updateButton = page.getByRole( 'button', {
-				name: 'Manually trigger analytics data import',
-			} );
-			await expect( updateButton ).toBeVisible();
-
-			// Click "Update now" button
-			const responsePromise = page.waitForResponse(
-				( response ) =>
-					response
-						.url()
-						.includes( '/wc-analytics/imports/trigger' ) &&
-					response.ok()
-			);
-
-			await updateButton.click();
-
-			// Verify button shows loading state (isBusy)
-			// After clicking, the aria-label changes to "Analytics data import in progress"
-			const busyButton = page.getByRole( 'button', {
-				name: 'Analytics data import in progress',
-			} );
-			await expect( busyButton ).toBeDisabled();
-
-			// Wait for API response
-			await responsePromise;
-		} );
-
-		test( 'should hide manual update trigger in immediate mode', async ( {
-			baseURL,
-		} ) => {
-			// Set to immediate mode
-			await setOption(
-				request,
-				baseURL,
-				'woocommerce_analytics_scheduled_import',
-				'no'
-			);
-
-			// Reload the page
-			await page.reload();
-
-			// Verify import status bar wrapper is NOT rendered
-			await expect( page.getByText( 'Data status' ) ).toBeHidden();
-		} );
-	}
-);
diff --git a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-data.spec.ts b/plugins/woocommerce/tests/e2e/tests/analytics/analytics.spec.ts
similarity index 87%
rename from plugins/woocommerce/tests/e2e/tests/analytics/analytics-data.spec.ts
rename to plugins/woocommerce/tests/e2e/tests/analytics/analytics.spec.ts
index 7041d85e6dd..415569b7ffd 100644
--- a/plugins/woocommerce/tests/e2e/tests/analytics/analytics-data.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/analytics/analytics.spec.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import type { Page } from '@playwright/test';
+import { request as apiRequest, type Page } from '@playwright/test';
 import {
 	WC_ADMIN_API_PATH,
 	WC_API_PATH,
@@ -12,6 +12,7 @@ import {
  */
 import { expect, tags, test as baseTest } from '../../fixtures/fixtures';
 import { ADMIN_STATE_PATH } from '../../playwright.config';
+import { setOption } from '../../utils/options';

 /**
  * Escape a string for literal use inside a RegExp.
@@ -632,3 +633,85 @@ test(
 		).toBeVisible();
 	}
 );
+
+// Overview manual-import trigger. Folded in here so its `scheduled_import`
+// toggle serialises with the data suite's within one core-parallel worker.
+test.describe(
+	'manual import trigger',
+	{ tag: [ tags.PAYMENTS, tags.SERVICES ] },
+	() => {
+		test.beforeEach( async ( { page } ) => {
+			await page.goto(
+				'wp-admin/admin.php?page=wc-admin&path=%2Fanalytics%2Foverview'
+			);
+		} );
+
+		test( 'shows the manual update trigger in scheduled mode', async ( {
+			page,
+			baseURL,
+		} ) => {
+			// Switch to scheduled mode, where the manual "Update now" trigger is
+			// offered.
+			await setOption(
+				apiRequest,
+				baseURL,
+				'woocommerce_analytics_scheduled_import',
+				'yes'
+			);
+
+			// Enabling scheduled mode schedules a recurring batch import whose
+			// first run is "now" (OrdersScheduler::schedule_recurring_batch_processor
+			// → as_schedule_recurring_action( time(), … )). Until that batch runs,
+			// the status bar renders its busy "in progress" state instead of the
+			// "Update now" trigger, and wp-env has no cron to run it. So drain the
+			// Action Scheduler queue via ?process-waiting-actions — exactly as this
+			// spec's data setup does — until the batch clears and the button
+			// settles to idle. Poll because the reload races the drain.
+			const updateButton = page.getByRole( 'button', {
+				name: 'Manually trigger analytics data import',
+			} );
+			await expect( async () => {
+				await page.request.get( '?process-waiting-actions' );
+				await page.reload();
+				await expect( page.getByText( 'Data status' ) ).toBeVisible();
+				await expect( updateButton ).toBeVisible( { timeout: 2_000 } );
+			} ).toPass( {
+				timeout: 30_000,
+				intervals: [ 1_000, 2_000, 3_000 ],
+			} );
+
+			// Clicking it fires the import and flips the button to a busy state.
+			const responsePromise = page.waitForResponse(
+				( response ) =>
+					response
+						.url()
+						.includes( '/wc-analytics/imports/trigger' ) &&
+					response.ok()
+			);
+			await updateButton.click();
+
+			const busyButton = page.getByRole( 'button', {
+				name: 'Analytics data import in progress',
+			} );
+			await expect( busyButton ).toBeDisabled();
+			await responsePromise;
+		} );
+
+		test( 'hides the manual update trigger in immediate mode', async ( {
+			page,
+			baseURL,
+		} ) => {
+			// In immediate mode there is no batch import to trigger, so the
+			// status bar (and its button) are not rendered.
+			await setOption(
+				apiRequest,
+				baseURL,
+				'woocommerce_analytics_scheduled_import',
+				'no'
+			);
+			await page.reload();
+
+			await expect( page.getByText( 'Data status' ) ).toBeHidden();
+		} );
+	}
+);