Commit 924bdc97e40 for woocommerce
commit 924bdc97e4027eed0438f03cd7d5d3866a1d2ca4
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Tue Jul 21 15:16:46 2026 +0300
[e2e] Bypass repeated wp-env startup during Blocks restores (#66699)
* perf(e2e): reuse Blocks CLI container for restores
Blocks E2E tests restore their database after every test. Even after combining reset and import, every restore still starts npm and wp-env before reaching the same CLI container.
Resolve the CLI container once per worker and execute subsequent combined restores directly through Docker with the same working directory and failure semantics. Run a command-contract test in the existing Blocks E2E command so lookup reuse, argument handling, and malformed discovery output remain covered without adding a CI job.
* chore(changelog): document direct Blocks restore
The direct-container follow-up is a separate performance change from combining reset and import, so it needs its own entry when reviewed as a stacked change.
Add a patch-level performance entry describing the once-per-worker container lookup and removal of repeated npm and wp-env startup from per-test restores.
* fix(e2e): retry Blocks CLI container discovery after failure
The restorer cached the container-ID discovery promise unconditionally, so a transient failure of the one-time wp-env invocation poisoned the cache: every later restore in that worker reused the rejection and failed without ever retrying.
Drop the cached promise when discovery rejects so the next restore re-attempts discovery, and cover the retry path with a unit test. Successful discoveries are still resolved once and reused.
* Update plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/perf-e2e-direct-container-db-restore b/plugins/woocommerce/changelog/perf-e2e-direct-container-db-restore
new file mode 100644
index 00000000000..3bfdb5e6cc3
--- /dev/null
+++ b/plugins/woocommerce/changelog/perf-e2e-direct-container-db-restore
@@ -0,0 +1,4 @@
+Significance: patch
+Type: performance
+
+Resolve the Blocks E2E CLI container once per worker so per-test database restores can bypass repeated npm and wp-env startup.
diff --git a/plugins/woocommerce/package.json b/plugins/woocommerce/package.json
index 40aecd38588..1f86470e5ba 100644
--- a/plugins/woocommerce/package.json
+++ b/plugins/woocommerce/package.json
@@ -77,7 +77,7 @@
"test:e2e:core-parallel": "pnpm test:e2e:default --project=core-parallel",
"test:e2e:default": "pnpm test:e2e:install && pnpm test:e2e:with-env default",
"test:e2e:install": "pnpm playwright install chromium",
- "test:e2e:blocks": "pnpm playwright test --config=tests/e2e/playwright.config.ts --project=blocks-chromium",
+ "test:e2e:blocks": "node --disable-warning=MODULE_TYPELESS_PACKAGE_JSON --test tests/e2e/utils/blocks/*.test.mjs && pnpm playwright test --config=tests/e2e/playwright.config.ts --project=blocks-chromium",
"test:e2e:blocks:performance": "pnpm playwright test --config=tests/e2e/playwright.performance.config.ts --project=blocks-performance",
"test:e2e:email-update-propagation:pr": "pnpm test:e2e:default --project=core-serial --project=core-parallel --grep @pr tests/email-editor/update-propagation",
"test:e2e:email-update-propagation:nightly": "pnpm test:e2e:default --project=core-serial --project=core-parallel tests/email-editor/update-propagation",
diff --git a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.test.mjs b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.test.mjs
new file mode 100644
index 00000000000..46806e9dca0
--- /dev/null
+++ b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.test.mjs
@@ -0,0 +1,115 @@
+/**
+ * External dependencies
+ */
+import assert from 'node:assert/strict';
+import { test } from 'node:test';
+
+/**
+ * Internal dependencies
+ */
+import { createBlocksDatabaseRestorer } from './wp-cli.ts';
+
+const CLI_CONTAINER_ID = '0123456789ab';
+
+test( 'reuses the discovered CLI container across database restores', async () => {
+ const calls = [];
+ const runCommand = async ( executable, args ) => {
+ calls.push( [ executable, args ] );
+
+ if ( executable === 'npm' ) {
+ return {
+ stdout: `npm output\n${ CLI_CONTAINER_ID }\n`,
+ stderr: '',
+ };
+ }
+
+ return { stdout: '', stderr: '' };
+ };
+ const restoreDatabase = createBlocksDatabaseRestorer( runCommand );
+
+ await restoreDatabase( '/tmp/first snapshot.sql' );
+ await restoreDatabase( '/tmp/second.sql' );
+
+ assert.deepEqual( calls, [
+ [
+ 'npm',
+ [ 'run', 'wp-env:e2e', 'run', 'cli', '--', 'printenv', 'HOSTNAME' ],
+ ],
+ [
+ 'docker',
+ [
+ 'exec',
+ '--workdir',
+ '/var/www/html',
+ CLI_CONTAINER_ID,
+ 'sh',
+ '-c',
+ 'wp db reset --yes && wp db import "$1"',
+ 'restore-blocks-database',
+ '/tmp/first snapshot.sql',
+ ],
+ ],
+ [
+ 'docker',
+ [
+ 'exec',
+ '--workdir',
+ '/var/www/html',
+ CLI_CONTAINER_ID,
+ 'sh',
+ '-c',
+ 'wp db reset --yes && wp db import "$1"',
+ 'restore-blocks-database',
+ '/tmp/second.sql',
+ ],
+ ],
+ ] );
+} );
+
+test( 'fails before restoring when CLI container discovery is malformed', async () => {
+ const calls = [];
+ const runCommand = async ( executable, args ) => {
+ calls.push( [ executable, args ] );
+ return { stdout: 'npm output without a container ID\n', stderr: '' };
+ };
+ const restoreDatabase = createBlocksDatabaseRestorer( runCommand );
+
+ await assert.rejects(
+ restoreDatabase( '/tmp/snapshot.sql' ),
+ /Failed to determine the Blocks E2E CLI container ID/
+ );
+ assert.equal( calls.length, 1 );
+} );
+
+test( 'retries CLI container discovery after a failed attempt', async () => {
+ const calls = [];
+ const runCommand = async ( executable, args ) => {
+ calls.push( [ executable, args ] );
+
+ if ( executable === 'npm' ) {
+ if ( calls.length === 1 ) {
+ throw new Error( 'transient wp-env failure' );
+ }
+
+ return {
+ stdout: `npm output\n${ CLI_CONTAINER_ID }\n`,
+ stderr: '',
+ };
+ }
+
+ return { stdout: '', stderr: '' };
+ };
+ const restoreDatabase = createBlocksDatabaseRestorer( runCommand );
+
+ await assert.rejects(
+ restoreDatabase( '/tmp/snapshot.sql' ),
+ /transient wp-env failure/
+ );
+
+ await restoreDatabase( '/tmp/snapshot.sql' );
+
+ assert.deepEqual(
+ calls.map( ( [ executable ] ) => executable ),
+ [ 'npm', 'npm', 'docker' ]
+ );
+} );
diff --git a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
index 0eb34a84692..736d2c6766e 100644
--- a/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
+++ b/plugins/woocommerce/tests/e2e/utils/blocks/wp-cli.ts
@@ -7,6 +7,20 @@ import { exec, execFile } from 'child_process';
const execPromisified = promisify( exec );
const execFilePromisified = promisify( execFile );
+type CommandResult = {
+ stdout: string;
+ stderr: string;
+};
+
+type RunCommand = (
+ executable: string,
+ args: string[]
+) => Promise< CommandResult >;
+
+async function runCommand( executable: string, args: string[] ) {
+ return await execFilePromisified( executable, args );
+}
+
/**
* Runs a WP-CLI command inside the single-container E2E environment's `cli`
* container (started via `wp-env --config .wp-env.e2e.json`).
@@ -18,22 +32,73 @@ export async function wpCLI( command: string ) {
}
/**
- * Resets the Blocks E2E database and imports its snapshot in one CLI-container
- * invocation.
+ * Creates a database restore function that resolves the Blocks E2E CLI
+ * container once and reuses it for subsequent restores.
+ */
+export function createBlocksDatabaseRestorer( execute: RunCommand ) {
+ let cliContainerIdPromise: Promise< string > | undefined;
+
+ const getCliContainerId = async () => {
+ if ( ! cliContainerIdPromise ) {
+ cliContainerIdPromise = execute( 'npm', [
+ 'run',
+ 'wp-env:e2e',
+ 'run',
+ 'cli',
+ '--',
+ 'printenv',
+ 'HOSTNAME',
+ ] ).then( ( { stdout, stderr } ) => {
+ // Match a 12 to 64 character hex string (Docker container ID) on its own line,
+ // optionally followed by a carriage return.
+ const cliContainerId = stdout.match(
+ /^(?<containerId>[a-f0-9]{12,64})\r?$/m
+ )?.groups?.containerId;
+
+ if ( ! cliContainerId ) {
+ throw new Error(
+ `Failed to determine the Blocks E2E CLI container ID: ${ stdout } ${ stderr }`
+ );
+ }
+
+ return cliContainerId;
+ } );
+
+ // Drop failed discoveries from the cache so the next restore
+ // retries instead of reusing the rejection forever.
+ cliContainerIdPromise.catch( () => {
+ cliContainerIdPromise = undefined;
+ } );
+ }
+
+ return await cliContainerIdPromise;
+ };
+
+ return async ( databaseFile: string ) => {
+ const cliContainerId = await getCliContainerId();
+
+ return await execute( 'docker', [
+ 'exec',
+ '--workdir',
+ '/var/www/html',
+ cliContainerId,
+ 'sh',
+ '-c',
+ 'wp db reset --yes && wp db import "$1"',
+ 'restore-blocks-database',
+ databaseFile,
+ ] );
+ };
+}
+
+const restoreDatabase = createBlocksDatabaseRestorer( runCommand );
+
+/**
+ * Resets the Blocks E2E database and imports its snapshot through the existing
+ * CLI container.
*/
export async function restoreBlocksDatabase( databaseFile: string ) {
- return await execFilePromisified( 'npm', [
- 'run',
- 'wp-env:e2e',
- 'run',
- 'cli',
- '--',
- 'sh',
- '-c',
- 'wp db reset --yes && wp db import "$1"',
- 'restore-blocks-database',
- databaseFile,
- ] );
+ return await restoreDatabase( databaseFile );
}
/**