@@ -9,7 +9,7 @@ import { runBrowserActionsCommand, runBrowserScenarioCommand } from "../packages
99import { isBrowserCommandArtifactError } from "../packages/runtime-playground/src/browser-command-artifact-error.js"
1010import { runBrowserMultiActorScenarioCommand } from "../packages/runtime-playground/src/browser-multi-actor-scenario-runner.js"
1111import { runBrowserProbeCommand } from "../packages/runtime-playground/src/browser-probe-runner.js"
12- import { runEditorCanvasProbeCommand , runEditorOpenCommand } from "../packages/runtime-playground/src/editor-command-runners.js"
12+ import { parseEditorPresentationContract , runEditorCanvasProbeCommand , runEditorOpenCommand } from "../packages/runtime-playground/src/editor-command-runners.js"
1313import { closeHttpServer , listenLocalHttpServer , type PlaygroundCliServer } from "../packages/runtime-playground/src/preview-server.js"
1414import { withTempDir } from "../scripts/test-kit.js"
1515
@@ -25,6 +25,7 @@ const SLOW_PRESENTATION_IDENTITY = "1".repeat(64)
2525const PENDING_STYLES_PRESENTATION_IDENTITY = "2" . repeat ( 64 )
2626const GROWING_PRESENTATION_IDENTITIES = [ "3" . repeat ( 64 ) , "4" . repeat ( 64 ) ]
2727const DELAYED_POST_PRESENTATION_IDENTITY = "5" . repeat ( 64 )
28+ const EDITOR_PRESENTATION_CONTRACT_MARKER = "WP_CODEBOX_EDITOR_PRESENTATION_CONTRACT:"
2829const matchedPresentationMarkup = `<style>html,body{margin:0}.block-editor-block-list__layout{box-sizing:border-box;width:200px;height:400px;background:linear-gradient(#123,#abc);color:white;padding:12px}</style><div class="block-editor-block-list__layout">Matched presentation</div>`
2930const editorShell = `<!doctype html><script>
3031globalThis.__name = (value) => value
@@ -45,14 +46,25 @@ window.wp = {
4546const editorHtml = `${ editorShell } <iframe name="unrelated" srcdoc="<style>/* blocks-engine-presentation:${ UNRELATED_PRESENTATION_IDENTITY } */<\/style>"></iframe><iframe name="editor-canvas" srcdoc="<script>globalThis.__name = (value) => value;setTimeout(() => { const style = document.createElement('style'); style.textContent = '/* blocks-engine-presentation:${ CANVAS_PRESENTATION_IDENTITY } */'; document.head.append(style) }, 400)<\/script><div class='block-editor-block-list__layout'><div class='block-editor-block-list__block' data-block='fixture'>Block</div></div>"></iframe>`
4647const onboardingEditorHtml = `${ editorShell } <script>setTimeout(() => document.body.insertAdjacentHTML('afterbegin', '<div class=components-guide>Late guide without controls</div>'), 1000); const fixtureSelect = wp.data.select; wp.data.select = (store) => store === 'core/edit-post' ? { isFeatureActive: () => true } : fixtureSelect(store); wp.data.dispatch = (store) => store === 'core/preferences' ? { set: (scope, feature, value) => { if (scope === 'core/edit-post' && feature === 'welcomeGuide' && value === false) document.querySelector('.components-guide')?.remove() } } : store === 'core/edit-post' ? { toggleFeature: () => { document.body.insertAdjacentHTML('afterbegin', '<div class=components-guide>Retoggled guide</div>') } } : ({})<\/script><iframe name="editor-canvas" srcdoc="<div class='block-editor-block-list__layout'><div class='block-editor-block-list__block' data-block='fixture'>Block</div></div>"></iframe>`
4748const matchedPresentationEditorHtml = `${ editorShell } <iframe name="editor-canvas" style="border:0;width:200px;height:80px" srcdoc="${ matchedPresentationMarkup . replaceAll ( '"' , '"' ) } "></iframe>`
48- const parentCanvasEditorHtml = `${ editorShell } <style>/* blocks-engine-presentation:${ PARENT_CANVAS_PRESENTATION_IDENTITY } */</style><div class="block-editor-block-list__layout"><div class="block-editor-block-list__block" data-block="fixture">Block</div></div>`
49+ const parentCanvasEditorHtml = `${ editorShell } <style>/* blocks-engine-presentation:${ PARENT_CANVAS_PRESENTATION_IDENTITY } */</style><div class="block-editor-block-list__layout" hidden>Hidden duplicate</div><div class="block-editor-block-list__layout" ><div class="block-editor-block-list__block" data-block="fixture">Block</div></div>`
4950const replacingCanvasEditorHtml = `${ editorShell } <iframe name="editor-canvas" srcdoc="<style>/* blocks-engine-presentation:${ INITIAL_CANVAS_PRESENTATION_IDENTITY } */<\/style>"></iframe><script>setTimeout(() => { document.querySelector('iframe[name=editor-canvas]').srcdoc = '<style>/* blocks-engine-presentation:${ REPLACED_CANVAS_PRESENTATION_IDENTITY } */<\\/style>' }, 150)</script>`
5051const delayedCanvasEditorHtml = `${ editorShell } <div class="block-editor-block-list__layout"><div class="block-editor-block-list__block" data-block="transition">Transition</div></div><script>setTimeout(() => { const iframe = document.createElement('iframe'); iframe.name = 'editor-canvas'; iframe.srcdoc = '<style>/* blocks-engine-presentation:${ DELAYED_CANVAS_PRESENTATION_IDENTITY } */<\\/style>'; document.body.append(iframe) }, 300)</script>`
5152const slowPresentationEditorHtml = `${ editorShell } <iframe name="editor-canvas" srcdoc="<script>setTimeout(() => { const style = document.createElement('style'); style.textContent = '/* blocks-engine-presentation:${ SLOW_PRESENTATION_IDENTITY } */'; document.head.append(style) }, 3500)<\/script><div class='block-editor-block-list__layout'><div class='block-editor-block-list__block' data-block='slow'>Slow</div></div>"></iframe>`
5253const pendingStylesEditorHtml = `${ editorShell } <iframe name="editor-canvas" srcdoc="<link rel='stylesheet' href='http://127.0.0.1:9/unavailable.css'><style>/* blocks-engine-presentation:${ PENDING_STYLES_PRESENTATION_IDENTITY } */<\/style><div class='block-editor-block-list__layout'><div class='block-editor-block-list__block' data-block='pending'>Pending</div></div>"></iframe>`
5354const delayedPostEditorHtml = `${ editorShell } <script>const fixtureSelect = wp.data.select; let currentPostId = null; wp.data.select = (store) => store === 'core/editor' ? { getCurrentPostId: () => currentPostId, getCurrentPostType: () => currentPostId ? 'page' : null } : fixtureSelect(store); setTimeout(() => { currentPostId = 4; const iframe = document.createElement('iframe'); iframe.name = 'editor-canvas'; iframe.srcdoc = "<style>/* blocks-engine-presentation:${ DELAYED_POST_PRESENTATION_IDENTITY } */<\\/style><div class='block-editor-block-list__layout'>Hydrated post</div>"; document.body.append(iframe) }, 1500)<\/script>`
5455const growingPresentationEditorHtml = `${ editorShell } <iframe name="editor-canvas" srcdoc="<style>/* blocks-engine-presentation:${ GROWING_PRESENTATION_IDENTITIES [ 0 ] } */<\/style><script>let identity = 0; setInterval(() => { const style = document.createElement('style'); style.textContent = '/* blocks-engine-presentation:' + (identity++).toString(16).padStart(64, '9') + ' */'; document.head.append(style) }, 100); setTimeout(() => { const style = document.createElement('style'); style.textContent = '/* blocks-engine-presentation:${ GROWING_PRESENTATION_IDENTITIES [ 1 ] } */'; document.head.append(style) }, 4100)<\/script><div class='block-editor-block-list__layout'><div class='block-editor-block-list__block' data-block='growing'>Growing</div></div>"></iframe>`
5556
57+ function editorPresentationContractOutput ( value : unknown ) : string {
58+ return `${ EDITOR_PRESENTATION_CONTRACT_MARKER } ${ Buffer . from ( JSON . stringify ( value ) ) . toString ( "base64" ) } \n`
59+ }
60+
61+ test ( "editor presentation contract isolates framed JSON from PHP diagnostics" , ( ) => {
62+ const warning = '<br />\n<b>Warning</b>: Undefined array key "css" in <b>/wordpress/wp-includes/block-editor.php</b> on line <b>123</b><br />\n'
63+ const value = { identities : GROWING_PRESENTATION_IDENTITIES , complete : true }
64+ assert . deepEqual ( parseEditorPresentationContract ( `${ warning } ${ editorPresentationContractOutput ( value ) } ` ) , value )
65+ assert . throws ( ( ) => parseEditorPresentationContract ( warning ) , / U n d e f i n e d a r r a y k e y " c s s " / )
66+ } )
67+
5668test ( "real browser commands sanitize console, artifacts, stdout, and failure stderr" , async ( ) => {
5769 const httpServer = createServer ( ( request , response ) => {
5870 response . setHeader ( "content-type" , "text/html" )
@@ -98,7 +110,7 @@ test("real browser commands sanitize console, artifacts, stdout, and failure std
98110 } as RuntimeCreateSpec
99111 const runPlaygroundCommand = async ( command : string ) => ( {
100112 text : command === "wordpress.editor-open.capture-presentation-contract"
101- ? JSON . stringify ( { identities : GROWING_PRESENTATION_IDENTITIES , complete : true } )
113+ ? editorPresentationContractOutput ( { identities : GROWING_PRESENTATION_IDENTITIES , complete : true } )
102114 : "[]" ,
103115 exitCode : 0 ,
104116 } )
@@ -257,7 +269,7 @@ test("real browser commands sanitize console, artifacts, stdout, and failure std
257269 artifactRoot,
258270 runPlaygroundCommand : async ( command ) => ( {
259271 text : command . includes ( "capture-presentation-contract" )
260- ? JSON . stringify ( { identities : [ DELAYED_POST_PRESENTATION_IDENTITY ] , complete : true } )
272+ ? editorPresentationContractOutput ( { identities : [ DELAYED_POST_PRESENTATION_IDENTITY ] , complete : true } )
261273 : "[]" ,
262274 exitCode : 0 ,
263275 } ) ,
0 commit comments