From e79d6571ba7a88d4ec006486774ece75e102b26f Mon Sep 17 00:00:00 2001 From: Reynold Hu Date: Mon, 12 Oct 2026 17:36:39 +0800 Subject: [PATCH] Actions: replay tests for crop, filters, selections and styled text on a second document (AUTO-219-4) (#3566) * test(engine): a recorded edit run replays on a second document * docs(scorecard): mark AUTO-219-4 done; the replay tests cover all four families --- crates/engine/src/actions_cmds.rs | 40 +++++++++++++++++++++++++++++++ docs/scorecard.md | 4 ++-- scorecard/automation.toml | 4 ++-- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/actions_cmds.rs b/crates/engine/src/actions_cmds.rs index 9b4a758d..f6707c1b 100644 --- a/crates/engine/src/actions_cmds.rs +++ b/crates/engine/src/actions_cmds.rs @@ -1224,4 +1224,44 @@ mod tests { assert!(bad.actions.list.is_empty()); let _ = std::fs::remove_dir_all(&dir); } + + /// AUTO-219-4: a recording made on one document replays on a second one — a crop, a filter, + /// a selection fill and styled text all act on the document the replay runs in. + #[test] + fn a_recorded_edit_run_replays_on_a_second_document() { + let mut s = Session::new(); + s.execute("file.new", json!({"width": 64, "height": 64, "background": "white"})).unwrap(); + s.execute("actions.record", json!({"name": "Studio"})).unwrap(); + s.execute("image.crop", json!({"x": 8, "y": 8, "width": 48, "height": 48})).unwrap(); + s.execute("filter.blur.gaussianBlur", json!({"radius": 2})).unwrap(); + s.execute("select.rect", json!({"x": 10, "y": 10, "width": 12, "height": 12})).unwrap(); + s.execute("edit.fill", json!({"color": "#ff0000"})).unwrap(); + s.execute("select.deselect", json!({})).unwrap(); + s.execute("type.create", json!({"x": 10, "y": 36, "text": "Hi", "size": 12, "color": "#000000"})).unwrap(); + s.execute("type.setStyle", json!({"fauxBold": true})).unwrap(); + let stopped = s.execute("actions.stop", json!({})).unwrap(); + let steps = stopped["steps"].as_u64().unwrap(); + assert!(steps >= 7, "the recording keeps every family: {stopped}"); + + // The second document: a different size, background and name. The replay must act here. + s.execute("file.new", json!({"width": 100, "height": 80, "background": "black", "name": "Second"})).unwrap(); + let played = s.execute("actions.play", json!({"action": "Studio"})).unwrap(); + assert!(played.get("failed").is_none(), "replay: {played}"); + assert_eq!(played["ran"].as_u64(), Some(steps), "every step ran: {played}"); + let d = &s.active().unwrap().doc; + assert_eq!((d.size.width, d.size.height), (48, 48), "the crop replayed onto the second document"); + assert_eq!(pixel(&s, 0, 16, 16), vec![1.0, 0.0, 0.0, 1.0], "the selection fill replayed"); + assert_eq!(pixel(&s, 0, 2, 2), vec![0.0, 0.0, 0.0, 1.0], "outside the selection stays black"); + let last = d.layers.last().unwrap(); + match &last.content { + photocraft_doc::LayerContent::Text(t) => { + assert_eq!(t.text, "Hi", "the styled text replayed"); + assert!(t.char_runs().iter().any(|r| r.style.faux_bold), "the bold run replayed"); + } + other => panic!("expected the recorded type layer, got {other:?}"), + } + // The first document is untouched. + let first = &s.documents()[0].doc; + assert_eq!((first.size.width, first.size.height), (48, 48), "the recording document keeps its own crop"); + } } diff --git a/docs/scorecard.md b/docs/scorecard.md index 8e8aa308..80cb52fa 100644 --- a/docs/scorecard.md +++ b/docs/scorecard.md @@ -11,7 +11,7 @@ Generated by `cargo xtask scorecard` from `perf/budgets.toml`, `perf/baseline.js | [Tools](#tools) | 28 | 14 | 1 | | | [Workspace and UI](#workspace-and-ui) | 13 | 5 | 5 | settings that do nothing: 30 of 155 (target 0) | | [Type](#type) | 4 | 2 | 3 | | -| [Automation](#automation) | 3 | 2 | 6 | 631 of 631 menu items live (100.0%) | +| [Automation](#automation) | 4 | 2 | 5 | 631 of 631 menu items live (100.0%) | | [Reliability](#reliability) | 5 | 2 | 0 | never-crash lints on 28 of 28 crates; panic_hunt present | | [Distribution](#distribution) | 5 | 4 | 2 | | | [Open bugs](#open-bugs) | 29 | 2 | 1 | | @@ -230,7 +230,7 @@ Menu wiring (`docs/parity-checklist.md`): 631 of 631 menu items live (100.0%). T | AUTO-219-1 | Record actions in a document-independent form (implicit layer, merged type keystrokes) | missing | [#219](https://github.com/storytold/photocraft/issues/219) | actions.rs:47 copies raw journal entries with explicit layer ids | | AUTO-219-2 | Actions panel: step and dialog toggles, reorder, delete a step, Stop, sets, .atn import | missing | [#219](https://github.com/storytold/photocraft/issues/219) | Panel deletes whole actions only (actions.rs:146) | | AUTO-219-3 | actions.record/stop/play/list commands for agents and the CLI | done | [#219](https://github.com/storytold/photocraft/issues/219) | actions.list/get/record/stop/play/delete in actions_cmds.rs; the panel, CLI and MCP share them. Nested steps pass the session authorize hook. | -| AUTO-219-4 | Replay tests for crop, filters, selections and styled text on a second document | missing | [#219](https://github.com/storytold/photocraft/issues/219) | One replay test (actions.rs:171: new layer, rect select, fill) | +| AUTO-219-4 | Replay tests for crop, filters, selections and styled text on a second document | done | [#219](https://github.com/storytold/photocraft/issues/219) | actions_cmds.rs: a_recorded_edit_run_replays_on_a_second_document records a crop, a Gaussian Blur, a selection fill and faux-bold type on one document and replays all of them onto a second (plus the original layer/select/fill replay) | | AUTO-219-5 | Batch: subfolders, open documents, naming template, progress/cancel, error log, stop | partial | [#219](https://github.com/storytold/photocraft/issues/219) | file_cmds.rs:590-631 folder/array input, per-file errors; no recursion, template or cancel | | AUTO-219-6 | Plug-in ABI v2: panel kind, progress/cancel calls, read other layers, Last Filter repeats | missing | [#219](https://github.com/storytold/photocraft/issues/219) | runtime.rs:15 ABI_VERSION = 1; manifest.rs:18 Kind::Filter only | | AUTO-219-7 | Local ONNX models behind a flag, managed in Preferences; subject select, background removal | missing | [#219](https://github.com/storytold/photocraft/issues/219) | No ONNX runtime dependency | diff --git a/scorecard/automation.toml b/scorecard/automation.toml index 002adc76..000e13f9 100644 --- a/scorecard/automation.toml +++ b/scorecard/automation.toml @@ -35,9 +35,9 @@ note = "actions.list/get/record/stop/play/delete in actions_cmds.rs; the panel, [[item]] id = "AUTO-219-4" target = "Replay tests for crop, filters, selections and styled text on a second document" -status = "missing" +status = "done" issue = 219 -note = "One replay test (actions.rs:171: new layer, rect select, fill)" +note = "actions_cmds.rs: a_recorded_edit_run_replays_on_a_second_document records a crop, a Gaussian Blur, a selection fill and faux-bold type on one document and replays all of them onto a second (plus the original layer/select/fill replay)" [[item]] id = "AUTO-219-5"