diff --git a/lib/content/conformance.sh b/lib/content/conformance.sh index 44845292..4697c991 100755 --- a/lib/content/conformance.sh +++ b/lib/content/conformance.sh @@ -15,7 +15,7 @@ if [ ! -x "$SX_SERVER" ]; then fi fi -SUITES=(block doc render api meta page page-full markdown text section compose tree-edit clone query stats table data wire validate store snapshot crdt crdt-store sync md-import md-doc fed) +SUITES=(block doc render api meta page page-full markdown text section compose tree-edit clone query transform stats table data wire validate store snapshot crdt crdt-store sync md-import md-doc fed) OUT_JSON="lib/content/scoreboard.json" OUT_MD="lib/content/scoreboard.md" @@ -49,6 +49,7 @@ run_suite() { (load "lib/content/tree-edit.sx") (load "lib/content/clone.sx") (load "lib/content/query.sx") +(load "lib/content/transform.sx") (load "lib/content/stats.sx") (load "lib/content/table.sx") (load "lib/content/data.sx") diff --git a/lib/content/scoreboard.json b/lib/content/scoreboard.json index f21aeccc..9f9b976b 100644 --- a/lib/content/scoreboard.json +++ b/lib/content/scoreboard.json @@ -14,6 +14,7 @@ "tree-edit": {"pass": 17, "fail": 0}, "clone": {"pass": 10, "fail": 0}, "query": {"pass": 13, "fail": 0}, + "transform": {"pass": 12, "fail": 0}, "stats": {"pass": 17, "fail": 0}, "table": {"pass": 15, "fail": 0}, "data": {"pass": 21, "fail": 0}, @@ -28,7 +29,7 @@ "md-doc": {"pass": 12, "fail": 0}, "fed": {"pass": 20, "fail": 0} }, - "total_pass": 574, + "total_pass": 586, "total_fail": 0, - "total": 574 + "total": 586 } diff --git a/lib/content/scoreboard.md b/lib/content/scoreboard.md index 1f2d0463..e1d94b7e 100644 --- a/lib/content/scoreboard.md +++ b/lib/content/scoreboard.md @@ -18,6 +18,7 @@ _Generated by `lib/content/conformance.sh`_ | tree-edit | 17 | 0 | 17 | | clone | 10 | 0 | 10 | | query | 13 | 0 | 13 | +| transform | 12 | 0 | 12 | | stats | 17 | 0 | 17 | | table | 15 | 0 | 15 | | data | 21 | 0 | 21 | @@ -31,4 +32,4 @@ _Generated by `lib/content/conformance.sh`_ | md-import | 38 | 0 | 38 | | md-doc | 12 | 0 | 12 | | fed | 20 | 0 | 20 | -| **Total** | **574** | **0** | **574** | +| **Total** | **586** | **0** | **586** | diff --git a/lib/content/tests/transform.sx b/lib/content/tests/transform.sx new file mode 100644 index 00000000..bf1a47b1 --- /dev/null +++ b/lib/content/tests/transform.sx @@ -0,0 +1,90 @@ +;; Extension — tree-wide block transforms. + +(st-bootstrap-classes!) +(content/bootstrap!) +(content-bootstrap-section!) + +(define + d + (doc-append + (doc-append (doc-empty "d") (mk-heading "h" 1 "Top")) + (mk-section + "s" + (list (mk-text "a" "A") (mk-heading "h2" 2 "Sub"))))) + +;; ── map-type bumps heading levels everywhere ── +(define + d1 + (content/map-type + d + "heading" + (fn (b) (blk-set b "level" (+ (blk-get b "level") 1))))) +(content-test + "map-type top heading" + (blk-send (doc-deep-find d1 "h") "level") + 2) +(content-test + "map-type nested heading" + (blk-send (doc-deep-find d1 "h2") "level") + 3) +(content-test + "map-type leaves text" + (str (blk-send (doc-deep-find d1 "a") "text")) + "A") +(content-test + "map-type immutable" + (blk-send (doc-deep-find d "h") "level") + 1) +(content-test "map-type preserves tree" (doc-tree-ids d1) (doc-tree-ids d)) + +;; ── set-field-on rewrites all text blocks ── +(define d2 (content/set-field-on d "text" "text" "REDACTED")) +(content-test + "set-field nested text" + (str (blk-send (doc-deep-find d2 "a") "text")) + "REDACTED") +(content-test + "set-field count" + (len + (filter + (fn (b) (= (str (blk-get b "text")) "REDACTED")) + (list (doc-deep-find d2 "a")))) + 1) + +;; ── map-blocks with custom predicate ── +(define + d3 + (content/map-blocks + d + (fn (b) (= (blk-id b) "h2")) + (fn (b) (blk-set b "text" "Changed")))) +(content-test + "map-blocks predicate hit" + (str (blk-send (doc-deep-find d3 "h2") "text")) + "Changed") +(content-test + "map-blocks predicate miss" + (str (blk-send (doc-deep-find d3 "h") "text")) + "Top") + +;; ── image src rewrite (cdn migration) ── +(define di (doc-append (doc-empty "d") (mk-image "img" "/old.png" "x"))) +(content-test + "image src rewrite" + (str + (blk-send + (doc-find (content/set-field-on di "image" "src" "/cdn/new.png") "img") + "src")) + "/cdn/new.png") + +;; ── no matching blocks → unchanged ── +(content-test + "no match unchanged" + (asHTML (content/map-type d "embed" (fn (b) b))) + (asHTML d)) + +;; ── render after transform ── +(content-test + "render after map-type" + (asHTML d1) + "
A