Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
1 change: 1 addition & 0 deletions .wp-env.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"config": {
"WP_DEBUG": true,
"WP_DEBUG_LOG": true,
"WP_DEBUG_DISPLAY": false,
"WP_ENVIRONMENT_TYPE": "local"
}
}
37 changes: 37 additions & 0 deletions bin/wp-env-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,40 @@ cat > "$CREDENTIALS_FILE" <<EOF
EOF

echo "Credentials written to $CREDENTIALS_FILE"

# ---------------------------------------------------------------------------
# Wait until WordPress is fully provisioned
#
# The Playground runtime keeps installing plugins (Blueprint steps) after it
# starts accepting requests. During those installs WordPress drops a
# `.maintenance` file and serves a 503, and there is a brief race where the
# file is removed mid-request and PHP fatals. Both corrupt the first editor
# REST call, so we gate here until the settings endpoint returns valid JSON.
# ---------------------------------------------------------------------------

echo "Waiting for the editor settings endpoint to be ready..."

SETTINGS_URL="$SITE_URL/?rest_route=/wp-block-editor/v1/settings"
READY_MAX_RETRIES=45
READY_RETRY_INTERVAL=2

for attempt in $(seq 1 $READY_MAX_RETRIES); do
if curl -s -H "Authorization: $AUTH_HEADER" "$SETTINGS_URL" | node -e "
let data = '';
process.stdin.on('data', chunk => data += chunk);
process.stdin.on('end', () => {
try { JSON.parse(data); }
catch { process.exit(1); }
});
"; then
echo "Editor settings endpoint is ready."
break
fi

if [ "$attempt" -eq "$READY_MAX_RETRIES" ]; then
echo "Error: editor settings endpoint did not return valid JSON after $((READY_MAX_RETRIES * READY_RETRY_INTERVAL)) seconds."
exit 1
fi

sleep $READY_RETRY_INTERVAL
done
89 changes: 66 additions & 23 deletions e2e/wp-env-fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,64 @@ let cachedEditorSettings = null;
/** Cached editor assets — fetched once and reused across all tests. */
let cachedEditorAssets = null;

const MAX_FETCH_ATTEMPTS = 15;
const FETCH_RETRY_DELAY_MS = 2000;

const delay = ( ms ) => new Promise( ( resolve ) => setTimeout( resolve, ms ) );

/**
* Fetch and parse JSON from a wp-env REST endpoint, retrying on transient
* failures.
*
* During warm-up the Playground/wp-env instance can briefly return a 5xx or
* leak a PHP notice into the body, which corrupts the JSON. Both surface here
* as errors we retry, and the last failure carries a snippet of the body so a
* genuine leak is diagnosable instead of an opaque "Unexpected token '<'".
*
* @param {string} url Fully-qualified REST URL.
* @param {Object} creds Credentials object from readCredentials().
* @param {string} label Human-readable resource name for error messages.
* @return {Promise<Object>} The parsed JSON response.
*/
async function fetchJson( url, creds, label ) {
let lastError;

for ( let attempt = 1; attempt <= MAX_FETCH_ATTEMPTS; attempt++ ) {
try {
const response = await fetch( url, {
headers: { Authorization: creds.authHeader },
} );

const body = await response.text();

if ( ! response.ok ) {
throw new Error(
`HTTP ${ response.status } ${
response.statusText
} — ${ body.slice( 0, 200 ) }`
);
}

try {
return JSON.parse( body );
} catch {
throw new Error(
`response was not valid JSON — ${ body.slice( 0, 200 ) }`
);
}
} catch ( error ) {
lastError = error;
if ( attempt < MAX_FETCH_ATTEMPTS ) {
await delay( FETCH_RETRY_DELAY_MS );
}
}
}

throw new Error(
`Failed to fetch ${ label } after ${ MAX_FETCH_ATTEMPTS } attempts: ${ lastError.message }`
);
}

/**
* Fetch editor settings from the wp-env WordPress instance.
*
Expand All @@ -44,20 +102,11 @@ async function fetchEditorSettings( creds ) {
return cachedEditorSettings;
}

const response = await fetch(
cachedEditorSettings = await fetchJson(
`${ creds.siteApiRoot }wp-block-editor/v1/settings`,
{
headers: { Authorization: creds.authHeader },
}
creds,
'editor settings'
);

if ( ! response.ok ) {
throw new Error(
`Failed to fetch editor settings: ${ response.status } ${ response.statusText }`
);
}

cachedEditorSettings = await response.json();
return cachedEditorSettings;
}

Expand All @@ -75,17 +124,11 @@ async function fetchEditorAssets( creds ) {
const url = new URL( `${ creds.siteApiRoot }wpcom/v2/editor-assets` );
url.searchParams.set( 'exclude', 'core,gutenberg' );

const response = await fetch( url.toString(), {
headers: { Authorization: creds.authHeader },
} );

if ( ! response.ok ) {
throw new Error(
`Failed to fetch editor assets: ${ response.status } ${ response.statusText }`
);
}

cachedEditorAssets = await response.json();
cachedEditorAssets = await fetchJson(
url.toString(),
creds,
'editor assets'
);
return cachedEditorAssets;
}

Expand Down
Loading