Skip to content

Commit a3e857e

Browse files
authored
Merge pull request #510 from Automattic/feature/issue-508-editor-actions
Add generic editor action command
2 parents a94931c + c8a2adb commit a3e857e

9 files changed

Lines changed: 628 additions & 3 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"browser-actions-artifact-smoke": "tsx scripts/browser-actions-artifact-smoke.ts",
7171
"browser-html-capture-smoke": "tsx scripts/browser-html-capture-smoke.ts",
7272
"editor-open-artifact-smoke": "tsx scripts/editor-open-artifact-smoke.ts",
73+
"editor-actions-artifact-smoke": "tsx scripts/editor-actions-artifact-smoke.ts",
7374
"browser-review-bridge-smoke": "tsx scripts/browser-review-bridge-smoke.ts",
7475
"browser-interaction-script-validation-smoke": "tsx scripts/browser-interaction-script-validation-smoke.ts",
7576
"preview-port-smoke": "tsx scripts/preview-port-smoke.ts",

packages/cli/src/recipe-validation.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,43 @@ async function validateRecipeStepArgs(step: WorkspaceRecipe["workflow"]["steps"]
616616
return
617617
}
618618

619+
if (step.command === "wordpress.editor-actions") {
620+
const stepsJson = recipeStepArgValue(step.args ?? [], "steps-json")
621+
if (!stepsJson) {
622+
addIssue("missing-steps", `${path}.args`, "wordpress.editor-actions requires steps-json=<array>.")
623+
}
624+
625+
if (stepsJson && !stepsJson.startsWith("@")) {
626+
let parsed: unknown
627+
try {
628+
parsed = JSON.parse(stepsJson)
629+
} catch (error) {
630+
addIssue("invalid-steps-json", `${path}.args`, `wordpress.editor-actions steps-json must be valid JSON: ${error instanceof Error ? error.message : String(error)}`)
631+
parsed = undefined
632+
}
633+
if (parsed !== undefined && !Array.isArray(parsed)) {
634+
addIssue("invalid-steps-json", `${path}.args`, "wordpress.editor-actions steps-json must be a JSON array.")
635+
}
636+
}
637+
638+
for (const name of ["wait-timeout", "step-timeout", "timeout"] as const) {
639+
const value = recipeStepArgValue(step.args ?? [], name)
640+
if (value && /^\d+(?:\.\d+)?(?:ms|s)$/.test(value) === false) {
641+
addIssue("invalid-duration", `${path}.args`, `wordpress.editor-actions ${name} must look like 500ms or 2s.`)
642+
}
643+
}
644+
645+
const capture = recipeStepArgValue(step.args ?? [], "capture")
646+
if (capture) {
647+
for (const item of capture.split(",").map((value) => value.trim()).filter(Boolean)) {
648+
if (!["steps", "console", "errors", "html", "screenshot", "editor-state"].includes(item)) {
649+
addIssue("invalid-capture", `${path}.args`, `wordpress.editor-actions capture does not support: ${item}`)
650+
}
651+
}
652+
}
653+
return
654+
}
655+
619656
if (step.command === "wordpress.ability") {
620657
if (!recipeStepArgValue(step.args ?? [], "name")?.trim()) {
621658
addIssue("missing-ability-name", `${path}.args`, "wordpress.ability requires name=<ability-name>.")

packages/runtime-core/src/command-registry.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,26 @@ export const commandRegistry = [
226226
recipe: true,
227227
handler: { kind: "playground", method: "runEditorOpen" },
228228
},
229+
{
230+
id: "wordpress.editor-actions",
231+
description: "Open a generic WordPress block editor target, run a bounded editor action script, and capture replayable mutation evidence artifacts.",
232+
acceptedArgs: [
233+
{ name: "target", description: "Editor target to open; defaults to post-new.", format: "post-new|site" },
234+
{ name: "post-id", description: "Existing post ID to open in the post editor.", format: "positive integer" },
235+
{ name: "post-type", description: "Post type for post-new or post-id targets; defaults to post.", format: "post type slug" },
236+
{ name: "url", description: "Explicit editor path or absolute URL to open instead of resolving a target.", format: "path or URL" },
237+
{ name: "steps-json", description: "Ordered editor action script: open, insertBlock, selectBlock, and inspectState.", required: true, format: "JSON array (inline or @<path>)" },
238+
{ name: "wait-selector", description: "Selector that marks the editor as ready; defaults to the block editor shell.", format: "CSS selector" },
239+
{ name: "wait-timeout", description: "Timeout for navigation and editor-ready waits.", format: "duration, e.g. 15s or 500ms" },
240+
{ name: "step-timeout", description: "Per-action timeout applied to each editor action step.", format: "duration, e.g. 15s or 500ms" },
241+
{ name: "timeout", description: "Total-script timeout bounding the whole editor action run.", format: "duration, e.g. 30s or 1500ms" },
242+
{ name: "capture", description: "Comma-separated artifacts to capture after actions.", format: "steps,console,errors,html,screenshot,editor-state" },
243+
],
244+
outputShape: "JSON summary plus files/browser/editor-action-steps.jsonl, editor-action-summary.json, editor-action-state.json, and optional console/errors/html/screenshot artifacts.",
245+
policyRequirement: "Runtime policy commands must include wordpress.editor-actions.",
246+
recipe: true,
247+
handler: { kind: "playground", method: "runEditorActions" },
248+
},
229249
{
230250
id: "wordpress.browser-actions.evaluate",
231251
description: "Policy capability gating arbitrary page-side JavaScript (the evaluate step) inside wordpress.browser-actions. Non-JS interaction steps do not require this capability.",

0 commit comments

Comments
 (0)