Skip to content

Conversation

@gcsecsey
Copy link
Contributor

@gcsecsey gcsecsey commented Jan 8, 2026

Related issues

Proposed Changes

Add a Playwright test that benchmarks the performance of some actions within the Site Editor. It measures load times for each step of a typical site editing workflow.

The benchmark measures the time taken for the following steps:

  1. wp-admin Load - Time to open /wp-admin with auto-login
  2. Site Editor Load - Time to navigate to the site editor and wait for it to fully load
  3. Templates View Load - Time to open the Templates view and wait for templates to load
  4. Template Open - Time to open a specific template in the editor
  5. Block Add - Time to add two blocks (paragraph and heading) to the template
  6. Template Save - Time to save the template

We can use the reported timings to compare load times across different environments (studio, playground cli, playground.wordpress.net)

Testing Instructions

Prerequisites

  • A running WordPress instance accessible via URL
  • The WordPress instance should have the Site Editor enabled
  • For local testing, you can use:
    • Studio (local WordPress site)
    • Playground CLI (npx @wp-playground/cli@latest server)
    • Any other WordPress installation

Running the test

Set the BENCHMARK_URL environment variable to point to the WordPress instance:

BENCHMARK_URL=http://localhost:8888 npx playwright test --config=./metrics/playwright.metrics.config.ts site-editor-benchmark

Output

After running the test, results are automatically printed to the console as a table, and also saved to metrics/artifacts/site-editor-benchmark.results.json.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@gcsecsey gcsecsey changed the title Stu 1199 studio write a script to benchmark site editor performance Add a script to benchmark site editor performance Jan 8, 2026
results.forEach( ( result ) => {
const urlKey = result.url || 'unknown';
// Create a short identifier from the URL (e.g., "localhost:8888" or "playground-web")
const urlIdentifier = urlKey.includes( 'playground.wordpress.net' )

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

'
playground.wordpress.net
' can be anywhere in the URL, and arbitrary hosts may come before or after it.

Copilot Autofix

AI 10 days ago

In general, to avoid incomplete URL substring sanitization, parse the URL and make decisions based on its host (or hostname) rather than searching for substrings in the full URL string. If you need to detect a specific domain (or one of its subdomains), compare the parsed host against an explicit list or check that it matches exactly or ends with a known suffix and a dot boundary.

In this specific file, the problematic logic is the creation of urlIdentifier:

const urlIdentifier = urlKey.includes( 'playground.wordpress.net' )
    ? 'playground-web'
    : urlKey
            .replace( /^https?:\/\//, '' )
            .replace( /\/$/, '' )
            .replace( /[^a-z0-9]/gi, '-' );

We should instead parse urlKey with the standard URL class, extract hostname, and then decide whether the hostname is exactly playground.wordpress.net or a subdomain such as foo.playground.wordpress.net. Then we construct urlIdentifier from the hostname/path as before for all other hosts. To keep behavior similar and avoid assumptions about other code, we’ll:

  • Add a small helper function getUrlIdentifier(urlKey: string): string in this test file.
  • Inside it, safely parse urlKey using the built-in URL constructor when possible; on parse failure, fall back to the existing string-based transformation.
  • Determine whether the parsed hostname equals playground.wordpress.net or ends with .playground.wordpress.net. If so, return 'playground-web'; otherwise, apply the same replace chain as before on the original urlKey.

This change is entirely local to metrics/tests/site-editor-benchmark.test.ts and does not require modifying other files.

Suggested changeset 1
metrics/tests/site-editor-benchmark.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/metrics/tests/site-editor-benchmark.test.ts b/metrics/tests/site-editor-benchmark.test.ts
--- a/metrics/tests/site-editor-benchmark.test.ts
+++ b/metrics/tests/site-editor-benchmark.test.ts
@@ -14,6 +14,31 @@
 	};
 }
 
+function getUrlIdentifier( urlKey: string ): string {
+	const FALLBACK_IDENTIFIER = urlKey
+		.replace( /^https?:\/\//, '' )
+		.replace( /\/$/, '' )
+		.replace( /[^a-z0-9]/gi, '-' );
+
+	try {
+		const parsed = new URL( urlKey );
+		const hostname = parsed.hostname || '';
+
+		// Treat playground.wordpress.net and its subdomains as playground-web
+		if (
+			hostname === 'playground.wordpress.net' ||
+			hostname.endsWith( '.playground.wordpress.net' )
+		) {
+			return 'playground-web';
+		}
+
+		return FALLBACK_IDENTIFIER;
+	} catch {
+		// If urlKey is not a valid URL, fall back to the original string-based normalization
+		return FALLBACK_IDENTIFIER;
+	}
+}
+
 test.describe( 'Site Editor Performance Benchmark', () => {
 	const results: BenchmarkResults[] = [];
 
@@ -28,12 +53,7 @@
 		results.forEach( ( result ) => {
 			const urlKey = result.url || 'unknown';
 			// Create a short identifier from the URL (e.g., "localhost:8888" or "playground-web")
-			const urlIdentifier = urlKey.includes( 'playground.wordpress.net' )
-				? 'playground-web'
-				: urlKey
-						.replace( /^https?:\/\//, '' )
-						.replace( /\/$/, '' )
-						.replace( /[^a-z0-9]/gi, '-' );
+			const urlIdentifier = getUrlIdentifier( urlKey );
 
 			Object.entries( result.metrics ).forEach( ( [ key, value ] ) => {
 				if ( value !== undefined ) {
EOF
@@ -14,6 +14,31 @@
};
}

function getUrlIdentifier( urlKey: string ): string {
const FALLBACK_IDENTIFIER = urlKey
.replace( /^https?:\/\//, '' )
.replace( /\/$/, '' )
.replace( /[^a-z0-9]/gi, '-' );

try {
const parsed = new URL( urlKey );
const hostname = parsed.hostname || '';

// Treat playground.wordpress.net and its subdomains as playground-web
if (
hostname === 'playground.wordpress.net' ||
hostname.endsWith( '.playground.wordpress.net' )
) {
return 'playground-web';
}

return FALLBACK_IDENTIFIER;
} catch {
// If urlKey is not a valid URL, fall back to the original string-based normalization
return FALLBACK_IDENTIFIER;
}
}

test.describe( 'Site Editor Performance Benchmark', () => {
const results: BenchmarkResults[] = [];

@@ -28,12 +53,7 @@
results.forEach( ( result ) => {
const urlKey = result.url || 'unknown';
// Create a short identifier from the URL (e.g., "localhost:8888" or "playground-web")
const urlIdentifier = urlKey.includes( 'playground.wordpress.net' )
? 'playground-web'
: urlKey
.replace( /^https?:\/\//, '' )
.replace( /\/$/, '' )
.replace( /[^a-z0-9]/gi, '-' );
const urlIdentifier = getUrlIdentifier( urlKey );

Object.entries( result.metrics ).forEach( ( [ key, value ] ) => {
if ( value !== undefined ) {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
};

// Check if this is playground.wordpress.net (runs in iframe)
const isPlaygroundWeb = wpAdminUrl.includes( 'playground.wordpress.net' );

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

'
playground.wordpress.net
' can be anywhere in the URL, and arbitrary hosts may come before or after it.

Copilot Autofix

AI 10 days ago

In general, to fix this kind of issue you should parse the URL string into a URL object and perform host-based checks on the parsed hostname (and possibly protocol), rather than doing substring checks on the full URL text. That ensures that you only match the actual host, not occurrences in the path, query, or a different host name (e.g., example.com.evil.com).

For this specific file, the best fix is to replace wpAdminUrl.includes('playground.wordpress.net') with a check on the parsed hostname of wpAdminUrl. Since this is Node/TypeScript test code, we can safely use the built-in URL class (new URL(...)). The logic should remain functionally equivalent for valid URLs that actually point to Playground, but avoid accidental matches. A robust, minimal-change fix is:

  • Ensure wpAdminUrl is a fully qualified URL (already done: it adds http:// if missing).
  • Wrap it in new URL(wpAdminUrl) to parse.
  • Compare url.hostname to 'playground.wordpress.net' (and optionally accept the bare hostname without protocol or with trailing slash, but in this case we already normalized).

Concretely:

  • In metrics/tests/site-editor-benchmark.test.ts, around line 88–90, change:
// Check if this is playground.wordpress.net (runs in iframe)
const isPlaygroundWeb = wpAdminUrl.includes( 'playground.wordpress.net' );

to:

// Check if this is playground.wordpress.net (runs in iframe) using hostname, not substring
let isPlaygroundWeb = false;
try {
    const parsedUrl = new URL( wpAdminUrl );
    isPlaygroundWeb = parsedUrl.hostname === 'playground.wordpress.net';
} catch {
    isPlaygroundWeb = false;
}

This keeps existing behavior for proper Playground URLs, avoids substring pitfalls, and gracefully handles malformed URLs by treating them as non-Playground. No new imports are necessary because URL is globally available in modern Node, and we are not changing any other behavior or public API.

Suggested changeset 1
metrics/tests/site-editor-benchmark.test.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/metrics/tests/site-editor-benchmark.test.ts b/metrics/tests/site-editor-benchmark.test.ts
--- a/metrics/tests/site-editor-benchmark.test.ts
+++ b/metrics/tests/site-editor-benchmark.test.ts
@@ -85,8 +85,14 @@
 			metrics: {},
 		};
 
-		// Check if this is playground.wordpress.net (runs in iframe)
-		const isPlaygroundWeb = wpAdminUrl.includes( 'playground.wordpress.net' );
+		// Check if this is playground.wordpress.net (runs in iframe) using hostname, not substring
+		let isPlaygroundWeb = false;
+		try {
+			const parsedUrl = new URL( wpAdminUrl );
+			isPlaygroundWeb = parsedUrl.hostname === 'playground.wordpress.net';
+		} catch {
+			isPlaygroundWeb = false;
+		}
 		let playgroundFrame: Frame | null = null;
 
 		try {
EOF
@@ -85,8 +85,14 @@
metrics: {},
};

// Check if this is playground.wordpress.net (runs in iframe)
const isPlaygroundWeb = wpAdminUrl.includes( 'playground.wordpress.net' );
// Check if this is playground.wordpress.net (runs in iframe) using hostname, not substring
let isPlaygroundWeb = false;
try {
const parsedUrl = new URL( wpAdminUrl );
isPlaygroundWeb = parsedUrl.hostname === 'playground.wordpress.net';
} catch {
isPlaygroundWeb = false;
}
let playgroundFrame: Frame | null = null;

try {
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@gcsecsey gcsecsey force-pushed the stu-1199-studio-write-a-script-to-benchmark-site-editor-performance branch from 25691e3 to 864f0b3 Compare January 9, 2026 12:28
@gcsecsey gcsecsey changed the base branch from trunk to dev/studio-cli-i2 January 9, 2026 12:29
Base automatically changed from dev/studio-cli-i2 to trunk January 16, 2026 10:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants