Skip to content
Merged
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
8 changes: 6 additions & 2 deletions packages/runtime-core/src/command-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1186,9 +1186,13 @@ export const commandRegistry = [
},
{
id: "wordpress.editor-canvas-probe",
description: "Open a WordPress editor URL and wait for the iframe-backed block canvas to become visible, non-loading, and populated before capturing selector diagnostics and optional canvas screenshot evidence.",
description: "Open a WordPress editor target and wait for the iframe-backed block canvas to become visible, non-loading, and populated before capturing selector diagnostics and optional canvas screenshot evidence.",
acceptedArgs: [
{ name: "url", description: "Editor path or absolute URL to visit.", required: true, format: "path or URL" },
{ name: "target", description: "Editor target to open; defaults to post-new. Use front-page to open the site's configured static front page.", format: "post-new|site|front-page" },
{ name: "post-id", description: "Existing post ID to open in the post editor.", format: "positive integer" },
{ name: "post-slug", description: "Existing post slug or hierarchical path to resolve and open in the post editor.", format: "post slug or path" },
{ name: "post-type", description: "Post type for post-new, post-id, or post-slug targets; defaults to post.", format: "post type slug" },
{ name: "url", description: "Explicit editor path or absolute URL to open instead of resolving a target.", format: "path or URL" },
{ name: "iframe-selector", description: "Editor canvas iframe selector; defaults to iframe[name=\"editor-canvas\"].", format: "CSS selector" },
{ name: "layout-selector", description: "Canvas layout selector inside the editor iframe; defaults to .block-editor-block-list__layout.", format: "CSS selector" },
{ name: "block-selector", description: "Block selector inside the editor canvas layout; defaults to .block-editor-block-list__block, [data-block].", format: "CSS selector" },
Expand Down
16 changes: 10 additions & 6 deletions packages/runtime-playground/src/editor-command-runners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { browserPreviewCleanupErrorIsFatal, browserPreviewNetworkPolicyIsActive,
import { browserCommandResult } from "./browser-result-sanitization.js"
import { browserProbeReplayability, browserProbeViewport } from "./browser-probe.js"
import { argValue, commaListArg, durationArg, jsonArrayArg } from "./commands.js"
import { DEFAULT_EDITOR_WAIT_SELECTOR, editorActionStepsFromArgs, editorOpenTargetFromArgs, editorValidateContentFromArgs, editorValidateProviderFromArgs, resolveEditorOpenTarget, type EditorActionStep, type EditorBlockSpec, type EditorBlockTarget, type EditorOpenTarget } from "./editor-actions.js"
import { DEFAULT_EDITOR_WAIT_SELECTOR, editorActionStepsFromArgs, editorOpenTargetFromArgs, editorValidateContentFromArgs, editorValidateProviderFromArgs, resolveEditorOpenTarget, type EditorActionStep, type EditorBlockSpec, type EditorBlockTarget, type EditorOpenTarget, type RunPlaygroundCommand } from "./editor-actions.js"
import { assertPlaygroundResponseOk, attachPlaygroundDiagnostics, type PlaygroundRunResponse } from "./playground-command-errors.js"
import type { PlaygroundCliServer } from "./preview-server.js"
import { serializeBrowserError } from "./browser-metrics.js"
Expand Down Expand Up @@ -49,20 +49,24 @@ function editorCommandPreviewTopology(args: string[], runtimeSpec: RuntimeCreate

export async function runEditorCanvasProbeCommand({
artifactRoot,
runPlaygroundCommand,
runtimeSpec,
server,
spec,
}: {
artifactRoot: string
runPlaygroundCommand: RunPlaygroundCommand
runtimeSpec: RuntimeCreateSpec
server: PlaygroundCliServer
spec: ExecutionSpec
}): Promise<{ artifact: BrowserArtifact; output: string }> {
const args = spec.args ?? []
const urlArg = argValue(args, "url")?.trim()
if (!urlArg) {
throw new Error("wordpress.editor-canvas-probe requires url=<path-or-url>")
}
const target = await resolveEditorOpenTarget(editorOpenTargetFromArgs(args), {
command: "wordpress.editor-canvas-probe",
runPlaygroundCommand,
runtimeSpec,
server,
})

const capture = new Set(commaListArg(args, "capture"))
for (const item of capture) {
Expand All @@ -79,7 +83,7 @@ export async function runEditorCanvasProbeCommand({
const topology = browserPreviewTopology(args, runtimeSpec, server.serverUrl, server.previewProxyDiagnostics?.targetOrigin)
const { preview, networkPolicy } = topology
const previewOrigins = topology.origins
const targetUrl = topology.resolveUrl(urlArg)
const targetUrl = topology.resolveUrl(target.url)
const artifactSession = new BrowserArtifactSession(artifactRoot, "files/browser", { source: "wordpress.editor-canvas-probe", operation: "editor-canvas-probe" })
const screenshotPath = artifactSession.absolutePath("editor-canvas-screenshot.png")
const startedAt = now()
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-playground/src/playground-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ class PlaygroundRuntime implements Runtime {
const server = await this.bootPlayground()
let result: Awaited<ReturnType<typeof runEditorCanvasProbeCommand>>
try {
result = await runEditorCanvasProbeCommand({ artifactRoot: this.artifactRoot, runtimeSpec: this.spec, server, spec })
result = await runEditorCanvasProbeCommand({ artifactRoot: this.artifactRoot, runPlaygroundCommand: (command, targetServer, options) => this.runPlaygroundCommand(command, targetServer, options), runtimeSpec: this.spec, server, spec })
} catch (error) {
if (isBrowserCommandArtifactError(error)) {
this.browserProbes.push(error.artifact)
Expand Down
11 changes: 8 additions & 3 deletions tests/browser-routed-command-security.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ test("editor presentation contract isolates framed JSON from PHP diagnostics", (
test("real browser commands sanitize console, artifacts, stdout, and failure stderr", async () => {
const httpServer = createServer((request, response) => {
response.setHeader("content-type", "text/html")
response.end(request.url?.includes("post=4")
response.end(request.url?.includes("post=7")
? editorHtml
: request.url?.includes("post=4")
? delayedPostEditorHtml
: request.url?.includes("post=2")
? missingPresentationEditorHtml
Expand Down Expand Up @@ -137,7 +139,9 @@ test("real browser commands sanitize console, artifacts, stdout, and failure std
const runPlaygroundCommand = async (command: string) => ({
text: command === "wordpress.editor-open.capture-presentation-contract"
? editorPresentationContractOutput({ identities: GROWING_PRESENTATION_IDENTITIES, complete: true })
: "[]",
: command === "wordpress.editor-canvas-probe.resolve-front-page"
? "7"
: "[]",
exitCode: 0,
})

Expand Down Expand Up @@ -399,9 +403,10 @@ test("real browser commands sanitize console, artifacts, stdout, and failure std
await withTempDir("wp-codebox-real-editor-canvas-security-", async (artifactRoot) => {
const result = await runEditorCanvasProbeCommand({
artifactRoot,
runPlaygroundCommand,
runtimeSpec,
server,
spec: { command: "wordpress.editor-canvas-probe", args: [`url=${PUBLIC_URL}`, "route-host=routed.test", "timeout=5s"] },
spec: { command: "wordpress.editor-canvas-probe", args: ["target=front-page", "route-host=routed.test", "timeout=5s"] },
})
await assertCommandSurfacesSafe(result, artifactRoot, ["files/browser/editor-canvas-summary.json"])
})
Expand Down
6 changes: 6 additions & 0 deletions tests/editor-actions.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import assert from "node:assert/strict"
import { getCommandDefinition } from "../packages/runtime-core/src/command-registry.js"
import { assertEditorMutationPostcondition, captureEditorIdleCanvas, captureEditorState, captureEditorValidity, editorCommandWordPressUrl, editorOpenArtifactError, editorOpenArtifactFilesForCapture, editorOpenArtifactPathPrefixFromArgs, executeEditorActionStep, summarizeEditorPresentation, type EditorStateSnapshot, waitForEditorOpenReadiness } from "../packages/runtime-playground/src/editor-command-runners.js"
import { isBrowserCommandArtifactError } from "../packages/runtime-playground/src/browser-command-artifact-error.js"
import { editorActionStepsFromArgs, editorOpenTargetFromArgs, resolveEditorOpenTarget } from "../packages/runtime-playground/src/editor-actions.js"
Expand Down Expand Up @@ -394,6 +395,11 @@ assert.equal(nestedValidity.warnings[0]?.clientId, "nested-invalid")
const frontPageTarget = editorOpenTargetFromArgs(["target=front-page"])
assert.equal(frontPageTarget.kind, "front-page")
assert.equal(frontPageTarget.url, "")
const canvasProbeArgs = getCommandDefinition("wordpress.editor-canvas-probe")?.acceptedArgs ?? []
for (const name of ["target", "post-id", "post-slug", "post-type", "url"]) {
assert.equal(canvasProbeArgs.some((arg) => arg.name === name), true, `editor-canvas-probe accepts ${name}`)
}
assert.equal(canvasProbeArgs.find((arg) => arg.name === "url")?.required, undefined)
const editorRuntimeSpec = { wp: "latest", environment: {} } as never
assert.equal(editorCommandWordPressUrl({ serverUrl: "http://preview.test", wordpressUrl: "http://wordpress.test" } as never), "http://wordpress.test")
assert.equal(editorCommandWordPressUrl({ serverUrl: "http://wordpress.test" } as never), "http://wordpress.test")
Expand Down
Loading