Commit 9ef3ad9e7b7 for woocommerce
commit 9ef3ad9e7b7e7bcc90fea77b980fb8ea835dcc38
Author: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com>
Date: Wed Jul 15 20:32:15 2026 +0300
e2e infra: organize test helper plugins into self-contained folders (#66594)
diff --git a/plugins/woocommerce/.wp-env.e2e.json b/plugins/woocommerce/.wp-env.e2e.json
index b9a661065d5..5cd54d68277 100644
--- a/plugins/woocommerce/.wp-env.e2e.json
+++ b/plugins/woocommerce/.wp-env.e2e.json
@@ -6,6 +6,7 @@
"plugins": [
".",
"./tests/e2e/test-plugins/wc-email-template-sync-test-helper",
+ "./tests/e2e/test-plugins/woocommerce-e2e-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",
@@ -27,7 +28,7 @@
"afterClean": "./tests/e2e/bin/test-env-setup.sh"
},
"mappings": {
- "wp-content/plugins/e2e-test-helpers": "./tests/e2e/bin",
+ "wp-content/plugins/e2e-test-bin": "./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",
diff --git a/plugins/woocommerce/changelog/testops-174-organize-e2e-test-plugins b/plugins/woocommerce/changelog/testops-174-organize-e2e-test-plugins
new file mode 100644
index 00000000000..cccf7ef3581
--- /dev/null
+++ b/plugins/woocommerce/changelog/testops-174-organize-e2e-test-plugins
@@ -0,0 +1,3 @@
+Significance: patch
+Type: dev
+Comment: Reorganize E2E test helper plugins into self-contained folders under tests/e2e/test-plugins/ and auto-activate the always-on helpers via the wp-env plugins array (no runtime change).
diff --git a/plugins/woocommerce/tests/e2e/README.md b/plugins/woocommerce/tests/e2e/README.md
index c1e2fce9825..f8d8de83eba 100644
--- a/plugins/woocommerce/tests/e2e/README.md
+++ b/plugins/woocommerce/tests/e2e/README.md
@@ -169,7 +169,25 @@ 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.e2e.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/`.
+
+### Convention
+
+Every always-on or externally downloaded helper is a **self-contained folder** at `tests/e2e/test-plugins/<slug>/<slug>.php` (the main file matches the folder name), with a full plugin header (`Plugin Name`, `Description`, `Version`, `Requires PHP`, `Author`). Never bind-mount an individual `.php` file — mount a folder or download a zip. The per-test block plugins under `blocks/` are single files, but their whole parent folder is mounted at once (see below).
+
+Keep `Requires PHP` at the **lowest PHP version any E2E environment runs** (currently `7.4`, the same floor as WooCommerce itself) and keep the helper's code compatible with it. WordPress silently refuses to load a plugin whose `Requires PHP` is higher than the running version: it still reports as active, but none of its hooks run and its REST routes return `rest_no_route` (404).
+
+How a helper is wired up depends on when it needs to be active:
+
+- **Always-on helpers** are listed in `.wp-env.e2e.json`'s `plugins` array, which mounts the folder **and auto-activates** it. Do not add a manual `wp plugin activate …` line for these. Current always-on helpers:
+ - `woocommerce-e2e-test-helper` — the general-purpose helper bundle, covering three concerns in one plugin:
+ - **Filter setter** — registers WordPress filters from an `e2e-filters` cookie so tests can override filtered values on the fly.
+ - **Process waiting actions** — runs the Action Scheduler queue synchronously when a request carries the `?process-waiting-actions` query param (used by the analytics suite so order data lands in reports immediately).
+ - **Test helper REST API** — endpoints (`e2e-feature-flags`, `e2e-options`, `e2e-environment`, `e2e-theme`) for toggling feature flags, setting/deleting options, reading environment info and switching themes during a test.
+ - `wc-email-template-sync-test-helper` — see below (email template sync fixtures for RSM-146).
+- **Per-test block plugins** live in `tests/e2e/test-plugins/blocks/`, mounted (not auto-activated) via the `woocommerce-blocks-test-plugins` mapping. Each is activated and deactivated by the spec that needs it (e.g. `wp plugin activate woocommerce-blocks-test-plugins/<file>.php`), because they change store behavior globally and must not be on for every test.
+
+`woocommerce-cleanup` also lives under `test-plugins/`, but it is **not** in the wp-env `plugins` array — it's an on-demand site-reset tool installed only by the external (non-wp-env) setup path, `bin/test-env-setup-external.sh`.
### `wc-email-template-sync-test-helper`
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 6b6db7c6e71..4c0958a7719 100755
--- a/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/blocks/test-env-setup.sh
@@ -24,10 +24,6 @@ $prefs["core/edit-site"]["welcomeGuidePage"] = false;
$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_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"
node $script_dir/generate-test-translations.js
diff --git a/plugins/woocommerce/tests/e2e/bin/filter-setter.php b/plugins/woocommerce/tests/e2e/bin/filter-setter.php
deleted file mode 100644
index 92c9d7ff74c..00000000000
--- a/plugins/woocommerce/tests/e2e/bin/filter-setter.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-/**
- * Plugin name: Filter Setter
- * Description: Utility intended to be used during E2E testing, to make it easy to setup WordPress filters.
- *
- * Intended to function as a (mu-)plugin while tests are running, this code works by inspecting the current cookie
- * for an entry called 'e2e-filters', which is expected to be a JSON description of filter hooks and the values we want
- * to set via those filters. For example, given the JSON (pretty printed here for clarity):
- *
- * {
- * "woocommerce_system_timeout": 10
- * }
- *
- * Then a filter will be added that returns 10 when 'woocommerce_system_timeout' is invoked. Or, given:
- *
- * {
- * "woocommerce_enable_deathray": {
- * "callback": "__return_false"
- * }
- * }
- *
- * Then the `__return_false()` convenience function will be set up in relation to filter hook
- * 'woocommerce_enable_deathray'. Additionally, priorities can be specified. Example:
- *
- * {
- * "woocommerce_enable_deathray": {
- * "callback": "__return_false",
- * "priority": 20
- * }
- * }
- *
- * Priorities can also be used in combination with literal values. For example:
- *
- * {
- * "woocommerce_default_username": {
- * "value": "Geoffrey",
- * "priority": 20
- * }
- * }
- *
- * It hopefully goes without saying, this should not be used in a production environment.
- *
- * @package Automattic\WooCommerce\E2EPlaywright
- */
-
-if ( ! isset( $_COOKIE ) || ! isset( $_COOKIE['e2e-filters'] ) ) {
- return;
-}
-
-// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
-$filters = json_decode( $_COOKIE['e2e-filters'], true );
-
-if ( ! is_array( $filters ) ) {
- return;
-}
-
-foreach ( $filters as $hook => $spec ) {
- // A priority may be specified as part of the spec, else use the default priority (10).
- $priority = isset( $spec['priority'] ) && is_int( $spec['priority'] )
- ? $spec['priority']
- : 10;
-
- // If the spec is not an array, then it is probably intended as the literal value.
- if ( ! is_array( $spec ) ) {
- $value = $spec;
- } elseif ( isset( $spec['value'] ) ) {
- $value = $spec['value'];
- }
-
- // If we know the value, we can establish our filter callback.
- if ( isset( $value ) ) {
- $callback = function () use ( $value ) {
- return $value;
- };
- }
-
- // We also support specifying a callback function.
- if ( is_array( $spec ) && isset( $spec['callback'] ) && is_string( $spec['callback'] ) ) {
- $callback = $spec['callback'];
- }
-
- // Ensure we have a callback, then setup the filter.
- if ( isset( $callback ) ) {
- add_filter( $hook, $callback, $priority );
- }
-}
-
diff --git a/plugins/woocommerce/tests/e2e/bin/process-waiting-actions.php b/plugins/woocommerce/tests/e2e/bin/process-waiting-actions.php
deleted file mode 100644
index 1b9b02caad4..00000000000
--- a/plugins/woocommerce/tests/e2e/bin/process-waiting-actions.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-/**
- * Plugin name: Process Waiting Actions
- * Description: Utility intended to be used during E2E testing, to make it easy to process any pending scheduled actions.
- *
- * Intended to function as a (mu-)plugin while tests are running. It listens for requests made with the
- * 'process-waiting-actions' query parameter and then starts an Action Scheduler queue runner. It exits immediately
- * after this, to avoid overhead of building up a full response.
- *
- * @package Automattic\WooCommerce\E2EPlaywright
- */
-
-add_action(
- 'init',
- function () {
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- if ( ! isset( $_GET['process-waiting-actions'] ) ) {
- return;
- }
-
- if ( ! class_exists( ActionScheduler_QueueRunner::class ) ) {
- return;
- }
-
- exit( ActionScheduler_QueueRunner::instance()->run( 'E2E Tests' ) ? 1 : 0 );
- }
-);
diff --git a/plugins/woocommerce/tests/e2e/bin/test-env-setup-external.sh b/plugins/woocommerce/tests/e2e/bin/test-env-setup-external.sh
index 0333be93afc..373635bbbbe 100755
--- a/plugins/woocommerce/tests/e2e/bin/test-env-setup-external.sh
+++ b/plugins/woocommerce/tests/e2e/bin/test-env-setup-external.sh
@@ -35,9 +35,7 @@ echo "--------------------------------------------------------"
# Define the list of PHP files to process
mu_plugins=(
- "filter-setter"
- "process-waiting-actions"
- "test-helper-apis"
+ "woocommerce-e2e-test-helper"
"woocommerce-cleanup"
)
@@ -46,7 +44,7 @@ for plugin in "${mu_plugins[@]}"; do
echo "Processing $plugin..."
# Download the PHP file
- curl -o "$plugin.php" "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/tests/e2e/bin/$plugin.php"
+ curl -o "$plugin.php" "https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/tests/e2e/test-plugins/$plugin/$plugin.php"
# Create a zip file
(chmod 755 "$plugin.php" && zip "${plugin%}.zip" "$plugin.php")
diff --git a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
index 9255bd78df0..3ace5accbf2 100755
--- a/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
+++ b/plugins/woocommerce/tests/e2e/bin/test-env-setup.sh
@@ -13,9 +13,9 @@ if [ ! -z ${CI+y} ]; then
# 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
+ # Source from the e2e-test-bin directory mount; a single-file mount of this
# script can surface as an empty file under Docker gRPC FUSE.
- $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 cp wp-content/plugins/e2e-test-bin/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
@@ -43,17 +43,6 @@ $WP_CLI_PREFIX bash -c 'printf "apache_modules:\n - mod_rewrite\n" > /var/www/h
echo -e 'Update URL structure \n'
$WP_CLI_PREFIX wp rewrite structure '/%postname%/' --hard
-echo -e 'Activate Filter Setter utility plugin \n'
-$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_CLI_PREFIX wp plugin activate e2e-test-helpers/process-waiting-actions.php
-
-echo -e 'Activate Test Helper APIs utility plugin \n'
-$WP_CLI_PREFIX wp plugin activate e2e-test-helpers/test-helper-apis.php
-
echo -e 'Add Customer user \n'
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 \
diff --git a/plugins/woocommerce/tests/e2e/bin/woocommerce-cleanup.php b/plugins/woocommerce/tests/e2e/test-plugins/woocommerce-cleanup/woocommerce-cleanup.php
similarity index 99%
rename from plugins/woocommerce/tests/e2e/bin/woocommerce-cleanup.php
rename to plugins/woocommerce/tests/e2e/test-plugins/woocommerce-cleanup/woocommerce-cleanup.php
index 6e47cf2f913..4a927404284 100644
--- a/plugins/woocommerce/tests/e2e/bin/woocommerce-cleanup.php
+++ b/plugins/woocommerce/tests/e2e/test-plugins/woocommerce-cleanup/woocommerce-cleanup.php
@@ -15,7 +15,8 @@
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
- exit; // Exit if accessed directly.
+ // Exit if accessed directly.
+ exit;
}
/**
diff --git a/plugins/woocommerce/tests/e2e/bin/test-helper-apis.php b/plugins/woocommerce/tests/e2e/test-plugins/woocommerce-e2e-test-helper/woocommerce-e2e-test-helper.php
similarity index 54%
rename from plugins/woocommerce/tests/e2e/bin/test-helper-apis.php
rename to plugins/woocommerce/tests/e2e/test-plugins/woocommerce-e2e-test-helper/woocommerce-e2e-test-helper.php
index 1da98c73672..875e1e04a48 100644
--- a/plugins/woocommerce/tests/e2e/bin/test-helper-apis.php
+++ b/plugins/woocommerce/tests/e2e/test-plugins/woocommerce-e2e-test-helper/woocommerce-e2e-test-helper.php
@@ -1,8 +1,130 @@
<?php
+/**
+ * Plugin Name: WooCommerce E2E Test Helper
+ * Description: Always-on utilities for the WooCommerce E2E suite: cookie-driven filter overrides, synchronous Action Scheduler processing, and a REST API for feature flags, options, environment info and theme switching.
+ * Version: 1.0.0
+ * Requires PHP: 7.4
+ * Author: WooCommerce
+ *
+ * This bundles three previously separate helpers (filter-setter, process-waiting-actions and
+ * test-helper-apis). They share the same lifecycle — mounted and auto-activated for every E2E run
+ * via the .wp-env.e2e.json "plugins" array — so they live together here. Each concern is kept in its
+ * own section below and none of them touch the others.
+ *
+ * It hopefully goes without saying, none of this should ever run in a production environment.
+ *
+ * @package Automattic\WooCommerce\E2EPlaywright
+ */
+
+declare(strict_types=1);
+
+/*
+ * -----------------------------------------------------------------------------
+ * Filter setter
+ * -----------------------------------------------------------------------------
+ *
+ * Registers WordPress filters from an 'e2e-filters' cookie, so a spec can override filtered values on
+ * the fly. The cookie is a JSON map of hook => spec. For example (pretty printed here for clarity):
+ *
+ * { "woocommerce_system_timeout": 10 }
+ *
+ * adds a filter returning 10 for 'woocommerce_system_timeout'. A spec may instead be an object naming
+ * a callback and/or a priority:
+ *
+ * { "woocommerce_enable_deathray": { "callback": "__return_false", "priority": 20 } }
+ *
+ * or a literal value with a priority:
+ *
+ * { "woocommerce_default_username": { "value": "Geoffrey", "priority": 20 } }
+ *
+ * Runs at plugin load so the filters are in place before anything reads them.
+ */
+
+/**
+ * Read the `e2e-filters` cookie and register the filters it describes.
+ */
+function woocommerce_e2e_apply_cookie_filters(): void {
+ if ( ! isset( $_COOKIE['e2e-filters'] ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
+ $filters = json_decode( $_COOKIE['e2e-filters'], true );
+
+ if ( ! is_array( $filters ) ) {
+ return;
+ }
+
+ foreach ( $filters as $hook => $spec ) {
+ // A priority may be specified as part of the spec, else use the default priority (10).
+ $priority = isset( $spec['priority'] ) && is_int( $spec['priority'] )
+ ? $spec['priority']
+ : 10;
+
+ // If the spec is not an array, then it is probably intended as the literal value.
+ if ( ! is_array( $spec ) ) {
+ $value = $spec;
+ } elseif ( isset( $spec['value'] ) ) {
+ $value = $spec['value'];
+ }
+
+ // If we know the value, we can establish our filter callback.
+ if ( isset( $value ) ) {
+ $callback = function () use ( $value ) {
+ return $value;
+ };
+ }
+
+ // We also support specifying a callback function.
+ if ( is_array( $spec ) && isset( $spec['callback'] ) && is_string( $spec['callback'] ) ) {
+ $callback = $spec['callback'];
+ }
+
+ // Ensure we have a callback, then setup the filter.
+ if ( isset( $callback ) ) {
+ add_filter( $hook, $callback, $priority );
+ }
+ }
+}
+
+woocommerce_e2e_apply_cookie_filters();
+
+/*
+ * -----------------------------------------------------------------------------
+ * Process waiting actions
+ * -----------------------------------------------------------------------------
+ *
+ * Listens for requests carrying the 'process-waiting-actions' query parameter and starts an Action
+ * Scheduler queue runner, exiting immediately afterwards to avoid the overhead of a full response.
+ * Used by the analytics suite so scheduled order data lands in reports synchronously.
+ */
+add_action(
+ 'init',
+ function () {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['process-waiting-actions'] ) ) {
+ return;
+ }
+
+ if ( ! class_exists( ActionScheduler_QueueRunner::class ) ) {
+ return;
+ }
+
+ exit( ActionScheduler_QueueRunner::instance()->run( 'E2E Tests' ) ? 1 : 0 );
+ }
+);
+
+/*
+ * -----------------------------------------------------------------------------
+ * Test helper REST API
+ * -----------------------------------------------------------------------------
+ *
+ * REST routes for toggling feature flags, setting/deleting options, reading environment info and
+ * switching themes during a test.
+ */
/**
- * Plugin Name: Test Helper APIs
- * Description: Utility REST API designed for E2E testing purposes. Allows turning features on or off, and setting option values
+ * Register the E2E test helper REST routes (feature flags and options).
*/
function register_helper_api() {
register_rest_route(
@@ -122,7 +244,8 @@ add_filter( 'comment_flood_filter', '__return_false', 99 );
/**
* Update a WordPress option.
- * @param WP_REST_Request $request
+ *
+ * @param WP_REST_Request $request The REST request, carrying `option_name` and `option_value`.
* @return WP_REST_Response
*/
function api_update_option( WP_REST_Request $request ) {
@@ -145,11 +268,11 @@ function api_update_option( WP_REST_Request $request ) {
/**
* Delete a WordPress option.
*
- * @param WP_REST_Request $request
+ * @param WP_REST_Request $request The REST request, carrying `option_name`.
* @return WP_REST_Response
*/
function api_delete_option( WP_REST_Request $request ) {
- $option_name = sanitize_text_field( $request['option_name'] );
+ $option_name = sanitize_text_field( $request['option_name'] );
$option_exists = get_option( $option_name, null );