Commit 162a9d4b434 for woocommerce

commit 162a9d4b43497e58ec758c3861cb8c2d6c22c413
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date:   Wed Jul 8 21:01:20 2026 +0300

    Split wp-env test config into lean PHP-unit and full E2E configs (#66401)

diff --git a/.github/workflows/scripts/override-wp-env-plugins.js b/.github/workflows/scripts/override-wp-env-plugins.js
index 363a23296cb..95cc0905614 100644
--- a/.github/workflows/scripts/override-wp-env-plugins.js
+++ b/.github/workflows/scripts/override-wp-env-plugins.js
@@ -20,10 +20,6 @@ if ( ! WP_ENV_CONFIG_PATH ) {

 const artifactUrl = `https://github.com/woocommerce/woocommerce/releases/download/${ RELEASE_TAG }/${ ARTIFACT_NAME }`;

-const configPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.test.json`;
-console.log( `Reading ${ configPath }` );
-const wpEnvConfig = JSON.parse( fs.readFileSync( configPath, 'utf8' ) );
-
 // wp-env names an installed plugin's folder after the source basename, so
 // installing WooCommerce straight from the release URL would create a
 // `woocommerce-trunk-nightly` folder - a name no real install produces and which
@@ -34,49 +30,81 @@ const wpEnvConfig = JSON.parse( fs.readFileSync( configPath, 'utf8' ) );
 // the source entry from the plugin lists. Mapped plugins are not auto-activated,
 // so `tests/e2e/bin/test-env-setup.sh` activates WooCommerce explicitly.
 const wooCommerceEntries = [ '.', '../woocommerce' ];
-
-let removed = 0;
-const withoutWooCommerce = ( plugins ) => {
-	if ( ! Array.isArray( plugins ) ) {
-		return plugins;
-	}
-	const filtered = plugins.filter(
-		( entry ) => ! wooCommerceEntries.includes( entry )
-	);
-	removed += plugins.length - filtered.length;
-	return filtered;
-};
-
 const wooCommerceMapping = {
 	'wp-content/plugins/woocommerce': artifactUrl,
 };

-// `.wp-env.test.json` is a single-container-set config (testsEnvironment:
-// false), so plugins and mappings live at the top level - no env.tests nesting.
-const overrideConfig = {
-	plugins: withoutWooCommerce( wpEnvConfig.plugins ),
-	mappings: wooCommerceMapping,
-};
+// The PHP-unit jobs run against the lean `.wp-env.test.json`; the E2E/API/
+// performance jobs run against the full `.wp-env.e2e.json`. A given CI job starts
+// wp-env with exactly one of these via `--config`, and wp-env reads the override
+// file whose basename matches (`.wp-env.<name>.override.json`). We don't know here
+// which config the calling job uses, so write an override for every config present
+// - the ones that aren't the job's active config are simply ignored by wp-env.
+const configFiles = [ '.wp-env.test.json', '.wp-env.e2e.json' ];
+
+let processed = 0;
+
+for ( const configFile of configFiles ) {
+	const configPath = `${ WP_ENV_CONFIG_PATH }/${ configFile }`;
+	if ( ! fs.existsSync( configPath ) ) {
+		console.log( `Skipping ${ configPath } (not found)` );
+		continue;
+	}
+
+	console.log( `Reading ${ configPath }` );
+	const wpEnvConfig = JSON.parse( fs.readFileSync( configPath, 'utf8' ) );

-if ( removed === 0 ) {
+	let removed = 0;
+	const withoutWooCommerce = ( plugins ) => {
+		if ( ! Array.isArray( plugins ) ) {
+			return plugins;
+		}
+		const filtered = plugins.filter(
+			( entry ) => ! wooCommerceEntries.includes( entry )
+		);
+		removed += plugins.length - filtered.length;
+		return filtered;
+	};
+
+	// These are single-container-set configs (testsEnvironment: false), so plugins
+	// and mappings live at the top level - no env.tests nesting.
+	const overrideConfig = {
+		plugins: withoutWooCommerce( wpEnvConfig.plugins ),
+		mappings: wooCommerceMapping,
+	};
+
+	if ( removed === 0 ) {
+		console.error(
+			`No WooCommerce source entry (${ wooCommerceEntries.join(
+				' or '
+			) }) found in ${ configPath }. The artifact would not land at ` +
+				`wp-content/plugins/woocommerce - the plugin layout likely changed. Aborting.`
+		);
+		process.exit( 1 );
+	}
+
+	console.log(
+		`Removed ${ removed } WooCommerce source entr${
+			removed === 1 ? 'y' : 'ies'
+		} from ${ configFile }; mapping ${ artifactUrl } -> wp-content/plugins/woocommerce`
+	);
+
+	const overrideConfigPath = configPath.endsWith( '.json' )
+		? configPath.slice( 0, -5 ) + '.override.json'
+		: configPath;
+	console.log( `Saving ${ overrideConfigPath }` );
+	fs.writeFileSync(
+		overrideConfigPath,
+		JSON.stringify( overrideConfig, null, 2 )
+	);
+	processed++;
+}
+
+if ( processed === 0 ) {
 	console.error(
-		`No WooCommerce source entry (${ wooCommerceEntries.join(
-			' or '
-		) }) found in ${ configPath }. The artifact would not land at ` +
-			`wp-content/plugins/woocommerce - the plugin layout likely changed. Aborting.`
+		`No wp-env config files (${ configFiles.join(
+			', '
+		) }) found under ${ WP_ENV_CONFIG_PATH }. Aborting.`
 	);
 	process.exit( 1 );
 }
-
-console.log(
-	`Removed ${ removed } WooCommerce source entr${
-		removed === 1 ? 'y' : 'ies'
-	}; mapping ${ artifactUrl } -> wp-content/plugins/woocommerce`
-);
-
-const overrideConfigPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.test.override.json`;
-console.log( `Saving ${ overrideConfigPath }` );
-fs.writeFileSync(
-	overrideConfigPath,
-	JSON.stringify( overrideConfig, null, 2 )
-);
diff --git a/.github/workflows/scripts/verify-php-version.sh b/.github/workflows/scripts/verify-php-version.sh
deleted file mode 100644
index a1dfb75282d..00000000000
--- a/.github/workflows/scripts/verify-php-version.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-
-#
-# Verify that the PHP version in the launched WP ENV environment is equal to expected.
-#
-
-cd $GITHUB_WORKSPACE/plugins/woocommerce
-ACTUAL_PHP_VERSION=$(pnpm exec wp-env --config .wp-env.test.json run cli wp --info | grep 'PHP version:')
-EXIT_CODE=''
-
-echo "PHP version found in WP Env environment: \"$ACTUAL_PHP_VERSION\""
-echo "Expected PHP version: \"$EXPECTED_PHP_VERSION\""
-
-if [[ $ACTUAL_PHP_VERSION == *"$EXPECTED_PHP_VERSION"* ]]
-    then
-        EXIT_CODE=0
-    else
-        EXIT_CODE=1
-fi
-
-exit $EXIT_CODE
diff --git a/.gitignore b/.gitignore
index bc0f74252f8..3f218e0dd16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,6 +60,7 @@ package-lock.json
 # wp-env config
 .wp-env.override.json
 .wp-env.test.override.json
+.wp-env.e2e.override.json

 # Unit tests
 tmp/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 86de00a7142..004ed900d59 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -86,7 +86,7 @@ pnpm test:unit:env:watch
 # E2E tests (requires Docker)
 cd plugins/woocommerce
 # Start the E2E environment
-pnpm env:test
+pnpm env:e2e
 # Run Playwright E2E tests
 pnpm test:e2e

diff --git a/plugins/woocommerce/.gitignore b/plugins/woocommerce/.gitignore
index 96e903aa9b5..47c9eac6584 100644
--- a/plugins/woocommerce/.gitignore
+++ b/plugins/woocommerce/.gitignore
@@ -33,3 +33,4 @@ i18n/languages/woocommerce.pot
 # Environment files
 .wp-env.override.json
 .wp-env.test.override.json
+.wp-env.e2e.override.json
diff --git a/plugins/woocommerce/.wp-env.e2e.json b/plugins/woocommerce/.wp-env.e2e.json
new file mode 100644
index 00000000000..2b1620ca158
--- /dev/null
+++ b/plugins/woocommerce/.wp-env.e2e.json
@@ -0,0 +1,47 @@
+{
+	"core": "https://wordpress.org/wordpress-latest.zip",
+	"phpVersion": "8.1",
+	"testsEnvironment": false,
+	"port": 8086,
+	"plugins": [
+		".",
+		"./tests/e2e/test-plugins/wc-email-template-sync-test-helper",
+		"https://downloads.wordpress.org/plugin/akismet.zip",
+		"https://github.com/WP-API/Basic-Auth/archive/master.zip",
+		"https://downloads.wordpress.org/plugin/wp-mail-logging.zip",
+		"https://downloads.wordpress.org/plugin/wordpress-importer.0.8.zip"
+	],
+	"themes": [],
+	"config": {
+		"JETPACK_AUTOLOAD_DEV": true,
+		"WP_DEBUG": false,
+		"SCRIPT_DEBUG": false,
+		"WP_DEBUG_LOG": true,
+		"WP_DEBUG_DISPLAY": false,
+		"WP_TESTS_DOMAIN": "localhost",
+		"ALTERNATE_WP_CRON": false
+	},
+	"lifecycleScripts": {
+		"afterStart": "./tests/e2e/bin/test-env-setup.sh",
+		"afterClean": "./tests/e2e/bin/test-env-setup.sh"
+	},
+	"mappings": {
+		"wp-content/plugins/e2e-test-helpers": "./tests/e2e/bin",
+		"test-data/images/": "./tests/e2e/test-data/images/",
+		"wp-content/themes/emptytheme": "./tests/e2e/themes/blocks/emptytheme",
+		"wp-content/themes/theme-with-woo-templates": "./tests/e2e/themes/blocks/theme-with-woo-templates",
+		"wp-content/themes/storefront-child__block-notices-filter": "./tests/e2e/themes/blocks/storefront-child__block-notices-filter",
+		"wp-content/themes/storefront-child__block-notices-template": "./tests/e2e/themes/blocks/storefront-child__block-notices-template",
+		"wp-content/themes/storefront-child__classic-notices-template": "./tests/e2e/themes/blocks/storefront-child__classic-notices-template",
+		"wp-content/themes/storefront-child__with-block-template-part": "./tests/e2e/themes/blocks/storefront-child__with-block-template-part",
+		"wp-content/themes/storefront-child__with-block-template-part-support": "./tests/e2e/themes/blocks/storefront-child__with-block-template-part-support",
+		"wp-content/themes/twentytwentyfour-child__block-notices-filter": "./tests/e2e/themes/blocks/twentytwentyfour-child__block-notices-filter",
+		"wp-content/themes/twentytwentyfour-child__block-notices-template": "./tests/e2e/themes/blocks/twentytwentyfour-child__block-notices-template",
+		"wp-content/themes/twentytwentyfour-child__classic-notices-template": "./tests/e2e/themes/blocks/twentytwentyfour-child__classic-notices-template",
+		"wp-content/themes/twentytwentyfour": "https://downloads.wordpress.org/theme/twentytwentyfour.latest-stable.zip",
+		"wp-content/plugins/woocommerce/blocks-bin": "./client/blocks/bin",
+		"wp-content/plugins/woocommerce/blocks-bin/playwright": "./tests/e2e/bin/blocks",
+		"wp-content/plugins/woocommerce-blocks-test-plugins": "./tests/e2e/test-plugins/blocks",
+		"wp-content/plugins/woocommerce/i18n/languages": "./i18n/languages"
+	}
+}
diff --git a/plugins/woocommerce/.wp-env.test.json b/plugins/woocommerce/.wp-env.test.json
index 2b1620ca158..95b031bd753 100644
--- a/plugins/woocommerce/.wp-env.test.json
+++ b/plugins/woocommerce/.wp-env.test.json
@@ -2,16 +2,12 @@
 	"core": "https://wordpress.org/wordpress-latest.zip",
 	"phpVersion": "8.1",
 	"testsEnvironment": false,
-	"port": 8086,
-	"plugins": [
-		".",
-		"./tests/e2e/test-plugins/wc-email-template-sync-test-helper",
-		"https://downloads.wordpress.org/plugin/akismet.zip",
-		"https://github.com/WP-API/Basic-Auth/archive/master.zip",
-		"https://downloads.wordpress.org/plugin/wp-mail-logging.zip",
-		"https://downloads.wordpress.org/plugin/wordpress-importer.0.8.zip"
+	"port": 8186,
+	"plugins": [ "." ],
+	"themes": [
+		"https://downloads.wordpress.org/theme/storefront.zip",
+		"https://downloads.wordpress.org/theme/twentytwentytwo.zip"
 	],
-	"themes": [],
 	"config": {
 		"JETPACK_AUTOLOAD_DEV": true,
 		"WP_DEBUG": false,
@@ -22,26 +18,7 @@
 		"ALTERNATE_WP_CRON": false
 	},
 	"lifecycleScripts": {
-		"afterStart": "./tests/e2e/bin/test-env-setup.sh",
-		"afterClean": "./tests/e2e/bin/test-env-setup.sh"
-	},
-	"mappings": {
-		"wp-content/plugins/e2e-test-helpers": "./tests/e2e/bin",
-		"test-data/images/": "./tests/e2e/test-data/images/",
-		"wp-content/themes/emptytheme": "./tests/e2e/themes/blocks/emptytheme",
-		"wp-content/themes/theme-with-woo-templates": "./tests/e2e/themes/blocks/theme-with-woo-templates",
-		"wp-content/themes/storefront-child__block-notices-filter": "./tests/e2e/themes/blocks/storefront-child__block-notices-filter",
-		"wp-content/themes/storefront-child__block-notices-template": "./tests/e2e/themes/blocks/storefront-child__block-notices-template",
-		"wp-content/themes/storefront-child__classic-notices-template": "./tests/e2e/themes/blocks/storefront-child__classic-notices-template",
-		"wp-content/themes/storefront-child__with-block-template-part": "./tests/e2e/themes/blocks/storefront-child__with-block-template-part",
-		"wp-content/themes/storefront-child__with-block-template-part-support": "./tests/e2e/themes/blocks/storefront-child__with-block-template-part-support",
-		"wp-content/themes/twentytwentyfour-child__block-notices-filter": "./tests/e2e/themes/blocks/twentytwentyfour-child__block-notices-filter",
-		"wp-content/themes/twentytwentyfour-child__block-notices-template": "./tests/e2e/themes/blocks/twentytwentyfour-child__block-notices-template",
-		"wp-content/themes/twentytwentyfour-child__classic-notices-template": "./tests/e2e/themes/blocks/twentytwentyfour-child__classic-notices-template",
-		"wp-content/themes/twentytwentyfour": "https://downloads.wordpress.org/theme/twentytwentyfour.latest-stable.zip",
-		"wp-content/plugins/woocommerce/blocks-bin": "./client/blocks/bin",
-		"wp-content/plugins/woocommerce/blocks-bin/playwright": "./tests/e2e/bin/blocks",
-		"wp-content/plugins/woocommerce-blocks-test-plugins": "./tests/e2e/test-plugins/blocks",
-		"wp-content/plugins/woocommerce/i18n/languages": "./i18n/languages"
+		"afterStart": "./tests/php/bin/test-env-setup.sh",
+		"afterClean": "./tests/php/bin/test-env-setup.sh"
 	}
 }
diff --git a/plugins/woocommerce/changelog/testops-207-split-wp-env-configs b/plugins/woocommerce/changelog/testops-207-split-wp-env-configs
new file mode 100644
index 00000000000..092e834ed75
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-207-split-wp-env-configs
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Split the shared wp-env test config into a lean PHP-unit config (.wp-env.test.json) and a full E2E config (.wp-env.e2e.json) so the PHP-unit environment no longer provisions E2E-only plugins, themes, and setup.
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index 4094d904319..6f93d99570b 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -33,11 +33,16 @@
 		"env:dev:start": "pnpm wp-env start --update",
 		"env:dev:stop": "pnpm wp-env stop",
 		"env:down": "pnpm env:dev:stop",
+		"env:e2e": "pnpm env:e2e:start",
+		"env:e2e:destroy": "pnpm wp-env:e2e destroy",
+		"env:e2e:restart": "pnpm wp-env:e2e destroy --force && pnpm wp-env:e2e start --update",
+		"env:e2e:start": "pnpm wp-env:e2e start --update",
+		"env:e2e:stop": "pnpm wp-env:e2e stop",
 		"env:performance-init": "./tests/performance/bin/init-environment.sh",
-		"env:perf": "pnpm env:test:start && pnpm env:performance-init",
+		"env:perf": "pnpm env:e2e:start && pnpm env:performance-init",
 		"env:restart": "pnpm env:dev:restart",
 		"env:start": "pnpm env:dev:start",
-		"env:start:blocks": "pnpm env:test:start && bash ./tests/e2e/bin/blocks/test-env-setup.sh && pnpm playwright install chromium",
+		"env:start:blocks": "pnpm env:e2e:start && bash ./tests/e2e/bin/blocks/test-env-setup.sh && pnpm playwright install chromium",
 		"env:stop": "pnpm env:dev:stop",
 		"env:test": "pnpm env:test:start",
 		"env:test:destroy": "pnpm wp-env:test destroy",
@@ -115,6 +120,7 @@
 		"watch:build:project:classic-assets": "pnpm --filter='@woocommerce/classic-assets' watch:build",
 		"watch:build:project:packages": "node ./bin/watch-packages.mjs",
 		"wp-env": "wp-env",
+		"wp-env:e2e": "wp-env --config .wp-env.e2e.json",
 		"wp-env:test": "wp-env --config .wp-env.test.json"
 	},
 	"lint-staged": {
@@ -295,12 +301,12 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"events": [
 						"pull_request",
@@ -332,12 +338,12 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"events": [
 						"pull_request",
@@ -366,12 +372,12 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"events": [
 						"pull_request",
@@ -401,7 +407,7 @@
 						"core-e2e-gutenberg"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "gutenberg-stable-e2e-report",
@@ -426,7 +432,7 @@
 						"core-e2e-gutenberg"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "gutenberg-nightly-e2e-report",
@@ -452,7 +458,7 @@
 						"core-e2e-hpos-disabled"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "core-e2e-reports-hpos-disabled",
@@ -475,7 +481,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"phpVersion": "8.5"
 						}
@@ -507,7 +513,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"phpVersion": "8.5"
 						}
@@ -539,7 +545,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"wpVersion": "latest-1"
 						}
@@ -571,7 +577,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"wpVersion": "latest-1"
 						}
@@ -604,7 +610,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"wpVersion": "prerelease"
 						}
@@ -637,7 +643,7 @@
 						"@woocommerce/e2e-utils-playwright"
 					],
 					"testEnv": {
-						"start": "env:test --debug",
+						"start": "env:e2e --debug",
 						"config": {
 							"wpVersion": "prerelease"
 						}
@@ -669,13 +675,13 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
 					"onlyForDependencies": [],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"events": [
 						"pull_request",
@@ -703,7 +709,7 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -713,7 +719,7 @@
 						"core-api-hpos-disabled"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "core-api-report-hpos-disabled",
@@ -768,7 +774,7 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.test.json",
+						".wp-env.e2e.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -778,7 +784,7 @@
 						"core-api-object-cache"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "core-api-report-object-cache",
@@ -818,7 +824,7 @@
 						"templates/**/*.php",
 						"templates/**/*.html",
 						"tests/metrics/**",
-						".wp-env.test.json"
+						".wp-env.e2e.json"
 					],
 					"onlyForDependencies": [
 						"@woocommerce/admin-library",
@@ -1077,7 +1083,7 @@
 						"core-e2e"
 					],
 					"testEnv": {
-						"start": "env:test --debug"
+						"start": "env:e2e --debug"
 					},
 					"report": {
 						"resultsBlobName": "core-e2e-report",
diff --git a/plugins/woocommerce/tests/e2e/README.md b/plugins/woocommerce/tests/e2e/README.md
index e21ec87d459..c1e2fce9825 100644
--- a/plugins/woocommerce/tests/e2e/README.md
+++ b/plugins/woocommerce/tests/e2e/README.md
@@ -36,12 +36,12 @@ Start in the repository root folder:
 - `pnpm install` (installs dependencies; PNPM uses the pinned Node version automatically)
 - `pnpm --filter='@woocommerce/plugin-woocommerce' build` (builds WooCommerce locally)
 - `cd plugins/woocommerce` (changes into the WooCommerce plugin folder)
-- `pnpm env:test` (starts the `wp-env` based test environment)
+- `pnpm env:e2e` (starts the `wp-env` based E2E test environment)
 - `pnpm test:e2e` (runs all the tests in headless mode)

 To re-create the environment for a fresh state:

-`pnpm env:test:restart` (resets and restarts the test environment)
+`pnpm env:e2e:restart` (resets and restarts the E2E test environment)

 You can refer to the pnpm scripts in the `package.json` file for more commands. Check out the `env:some-command` scripts
 for managing the `wp-env` environment.
@@ -70,8 +70,8 @@ run `pnpm playwright test --help`

 ## Test environment

-The e2e test environment configuration can be found in the `.wp-env.test.json` file in the `plugins/woocommerce`
-folder (the `.wp-env.json` file configures the separate dev environment).
+The e2e test environment configuration can be found in the `.wp-env.e2e.json` file in the `plugins/woocommerce`
+folder (the `.wp-env.json` file configures the separate dev environment, and `.wp-env.test.json` the lean PHP-unit environment).

 For more information on how to configure the test environment for `wp-env`, please check out
 the official [documentation](https://github.com/WordPress/gutenberg/tree/trunk/packages/env).
@@ -169,7 +169,7 @@ read: [Playwright Best Practices](https://playwright.dev/docs/best-practices).

 ## Test helper plugins

-Some E2E suites need fixture mechanisms that can't be expressed cleanly with REST or WP-CLI alone — for example, filter-driven content overrides, server-side event mirroring, or synchronous triggers for normally-scheduled jobs. These ship as small PHP plugins under `tests/e2e/test-plugins/`, mounted via `.wp-env.test.json`'s `plugins` array.
+Some E2E suites need fixture mechanisms that can't be expressed cleanly with REST or WP-CLI alone — for example, filter-driven content overrides, server-side event mirroring, or synchronous triggers for normally-scheduled jobs. These ship as small PHP plugins under `tests/e2e/test-plugins/`, mounted via `.wp-env.e2e.json`'s `plugins` array.

 ### `wc-email-template-sync-test-helper`

@@ -182,7 +182,7 @@ Powers the `tests/email-editor/update-propagation/` suite (RSM-146). Exposes:

 The plugin is dormant when its driving options are empty. It has a `WP_DEBUG` plus `X-Playwright` header safety rail to prevent accidental activation outside test contexts.

-If a test fails with `404` on `/wp-json/wc-email-test-helper/v1/health`, the plugin isn't loaded — run `pnpm env:test:restart`.
+If a test fails with `404` on `/wp-json/wc-email-test-helper/v1/health`, the plugin isn't loaded — run `pnpm env:e2e:restart`.

 The PR-tier subset of these tests can be run locally with:

diff --git a/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh b/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
index cf137f2cf94..6b6db7c6e71 100755
--- a/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
@@ -1,9 +1,9 @@
 #!/usr/bin/env bash
 script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

-# Command prefix for running wp-cli against the single-container test environment
-# (started via `wp-env --config .wp-env.test.json`, whose container is `cli`).
-wp_cli="wp-env --config .wp-env.test.json run cli"
+# Command prefix for running wp-cli against the single-container E2E environment
+# (started via `wp-env --config .wp-env.e2e.json`, whose container is `cli`).
+wp_cli="wp-env --config .wp-env.e2e.json run cli"

 # Remove the database snapshot if it exists.
 $wp_cli -- rm -f blocks_e2e.sql
diff --git a/plugins/woocommerce/tests/e2e/bin/install-plugin.sh b/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
index 754243765ca..d160762cd95 100755
--- a/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
+++ b/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
@@ -17,9 +17,9 @@ if [[ -z "$PLUGIN_SLUG" ]]; then
 	exit 1
 fi

-# Command prefix for running wp-cli against the single-container test environment
-# (started via `wp-env --config .wp-env.test.json`, whose container is `cli`).
-wp_cli="pnpm wp-env:test run cli"
+# Command prefix for running wp-cli against the single-container E2E environment
+# (started via `wp-env --config .wp-env.e2e.json`, whose container is `cli`).
+wp_cli="pnpm wp-env:e2e run cli"

 echo "Installing $PLUGIN_NAME from $PLUGIN_REPOSITORY"
 download_url=$( curl -s "https://api.github.com/repos/$PLUGIN_REPOSITORY/releases/latest" | grep browser_download_url | cut -d '"' -f 4 )
diff --git a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
index 74205e86264..15ae18fb2d6 100755
--- a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
@@ -1,12 +1,12 @@
 #!/usr/bin/env bash

-# Command prefix for running wp-cli against the single-container test environment
-# (started via `wp-env --config .wp-env.test.json`, whose container is `cli`).
+# Command prefix for running wp-cli against the single-container E2E environment
+# (started via `wp-env --config .wp-env.e2e.json`, whose container is `cli`).
 # The CI fast-path below re-runs this script inside the container with the prefix
 # blanked out (WP_CLI_PREFIX=), so each command runs as a bare `wp …` in a single
 # container exec instead of one `wp-env run` round-trip per command.
-WP_ENV_TEST_CMD="wp-env --config .wp-env.test.json"
-WP_CLI_PREFIX="${WP_CLI_PREFIX-$WP_ENV_TEST_CMD run cli}"
+WP_ENV_CMD="wp-env --config .wp-env.e2e.json"
+WP_CLI_PREFIX="${WP_CLI_PREFIX-$WP_ENV_CMD run cli}"

 if [ ! -z ${CI+y} ]; then
     # In CI we execute the setup in a single container call, while in dev
@@ -15,8 +15,8 @@ if [ ! -z ${CI+y} ]; then
     echo -e '--> Dispatching script execution into cli\n'
     # Source from the e2e-test-helpers directory mount; a single-file mount of this
     # script can surface as an empty file under Docker gRPC FUSE.
-    $WP_ENV_TEST_CMD run --debug cli cp wp-content/plugins/e2e-test-helpers/test-env-setup.sh test-env-setup-ci.sh
-    $WP_ENV_TEST_CMD run --debug cli env -u CI WP_CLI_PREFIX= bash test-env-setup-ci.sh
+    $WP_ENV_CMD run --debug cli cp wp-content/plugins/e2e-test-helpers/test-env-setup.sh test-env-setup-ci.sh
+    $WP_ENV_CMD run --debug cli env -u CI WP_CLI_PREFIX= bash test-env-setup-ci.sh
     exit $?
 fi

diff --git a/plugins/woocommerce/tests/e2e/envs/default-object-cache/env-setup.sh b/plugins/woocommerce/tests/e2e/envs/default-object-cache/env-setup.sh
index bea3fa87601..4d9c7d61047 100755
--- a/plugins/woocommerce/tests/e2e/envs/default-object-cache/env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/envs/default-object-cache/env-setup.sh
@@ -4,8 +4,8 @@ set -eo pipefail

 echo "Default environment (+ object cache plugin) setup."

-# Command prefix for running wp-cli against the single-container test environment
-# (started via `wp-env --config .wp-env.test.json`, whose container is `cli`).
-wp_cli="pnpm wp-env:test run cli"
+# Command prefix for running wp-cli against the single-container E2E environment
+# (started via `wp-env --config .wp-env.e2e.json`, whose container is `cli`).
+wp_cli="pnpm wp-env:e2e run cli"

 $wp_cli wp plugin install sqlite-object-cache --activate
diff --git a/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/includes/class-rest-controller.php b/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/includes/class-rest-controller.php
index de9af1e6a0e..008642f1b16 100644
--- a/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/includes/class-rest-controller.php
+++ b/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/includes/class-rest-controller.php
@@ -19,7 +19,7 @@ defined( 'ABSPATH' ) || exit;
  * Helper REST endpoints exposed under /wc-email-test-helper/v1/ for Playwright E2E tests.
  *
  * Health endpoint is open; every other route requires manage_options. The plugin's location
- * under tests/e2e/test-plugins/ — only mounted via .wp-env.test.json for the test environment —
+ * under tests/e2e/test-plugins/ — only mounted via .wp-env.e2e.json for the test environment —
  * provides the second layer of defense.
  */
 class REST_Controller {
@@ -636,7 +636,7 @@ class REST_Controller {

 	/**
 	 * Permission callback used by every non-health endpoint. Requires the manage_options
-	 * capability. The plugin is only mounted in test environments via .wp-env.test.json — it
+	 * capability. The plugin is only mounted in test environments via .wp-env.e2e.json — it
 	 * does not ship in any production WooCommerce build — which provides the second
 	 * layer of defense.
 	 *
diff --git a/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/wc-email-template-sync-test-helper.php b/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/wc-email-template-sync-test-helper.php
index 00fd1be9bf9..cbaf3f0b085 100644
--- a/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/wc-email-template-sync-test-helper.php
+++ b/plugins/woocommerce/tests/e2e/test-plugins/wc-email-template-sync-test-helper/wc-email-template-sync-test-helper.php
@@ -13,7 +13,7 @@ declare( strict_types=1 );

 defined( 'ABSPATH' ) || exit;

-// This plugin is only mounted by .wp-env.test.json for E2E test environments — it does not ship
+// This plugin is only mounted by .wp-env.e2e.json for E2E test environments — it does not ship
 // in any production WooCommerce build. REST permission callbacks still enforce manage_options.

 define( 'WC_EMAIL_TEMPLATE_SYNC_TEST_HELPER_DIR', plugin_dir_path( __FILE__ ) );
diff --git a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
index 78a45c0b76f..37fb6e487c9 100644
--- a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
+++ b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
@@ -7,12 +7,12 @@ import { exec } from 'child_process';
 const execPromisified = promisify( exec );

 /**
- * Runs a WP-CLI command inside the single-container test environment's `cli`
- * container (started via `wp-env --config .wp-env.test.json`).
+ * Runs a WP-CLI command inside the single-container E2E environment's `cli`
+ * container (started via `wp-env --config .wp-env.e2e.json`).
  */
 export async function wpCLI( command: string ) {
 	return await execPromisified(
-		'npm run wp-env:test run cli -- wp ' + command
+		'npm run wp-env:e2e run cli -- wp ' + command
 	);
 }

diff --git a/plugins/woocommerce/tests/e2e/utils/cli.ts b/plugins/woocommerce/tests/e2e/utils/cli.ts
index 6f8d190a329..ce9fa249e8c 100644
--- a/plugins/woocommerce/tests/e2e/utils/cli.ts
+++ b/plugins/woocommerce/tests/e2e/utils/cli.ts
@@ -8,7 +8,7 @@ const execAsync = promisify( exec );

 const wpCLI = async ( command: string ) => {
 	const { stdout, stderr } = await execAsync(
-		`pnpm exec wp-env --config .wp-env.test.json run cli -- ${ command }`
+		`pnpm exec wp-env --config .wp-env.e2e.json run cli -- ${ command }`
 	);

 	return { stdout, stderr };
diff --git a/plugins/woocommerce/tests/e2e/utils/wordpress.ts b/plugins/woocommerce/tests/e2e/utils/wordpress.ts
index 562394fce1b..f467591ffde 100644
--- a/plugins/woocommerce/tests/e2e/utils/wordpress.ts
+++ b/plugins/woocommerce/tests/e2e/utils/wordpress.ts
@@ -56,7 +56,7 @@ const getVersionWPLatestMinusOne = async ( {
 const getInstalledWordPressVersion = async () => {
 	try {
 		const { stdout } = await execAsync(
-			`pnpm exec wp-env --config .wp-env.test.json run cli -- wp core version`
+			`pnpm exec wp-env --config .wp-env.e2e.json run cli -- wp core version`
 		);

 		return Number.parseFloat( stdout.trim() );
diff --git a/plugins/woocommerce/tests/performance/bin/init-environment.sh b/plugins/woocommerce/tests/performance/bin/init-environment.sh
index a326f9d8864..ae4604aa157 100755
--- a/plugins/woocommerce/tests/performance/bin/init-environment.sh
+++ b/plugins/woocommerce/tests/performance/bin/init-environment.sh
@@ -2,9 +2,9 @@

 echo "Initializing WooCommerce E2E"

-# Command prefix for running wp-cli against the single-container test environment
-# (started via `wp-env --config .wp-env.test.json`, whose container is `cli`).
-wp_cli="wp-env --config .wp-env.test.json run cli"
+# Command prefix for running wp-cli against the single-container E2E environment
+# (started via `wp-env --config .wp-env.e2e.json`, whose container is `cli`).
+wp_cli="wp-env --config .wp-env.e2e.json run cli"

 $wp_cli wp config set WP_HTTP_BLOCK_EXTERNAL false --raw --type=constant

@@ -52,14 +52,15 @@ $wp_cli wp config set DISABLE_WP_CRON true --raw --type=constant
 $wp_cli wp config set WP_HTTP_BLOCK_EXTERNAL true --raw --type=constant

 # Resolve container names once; fail loudly if wp-env is not running.
-# Scope to the test env's compose project ("...-woocommerce-test-<hash>"), which is
-# distinct from a dev env's "...-woocommerce-<hash>" project, so a co-running dev
-# environment can't be matched by mistake (both expose a "...-wordpress-1" container).
-# The project hash is hex, so it can never contain "test" - the match is unambiguous.
-_wp_container="$(docker ps --filter name=woocommerce-test --format '{{.Names}}' | grep -- '-wordpress-1$' | head -1)"
-_db_container="$(docker ps --filter name=woocommerce-test --format '{{.Names}}' | grep -- '-mysql-1$' | head -1)"
+# Scope to the E2E env's compose project ("...-woocommerce-e2e-<hash>"), which the
+# performance env runs on, so a co-running dev env ("...-woocommerce-<hash>") can't
+# be matched by mistake (both expose a "...-wordpress-1" container). Match the
+# "woocommerce-e2e-" prefix including its trailing dash: the hash is hex and "e2e" is
+# all hex digits, so a dev hash starting "e2e" is followed by more hex, never a dash.
+_wp_container="$(docker ps --filter name=woocommerce-e2e- --format '{{.Names}}' | grep -- '-wordpress-1$' | head -1)"
+_db_container="$(docker ps --filter name=woocommerce-e2e- --format '{{.Names}}' | grep -- '-mysql-1$' | head -1)"
 if [ -z "$_wp_container" ] || [ -z "$_db_container" ]; then
-    echo "Error: wp-env test containers not found. Run 'pnpm env:perf' first." >&2
+    echo "Error: wp-env containers not found. Run 'pnpm env:perf' first." >&2
     exit 1
 fi

diff --git a/plugins/woocommerce/tests/php/bin/test-env-setup.sh b/plugins/woocommerce/tests/php/bin/test-env-setup.sh
new file mode 100755
index 00000000000..3bce371318e
--- /dev/null
+++ b/plugins/woocommerce/tests/php/bin/test-env-setup.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+# Lifecycle setup for the lean PHP-unit wp-env (.wp-env.test.json). Themes come from
+# the config's `themes` array; the only thing config cannot express is the CSV
+# fixture that WC_Tests_Product_CSV_Importer::test_server_path_traversal reads from
+# `/var/www/sample.csv` (above ABSPATH, where www-data cannot write, so copy as root).
+WP_ENV_TEST_CMD="wp-env --config .wp-env.test.json"
+WP_CLI_PREFIX="${WP_CLI_PREFIX-$WP_ENV_TEST_CMD run cli}"
+
+echo -e 'Pre-place sample.csv fixture \n'
+$WP_CLI_PREFIX sudo cp /var/www/html/wp-content/plugins/woocommerce/tests/legacy/unit-tests/importer/sample.csv /var/www/sample.csv