@@ -9,7 +9,7 @@ import { browserStepRecord } from "./browser-interactions.js"
99import { browserPreviewNetworkPolicyIsActive , browserPreviewNetworkPolicySummary , browserPreviewNeedsContextRouting , browserPreviewOrigins , browserPreviewReadinessError , browserPreviewRouting , browserPreviewSecureContextError , browserPreviewTopology , resolveBrowserPreviewUrl , routeBrowserPreviewContextNetwork } from "./browser-preview-routing.js"
1010import { browserProbeReplayability , browserProbeViewport } from "./browser-probe.js"
1111import { argValue , commaListArg , durationArg , jsonArrayArg } from "./commands.js"
12- import { editorActionStepsFromArgs , editorOpenTargetFromArgs , editorValidateContentFromArgs , editorValidateProviderFromArgs , resolveEditorOpenTarget , type EditorActionStep } from "./editor-actions.js"
12+ import { DEFAULT_EDITOR_WAIT_SELECTOR , editorActionStepsFromArgs , editorOpenTargetFromArgs , editorValidateContentFromArgs , editorValidateProviderFromArgs , resolveEditorOpenTarget , type EditorActionStep } from "./editor-actions.js"
1313import type { PlaygroundRunResponse } from "./playground-command-errors.js"
1414import type { PlaygroundCliServer } from "./preview-server.js"
1515import { serializeBrowserError } from "./browser-metrics.js"
@@ -538,6 +538,7 @@ export async function runEditorOpenCommand({
538538 let editorState : EditorStateSnapshot | undefined
539539 let editorValidity : EditorValidityArtifact | undefined
540540 let editorCanvasReadiness : BrowserEditorCanvasProbeSummary | undefined
541+ let editorReadiness : BrowserEditorReadinessSummary | undefined
541542 let authSummary : BrowserProbeAuthSummary | undefined
542543 let pendingError : Error | undefined
543544 let artifact : BrowserArtifact | undefined
@@ -577,10 +578,12 @@ export async function runEditorOpenCommand({
577578 const waitStartedAt = now ( )
578579 const waitStartedAtMs = Date . now ( )
579580 try {
580- await waitForAnyVisibleSelector ( page , target . waitSelector , waitTimeoutMs )
581- editorCanvasReadiness = await waitForEditorOpenCanvasReadiness ( page , target . waitSelector , waitTimeoutMs )
581+ const readiness = await waitForEditorOpenReadiness ( page , target . waitSelector , waitTimeoutMs )
582+ editorReadiness = readiness . editorReadiness
583+ editorCanvasReadiness = readiness . editorCanvasReadiness
582584 finalUrl = page . url ( )
583- stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : target . waitSelector } , "ok" , waitStartedAt , waitStartedAtMs , finalUrl , {
585+ stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : target . waitSelector ?? "wp.data core/block-editor.getBlocks" } , "ok" , waitStartedAt , waitStartedAtMs , finalUrl , {
586+ editorReadiness,
584587 ...( editorCanvasReadiness ? { editorCanvas : editorCanvasReadiness } : { } ) ,
585588 } as never ) )
586589 } catch ( error ) {
@@ -606,7 +609,7 @@ export async function runEditorOpenCommand({
606609 }
607610 if ( capture . has ( "screenshot" ) ) {
608611 await artifactSession . writeGenerated ( "screenshot" , "editor-screenshot.png" , async ( path ) => {
609- if ( editorCanvasReadiness ?. ready ) {
612+ if ( editorCanvasReadiness ?. ready && target . waitSelector ) {
610613 const frame = await resolveEditorCanvasFrame ( page , target . waitSelector )
611614 if ( frame ) {
612615 await frame . locator ( EDITOR_CANVAS_DEFAULT_LAYOUT_SELECTOR ) . first ( ) . screenshot ( { path, timeout : waitTimeoutMs } )
@@ -653,6 +656,7 @@ export async function runEditorOpenCommand({
653656 screenshot : capture . has ( "screenshot" ) ,
654657 ...( editorSummary ? { editor : editorSummary } : { } ) ,
655658 ...( editorValidity ? { editorValidity : editorValidity . summary } : { } ) ,
659+ ...( editorReadiness ? { editorReadiness } : { } ) ,
656660 ...( editorCanvasReadiness ? { editorCanvas : editorCanvasReadiness } : { } ) ,
657661 viewport,
658662 } ,
@@ -682,7 +686,7 @@ export async function runEditorOpenCommand({
682686 }
683687
684688 if ( pendingError ) {
685- throw new Error ( `wordpress.editor-open failed after ${ stepRecords . length } step(s): ${ pendingError . message } ` )
689+ throw editorOpenArtifactError ( stepRecords . length , pendingError , artifact )
686690 }
687691
688692 return {
@@ -727,9 +731,15 @@ export function editorOpenArtifactFilesForCapture(capture: ReadonlySet<string>,
727731 }
728732}
729733
730- async function waitForEditorOpenCanvasReadiness ( page : import ( "playwright" ) . Page , waitSelector : string , timeoutMs : number ) : Promise < BrowserEditorCanvasProbeSummary | undefined > {
734+ export async function waitForEditorOpenReadiness ( page : import ( "playwright" ) . Page , waitSelector : string | undefined , timeoutMs : number ) : Promise < { editorReadiness : BrowserEditorReadinessSummary ; editorCanvasReadiness ?: BrowserEditorCanvasProbeSummary } > {
735+ const editorReadiness = await waitForEditorSemanticReadiness ( page , timeoutMs )
736+ if ( ! waitSelector ) {
737+ return { editorReadiness }
738+ }
739+
740+ await waitForAnyVisibleSelector ( page , waitSelector , timeoutMs )
731741 if ( ! waitSelector . includes ( "editor-canvas" ) ) {
732- return undefined
742+ return { editorReadiness }
733743 }
734744
735745 const probe = await waitForEditorCanvasProbe ( page , {
@@ -744,7 +754,11 @@ async function waitForEditorOpenCanvasReadiness(page: import("playwright").Page,
744754 throw new Error ( `Editor canvas was not ready: ${ probe . summary . diagnostics . map ( ( diagnostic ) => diagnostic . code ) . join ( ", " ) || "not-ready" } ` )
745755 }
746756
747- return probe . summary
757+ return { editorReadiness, editorCanvasReadiness : probe . summary }
758+ }
759+
760+ export function editorOpenArtifactError ( stepCount : number , error : Error , artifact : BrowserArtifact ) : BrowserCommandArtifactError {
761+ return new BrowserCommandArtifactError ( `wordpress.editor-open failed after ${ stepCount } step(s): ${ error . message } ` , artifact )
748762}
749763
750764export async function runEditorActionsCommand ( {
@@ -768,6 +782,7 @@ export async function runEditorActionsCommand({
768782 server,
769783 } )
770784 const actionSteps = await editorActionStepsFromArgs ( args )
785+ const editorWaitSelector = target . waitSelector ?? DEFAULT_EDITOR_WAIT_SELECTOR
771786 const capture = new Set ( commaListArg ( args , "capture" ) )
772787 if ( capture . size === 0 ) {
773788 capture . add ( "steps" )
@@ -845,13 +860,13 @@ export async function runEditorActionsCommand({
845860 const waitStartedAt = now ( )
846861 const waitStartedAtMs = Date . now ( )
847862 try {
848- await waitForAnyVisibleSelector ( page , target . waitSelector , waitTimeoutMs )
863+ await waitForAnyVisibleSelector ( page , editorWaitSelector , waitTimeoutMs )
849864 finalUrl = page . url ( )
850- stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : target . waitSelector } , "ok" , waitStartedAt , waitStartedAtMs , finalUrl , { } ) )
865+ stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : editorWaitSelector } , "ok" , waitStartedAt , waitStartedAtMs , finalUrl , { } ) )
851866 } catch ( error ) {
852867 const serialized = serializeBrowserError ( "probe-error" , error )
853868 errors . push ( serialized )
854- stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : target . waitSelector } , "failed" , waitStartedAt , waitStartedAtMs , page . url ( ) , { error : serialized } ) )
869+ stepRecords . push ( browserStepRecord ( 1 , { kind : "waitFor" , selector : editorWaitSelector } , "failed" , waitStartedAt , waitStartedAtMs , page . url ( ) , { error : serialized } ) )
855870 pendingError = error instanceof Error ? error : new Error ( String ( error ) )
856871 }
857872 }
@@ -1113,14 +1128,14 @@ async function waitForEditorReadiness(page: import("playwright").Page, timeoutMs
11131128 const editor = select ( "core/editor" )
11141129 const blockEditor = select ( "core/block-editor" )
11151130 const editorDispatch = dispatch ( "core/editor" )
1116- if ( ! editor || ! blockEditor || ! editorDispatch ) {
1131+ if ( ! editor || ! blockEditor || ! editorDispatch || typeof editorDispatch . savePost !== "function" ) {
11171132 return false
11181133 }
11191134 return {
11201135 schema : "wp-codebox/editor-readiness/v1" ,
11211136 status : "ready" ,
11221137 storesAvailable : true ,
1123- canSave : typeof editorDispatch . savePost === "function" ,
1138+ canSave : true ,
11241139 postId : typeof editor . getCurrentPostId === "function" ? editor . getCurrentPostId ( ) : undefined ,
11251140 postType : typeof editor . getCurrentPostType === "function" ? editor . getCurrentPostType ( ) : undefined ,
11261141 }
@@ -1133,6 +1148,49 @@ async function waitForEditorReadiness(page: import("playwright").Page, timeoutMs
11331148 } )
11341149}
11351150
1151+ // Opening and validating an editor require the block-editor data store. Global
1152+ // block APIs and save availability are stricter, separate capabilities.
1153+ async function waitForEditorSemanticReadiness ( page : import ( "playwright" ) . Page , timeoutMs : number ) : Promise < BrowserEditorReadinessSummary > {
1154+ return page . waitForFunction ( ( ) => {
1155+ const win = window as unknown as {
1156+ wp ?: {
1157+ blocks ?: { parse ?: unknown ; getBlockTypes ?: ( ) => unknown [ ] }
1158+ data ?: { select ?: ( store : string ) => Record < string , unknown > ; dispatch ?: ( store : string ) => Record < string , unknown > }
1159+ }
1160+ }
1161+ const select = win . wp ?. data ?. select
1162+ if ( typeof select !== "function" ) {
1163+ return false
1164+ }
1165+
1166+ const blockEditor = select ( "core/block-editor" )
1167+ if ( ! blockEditor || typeof blockEditor . getBlocks !== "function" ) {
1168+ return false
1169+ }
1170+
1171+ const wpBlocks = win . wp ?. blocks
1172+ const blockTypes = typeof wpBlocks ?. getBlockTypes === "function" ? wpBlocks . getBlockTypes ( ) : undefined
1173+ const dispatch = win . wp ?. data ?. dispatch
1174+ const editor = select ( "core/editor" )
1175+ const editorDispatch = typeof dispatch === "function" ? dispatch ( "core/editor" ) : undefined
1176+ return {
1177+ schema : "wp-codebox/editor-readiness/v1" ,
1178+ status : "ready" ,
1179+ storesAvailable : Boolean ( editor && blockEditor ) ,
1180+ canSave : typeof editorDispatch ?. savePost === "function" ,
1181+ ...( Array . isArray ( blockTypes ) ? { blockTypesRegistered : blockTypes . length } : { } ) ,
1182+ postId : typeof editor ?. getCurrentPostId === "function" ? editor . getCurrentPostId ( ) : undefined ,
1183+ postType : typeof editor ?. getCurrentPostType === "function" ? editor . getCurrentPostType ( ) : undefined ,
1184+ }
1185+ } , undefined , { timeout : timeoutMs } ) . then ( async ( handle ) => {
1186+ const readiness = await handle . jsonValue ( ) as BrowserEditorReadinessSummary | false
1187+ if ( ! readiness ) {
1188+ throw new Error ( "wp-codebox-editor-readiness-timeout: Gutenberg block runtime did not become available" )
1189+ }
1190+ return readiness
1191+ } )
1192+ }
1193+
11361194async function saveEditorPost ( page : import ( "playwright" ) . Page , step : Extract < EditorActionStep , { kind : "savePost" } > , timeoutMs : number ) : Promise < BrowserEditorSaveSummary > {
11371195 const save = await page . evaluate ( async ( input ) => {
11381196 const win = window as unknown as {
@@ -1601,6 +1659,7 @@ export async function runEditorValidateBlocksCommand({
16011659 runtimeSpec,
16021660 server,
16031661 } )
1662+ const editorWaitSelector = target . waitSelector ?? DEFAULT_EDITOR_WAIT_SELECTOR
16041663 const content = await editorValidateContentFromArgs ( args )
16051664 const provider = editorValidateProviderFromArgs ( args )
16061665 const waitTimeoutMs = durationArg ( args , "wait-timeout" , EDITOR_VALIDATE_BLOCKS_READY_TIMEOUT_MS )
@@ -1639,7 +1698,7 @@ export async function runEditorValidateBlocksCommand({
16391698
16401699 await page . goto ( targetUrl , { waitUntil : "domcontentloaded" , timeout : waitTimeoutMs } )
16411700 finalUrl = page . url ( )
1642- await waitForAnyVisibleSelector ( page , target . waitSelector , waitTimeoutMs )
1701+ await waitForAnyVisibleSelector ( page , editorWaitSelector , waitTimeoutMs )
16431702 await waitForEditorBlocksRuntime ( page , waitTimeoutMs )
16441703 finalUrl = page . url ( )
16451704 validation = await validateEditorBlocks ( page , { content, provider } )
0 commit comments