;; lib/host/tests/compose.sx — the composition CORE + render-fold (lib/host/compose.sx). ;; Tests host/comp-fold's shared dispatch (seq/alt/each + when + each-source + recursion + ;; depth guard) through the RENDER domain (render → HTML). The execute domain is tested in ;; tests/execute.sx; together they show one core, two folds (plans/composition-objects.md). (define host-cp-pass 0) (define host-cp-fail 0) (define host-cp-fails (list)) (define host-cp-test (fn (name actual expected) (if (= actual expected) (set! host-cp-pass (+ host-cp-pass 1)) (begin (set! host-cp-fail (+ host-cp-fail 1)) (append! host-cp-fails {:name name :actual actual :expected expected}))))) ;; -- leaves -- (host-cp-test "text leaf passes markup through" (host/comp-render (quote (text "

hi

")) {}) "

hi

") (host-cp-test "field wraps the value in a span; reads the context" (host/comp-render (quote (field :title)) {"title" "Hello"}) "Hello") (host-cp-test "val is the raw value (no markup) — for attributes" (host/comp-render (quote (val :slug)) {"slug" "p1"}) "p1") (host-cp-test "a missing field renders empty, not an error" (host/comp-render (quote (field :nope)) {}) "") ;; -- seq: render all in order -- (host-cp-test "seq renders children in order" (host/comp-render (quote (seq (text "a") (text "b") (text "c"))) {}) "abc") ;; -- row/grid: layout combinators wrap + recurse via the core -- (host-cp-test "row wraps its children in a flex div" (host/comp-render (quote (row (text "A") (text "B"))) {}) "
AB
") ;; -- alt + when: render the first branch whose predicate holds -- (host-cp-test "alt renders the when-branch when the predicate holds" (host/comp-render (quote (alt (when (has "auth") (text "in")) (else (text "out")))) {"auth" "y"}) "in") (host-cp-test "alt falls through to else" (host/comp-render (quote (alt (when (has "auth") (text "in")) (else (text "out")))) {}) "out") (host-cp-test "alt eq predicate matches a context value" (host/comp-render (quote (alt (when (eq "t" "dark") (text "D")) (else (text "L")))) {"t" "dark"}) "D") (host-cp-test "alt not predicate negates" (host/comp-render (quote (alt (when (not (has "auth")) (text "anon")) (else (text "user")))) {}) "anon") ;; -- each: iterate a source, binding :item, with field resolution -- (host-cp-test "each renders the template per item (items source)" (host/comp-render (quote (each (items {:n "x"} {:n "y"}) (seq (text "
  • ") (field :n) (text "
  • ")))) {}) "
  • x
  • y
  • ") (host-cp-test "each over an empty source renders empty" (host/comp-render (quote (each (items) (field :n))) {}) "") (host-cp-test "each query source delegates to the context resolver" (host/comp-render (quote (each (query is-a t) (field :title))) {"query" (fn (qargs ctx) (list {:title "One"} {:title "Two"}))}) "OneTwo") ;; -- recursion via named templates + a depth guard -- (host/comp--def-tmpl! "node" (quote (seq (field :name) (each (children) (tmpl "node"))))) (host-cp-test "tmpl recurses over a (children) tree until the source runs dry" (host/comp-render (quote (tmpl "node")) {"item" {:name "root" :children (list {:name "a" :children (list)} {:name "b" :children (list)})}}) "rootab") ;; -- ref: transclude via the context resolver -- (host-cp-test "ref transcludes via the context resolver" (host/comp-render (quote (ref "c1")) {"ref" (fn (id ctx) (str ""))}) "") (host-cp-test "ref with no resolver renders empty" (host/comp-render (quote (ref "c1")) {}) "") ;; -- the unifying property: ONE object renders differently per context -- (host-cp-test "the SAME object renders two ways by context (anon vs authed)" (let ((obj (quote (alt (when (has "auth") (text "member")) (else (text "guest")))))) (list (host/comp-render obj {}) (host/comp-render obj {"auth" "y"}))) (list "guest" "member")) (define host-cp-tests-run! (fn () {:total (+ host-cp-pass host-cp-fail) :passed host-cp-pass :failed host-cp-fail :fails host-cp-fails}))