Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
554 changes: 548 additions & 6 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tools/benchmark-site-editor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
package-lock.json
dist/
results-*.json
54 changes: 45 additions & 9 deletions tools/benchmark-site-editor/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Site Editor Performance Benchmark

Benchmarks site editor performance across Studio, Playground CLI, and Playground Web environments, with optional plugin and multi-worker configurations.
Benchmarks site editor performance across Studio, Playground CLI, Playground Web, and custom WordPress environments, with optional plugin and multi-worker configurations.

## Related Issue

Expand Down Expand Up @@ -32,6 +32,7 @@ The benchmark launches a headless Chromium browser against each environment, mea
| Playground CLI + MW + Plugins | `pg-cli-mw-plugins` | Playground CLI with multi-worker and 10 plugins |
| Playground Web | `pg-web` | playground.wordpress.net (bare) |
| Playground Web + Plugins | `pg-web-plugins` | playground.wordpress.net with 10 plugins |
| Custom | user-defined | Any running WordPress site via `--custom` |

## Plugins

Expand All @@ -46,25 +47,30 @@ When the "plugins" variant is enabled, these 10 plugins are installed via a blue
- Jetpack VideoPress
- WooCommerce Payments
- Contact Form 7
- Elementor
- CoBlocks

## Usage

```bash
cd scripts/benchmark-site-editor
cd tools/benchmark-site-editor
npm install
npm run benchmark
```

### Options

```
--rounds=N Number of benchmark runs per environment (default: 1)
--skip-studio Skip Studio environments
--skip-playground-cli Skip Playground CLI environments
--skip-playground-web Skip Playground web environments
--only=<env1,env2> Run only named environments (comma-separated)
--help Show help
--rounds=N Number of benchmark runs per environment (default: 1)
--skip-studio Skip Studio environments
--skip-playground-cli Skip Playground CLI environments
--skip-playground-web Skip Playground web environments
--custom=<name>,<url>[,<user>,<password>] Add a custom WordPress site (repeatable)
user defaults to "admin", password to "password"
--install-plugins Install blueprint plugins on ALL custom environments
--install-plugins=<name1>,<name2> Install blueprint plugins on specific custom environments
--only=<env1,env2> Run only named environments (comma-separated)
--headed Launch browser in headed mode for debugging
--help Show help
```

### Examples
Expand All @@ -81,8 +87,38 @@ npm run benchmark -- --skip-studio --skip-playground-web

# Single specific environment
npm run benchmark -- --only=studio-mw-plugins --rounds=5

# Benchmark a single custom WordPress site
npm run benchmark -- --custom=my-site,http://localhost:10003

# Two custom sites: bare vs with plugins
npm run benchmark -- \
--custom=local-bare,http://localhost:10003 \
--custom=local-plugins,http://localhost:10004 \
--install-plugins=local-plugins

# Custom site with non-default credentials
npm run benchmark -- --custom=my-site,http://localhost:10003,admin,secret

# Compare Studio vs a custom site
npm run benchmark -- --only=studio,my-site --custom=my-site,http://localhost:10003 --rounds=3
```

## Custom Environments

Use `--custom` to add any running WordPress site to the benchmark. The flag is repeatable, so you can benchmark multiple custom sites in a single run. Each site must be accessible and have a WordPress admin account.

Format: `--custom=<name>,<url>[,<user>,<password>]`

- **name** — Label for the environment in the results table
- **url** — Base URL of the running WordPress site
- **user** — WordPress admin username (default: `admin`)
- **password** — WordPress admin password (default: `password`)

The benchmark logs in via `wp-login.php` using the provided credentials.

With `--install-plugins`, the benchmark installs the same set of plugins used by the built-in environments via the WordPress REST API before measuring. Use `--install-plugins` to install on all custom environments, or `--install-plugins=name1,name2` to target specific ones.

## Prerequisites

- **Studio CLI**: Built automatically if `dist/cli/main.js` doesn't exist (`npm run cli:build`)
Expand Down
147 changes: 133 additions & 14 deletions tools/benchmark-site-editor/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
* - Studio (bare, multi-worker, plugins, multi-worker+plugins)
* - Playground CLI (bare, multi-worker, plugins, multi-worker+plugins)
* - Playground Web (bare, plugins)
* - Custom (any running WordPress site via --custom-url)
*
* Usage:
* cd scripts/benchmark-site-editor
* cd tools/benchmark-site-editor
* npm install
* npm run benchmark
*
Expand All @@ -18,6 +19,8 @@
* --skip-studio Skip Studio environments
* --skip-playground-cli Skip Playground CLI environments
* --skip-playground-web Skip Playground web environments
* --custom=<name>,<url>[,<user>,<password>] Add a custom WordPress site (repeatable)
* --install-plugins[=<name1>,<name2>] Install plugins from blueprint via WP REST API
* --only=<env1,env2> Run only these environments (comma-separated)
* --help Show help
*/
Expand All @@ -27,7 +30,8 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import chalk from 'chalk';
import { measureSiteEditor, METRIC_NAMES, type MeasurementResult } from './measure-site-editor.ts';
import { installPlugins } from './install-plugins.js';
import { measureSiteEditor, METRIC_NAMES, type MeasurementResult } from './measure-site-editor.js';

// ---------------------------------------------------------------------------
// Configuration
Expand All @@ -54,13 +58,17 @@ const PLAYGROUND_CLI_PORT_BASE = 9500;
// Types
// ---------------------------------------------------------------------------

type EnvironmentType = 'studio' | 'playground-cli' | 'playground-web';
type EnvironmentType = 'studio' | 'playground-cli' | 'playground-web' | 'custom';

interface EnvironmentConfig {
name: string;
type: EnvironmentType;
plugins: boolean;
multiWorker: boolean;
/** Base URL for custom environments. */
customUrl?: string;
/** WordPress admin credentials for custom environments. */
credentials?: { username: string; password: string };
}

interface BenchmarkResult {
Expand Down Expand Up @@ -94,13 +102,39 @@ const ALL_ENVIRONMENTS: EnvironmentConfig[] = [
// Argument parsing
// ---------------------------------------------------------------------------

interface CustomEnvInput {
name: string;
url: string;
user: string;
password: string;
}

interface Options {
rounds: number;
skipStudio: boolean;
skipPlaygroundCli: boolean;
skipPlaygroundWeb: boolean;
only: string[];
headed: boolean;
customs: CustomEnvInput[];
/** Names of custom environments to install plugins on, or 'all'. */
installPlugins: string[] | 'all' | false;
}

function parseCustomFlag( value: string ): CustomEnvInput {
const parts = value.split( ',' );
if ( parts.length < 2 ) {
console.error(
`Invalid --custom value: "${ value }"\nExpected: --custom=name,url[,user,password]`
);
process.exit( 1 );
}
return {
name: parts[ 0 ],
url: parts[ 1 ],
user: parts[ 2 ] || 'admin',
password: parts[ 3 ] || 'password',
};
}

function parseArgs(): Options {
Expand All @@ -112,6 +146,8 @@ function parseArgs(): Options {
skipPlaygroundWeb: false,
only: [],
headed: false,
customs: [],
installPlugins: false,
};

for ( const arg of args ) {
Expand All @@ -123,6 +159,15 @@ function parseArgs(): Options {
opts.skipPlaygroundCli = true;
} else if ( arg === '--skip-playground-web' ) {
opts.skipPlaygroundWeb = true;
} else if ( arg.startsWith( '--custom=' ) ) {
opts.customs.push( parseCustomFlag( arg.split( '=' ).slice( 1 ).join( '=' ) ) );
} else if ( arg === '--install-plugins' ) {
opts.installPlugins = 'all';
} else if ( arg.startsWith( '--install-plugins=' ) ) {
opts.installPlugins = arg
.split( '=' )[ 1 ]
.split( ',' )
.map( ( s ) => s.trim() );
} else if ( arg.startsWith( '--only=' ) ) {
opts.only = arg
.split( '=' )[ 1 ]
Expand All @@ -144,15 +189,35 @@ function printHelp() {
Usage: npm run benchmark [options]

Options:
--rounds=N Number of benchmark runs per environment (default: 1)
--skip-studio Skip Studio environments
--skip-playground-cli Skip Playground CLI environments
--skip-playground-web Skip Playground web environments
--only=<env1,env2> Run only named environments (comma-separated)
--headed Launch browser in headed mode for debugging
--help Show this help message

Environments: ${ ALL_ENVIRONMENTS.map( ( e ) => e.name ).join( ', ' ) }
--rounds=N Number of benchmark runs per environment (default: 1)
--skip-studio Skip Studio environments
--skip-playground-cli Skip Playground CLI environments
--skip-playground-web Skip Playground web environments
--custom=<name>,<url>[,<user>,<password>] Add a custom WordPress site (repeatable)
user defaults to "admin", password to "password"
--install-plugins Install blueprint plugins on ALL custom environments
--install-plugins=<name1>,<name2> Install blueprint plugins on specific custom environments
--only=<env1,env2> Run only named environments (comma-separated)
--headed Launch browser in headed mode for debugging
--help Show this help message

Built-in environments: ${ ALL_ENVIRONMENTS.map( ( e ) => e.name ).join( ', ' ) }

Examples:
# Benchmark a single custom WordPress site
npm run benchmark -- --custom=my-site,http://localhost:10003

# Two custom sites: bare vs with plugins
npm run benchmark -- \\
--custom=local-bare,http://localhost:10003 \\
--custom=local-plugins,http://localhost:10004 \\
--install-plugins=local-plugins

# Custom site with non-default credentials
npm run benchmark -- --custom=my-site,http://localhost:10003,admin,secret

# Compare Studio vs a custom site
npm run benchmark -- --only=studio,my-site --custom=my-site,http://localhost:10003 --rounds=3
` );
}

Expand Down Expand Up @@ -516,7 +581,13 @@ async function runBenchmark(
}
try {
const result = await Promise.race( [
measureSiteEditor( { url: benchmarkUrl, isPlaygroundWeb, isPlaygroundCli, headed } ),
measureSiteEditor( {
url: benchmarkUrl,
isPlaygroundWeb,
isPlaygroundCli,
credentials: env.credentials,
headed,
} ),
sleep( MEASUREMENT_TIMEOUT ).then( () => {
throw new Error( 'Measurement timed out' );
} ),
Expand Down Expand Up @@ -620,6 +691,21 @@ function saveResultsSummary( results: BenchmarkResult[] ): void {
// ---------------------------------------------------------------------------

async function ensureStudioCLIBuilt(): Promise< boolean > {
// Ensure CLI dependencies are installed (required for the Vite build to resolve pm2-axon, etc.)
const cliNodeModules = path.resolve( STUDIO_ROOT, 'apps/cli', 'node_modules' );
if ( ! fs.existsSync( cliNodeModules ) ) {
console.log( chalk.yellow( ' Installing CLI dependencies...' ) );
try {
execSync( 'npm install', {
cwd: path.resolve( STUDIO_ROOT, 'apps/cli' ),
stdio: 'inherit',
} );
} catch {
console.error( chalk.red( ' Failed to install CLI dependencies' ) );
return false;
}
}

if ( ! fs.existsSync( STUDIO_CLI_PATH ) ) {
console.log( chalk.yellow( ' Building Studio CLI...' ) );
try {
Expand Down Expand Up @@ -655,7 +741,23 @@ async function main() {
const opts = parseArgs();

// Filter environments
let environments = ALL_ENVIRONMENTS;
let environments = [ ...ALL_ENVIRONMENTS ];

// Add custom environments from --custom flags
for ( const custom of opts.customs ) {
const shouldInstallPlugins =
opts.installPlugins === 'all' ||
( Array.isArray( opts.installPlugins ) && opts.installPlugins.includes( custom.name ) );

environments.push( {
name: custom.name,
type: 'custom',
plugins: shouldInstallPlugins,
multiWorker: false,
customUrl: custom.url,
credentials: { username: custom.user, password: custom.password },
} );
}

if ( opts.only.length > 0 ) {
environments = environments.filter( ( e ) => opts.only.includes( e.name ) );
Expand Down Expand Up @@ -744,6 +846,23 @@ async function main() {
const setup = await setupPlaygroundCliSite( env, port );
benchmarkUrl = setup.url;
teardownFn = () => teardownPlaygroundCliSite( setup.process, port, setup.siteDir );
} else if ( env.type === 'custom' ) {
benchmarkUrl = env.customUrl!;
console.log( chalk.gray( ` URL: ${ benchmarkUrl }` ) );

if ( env.plugins && env.credentials ) {
console.log( chalk.gray( ` Installing plugins via REST API...` ) );
try {
await installPlugins(
benchmarkUrl,
PLUGINS_BLUEPRINT_PATH,
env.credentials.username,
env.credentials.password
);
} catch ( err ) {
console.warn( chalk.yellow( ` Plugin installation failed: ${ err }` ) );
}
}
} else {
benchmarkUrl = getPlaygroundWebUrl( env );
console.log( chalk.gray( ` URL: ${ benchmarkUrl.slice( 0, 80 ) }...` ) );
Expand Down
Loading