Commit 0dd75e709b2 for woocommerce

commit 0dd75e709b2075508dea1ddd2f1574a836b8df94
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date:   Tue Jul 7 00:52:18 2026 +0300

    Start a single wp-env container set via --config (#66243)

diff --git a/.github/workflows/scripts/override-wp-env-plugins.js b/.github/workflows/scripts/override-wp-env-plugins.js
index 9fbde16f6ac..363a23296cb 100644
--- a/.github/workflows/scripts/override-wp-env-plugins.js
+++ b/.github/workflows/scripts/override-wp-env-plugins.js
@@ -20,7 +20,7 @@ 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.json`;
+const configPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.test.json`;
 console.log( `Reading ${ configPath }` );
 const wpEnvConfig = JSON.parse( fs.readFileSync( configPath, 'utf8' ) );

@@ -51,27 +51,12 @@ const wooCommerceMapping = {
 	'wp-content/plugins/woocommerce': artifactUrl,
 };

-const overrideConfig = {};
-
-if ( wpEnvConfig.plugins ) {
-	overrideConfig.plugins = withoutWooCommerce( wpEnvConfig.plugins );
-}
-
-if ( wpEnvConfig.env?.tests?.plugins ) {
-	// Scope the mapping to env.tests: `wp-env start` provisions both the
-	// development and tests instances, but the Blocks e2e suite only runs
-	// against tests. Mapping here avoids extracting the artifact into the dev
-	// instance that is never exercised. Move this to a root-level `mappings` if
-	// WooCommerce is ever needed in the dev environment too.
-	overrideConfig.env = {
-		tests: {
-			plugins: withoutWooCommerce( wpEnvConfig.env.tests.plugins ),
-			mappings: wooCommerceMapping,
-		},
-	};
-} else {
-	overrideConfig.mappings = wooCommerceMapping;
-}
+// `.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,
+};

 if ( removed === 0 ) {
 	console.error(
@@ -89,7 +74,7 @@ console.log(
 	}; mapping ${ artifactUrl } -> wp-content/plugins/woocommerce`
 );

-const overrideConfigPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.override.json`;
+const overrideConfigPath = `${ WP_ENV_CONFIG_PATH }/.wp-env.test.override.json`;
 console.log( `Saving ${ overrideConfigPath }` );
 fs.writeFileSync(
 	overrideConfigPath,
diff --git a/.github/workflows/scripts/verify-php-version.sh b/.github/workflows/scripts/verify-php-version.sh
index 14be58adacc..a1dfb75282d 100644
--- a/.github/workflows/scripts/verify-php-version.sh
+++ b/.github/workflows/scripts/verify-php-version.sh
@@ -5,7 +5,7 @@
 #

 cd $GITHUB_WORKSPACE/plugins/woocommerce
-ACTUAL_PHP_VERSION=$(pnpm exec wp-env run tests-cli wp --info | grep 'PHP version:')
+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\""
diff --git a/.gitignore b/.gitignore
index b1dd0fb113c..bc0f74252f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,6 +59,7 @@ package-lock.json

 # wp-env config
 .wp-env.override.json
+.wp-env.test.override.json

 # Unit tests
 tmp/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1cb8999eb59..86de00a7142 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -75,7 +75,7 @@ pnpm --filter='@woocommerce/plugin-woocommerce' watch:build
 # PHP unit tests (requires wp-env)
 cd plugins/woocommerce
 # Start the test environment
-pnpm env:dev
+pnpm env:test
 # Run all PHP unit tests
 pnpm test:unit:env
 # Run a specific test class
@@ -86,7 +86,7 @@ pnpm test:unit:env:watch
 # E2E tests (requires Docker)
 cd plugins/woocommerce
 # Start the E2E environment
-pnpm env:start
+pnpm env:test
 # Run Playwright E2E tests
 pnpm test:e2e

diff --git a/plugins/woocommerce/.gitignore b/plugins/woocommerce/.gitignore
index 2876967b5e5..96e903aa9b5 100644
--- a/plugins/woocommerce/.gitignore
+++ b/plugins/woocommerce/.gitignore
@@ -32,3 +32,4 @@ i18n/languages/woocommerce.pot

 # Environment files
 .wp-env.override.json
+.wp-env.test.override.json
diff --git a/plugins/woocommerce/.wp-env.json b/plugins/woocommerce/.wp-env.json
index 2d8c73ff354..1a91c04eba8 100644
--- a/plugins/woocommerce/.wp-env.json
+++ b/plugins/woocommerce/.wp-env.json
@@ -1,52 +1,12 @@
 {
 	"core": "https://wordpress.org/wordpress-latest.zip",
 	"phpVersion": "8.1",
-	"plugins": [ ".", "./tests/e2e/test-plugins/wc-email-template-sync-test-helper" ],
+	"testsEnvironment": false,
+	"plugins": [ "." ],
 	"config": {
 		"JETPACK_AUTOLOAD_DEV": true,
 		"WP_DEBUG_LOG": true,
 		"WP_DEBUG_DISPLAY": true,
 		"ALTERNATE_WP_CRON": true
-	},
-	"lifecycleScripts": {
-		"afterStart": "./tests/e2e/bin/test-env-setup.sh",
-		"afterClean": "./tests/e2e/bin/test-env-setup.sh"
-	},
-	"env": {
-		"tests": {
-			"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": {
-				"WP_TESTS_DOMAIN": "localhost",
-				"ALTERNATE_WP_CRON": false
-			},
-			"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
new file mode 100644
index 00000000000..2b1620ca158
--- /dev/null
+++ b/plugins/woocommerce/.wp-env.test.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/changelog/testops-176-wp-env-single-container-set b/plugins/woocommerce/changelog/testops-176-wp-env-single-container-set
new file mode 100644
index 00000000000..a74c92c1623
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-176-wp-env-single-container-set
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Start a single wp-env container set for tests via `--config .wp-env.test.json` (testsEnvironment: false) instead of provisioning separate dev and tests environments; test-only tooling change, no merchant-facing impact.
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index 825ba6aaaa0..c6f89709276 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -26,16 +26,24 @@
 		"build:api:test": "php bin/api-builder/build-api.php --api-dir=tests/php/src/Internal/Api/Fixtures/DummyApi --autogen-dir=tests/php/src/Internal/Api/Fixtures/DummyApiAutogenerated --api-namespace='Automattic\\WooCommerce\\Tests\\Internal\\Api\\Fixtures\\DummyApi' --autogen-namespace='Automattic\\WooCommerce\\Tests\\Internal\\Api\\Fixtures\\DummyApiAutogenerated'",
 		"changelog": "XDEBUG_MODE=off composer install --quiet && composer exec -- changelogger",
 		"update:php": "XDEBUG_MODE=off composer update --quiet",
-		"env:destroy": "pnpm wp-env destroy",
-		"env:dev": "pnpm wp-env start --update",
-		"env:down": "pnpm wp-env stop",
+		"env:destroy": "pnpm env:dev:destroy",
+		"env:dev": "pnpm env:dev:start",
+		"env:dev:destroy": "pnpm wp-env destroy",
+		"env:dev:restart": "pnpm wp-env destroy --force && pnpm wp-env start --update",
+		"env:dev:start": "pnpm wp-env start --update",
+		"env:dev:stop": "pnpm wp-env stop",
+		"env:down": "pnpm env:dev:stop",
 		"env:performance-init": "./tests/performance/bin/init-environment.sh",
-		"env:restart": "pnpm wp-env destroy --force && pnpm wp-env start --update",
-		"env:start": "pnpm wp-env start",
-		"env:start:blocks": "pnpm wp-env start && bash ./tests/e2e/bin/blocks/test-env-setup.sh && pnpm playwright install chromium",
-		"env:stop": "pnpm wp-env stop",
-		"env:test": "pnpm env:dev",
-		"env:perf": "pnpm env:dev && pnpm env:performance-init",
+		"env:perf": "pnpm env:test: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:stop": "pnpm env:dev:stop",
+		"env:test": "pnpm env:test:start",
+		"env:test:destroy": "pnpm wp-env:test destroy",
+		"env:test:restart": "pnpm wp-env:test destroy --force && pnpm wp-env:test start --update",
+		"env:test:start": "pnpm wp-env:test start --update",
+		"env:test:stop": "pnpm wp-env:test stop",
 		"preinstall": "npx only-allow pnpm",
 		"postinstall": "rimraf -g vendor/woocommerce packages/email-editor packages/blueprint && XDEBUG_MODE=off composer install --quiet",
 		"lint": "pnpm --if-present '/^lint:lang:.*$/'",
@@ -88,10 +96,10 @@
 		"test:php:watch": "sh ./client/blocks/bin/copy-blocks-json.sh && ./vendor/bin/phpunit-watcher watch",
 		"test:metrics": "USE_WP_ENV=1 pnpm playwright test --config=tests/metrics/playwright.config.js",
 		"test:metrics:ci": "../../.github/workflows/scripts/run-metrics.sh",
-		"test:php:env": "sh ./client/blocks/bin/copy-blocks-json.sh && wp-env run --env-cwd='wp-content/plugins/woocommerce' tests-cli vendor/bin/phpunit -c phpunit.xml --verbose",
-		"test:php:env:hpos-off": "sh ./client/blocks/bin/copy-blocks-json.sh && wp-env run --env-cwd='wp-content/plugins/woocommerce' tests-cli env DISABLE_HPOS=1 vendor/bin/phpunit -c phpunit.xml --verbose",
-		"test:php:env:watch": "sh ./client/blocks/bin/copy-blocks-json.sh && wp-env run --env-cwd='wp-content/plugins/woocommerce' tests-cli vendor/bin/phpunit-watcher watch --verbose",
-		"test:plugincheck": "wp-env run tests-cli wp plugin check ${PLUGIN_SLUG:-woocommerce}",
+		"test:php:env": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli vendor/bin/phpunit -c phpunit.xml --verbose",
+		"test:php:env:hpos-off": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli env DISABLE_HPOS=1 vendor/bin/phpunit -c phpunit.xml --verbose",
+		"test:php:env:watch": "sh ./client/blocks/bin/copy-blocks-json.sh && pnpm wp-env:test run --env-cwd='wp-content/plugins/woocommerce' cli vendor/bin/phpunit-watcher watch --verbose",
+		"test:plugincheck": "pnpm wp-env:test run cli wp plugin check ${PLUGIN_SLUG:-woocommerce}",
 		"test:unit": "pnpm test:php",
 		"test:unit:watch": "pnpm test:php:watch",
 		"test:unit:env": "pnpm test:php:env",
@@ -106,7 +114,8 @@
 		"watch:build:project:blocks": "pnpm --filter='@woocommerce/block-library' watch:build",
 		"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": "wp-env",
+		"wp-env:test": "wp-env --config .wp-env.test.json"
 	},
 	"lint-staged": {
 		"src/Api/**/*.php": [
@@ -149,7 +158,7 @@
 						"composer.json",
 						"composer.lock",
 						"**/*.php",
-						".wp-env.json",
+						".wp-env.test.json",
 						"phpunit.xml"
 					],
 					"testEnv": {
@@ -181,7 +190,7 @@
 						"composer.json",
 						"composer.lock",
 						"**/*.php",
-						".wp-env.json",
+						".wp-env.test.json",
 						"phpunit.xml"
 					],
 					"testEnv": {
@@ -210,7 +219,7 @@
 						"composer.json",
 						"composer.lock",
 						"**/*.php",
-						".wp-env.json",
+						".wp-env.test.json",
 						"phpunit.xml"
 					],
 					"testEnv": {
@@ -286,7 +295,7 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -323,7 +332,7 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -357,7 +366,7 @@
 						"src/**/*.php",
 						"templates/**/*.php",
 						"tests/e2e/**",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -660,7 +669,7 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -694,7 +703,7 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -728,7 +737,7 @@
 						"composer.json",
 						"composer.lock",
 						"**/*.php",
-						".wp-env.json",
+						".wp-env.test.json",
 						"phpunit.xml"
 					],
 					"testEnv": {
@@ -759,7 +768,7 @@
 						"tests/e2e/tests/api-tests/**",
 						"tests/e2e/*.js",
 						"tests/e2e/*.sh",
-						".wp-env.json",
+						".wp-env.test.json",
 						"woocommerce.php",
 						"uninstall.php"
 					],
@@ -809,7 +818,7 @@
 						"templates/**/*.php",
 						"templates/**/*.html",
 						"tests/metrics/**",
-						".wp-env.json"
+						".wp-env.test.json"
 					],
 					"onlyForDependencies": [
 						"@woocommerce/admin-library",
diff --git a/plugins/woocommerce/tests/e2e/README.md b/plugins/woocommerce/tests/e2e/README.md
index c044ed7ec49..e21ec87d459 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:start` (starts the `wp-env` based local environment)
+- `pnpm env:test` (starts the `wp-env` based test environment)
 - `pnpm test:e2e` (runs all the tests in headless mode)

 To re-create the environment for a fresh state:

-`pnpm env:restart` (resets and restarts the local environment)
+`pnpm env:test:restart` (resets and restarts the 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 default environment configuration can be found in the `.wp-env.json` file in the `plugins/woocommerce`
-folder.
+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).

 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.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.test.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:restart`.
+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`.

 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 0429fe3c1fc..cf137f2cf94 100755
--- a/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
@@ -1,16 +1,20 @@
 #!/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"
+
 # Remove the database snapshot if it exists.
-wp-env run tests-cli -- rm -f blocks_e2e.sql
+$wp_cli -- rm -f blocks_e2e.sql
 # Run the main script in the container for better performance.
-wp-env run tests-cli -- bash wp-content/plugins/woocommerce/blocks-bin/playwright/scripts/index.sh
+$wp_cli -- bash wp-content/plugins/woocommerce/blocks-bin/playwright/scripts/index.sh
 # Disable the LYS Coming Soon banner.
-wp-env run tests-cli -- wp option update woocommerce_coming_soon 'no'
+$wp_cli -- wp option update woocommerce_coming_soon 'no'
 # Dismiss the site editor welcome guide for the admin user so it does not
 # block interactions during tests. The preference is stored in user meta and
 # will be included in the database snapshot that is restored between tests.
-wp-env run tests-cli -- wp eval '
+$wp_cli -- wp eval '
 $prefs = get_user_meta( 1, "wp_persisted_preferences", true );
 if ( ! is_array( $prefs ) ) { $prefs = array(); }
 if ( ! isset( $prefs["core/edit-site"] ) ) { $prefs["core/edit-site"] = array(); }
@@ -21,8 +25,8 @@ $prefs["core/edit-site"]["welcomeGuideTemplate"] = false;
 update_user_meta( 1, "wp_persisted_preferences", $prefs );
 '
 # Activate the Test Helper APIs utility plugin if not already activated.
-if ! wp-env run tests-cli -- wp plugin is-active e2e-test-helpers/test-helper-apis.php >/dev/null 2>&1; then
-	wp-env run tests-cli -- wp plugin activate e2e-test-helpers/test-helper-apis.php
+if ! $wp_cli -- wp plugin is-active e2e-test-helpers/test-helper-apis.php >/dev/null 2>&1; then
+	$wp_cli -- wp plugin activate e2e-test-helpers/test-helper-apis.php
 fi

 echo "Generating test translations"
diff --git a/plugins/woocommerce/tests/e2e/bin/install-plugin.sh b/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
index 803b6753627..754243765ca 100755
--- a/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
+++ b/plugins/woocommerce/tests/e2e/bin/install-plugin.sh
@@ -17,9 +17,13 @@ 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"
+
 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 )
-pnpm wp-env run tests-cli wp plugin install "$download_url" --force --activate || ( sleep 5 && pnpm wp-env run tests-cli wp plugin install "$download_url" --force --activate )
+$wp_cli wp plugin install "$download_url" --force --activate || ( sleep 5 && $wp_cli wp plugin install "$download_url" --force --activate )

-pnpm wp-env run tests-cli wp plugin list
-pnpm wp-env run tests-cli wp plugin is-active "$PLUGIN_SLUG" || ( echo "Plugin \"$PLUGIN_SLUG\" is not active!" && exit 1 )
+$wp_cli wp plugin list
+$wp_cli wp plugin is-active "$PLUGIN_SLUG" || ( echo "Plugin \"$PLUGIN_SLUG\" is not active!" && exit 1 )
diff --git a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
index ca553c6aab2..74205e86264 100755
--- a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
@@ -1,14 +1,22 @@
 #!/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`).
+# 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}"
+
 if [ ! -z ${CI+y} ]; then
-    # In CI we want to execute the setup behind single container call, while in dev-environments we use the script as it is.
-    # Inside the container the command executed from /var/www/html path as pwd
-    echo -e '--> Dispatching script execution into tests-cli\n'
+    # In CI we execute the setup in a single container call, while in dev
+    # environments we use the script as it is. Inside the container the command is
+    # executed from the /var/www/html path as pwd.
+    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 run --debug tests-cli cp wp-content/plugins/e2e-test-helpers/test-env-setup.sh test-env-setup-ci.sh
-    wp-env run --debug tests-cli sed -i -e 's/wp-env run tests-cli //' test-env-setup-ci.sh
-    wp-env run --debug tests-cli bash test-env-setup-ci.sh
+    $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
     exit $?
 fi

@@ -17,41 +25,41 @@ fi
 # auto-activated, so activate it before any WC-dependent setup below (e.g. the
 # `customer` role user). Harmless when WC is already active (PR/source-mapped).
 echo -e 'Activate WooCommerce \n'
-wp-env run tests-cli wp plugin activate woocommerce
+$WP_CLI_PREFIX wp plugin activate woocommerce

 echo -e 'Install twentytwenty, twentytwentytwo and storefront themes \n'
-wp-env run tests-cli wp theme install storefront twentytwenty twentytwentytwo &
+$WP_CLI_PREFIX wp theme install storefront twentytwenty twentytwentytwo &

 echo -e 'Activate default theme \n'
-wp-env run tests-cli wp theme activate twentytwentythree
+$WP_CLI_PREFIX wp theme activate twentytwentythree

 # Provision wp-cli.yml in-container instead of mapping it. Single-file Docker
 # mounts can surface as empty files under gRPC FUSE, which would silently drop
 # the apache_modules declaration that `wp rewrite ... --hard` needs to write the
 # mod_rewrite block to .htaccess.
 echo -e 'Provision wp-cli.yml \n'
-wp-env run tests-cli bash -c 'printf "apache_modules:\n  - mod_rewrite\n" > /var/www/html/wp-cli.yml'
+$WP_CLI_PREFIX bash -c 'printf "apache_modules:\n  - mod_rewrite\n" > /var/www/html/wp-cli.yml'

 echo -e 'Update URL structure \n'
-wp-env run tests-cli wp rewrite structure '/%postname%/' --hard
+$WP_CLI_PREFIX wp rewrite structure '/%postname%/' --hard

 echo -e 'Activate Filter Setter utility plugin \n'
-wp-env run tests-cli wp plugin activate e2e-test-helpers/filter-setter.php
+$WP_CLI_PREFIX wp plugin activate e2e-test-helpers/filter-setter.php

 # This plugin allows you to process queued scheduled actions immediately.
 # It's used in the analytics e2e tests so that order numbers are shown in Analytics.
 echo -e 'Activate Process Waiting Actions utility plugin \n'
-wp-env run tests-cli wp plugin activate e2e-test-helpers/process-waiting-actions.php
+$WP_CLI_PREFIX wp plugin activate e2e-test-helpers/process-waiting-actions.php

 echo -e 'Activate Test Helper APIs utility plugin \n'
-wp-env run tests-cli wp plugin activate e2e-test-helpers/test-helper-apis.php
+$WP_CLI_PREFIX wp plugin activate e2e-test-helpers/test-helper-apis.php

 echo -e 'Install Plugin-check utility plugin \n'
-wp-env run tests-cli wp plugin install plugin-check --activate
+$WP_CLI_PREFIX wp plugin install plugin-check --activate

 echo -e 'Add Customer user \n'
-if ! wp-env run tests-cli wp user get customer --field=ID >/dev/null 2>&1; then
-	wp-env run tests-cli wp user create customer customer@woocommercecoree2etestsuite.com \
+if ! $WP_CLI_PREFIX wp user get customer --field=ID >/dev/null 2>&1; then
+	$WP_CLI_PREFIX wp user create customer customer@woocommercecoree2etestsuite.com \
 		--user_pass=password \
 		--role=customer \
 		--first_name='Jane' \
@@ -60,17 +68,17 @@ if ! wp-env run tests-cli wp user get customer --field=ID >/dev/null 2>&1; then
 fi

 echo -e 'Update Blog Name \n'
-wp-env run tests-cli wp option update blogname 'WooCommerce Core E2E Test Suite'
+$WP_CLI_PREFIX wp option update blogname 'WooCommerce Core E2E Test Suite'

 echo -e 'Preparing Test Files \n'
-wp-env run tests-cli sudo cp /var/www/html/wp-content/plugins/woocommerce/tests/legacy/unit-tests/importer/sample.csv /var/www/sample.csv
+$WP_CLI_PREFIX sudo cp /var/www/html/wp-content/plugins/woocommerce/tests/legacy/unit-tests/importer/sample.csv /var/www/sample.csv

 ENABLE_TRACKING="${ENABLE_TRACKING:-0}"

 if [ $ENABLE_TRACKING == 1 ]; then
 	echo -e 'Enable tracking\n'
-	wp-env run tests-cli wp option update woocommerce_allow_tracking 'yes'
+	$WP_CLI_PREFIX wp option update woocommerce_allow_tracking 'yes'
 fi

 echo -e 'Upload test images \n'
-wp-env run tests-cli wp media import './test-data/images/image-01.png' './test-data/images/image-02.png' './test-data/images/image-03.png'
+$WP_CLI_PREFIX wp media import './test-data/images/image-01.png' './test-data/images/image-02.png' './test-data/images/image-03.png'
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 7c5e7d020b9..bea3fa87601 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,4 +4,8 @@ set -eo pipefail

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

-pnpm wp-env run tests-cli wp plugin install sqlite-object-cache --activate
+# 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"
+
+$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 e082136815b..de9af1e6a0e 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.json for the test environment —
+ * under tests/e2e/test-plugins/ — only mounted via .wp-env.test.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.json — it
+	 * capability. The plugin is only mounted in test environments via .wp-env.test.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 00e4c552e95..00fd1be9bf9 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.json for E2E test environments — it does not ship
+// This plugin is only mounted by .wp-env.test.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 77dab6a7219..78a45c0b76f 100644
--- a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
+++ b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
@@ -7,11 +7,12 @@ import { exec } from 'child_process';
 const execPromisified = promisify( exec );

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

diff --git a/plugins/woocommerce/tests/e2e/utils/cli.ts b/plugins/woocommerce/tests/e2e/utils/cli.ts
index 1ca90ba0256..6f8d190a329 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 run tests-cli -- ${ command }`
+		`pnpm exec wp-env --config .wp-env.test.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 cd86614b01d..562394fce1b 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 run tests-cli -- wp core version`
+			`pnpm exec wp-env --config .wp-env.test.json run cli -- wp core version`
 		);

 		return Number.parseFloat( stdout.trim() );
diff --git a/plugins/woocommerce/tests/performance/README.md b/plugins/woocommerce/tests/performance/README.md
index adafcf7ff2d..caa49c56913 100644
--- a/plugins/woocommerce/tests/performance/README.md
+++ b/plugins/woocommerce/tests/performance/README.md
@@ -53,8 +53,7 @@ Before using the tests a test environment is needed to run against.
 We first spin up an environment using `wp-env` and configure that environment with the necessary plugins and data using the Initialization Script [`init-environment.sh`](https://github.com/woocommerce/woocommerce/tree/trunk/plugins/woocommerce/tests/performance/bin/init-environment.sh) that will set up a shop with sample products imported and the shop settings (payment method, permalinks, address etc) needed for the tests already set. It is recommended using this to just see the tests in action.

 ```sh
-pnpm env:dev --filter=@woocommerce/plugin-woocommerce
-pnpm env:performance-init --filter=@woocommerce/plugin-woocommerce
+pnpm --filter='@woocommerce/plugin-woocommerce' env:perf
 ```

 If using a different environment the details can be changed in `config.js`.
diff --git a/plugins/woocommerce/tests/performance/bin/init-environment.sh b/plugins/woocommerce/tests/performance/bin/init-environment.sh
index bd701ca7472..a326f9d8864 100755
--- a/plugins/woocommerce/tests/performance/bin/init-environment.sh
+++ b/plugins/woocommerce/tests/performance/bin/init-environment.sh
@@ -2,36 +2,40 @@

 echo "Initializing WooCommerce E2E"

-wp-env run tests-cli wp config set WP_HTTP_BLOCK_EXTERNAL false --raw --type=constant
+# 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"

-wp-env run tests-cli wp plugin activate woocommerce
+$wp_cli wp config set WP_HTTP_BLOCK_EXTERNAL false --raw --type=constant

-wp-env run tests-cli wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=subscriber --path=/var/www/html
+$wp_cli wp plugin activate woocommerce
+
+$wp_cli wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=subscriber --path=/var/www/html

 # Installing and activating the WordPress Importer plugin to import sample products"
-wp-env run tests-cli wp plugin install wordpress-importer --activate
+$wp_cli wp plugin install wordpress-importer --activate

 # Adding basic WooCommerce settings"
-wp-env run tests-cli wp option set woocommerce_store_address 'Example Address Line 1'
-wp-env run tests-cli wp option set woocommerce_store_address_2 'Example Address Line 2'
-wp-env run tests-cli wp option set woocommerce_store_city 'Example City'
-wp-env run tests-cli wp option set woocommerce_default_country 'US:CA'
-wp-env run tests-cli wp option set woocommerce_store_postcode '94110'
-wp-env run tests-cli wp option set woocommerce_currency 'USD'
-wp-env run tests-cli wp option set woocommerce_product_type 'both'
-wp-env run tests-cli wp option set woocommerce_allow_tracking 'no'
-wp-env run tests-cli wp option set woocommerce_enable_checkout_login_reminder 'yes'
-wp-env run tests-cli wp option set --format=json woocommerce_cod_settings '{"enabled":"yes"}'
-wp-env run tests-cli wp option set woocommerce_coming_soon 'no'
+$wp_cli wp option set woocommerce_store_address 'Example Address Line 1'
+$wp_cli wp option set woocommerce_store_address_2 'Example Address Line 2'
+$wp_cli wp option set woocommerce_store_city 'Example City'
+$wp_cli wp option set woocommerce_default_country 'US:CA'
+$wp_cli wp option set woocommerce_store_postcode '94110'
+$wp_cli wp option set woocommerce_currency 'USD'
+$wp_cli wp option set woocommerce_product_type 'both'
+$wp_cli wp option set woocommerce_allow_tracking 'no'
+$wp_cli wp option set woocommerce_enable_checkout_login_reminder 'yes'
+$wp_cli wp option set --format=json woocommerce_cod_settings '{"enabled":"yes"}'
+$wp_cli wp option set woocommerce_coming_soon 'no'

 #  WooCommerce shop pages
-wp-env run tests-cli wp wc --user=admin tool run install_pages
+$wp_cli wp wc --user=admin tool run install_pages

 # Importing WooCommerce sample products"
-wp-env run tests-cli wp import wp-content/plugins/woocommerce/sample-data/sample_products.xml --authors=skip
+$wp_cli wp import wp-content/plugins/woocommerce/sample-data/sample_products.xml --authors=skip

 # install Storefront
-wp-env run tests-cli wp theme install storefront --activate
+$wp_cli wp theme install storefront --activate

 # Pin order storage to the legacy posts table (HPOS off) for deterministic runs.
 # These performance requests assert the classic post.php order-edit screens,
@@ -40,18 +44,22 @@ wp-env run tests-cli wp theme install storefront --activate
 # Health loopback to admin-ajax.php) while no orders exist yet, flipping order
 # storage mid-run and breaking those assertions. Force HPOS off and consume the
 # newly-installed flag so it cannot be re-enabled during the test run.
-wp-env run tests-cli wp option update woocommerce_custom_orders_table_enabled no
-wp-env run tests-cli wp option update woocommerce_newly_installed no
+$wp_cli wp option update woocommerce_custom_orders_table_enabled no
+$wp_cli wp option update woocommerce_newly_installed no

 # reduce the impact of background activities on the testing setup
-wp-env run tests-cli wp config set DISABLE_WP_CRON true --raw --type=constant
-wp-env run tests-cli wp config set WP_HTTP_BLOCK_EXTERNAL true --raw --type=constant
+$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.
-_wp_container="$(docker ps --filter name=tests-wordpress --format '{{.Names}}' | head -1)"
-_db_container="$(docker ps --filter name=tests-mysql --format '{{.Names}}' | head -1)"
+# 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)"
 if [ -z "$_wp_container" ] || [ -z "$_db_container" ]; then
-    echo "Error: wp-env containers not found. Run 'pnpm env:perf' first." >&2
+    echo "Error: wp-env test containers not found. Run 'pnpm env:perf' first." >&2
     exit 1
 fi