OCaml evaluator: has_rest_param and bind_lambda_params checked for String "&rest" but the parser produces Symbol "&rest". Both forms now accepted. Fixes swap! extra args (signal 10 → swap! s + 5 → 15). test-adapter-html.sx: fix define shorthand → explicit fn form, move defcomp/defisland to top level with (test-env) for component resolution. 2515 passed, 0 failed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
210 lines
6.1 KiB
Plaintext
210 lines
6.1 KiB
Plaintext
(define ahtml (fn (expr) (render-to-html expr {})))
|
|
|
|
(defcomp ~ahtml-card (&key title) (div :class "card" (h2 title)))
|
|
|
|
(defcomp ~ahtml-box (&rest children) (div :class "box" children))
|
|
|
|
(defcomp
|
|
~ahtml-panel
|
|
(&key heading &rest children)
|
|
(section (h3 heading) children))
|
|
|
|
(defisland ~ahtml-counter (&key count) (span (str count)))
|
|
|
|
(defisland ~ahtml-display (&key label) (span label))
|
|
|
|
(defsuite
|
|
"adapter-html-basics"
|
|
(deftest "nil renders empty" (assert-equal "" (ahtml nil)))
|
|
(deftest "string escapes html" (assert-equal "<b>" (ahtml "<b>")))
|
|
(deftest "number renders as string" (assert-equal "42" (ahtml 42)))
|
|
(deftest
|
|
"boolean renders"
|
|
(assert-equal "true" (ahtml true))
|
|
(assert-equal "false" (ahtml false)))
|
|
(deftest "keyword renders name" (assert-equal "foo" (ahtml :foo))))
|
|
|
|
(defsuite
|
|
"adapter-html-elements"
|
|
(deftest
|
|
"div with text"
|
|
(assert-equal "<div>hello</div>" (ahtml (quote (div "hello")))))
|
|
(deftest
|
|
"div with class"
|
|
(assert-equal
|
|
"<div class=\"card\">hi</div>"
|
|
(ahtml (quote (div :class "card" "hi")))))
|
|
(deftest
|
|
"nested elements"
|
|
(assert-equal
|
|
"<div><span>inner</span></div>"
|
|
(ahtml (quote (div (span "inner"))))))
|
|
(deftest
|
|
"void element"
|
|
(assert-true (starts-with? (ahtml (quote (br))) "<br")))
|
|
(deftest
|
|
"input void with attrs"
|
|
(assert-true
|
|
(string-contains?
|
|
(ahtml (quote (input :type "text" :name "q")))
|
|
"type=\"text\"")))
|
|
(deftest
|
|
"multiple children"
|
|
(assert-equal
|
|
"<ul><li>a</li><li>b</li></ul>"
|
|
(ahtml (quote (ul (li "a") (li "b")))))))
|
|
|
|
(defsuite
|
|
"adapter-html-control-flow"
|
|
(deftest
|
|
"if true branch"
|
|
(assert-equal
|
|
"<b>yes</b>"
|
|
(ahtml (quote (if true (b "yes") (i "no"))))))
|
|
(deftest
|
|
"if false branch"
|
|
(assert-equal
|
|
"<i>no</i>"
|
|
(ahtml (quote (if false (b "yes") (i "no"))))))
|
|
(deftest
|
|
"when truthy renders body"
|
|
(assert-equal "<p>ok</p>" (ahtml (quote (when true (p "ok"))))))
|
|
(deftest
|
|
"when falsy renders empty"
|
|
(assert-equal "" (ahtml (quote (when false (p "no")))))))
|
|
|
|
(defsuite
|
|
"adapter-html-let"
|
|
(deftest
|
|
"let binds and renders"
|
|
(assert-equal
|
|
"<span>hi</span>"
|
|
(ahtml (quote (let ((x "hi")) (span x))))))
|
|
(deftest
|
|
"let multiple bindings"
|
|
(assert-equal
|
|
"<div>AB</div>"
|
|
(ahtml (quote (let ((a "A") (b "B")) (div (begin a b))))))))
|
|
|
|
(defsuite
|
|
"adapter-html-map"
|
|
(deftest
|
|
"map renders each item"
|
|
(assert-true
|
|
(string-contains?
|
|
(ahtml
|
|
(quote
|
|
(let
|
|
((items (list "a" "b" "c")))
|
|
(map (fn (x) (li x)) items))))
|
|
"<li>a</li>")))
|
|
(deftest
|
|
"for-each renders items"
|
|
(assert-true
|
|
(string-contains?
|
|
(ahtml (quote (for-each (fn (x) (span x)) (list "x" "y"))))
|
|
"<span>x</span>"))))
|
|
|
|
(defsuite
|
|
"adapter-html-components"
|
|
(deftest
|
|
"component with kwargs renders"
|
|
(let
|
|
((html (render-to-html (quote (~ahtml-card :title "Hello")) (test-env))))
|
|
(assert-true (string-contains? html "Hello"))
|
|
(assert-true (string-contains? html "card"))))
|
|
(deftest
|
|
"component with children renders"
|
|
(let
|
|
((html (render-to-html (quote (~ahtml-box (p "inside"))) (test-env))))
|
|
(assert-true (string-contains? html "inside"))
|
|
(assert-true (string-contains? html "box"))))
|
|
(deftest
|
|
"component with keyword and rest"
|
|
(let
|
|
((html (render-to-html (quote (~ahtml-panel :heading "Title" (p "body"))) (test-env))))
|
|
(assert-true (string-contains? html "Title"))
|
|
(assert-true (string-contains? html "body")))))
|
|
|
|
(defsuite
|
|
"adapter-html-fragments"
|
|
(deftest
|
|
"fragment renders children"
|
|
(assert-equal "<b>a</b><i>b</i>" (ahtml (quote (<> (b "a") (i "b"))))))
|
|
(deftest "empty fragment" (assert-equal "" (ahtml (quote (<>))))))
|
|
|
|
(defsuite
|
|
"adapter-html-raw"
|
|
(deftest
|
|
"raw! passes through unescaped"
|
|
(assert-equal "<b>bold</b>" (ahtml (quote (raw! "<b>bold</b>"))))))
|
|
|
|
(defsuite
|
|
"adapter-html-render-form-predicate"
|
|
(deftest "if is a render form" (assert-true (render-html-form? "if")))
|
|
(deftest "when is a render form" (assert-true (render-html-form? "when")))
|
|
(deftest "map is a render form" (assert-true (render-html-form? "map")))
|
|
(deftest
|
|
"div is not a render form"
|
|
(assert-false (render-html-form? "div")))
|
|
(deftest
|
|
"scope is a render form"
|
|
(assert-true (render-html-form? "scope")))
|
|
(deftest
|
|
"provide is a render form"
|
|
(assert-true (render-html-form? "provide"))))
|
|
|
|
(defsuite
|
|
"adapter-html-serialize-island-state"
|
|
(deftest
|
|
"empty dict returns nil"
|
|
(assert-nil (serialize-island-state {})))
|
|
(deftest
|
|
"non-empty dict returns sx string"
|
|
(let
|
|
((result (serialize-island-state {:count 0})))
|
|
(assert-true (string? result))
|
|
(assert-true (string-contains? result "count")))))
|
|
|
|
(defsuite
|
|
"adapter-html-islands"
|
|
(deftest
|
|
"island renders with data attributes"
|
|
(let
|
|
((html (render-to-html (quote (~ahtml-counter :count 0)) (test-env))))
|
|
(assert-true (string-contains? html "data-sx-island"))
|
|
(assert-true (string-contains? html "ahtml-counter"))))
|
|
(deftest
|
|
"island includes state"
|
|
(let
|
|
((html (render-to-html (quote (~ahtml-display :label "hi")) (test-env))))
|
|
(assert-true (string-contains? html "data-sx-state"))
|
|
(assert-true (string-contains? html "label")))))
|
|
|
|
(defsuite
|
|
"adapter-html-lakes"
|
|
(deftest
|
|
"lake renders with data attribute"
|
|
(let
|
|
((html (ahtml (quote (lake :id "my-lake" (p "content"))))))
|
|
(assert-true (string-contains? html "data-sx-lake"))
|
|
(assert-true (string-contains? html "content")))))
|
|
|
|
(defsuite
|
|
"adapter-html-marshes"
|
|
(deftest
|
|
"marsh renders with data attribute"
|
|
(let
|
|
((html (ahtml (quote (marsh :id "my-marsh" (p "data"))))))
|
|
(assert-true (string-contains? html "data-sx-marsh"))
|
|
(assert-true (string-contains? html "data")))))
|
|
|
|
(defsuite
|
|
"adapter-html-definitions"
|
|
(deftest
|
|
"define renders empty"
|
|
(assert-equal "" (ahtml (quote (define test-val 42)))))
|
|
(deftest
|
|
"defmacro renders empty"
|
|
(assert-equal "" (ahtml (quote (defmacro test-m (x) x))))))
|