Skip to content
Open
Show file tree
Hide file tree
Changes from all 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