;; Hyperscript behavioral tests — auto-generated from upstream _hyperscript test suite
;; Source: spec/tests/hyperscript-upstream-tests.json (1496 tests, v0.9.14 + dev)
;; DO NOT EDIT — regenerate with: python3 tests/playwright/generate-sx-tests.py
;; ── Test helpers ──────────────────────────────────────────────────
;; Bind `window` and `document` as plain SX symbols so HS code that
;; references them (e.g. `window.tmp`) can resolve through the host.
(define window (host-global "window"))
(define document (host-global "document"))
(define hs-test-el
(fn (tag hs-src)
(let ((el (dom-create-element tag)))
(dom-set-attr el "_" hs-src)
(dom-append (dom-body) el)
(hs-activate! el)
el)))
(define hs-cleanup!
(fn ()
(dom-set-inner-html (dom-body) "")))
;; Evaluate a hyperscript expression and return either the expression
;; value or `it` (whichever is non-nil). Multi-statement scripts that
;; mutate `it` (e.g. `pick first 3 of arr; set $test to it`) get `it` back;
;; bare expressions (e.g. `foo.foo`) get the expression value back.
(define _hs-wrap-body
(fn (sx)
;; Wrap body to capture return via `it`. `event` default is always nil.
;; `it` is NOT shadowed here — callers (eval-hs-locals) may pre-bind it.
(list (quote let)
(list (list (quote event) nil))
(list (quote let)
(list (list (quote _ret) sx))
(list (quote if) (list (quote nil?) (quote _ret)) (quote it) (quote _ret))))))
(define eval-hs
(fn (src)
(let ((sx (hs-to-sx (hs-compile src))))
(let ((handler (eval-expr-cek
(list (quote fn) (list (quote me))
(list (quote let) (list (list (quote it) nil) (list (quote event) nil)) sx)))))
(guard
(_e
(true
(if
(and (list? _e) (= (first _e) "hs-return"))
(nth _e 1)
(raise _e))))
(handler nil))))))
;; Evaluate a hyperscript expression with locals. bindings = list of (symbol value).
;; Locals are injected as a `let` wrapping the compiled body, then evaluated
;; in a fresh CEK env. Avoids `apply` (whose JIT path can loop on some forms).
(define eval-hs-locals
(fn (src bindings)
;; Also expose bindings on the `window` global so tests that reference
;; window.X (common in upstream tests) can resolve them.
(for-each (fn (b) (host-set! (host-global "window") (str (first b)) (nth b 1))) bindings)
(let ((sx (hs-to-sx (hs-compile src))))
;; Build (let ((name1 (quote val1)) ...) )
(let ((let-binds (map (fn (b) (list (first b) (list (quote quote) (nth b 1)))) bindings)))
(let ((wrapped (list (quote let) let-binds (_hs-wrap-body sx))))
(let ((thunk (list (quote fn) (list (quote me)) wrapped)))
(let ((handler (eval-expr-cek thunk)))
(guard
(_e
(true
(if
(and (list? _e) (= (first _e) "hs-return"))
(nth _e 1)
(raise _e))))
(handler nil)))))))))
;; Evaluate with a specific me value (for "I am between" etc.)
(define eval-hs-with-me
(fn (src me-val)
(let ((sx (hs-to-sx (hs-compile src))))
(let ((handler (eval-expr-cek
(list (quote fn) (list (quote me)) (_hs-wrap-body sx)))))
(guard
(_e
(true
(if
(and (list? _e) (= (first _e) "hs-return"))
(nth _e 1)
(raise _e))))
(handler me-val))))))
;; ── add (19 tests) ──
(defsuite "hs-upstream-add"
(deftest "can add a value to a set"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :s to [] as Set then add 'a' to :s then add 'b' to :s then add 'a' to :s then put :s.size into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "2")
))
(deftest "can add a value to an array"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then add 4 to :arr then put :arr as String into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "1,2,3,4")
))
(deftest "can add class ref on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? _el-div "foo")))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
))
(deftest "can add class ref on a single form"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")))
(dom-set-attr _el-form "_" "on click add .foo")
(dom-append (dom-body) _el-form)
(hs-activate! _el-form)
(assert (not (dom-has-class? _el-form "foo")))
(dom-dispatch _el-form "click" nil)
(assert (dom-has-class? _el-form "foo"))
))
(deftest "can add class ref w/ double dash on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo--bar")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? _el-div "foo--bar")))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo--bar"))
))
(deftest "can add class refs w/ colons and dashes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo:bar-doh")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo:bar-doh"))
))
(deftest "can add css properties"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add {color: red; font-family: monospace}")
(dom-set-attr _el-div "style" "color: blue")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert= (dom-get-style _el-div "color") "blue")
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-style _el-div "color") "red")
(assert= (dom-get-style _el-div "font-family") "monospace")
))
(deftest "can add multiple class refs"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo .bar")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? _el-div "foo")))
(assert (not (dom-has-class? _el-div "bar")))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
(assert (dom-has-class? _el-div "bar"))
))
(deftest "can add non-class attributes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add [@foo=\"bar\"]")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-attr? _el-div "foo")))
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-attr _el-div "foo") "bar")
))
(deftest "can add templated css properties"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add {color: ${}{\"red\"};}")
(dom-set-attr _el-div "style" "color: blue")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert= (dom-get-style _el-div "color") "blue")
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-style _el-div "color") "red")
))
(deftest "can add to an HTMLCollection"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-bar (dom-create-element "div")) (_el-c1 (dom-create-element "div")) (_el-c2 (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .foo to the children of #bar")
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-c1 "id" "c1")
(dom-set-attr _el-c2 "id" "c2")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-bar)
(dom-append _el-bar _el-c1)
(dom-append _el-bar _el-c2)
(hs-activate! _el-trigger)
(assert (not (dom-has-class? (dom-query-by-id "c1") "foo")))
(assert (not (dom-has-class? (dom-query-by-id "c2") "foo")))
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (dom-has-class? (dom-query-by-id "c1") "foo"))
(assert (dom-has-class? (dom-query-by-id "c2") "foo"))
))
(deftest "can add to children"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-outer "_" "on click add .foo to my children")
(dom-set-attr _el-p1 "id" "p1")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-p1)
(hs-activate! _el-outer)
(assert (not (dom-has-class? (dom-query-by-id "p1") "foo")))
(assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
(dom-dispatch (dom-query-by-id "outer") "click" nil)
(assert (dom-has-class? (dom-query-by-id "p1") "foo"))
(assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
))
(deftest "can add to query in me"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-outer "_" "on click add .foo to in me")
(dom-set-attr _el-p1 "id" "p1")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-p1)
(hs-activate! _el-outer)
(assert (not (dom-has-class? (dom-query-by-id "p1") "foo")))
(assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
(dom-dispatch (dom-query-by-id "outer") "click" nil)
(assert (dom-has-class? (dom-query-by-id "p1") "foo"))
(assert (not (dom-has-class? (dom-query-by-id "outer") "foo")))
))
(deftest "can filter class addition via the when clause"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .rey to .bar when it matches .doh")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "bar")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "bar")
(dom-add-class _el-d3 "doh")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
(hs-activate! _el-trigger)
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (not (dom-has-class? (dom-query-by-id "d2") "rey")))
(assert (dom-has-class? (dom-query-by-id "d3") "rey"))
))
(deftest "can filter property addition via the when clause"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add @rey to .bar when it matches .doh")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "bar")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "bar")
(dom-add-class _el-d3 "doh")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
(hs-activate! _el-trigger)
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "d2") "rey")))
(assert (dom-has-attr? (dom-query-by-id "d3") "rey"))
))
(deftest "can target another div for class ref"
(hs-cleanup!)
(let ((_el-bar (dom-create-element "div")) (_el-trigger (dom-create-element "div")))
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .foo to #bar")
(dom-append (dom-body) _el-bar)
(dom-append (dom-body) _el-trigger)
(hs-activate! _el-trigger)
(assert (not (dom-has-class? (dom-query-by-id "bar") "foo")))
(assert (not (dom-has-class? (dom-query-by-id "trigger") "foo")))
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (dom-has-class? (dom-query-by-id "bar") "foo"))
(assert (not (dom-has-class? (dom-query-by-id "trigger") "foo")))
))
(deftest "supports async expressions in when clause"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .foo to #d2 when asyncCheck()")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-trigger)
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (dom-has-class? (dom-query-by-id "d2") "foo"))
))
(deftest "when clause result is empty when nothing matches"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-none (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .foo to .item when it matches .nope then if the result is empty remove @hidden from #none")
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "item")
(dom-set-attr _el-none "id" "none")
(dom-set-attr _el-none "hidden" "")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-none)
(hs-activate! _el-trigger)
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "none") "hidden")))
))
(deftest "when clause sets result to matched elements"
(hs-cleanup!)
(let ((_el-trigger (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-none (dom-create-element "div")))
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click add .foo to .item when it matches .yes then if the result is empty show #none else hide #none")
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "item")
(dom-add-class _el-d1 "yes")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "item")
(dom-set-attr _el-none "id" "none")
(dom-set-attr _el-none "style" "display:none")
(dom-append (dom-body) _el-trigger)
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-none)
(hs-activate! _el-trigger)
(dom-dispatch (dom-query-by-id "trigger") "click" nil)
(assert (not (dom-visible? (dom-query-by-id "none"))))
))
)
;; ── append (13 tests) ──
(defsuite "hs-upstream-append"
(deftest "append preserves existing content rather than overwriting it"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-btn1 (dom-create-element "button")))
(dom-set-attr _el-div "_" "on click append 'New Content' to me")
(dom-set-attr _el-btn1 "id" "btn1")
(dom-set-inner-html _el-btn1 "Click Me")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-btn1)
(hs-activate! _el-div)
(host-set! (host-global "window") "clicks" 0)
(dom-dispatch (dom-query-by-id "btn1") "click" nil)
(dom-dispatch _el-div "click" nil)
(dom-dispatch (dom-query-by-id "btn1") "click" nil)
))
(deftest "append to undefined ignores the undefined"
(hs-cleanup!)
(let ((_el-id (dom-create-element "div")))
(dom-set-attr _el-id "id" "id")
(dom-set-attr _el-id "_" "on click append 'bar' then append it to me")
(dom-append (dom-body) _el-id)
(hs-activate! _el-id)
(dom-dispatch _el-id "click" nil)
(assert= (dom-text-content _el-id) "bar")
))
(deftest "can append a string to another string"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set value to 'Hello there.' then append ' General Kenobi.' to value then set my.innerHTML to value")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "Hello there. General Kenobi.")
))
(deftest "can append a value into an array"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set value to [1,2,3] then append 4 to value then set my.innerHTML to value as String")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "1,2,3,4")
))
(deftest "can append a value to 'it'"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set result to [1,2,3] then append 4 then put it as String into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "1,2,3,4")
))
(deftest "can append a value to I"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click append 'Content' to I")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "Content")
))
(deftest "can append a value to a DOM element"
(hs-cleanup!)
(let ((_el-content (dom-create-element "div")))
(dom-set-attr _el-content "id" "content")
(dom-set-attr _el-content "_" "on click append 'Content' to #content")
(dom-append (dom-body) _el-content)
(hs-activate! _el-content)
(dom-dispatch _el-content "click" nil)
(assert= (dom-text-content _el-content) "Content")
))
(deftest "can append a value to a DOM node"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click append 'This is my inner HTML' to me then append 'With Tags' to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "This is my inner HTMLWith Tags")
))
(deftest "can append a value to a set"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :s to [1,2] as Set then append 3 to :s then append 1 to :s then put :s.size into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "3")
))
(deftest "can append a value to an object property"
(hs-cleanup!)
(let ((_el-id (dom-create-element "div")))
(dom-set-attr _el-id "id" "id")
(dom-set-attr _el-id "_" "on click append '_new' to my id")
(dom-append (dom-body) _el-id)
(hs-activate! _el-id)
(dom-dispatch _el-id "click" nil)
))
(deftest "multiple appends work"
(hs-cleanup!)
(let ((_el-id (dom-create-element "div")))
(dom-set-attr _el-id "id" "id")
(dom-set-attr _el-id "_" "on click get 'foo' then append 'bar' then append 'doh' then append it to me")
(dom-append (dom-body) _el-id)
(hs-activate! _el-id)
(dom-dispatch _el-id "click" nil)
(assert= (dom-text-content _el-id) "foobardoh")
))
(deftest "new DOM content added by append will be live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click make a then append it to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? (dom-query "span.topping") "topping"))
))
(deftest "new content added by append will be live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click append `` to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(dom-dispatch (dom-query-by-id "b1") "click" nil)
))
)
;; ── askAnswer (5 tests) ──
(defsuite "hs-upstream-askAnswer"
(deftest "confirm returns first choice on OK"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "Yes")
))
(deftest "confirm returns second choice on cancel"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "No")
))
(deftest "prompts and puts result in it"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click ask \"What is your name?\" then put it into #out")
(dom-set-inner-html _el-button "Ask")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "Alice")
))
(deftest "returns null on cancel"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click ask \"Name?\" then put it into #out")
(dom-set-inner-html _el-button "Ask")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "null")
))
(deftest "shows an alert"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click answer \"Hello!\" then put \"done\" into #out")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "done")
))
)
;; ── behavior (10 tests) ──
(defsuite "hs-upstream-behavior"
(deftest "can declare variables in init blocks"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave
init
set element's foo to 1
set element's bar to {}
end
on click
increment element's foo
set element's bar[\"count\"] to element's foo
put element's bar[\"count\"] into me
end
end")
(dom-set-attr _el-div "_" "install Behave")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can define behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior TheBehaviorWeAreDefiningForHyperscriptTestingPurposes init log 'foo' end end")
(dom-append (dom-body) _el-script)
))
(deftest "can install behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave on click add .foo end end")
(dom-set-attr _el-div "_" "install Behave")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can pass arguments to behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(foo, bar) on click put foo + bar into me end end")
(dom-set-attr _el-div "_" "install Behave(foo: 1, bar: 1)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can pass element arguments to listen to in behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-b1 (dom-create-element "button")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(elt) on click from elt put 'foo' into me end end")
(dom-set-attr _el-b1 "id" "b1")
(dom-set-attr _el-div "_" "install Behave(elt: #b1)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-b1)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can refer to arguments in init blocks"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(elt) init put 'foo' into elt end end")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-div "_" "install Behave(elt: #d1)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "install resolves namespaced behavior paths"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior App.Widgets.Clickable on click add .clicked end end")
(dom-set-attr _el-div "_" "install App.Widgets.Clickable")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "install throws when the behavior path does not exist"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "install NoSuchBehavior")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "install throws when the path resolves to a non-function"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "install NotABehavior")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "supports init blocks in behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave init add .foo to me end")
(dom-set-attr _el-div "_" "install Behave")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
)
;; ── bind (44 tests) ──
(defsuite "hs-upstream-bind"
(deftest "\"with\" is a synonym for \"and\""
(hs-cleanup!)
(let ((_el-city-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-city-input "id" "city-input")
(dom-set-attr _el-city-input "type" "text")
(dom-set-attr _el-city-input "value" "Paris")
(dom-set-attr _el-span "_" "bind $city to #city-input.value end when $city changes put it into me")
(dom-append (dom-body) _el-city-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
))
(deftest "attribute bound to another element input value"
(hs-cleanup!)
(let ((_el-title-input (dom-create-element "input")) (_el-h1 (dom-create-element "h1")))
(dom-set-attr _el-title-input "id" "title-input")
(dom-set-attr _el-title-input "type" "text")
(dom-set-attr _el-title-input "value" "Hello")
(dom-set-attr _el-h1 "_" "bind @data-title and #title-input's value")
(dom-append (dom-body) _el-title-input)
(dom-append (dom-body) _el-h1)
(hs-activate! _el-h1)
))
(deftest "bind element to element: both sides auto-detect"
(hs-cleanup!)
(let ((_el-range-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
(dom-set-attr _el-range-slider "id" "range-slider")
(dom-set-attr _el-range-slider "type" "range")
(dom-set-attr _el-range-slider "value" "50")
(dom-set-attr _el-input "_" "bind me to #range-slider")
(dom-set-attr _el-input "type" "number")
(dom-append (dom-body) _el-range-slider)
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "bind to contenteditable element auto-detects textContent"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $text to me")
(dom-set-attr _el-div "contenteditable" "true")
(dom-set-inner-html _el-div "initial")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "bind to custom element with value property auto-detects value"
(hs-cleanup!)
(let ((_el-test-input (dom-create-element "test-input")))
(dom-set-attr _el-test-input "_" "bind $custom to me")
(dom-append (dom-body) _el-test-input)
(hs-activate! _el-test-input)
))
(deftest "bind variable to checkbox by id auto-detects checked"
(hs-cleanup!)
(let ((_el-agree-cb (dom-create-element "input")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-agree-cb "id" "agree-cb")
(dom-set-attr _el-agree-cb "type" "checkbox")
(dom-set-attr _el-div "_" "bind $agreed to #agree-cb")
(dom-append (dom-body) _el-agree-cb)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "bind variable to element by id auto-detects value"
(hs-cleanup!)
(let ((_el-name-field (dom-create-element "input")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-name-field "id" "name-field")
(dom-set-attr _el-name-field "type" "text")
(dom-set-attr _el-name-field "value" "")
(dom-set-attr _el-div "_" "bind $name to #name-field")
(dom-append (dom-body) _el-name-field)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "bind variable to number input by id auto-detects valueAsNumber"
(hs-cleanup!)
(let ((_el-qty-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-qty-input "id" "qty-input")
(dom-set-attr _el-qty-input "type" "number")
(dom-set-attr _el-div "_" "bind $qty to #qty-input")
(dom-append (dom-body) _el-qty-input)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "boolean bind to aria-* attribute uses \"true\"/\"false\" strings"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $isHidden and @aria-hidden")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "boolean bind to attribute uses presence/absence"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $isEnabled and @data-active")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "class bound to another element checkbox"
(hs-cleanup!)
(let ((_el-dark-toggle (dom-create-element "input")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-dark-toggle "id" "dark-toggle")
(dom-set-attr _el-dark-toggle "type" "checkbox")
(dom-set-attr _el-div "_" "bind .dark and #dark-toggle's checked")
(dom-set-inner-html _el-div "test")
(dom-append (dom-body) _el-dark-toggle)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "clicking a radio sets the variable to its value"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $color to me")
(dom-set-attr _el-input "type" "radio")
(dom-set-attr _el-input "name" "color")
(dom-set-attr _el-input "value" "red")
(dom-set-attr _el-input1 "_" "bind $color to me")
(dom-set-attr _el-input1 "type" "radio")
(dom-set-attr _el-input1 "name" "color")
(dom-set-attr _el-input1 "value" "blue")
(dom-set-attr _el-input2 "_" "bind $color to me")
(dom-set-attr _el-input2 "type" "radio")
(dom-set-attr _el-input2 "name" "color")
(dom-set-attr _el-input2 "value" "green")
(dom-set-attr _el-span "_" "when $color changes put it into me")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
(dom-append (dom-body) _el-input2)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
(hs-activate! _el-input1)
(hs-activate! _el-input2)
(hs-activate! _el-span)
))
(deftest "dedup prevents infinite loop in two-way bind"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $color and @data-color")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "external JS property write does not sync (known limitation)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $searchTerm to me")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "original")
(dom-set-attr _el-span "_" "when $searchTerm changes put it into me")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
(hs-activate! _el-span)
))
(deftest "external class change syncs back to variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind .dark and $darkMode")
(dom-set-inner-html _el-div "test")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "form reset listener is removed on cleanup"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")) (_el-binput (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-binput "id" "binput")
(dom-set-attr _el-binput "_" "bind $val to me")
(dom-set-attr _el-binput "type" "text")
(dom-set-attr _el-binput "value" "initial")
(dom-set-attr _el-button "type" "reset")
(dom-set-inner-html _el-button "Reset")
(dom-append (dom-body) _el-form)
(dom-append _el-form _el-binput)
(dom-append _el-form _el-button)
(hs-activate! _el-binput)
))
(deftest "form.reset() syncs variable back to default value"
(hs-cleanup!)
(let ((_el-test-form (dom-create-element "form")) (_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-test-form "id" "test-form")
(dom-set-attr _el-input "_" "bind $formField to me")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "default")
(dom-set-attr _el-span "_" "when $formField changes put it into me")
(dom-append (dom-body) _el-test-form)
(dom-append _el-test-form _el-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
(hs-activate! _el-span)
))
(deftest "init: right side wins - attribute (Y) initializes variable (X)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $color to @data-color")
(dom-set-attr _el-div "data-color" "red")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "init: right side wins - class (Y) drives variable (X)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "dark")
(dom-set-attr _el-div "_" "bind $isDark to .dark")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "init: right side wins - input value (Y) overwrites variable (X)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $name to my value")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "Bob")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "init: right side wins - variable (Y) drives class (X)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind .dark to $isDark")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "init: right side wins - variable (Y) initializes attribute (X)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind @data-theme to $theme")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "init: right side wins - variable (Y) overwrites input value (X)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind my value to $name")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "Bob")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "initial value checks the correct radio on load"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $fruit to me")
(dom-set-attr _el-input "type" "radio")
(dom-set-attr _el-input "name" "fruit")
(dom-set-attr _el-input "value" "apple")
(dom-set-attr _el-input1 "_" "bind $fruit to me")
(dom-set-attr _el-input1 "type" "radio")
(dom-set-attr _el-input1 "name" "fruit")
(dom-set-attr _el-input1 "value" "banana")
(dom-set-attr _el-input2 "_" "bind $fruit to me")
(dom-set-attr _el-input2 "type" "radio")
(dom-set-attr _el-input2 "name" "fruit")
(dom-set-attr _el-input2 "value" "cherry")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
(dom-append (dom-body) _el-input2)
(hs-activate! _el-input)
(hs-activate! _el-input1)
(hs-activate! _el-input2)
))
(deftest "of-expression: bind $var to value of #input"
(hs-cleanup!)
(let ((_el-of-input (dom-create-element "input")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-of-input "id" "of-input")
(dom-set-attr _el-of-input "type" "text")
(dom-set-attr _el-of-input "value" "initial")
(dom-set-attr _el-div "_" "bind $search to value of #of-input")
(dom-append (dom-body) _el-of-input)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "possessive attribute: bind $var and my @data-label"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $label and my @data-label")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "possessive property: bind $var to my value"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $myVal to my value")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "hello")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "radio change listener is removed on cleanup"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $color to me")
(dom-set-attr _el-input "type" "radio")
(dom-set-attr _el-input "name" "color")
(dom-set-attr _el-input "value" "red")
(dom-set-attr _el-input "checked" "")
(dom-set-attr _el-input1 "_" "bind $color to me")
(dom-set-attr _el-input1 "type" "radio")
(dom-set-attr _el-input1 "name" "color")
(dom-set-attr _el-input1 "value" "blue")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
(hs-activate! _el-input)
(hs-activate! _el-input1)
))
(deftest "right side wins on class init"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind .highlight to $highlighted")
(dom-set-inner-html _el-div "test")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "right side wins on init: input (Y) initializes variable (X)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $name to me")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "Bob")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "right side wins on init: variable (Y) initializes input (X)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind me to $name")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "Bob")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "same value does not re-set input (prevents cursor jump)"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $message to me")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "hello")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "setting variable programmatically checks the matching radio"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
(dom-set-attr _el-input "_" "bind $size to me")
(dom-set-attr _el-input "type" "radio")
(dom-set-attr _el-input "name" "size")
(dom-set-attr _el-input "value" "small")
(dom-set-attr _el-input1 "_" "bind $size to me")
(dom-set-attr _el-input1 "type" "radio")
(dom-set-attr _el-input1 "name" "size")
(dom-set-attr _el-input1 "value" "medium")
(dom-set-attr _el-input2 "_" "bind $size to me")
(dom-set-attr _el-input2 "type" "radio")
(dom-set-attr _el-input2 "name" "size")
(dom-set-attr _el-input2 "value" "large")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
(dom-append (dom-body) _el-input2)
(hs-activate! _el-input)
(hs-activate! _el-input1)
(hs-activate! _el-input2)
))
(deftest "shorthand on checkbox binds to checked"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $isDarkMode to me")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-span "_" "when $isDarkMode changes put it into me")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
(hs-activate! _el-span)
))
(deftest "shorthand on select binds to value"
(hs-cleanup!)
(let ((_el-select (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-option3 (dom-create-element "option")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-select "_" "bind $country to me")
(dom-set-attr _el-option "value" "us")
(dom-set-inner-html _el-option "United States")
(dom-set-attr _el-option2 "value" "uk")
(dom-set-inner-html _el-option2 "United Kingdom")
(dom-set-attr _el-option3 "value" "fr")
(dom-set-inner-html _el-option3 "France")
(dom-set-attr _el-span "_" "when $country changes put it into me")
(dom-append (dom-body) _el-select)
(dom-append _el-select _el-option)
(dom-append _el-select _el-option2)
(dom-append _el-select _el-option3)
(dom-append (dom-body) _el-span)
(hs-activate! _el-select)
(hs-activate! _el-span)
))
(deftest "shorthand on text input binds to value"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $greeting to me end when $greeting changes put it into next ")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "hello")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
))
(deftest "shorthand on textarea binds to value"
(hs-cleanup!)
(let ((_el-textarea (dom-create-element "textarea")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-textarea "_" "bind $bio to me")
(dom-set-inner-html _el-textarea "Hello world")
(dom-set-attr _el-span "_" "when $bio changes put it into me")
(dom-append (dom-body) _el-textarea)
(dom-append (dom-body) _el-span)
(hs-activate! _el-textarea)
(hs-activate! _el-span)
))
(deftest "shorthand on type=number preserves number type"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $price to me")
(dom-set-attr _el-input "type" "number")
(dom-set-attr _el-span "_" "when $price changes put it into me")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-input)
(hs-activate! _el-span)
))
(deftest "style bind is one-way: variable drives style, not vice versa"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $opacity and *opacity")
(dom-set-inner-html _el-div "visible")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "syncs variable and attribute in both directions"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind $theme and @data-theme")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "syncs variable and input value in both directions"
(hs-cleanup!)
(let ((_el-name-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-name-input "id" "name-input")
(dom-set-attr _el-name-input "type" "text")
(dom-set-attr _el-name-input "value" "Alice")
(dom-set-attr _el-span "_" "bind $name and #name-input.value end when $name changes put it into me")
(dom-append (dom-body) _el-name-input)
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
))
(deftest "two inputs synced via bind"
(hs-cleanup!)
(let ((_el-slider (dom-create-element "input")) (_el-input (dom-create-element "input")))
(dom-set-attr _el-slider "id" "slider")
(dom-set-attr _el-slider "type" "range")
(dom-set-attr _el-slider "value" "50")
(dom-set-attr _el-input "_" "bind my value and #slider's value")
(dom-set-attr _el-input "type" "number")
(dom-append (dom-body) _el-slider)
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
))
(deftest "unsupported element: bind to plain div errors"
(error "SKIP (untranslated): unsupported element: bind to plain div errors"))
(deftest "variable drives class: setting variable adds/removes class"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "bind .dark and $darkMode")
(dom-set-inner-html _el-div "test")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
)
;; ── breakpoint (2 tests) ──
(defsuite "hs-upstream-breakpoint"
(deftest "parses as a top-level command"
(error "SKIP (untranslated): parses as a top-level command"))
(deftest "parses inside an event handler"
(error "SKIP (untranslated): parses inside an event handler"))
)
;; ── call (6 tests) ──
(defsuite "hs-upstream-call"
(deftest "call functions that return promises are waited on"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call promiseAnInt() then put it into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "42")
))
(deftest "can call functions w/ dollar signs"
(hs-cleanup!)
(host-set! (host-global "window") "called" false)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call $()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "can call functions w/ underscores"
(hs-cleanup!)
(host-set! (host-global "window") "called" false)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call global_function()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "can call global javascript functions"
(hs-cleanup!)
(host-set! (host-global "window") "calledWith" null)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call globalFunction(\"foo\")")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "can call javascript instance functions"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click call document.getElementById(\"d1\") then put it into window.results")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
))
(deftest "can call no argument functions"
(hs-cleanup!)
(host-set! (host-global "window") "called" false)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call globalFunction()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
)
;; ── core/api (1 tests) ──
(defsuite "hs-upstream-core/api"
(deftest "processNodes does not reinitialize a node already processed"
(hs-cleanup!)
(host-set! (host-global "window") "global_int" 0)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set window.global_int to window.global_int + 1")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(dom-dispatch _el-div "click" nil)
))
)
;; ── core/asyncError (2 tests) ──
(defsuite "hs-upstream-core/asyncError"
(deftest "rejected promise stops execution"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click call failAsync() then put 'should not reach' into #out then")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-set-inner-html _el-out "original")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "original")
))
(deftest "rejected promise triggers catch block"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-button "_" "on click call failAsync() then put 'unreachable' into #out catch e put e.message into #out then")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "boom")
))
)
;; ── core/bootstrap (26 tests) ──
(defsuite "hs-upstream-core/bootstrap"
(deftest "can call functions"
(hs-cleanup!)
(host-set! (host-global "window") "calledWith" null)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call globalFunction(\"foo\")")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "can change non-class properties"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add [@foo=\"bar\"]")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-attr? _el-div "foo")))
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-attr _el-div "foo") "bar")
))
(deftest "can respond to events on other elements"
(hs-cleanup!)
(let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-div "_" "on click from #bar then add .clicked")
(dom-append (dom-body) _el-bar)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? (dom-query "div:nth-of-type(2)") "clicked")))
(dom-dispatch (dom-query-by-id "bar") "click" nil)
(assert (dom-has-class? (dom-query "div:nth-of-type(2)") "clicked"))
))
(deftest "can send events"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click send foo to #bar")
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-bar "_" "on foo add .foo-sent")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-bar)
(hs-activate! _el-div)
(hs-activate! _el-bar)
(assert (not (dom-has-class? (dom-query-by-id "bar") "foo-sent")))
(dom-dispatch (nth (dom-query-all (dom-body) "div") 0) "click" nil)
(assert (dom-has-class? (dom-query-by-id "bar") "foo-sent"))
))
(deftest "can send events with args"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-bar (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click send foo(x:42) to #bar")
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-bar "_" "on foo put event.detail.x into my.innerHTML")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-bar)
(hs-activate! _el-div)
(hs-activate! _el-bar)
(dom-dispatch (nth (dom-query-all (dom-body) "div") 0) "click" nil)
(assert= (dom-text-content (dom-query-by-id "bar")) "42")
))
(deftest "can set properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click put \"foo\" into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "foo")
))
(deftest "can set styles"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put \"red\" into my.style.color")
(dom-set-inner-html _el-div "lolwat")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-style _el-div "color") "red")
))
(deftest "can take a class from other elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-add-class _el-div "divs")
(dom-add-class _el-div "foo")
(dom-add-class _el-div1 "divs")
(dom-set-attr _el-div1 "_" "on click take .foo from .divs")
(dom-add-class _el-div2 "divs")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
(hs-activate! _el-div1)
(dom-dispatch (nth (dom-query-all (dom-body) ".divs") 1) "click" nil)
(assert (not (dom-has-class? (nth (dom-query-all (dom-body) ".divs") 0) "foo")))
(assert (dom-has-class? (nth (dom-query-all (dom-body) ".divs") 1) "foo"))
(assert (not (dom-has-class? (nth (dom-query-all (dom-body) ".divs") 2) "foo")))
))
(deftest "can target another div"
(hs-cleanup!)
(let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-div "_" "on click add .foo to #bar")
(dom-append (dom-body) _el-bar)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? (dom-query-by-id "bar") "foo")))
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
(assert (dom-has-class? (dom-query-by-id "bar") "foo"))
))
(deftest "can wait"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo then wait 20ms then add .bar")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
(assert (dom-has-class? _el-div "bar"))
))
(deftest "cleanup clears elt._hyperscript"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "cleanup removes cross-element event listeners"
(hs-cleanup!)
(let ((_el-source (dom-create-element "div")) (_el-target (dom-create-element "div")))
(dom-set-attr _el-source "id" "source")
(dom-set-attr _el-target "id" "target")
(dom-set-attr _el-target "_" "on click from #source add .foo")
(dom-append (dom-body) _el-source)
(dom-append (dom-body) _el-target)
(hs-activate! _el-target)
(dom-dispatch (dom-query-by-id "source") "click" nil)
(assert (dom-has-class? (dom-query-by-id "target") "foo"))
))
(deftest "cleanup removes data-hyperscript-powered"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert= (dom-get-attr _el-div "data-hyperscript-powered") "true")
))
(deftest "cleanup removes event listeners on the element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
(assert (not (dom-has-class? _el-div "foo")))
))
(deftest "cleanup tracks listeners in elt._hyperscript"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "fires hyperscript:before:cleanup and hyperscript:after:cleanup"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "fires hyperscript:before:init and hyperscript:after:init"
(error "SKIP (untranslated): fires hyperscript:before:init and hyperscript:after:init"))
(deftest "hyperscript can have more than one action"
(hs-cleanup!)
(let ((_el-bar (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-bar "id" "bar")
(dom-set-attr _el-div "_" "on click add .foo to #bar then add .blah")
(dom-append (dom-body) _el-bar)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
(assert (dom-has-class? (dom-query-by-id "bar") "foo"))
(assert (not (dom-has-class? (dom-query-by-id "bar") "blah")))
(assert (not (dom-has-class? (dom-query "div:nth-of-type(2)") "foo")))
(assert (dom-has-class? (dom-query "div:nth-of-type(2)") "blah"))
))
(deftest "hyperscript:before:init can cancel initialization"
(error "SKIP (untranslated): hyperscript:before:init can cancel initialization"))
(deftest "logAll config logs events to console"
(hs-cleanup!)
(hs-clear-log-captured!)
(hs-set-log-all! true)
(let ((wa (dom-create-element "div")))
(dom-set-inner-html wa "")
(hs-boot-subtree! wa))
(hs-set-log-all! false)
(assert= (some (fn (l) (string-contains? l "hyperscript:"))
(hs-get-log-captured))
true)
)
(deftest "on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? _el-div "foo")))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
))
(deftest "reinitializes if script attribute changes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "bar"))
))
(deftest "sets data-hyperscript-powered on initialized elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert= (dom-get-attr _el-div "data-hyperscript-powered") "true")
))
(deftest "skips reinitialization if script unchanged"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "stores state on elt._hyperscript"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "toggles"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click toggle .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (not (dom-has-class? _el-div "foo")))
(dom-dispatch _el-div "click" nil)
(assert (dom-has-class? _el-div "foo"))
(dom-dispatch _el-div "click" nil)
(assert (not (dom-has-class? _el-div "foo")))
))
)
;; ── core/dom-scope (5 tests) ──
(defsuite "hs-upstream-core/dom-scope"
(deftest "closest jumps to matching ancestor"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-add-class _el-div "outer")
(dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
(dom-set-attr _el-div1 "_" "init set ^val to 'from-inner'")
(dom-set-attr _el-div1 "dom-scope" "isolated")
(dom-set-attr _el-span "_" "init put ^val into me")
(dom-set-attr _el-span "dom-scope" "closest .outer")
(dom-set-inner-html _el-span "none")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-span)
))
(deftest "closest with no match stops resolution"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^val to 'found'")
(dom-set-attr _el-span "_" "init if ^val is not undefined put 'leaked' into me else put 'blocked' into me")
(dom-set-attr _el-span "dom-scope" "closest .nonexistent")
(dom-set-inner-html _el-span "waiting")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-span)
))
(deftest "isolated allows setting ^var on the isolated element itself"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^outer to 'leaked'")
(dom-set-attr _el-div1 "_" "init set ^inner to 'contained'")
(dom-set-attr _el-div1 "dom-scope" "isolated")
(dom-set-attr _el-span "_" "init put ^inner into me")
(dom-set-inner-html _el-span "none")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-span)
))
(deftest "isolated stops ^var resolution"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^color to 'red'")
(dom-set-attr _el-div1 "dom-scope" "isolated")
(dom-set-attr _el-span "_" "init if ^color is not undefined put 'leaked' into me else put 'blocked' into me")
(dom-set-inner-html _el-span "waiting")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-span)
))
(deftest "parent of jumps past matching ancestor to its parent"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-add-class _el-div "outer")
(dom-set-attr _el-div "_" "init set ^val to 'from-outer'")
(dom-add-class _el-div1 "middle")
(dom-set-attr _el-div1 "_" "init set ^val to 'from-middle'")
(dom-set-attr _el-div1 "dom-scope" "isolated")
(dom-set-attr _el-span "_" "init put ^val into me")
(dom-set-attr _el-span "dom-scope" "parent of .middle")
(dom-set-inner-html _el-span "none")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-span)
))
)
;; ── core/evalStatically (8 tests) ──
(defsuite "hs-upstream-core/evalStatically"
(deftest "throws on math expressions"
(error "SKIP (untranslated): throws on math expressions"))
(deftest "throws on symbol references"
(error "SKIP (untranslated): throws on symbol references"))
(deftest "throws on template strings"
(error "SKIP (untranslated): throws on template strings"))
(deftest "works on boolean literals"
(assert= (eval-hs "true") true)
(assert= (eval-hs "false") false)
)
(deftest "works on null literal"
(assert= (eval-hs "null") nil)
)
(deftest "works on number literals"
(assert= (eval-hs "42") 42)
(assert= (eval-hs "3.14") 3.14)
)
(deftest "works on plain string literals"
(assert= (eval-hs "\"hello\"") "hello")
(assert= (eval-hs "'world'") "world")
)
(deftest "works on time expressions"
(assert= (eval-hs "200ms") 200)
(assert= (eval-hs "2s") 2000)
)
)
;; ── core/liveTemplate (16 tests) ──
(defsuite "hs-upstream-core/liveTemplate"
(deftest "applies init script from _ attribute"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^msg to 'initialized'")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "${}{^msg}")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "loop index variable is captured alongside loop variable"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
#for item in $idxItems index i
- ${}{item}
#end
")
(dom-append (dom-body) _el-script)
))
(deftest "loop variable capture works with remove for live list"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
#for item in $removeItems
- ${}{item.name}
#end
")
(dom-append (dom-body) _el-script)
))
(deftest "loop variables are captured and available in _= handlers"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
#for item in $captureItems index i
- ${}{item.name}
#end
")
(dom-append (dom-body) _el-script)
))
(deftest "multiple live templates are independent"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-script1 (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^x to 'first'")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "${}{^x}")
(dom-set-attr _el-script1 "_" "init set ^x to 'second'")
(dom-set-attr _el-script1 "type" "text/hyperscript-template")
(dom-set-inner-html _el-script1 "${}{^x}")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-script1)
(hs-activate! _el-script)
(hs-activate! _el-script1)
))
(deftest "processes hyperscript on inner elements"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^val to 0")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "\">+
")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "reactively updates when dependencies change"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^count to 0")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
Count: ${}{^count}")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "reacts to global state without init script"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "Hello, ${}{$ltGlobal}!
")
(dom-append (dom-body) _el-script)
))
(deftest "renders static content after the template"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "Hello World")
(dom-append (dom-body) _el-script)
))
(deftest "renders template expressions"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "Hello ${}{$ltName}!")
(dom-append (dom-body) _el-script)
))
(deftest "scope is refreshed after morph so surviving elements get updated indices"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
#for item in $morphItems index i
- ${}{item.name}
#end
")
(dom-append (dom-body) _el-script)
))
(deftest "script type=\"text/hyperscript-template\" works as a live template source"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^stMsg to 'from script'")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "${}{^stMsg}")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "script-based live template preserves ${} in bare attribute position"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^items to [{text:'A', done:true},{text:'B', done:false}]")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "supports #for loops"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^items to ['a', 'b', 'c']")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "
#for item in ^items
- ${}{item}
#end
")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "supports #if conditionals"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "_" "init set ^show to true")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "#if ^show
visible
#end")
(dom-append (dom-body) _el-script)
(hs-activate! _el-script)
))
(deftest "wrapper has display:contents"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")))
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-inner-html _el-script "test")
(dom-append (dom-body) _el-script)
))
)
;; ── core/parser (14 tests) ──
(defsuite "hs-upstream-core/parser"
(deftest "_hyperscript() evaluate API still throws on first error"
(error "SKIP (untranslated): _hyperscript() evaluate API still throws on first error"))
(deftest "basic parse error messages work"
(error "SKIP (untranslated): basic parse error messages work"))
(deftest "can have alternate comments in attributes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "clicked")
))
(deftest "can have alternate comments in scripts"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "// this is a comment def foo() // this is another comment return \"foo\" end // end with a comment"))))
)
(deftest "can have comments in attributes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "clicked")
))
(deftest "can have comments in attributes (triple dash)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "clicked")
))
(deftest "can have comments in scripts"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "-- this is a comment def foo() -- this is another comment return \"foo\" end -- end with a comment--- this is a comment ----this is a comment---- def bar() ---this is another comment return \"bar\" end --- end with a comment"))))
)
(deftest "can support parenthesized commands and features"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "(on click (log me) (trigger foo)) (on foo (put \"clicked\" into my.innerHTML))")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "clicked")
))
(deftest "continues initializing elements in the presence of a parse error"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click bad")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(dom-append _el-div _el-d2)
(hs-activate! _el-d1)
(hs-activate! _el-d2)
(dom-dispatch (dom-query-by-id "d2") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d2")) "clicked")
))
(deftest "element-level isolation still works with error recovery"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(dom-append _el-div _el-d2)
(hs-activate! _el-d1)
(hs-activate! _el-d2)
(dom-dispatch (dom-query-by-id "d2") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d2")) "clicked")
))
(deftest "fires hyperscript:parse-error event with all errors"
(error "SKIP (untranslated): fires hyperscript:parse-error event with all errors"))
(deftest "parse error at EOF on trailing newline does not crash"
(error "SKIP (untranslated): parse error at EOF on trailing newline does not crash"))
(deftest "recovers across feature boundaries and reports all errors"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter put \"hovered\" into my.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "recovers across multiple feature errors"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad end on focus put \"focused\" into my.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
)
;; ── core/reactivity (8 tests) ──
(defsuite "hs-upstream-core/reactivity"
(deftest "NaN → NaN does not retrigger handlers (Object.is semantics)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $rxNanVal changes increment $rxNanCount")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "effect switches its dependencies based on control flow"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live if $rxCond put $rxA into me else put $rxB into me end end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "effects fire in source registration order"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $rxTrigger changes call $rxOrder.push('first')")
(dom-set-attr _el-div1 "_" "when $rxTrigger changes call $rxOrder.push('second')")
(dom-set-attr _el-div2 "_" "when $rxTrigger changes call $rxOrder.push('third')")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-div2)
))
(deftest "effects on disconnected elements stop automatically"
(hs-cleanup!)
(let ((_el-persist (dom-create-element "div")) (_el-doomed (dom-create-element "div")))
(dom-set-attr _el-persist "id" "persist")
(dom-set-attr _el-persist "_" "when $rxDcVal changes increment $rxDcCount then put $rxDcVal into me")
(dom-set-attr _el-doomed "id" "doomed")
(dom-set-attr _el-doomed "_" "when $rxDcVal changes increment $rxDcCount")
(dom-append (dom-body) _el-persist)
(dom-append (dom-body) _el-doomed)
(hs-activate! _el-persist)
(hs-activate! _el-doomed)
))
(deftest "element-scoped writes only trigger effects on the same element"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "init set :count to 0 then on click increment :count then when :count changes put :count into me")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-b "_" "init set :count to 0 then when :count changes put :count into me")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
(hs-activate! _el-a)
(hs-activate! _el-b)
))
(deftest "multiple effects on the same global fire once per write"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "when $rxVal changes increment $rxCount then put $rxVal into me")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-b "_" "when $rxVal changes put $rxVal into me")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
(hs-activate! _el-a)
(hs-activate! _el-b)
))
(deftest "reactive loops are detected and stopped after 100 consecutive triggers"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $rxLoop changes increment $rxLoop")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "setting same value does not retrigger handler"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $rxSameVal changes increment $rxSameCount")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
)
;; ── core/regressions (16 tests) ──
(defsuite "hs-upstream-core/regressions"
(deftest "async exception"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click async transition opacity to 0 log \"hello!\"")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "button query in form"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")) (_el-b1 (dom-create-element "button")))
(dom-set-attr _el-form "_" "on click get the in me set it @disabled to true")
(dom-set-attr _el-b1 "id" "b1")
(dom-set-inner-html _el-b1 "Button")
(dom-append (dom-body) _el-form)
(dom-append _el-form _el-b1)
(hs-activate! _el-form)
(dom-dispatch _el-form "click" nil)
))
(deftest "can create a paragraph tag"
(hs-cleanup!)
(let ((_el-i1 (dom-create-element "input")) (_el-d2 (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-i1 "id" "i1")
(dom-set-attr _el-i1 "value" "foo")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-div "_" "on click make a then put #i1.value into its textContent put it.outerHTML at end of #d2")
(dom-append (dom-body) _el-i1)
(dom-append _el-i1 _el-d2)
(dom-append _el-i1 _el-div)
(hs-activate! _el-div)
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
))
(deftest "can invoke functions w/ numbers in name"
(hs-cleanup!)
(host-set! (host-global "window") "select2" (fn () "select2"))
(let ((_el-button (dom-create-element "button")))
(dom-set-attr _el-button "_" "on click put select2() into me")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content _el-button) "select2")
))
(deftest "can pick detail fields out by name"
(error "SKIP (skip-list): can pick detail fields out by name"))
(deftest "can refer to function in init blocks"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init call foo() end def foo() put \"here\" into #d1's innerHTML end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "init call foo() end def foo() put \\\"here\\\" into #d1's innerHTML end"))))
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-d1)
(assert= (dom-text-content (dom-query-by-id "d1")) "here")
))
(deftest "can remove by clicks elsewhere"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-other (dom-create-element "div")))
(dom-set-attr _el-target "id" "target")
(dom-set-attr _el-target "_" "on click elsewhere remove me")
(dom-set-attr _el-other "id" "other")
(dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-other)
(hs-activate! _el-target)
(dom-dispatch (dom-query-by-id "other") "click" nil)
))
(deftest "can remove class by id"
(hs-cleanup!)
(let ((_el-email-form (dom-create-element "form")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-email-form "id" "email-form")
(dom-add-class _el-email-form "hideme")
(dom-set-attr _el-div "_" "on click remove .hideme from #email-form")
(dom-append (dom-body) _el-email-form)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert (dom-has-class? (dom-query-by-id "email-form") "hideme"))
(dom-dispatch _el-div "click" nil)
(assert (not (dom-has-class? (dom-query-by-id "email-form") "hideme")))
))
(deftest "can trigger htmx events"
(hs-cleanup!)
(let ((_el-div1 (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-div1 "id" "div1")
(dom-set-attr _el-div1 "_" "on htmx:foo put \"foo\" into my.innerHTML")
(dom-set-attr _el-div "_" "on click send htmx:foo to #div1")
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-div)
(dom-dispatch (dom-query "div:nth-of-type(2)") "click" nil)
(assert= (dom-text-content (dom-query-by-id "div1")) "foo")
))
(deftest "extra chars cause error when evaling"
(error "SKIP (untranslated): extra chars cause error when evaling"))
(deftest "listen for event on form"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")) (_el-b1 (dom-create-element "button")))
(dom-set-attr _el-b1 "id" "b1")
(dom-set-attr _el-b1 "_" "on click from closest put \"clicked\" into me")
(dom-set-inner-html _el-b1 "Button")
(dom-append (dom-body) _el-form)
(dom-append _el-form _el-b1)
(hs-activate! _el-b1)
(dom-dispatch _el-form "click" nil)
(assert= (dom-text-content (dom-query-by-id "b1")) "clicked")
))
(deftest "me and it is properly set when responding to events"
(hs-cleanup!)
(let ((_el-name (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-name "id" "name")
(dom-set-attr _el-div "_" "on click from #name set window.me to me set window.it to it")
(dom-append (dom-body) _el-name)
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch (dom-query-by-id "name") "click" nil)
))
(deftest "me symbol works in from expressions"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click from closest parent put \"Foo\" into me")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(hs-activate! _el-d1)
(assert= (dom-text-content (dom-query-by-id "d1")) "")
(dom-dispatch (nth (dom-query-all (dom-body) "div") 0) "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "Foo")
))
(deftest "properly interpolates values"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")))
(dom-set-attr _el-button "_" "on click set count to 1 then set optName to `options_${count}_value` then put optName into me")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content _el-button) "options_1_value")
))
(deftest "properly interpolates values 2"
(hs-cleanup!)
(let ((_el-button (dom-create-element "button")))
(dom-set-attr _el-button "_" "on click set trackingcode to `AB123456789KK` then set pdfurl to `https://yyy.xxxxxx.com/path/out/${trackingcode}.pdf` then put pdfurl into me")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content _el-button) "https://yyy.xxxxxx.com/path/out/AB123456789KK.pdf")
))
(deftest "string literals can dot-invoked against"
(assert= (eval-hs "'foo'.length") 3)
(assert= (eval-hs "`foo`.length") 3)
(assert= (eval-hs "\"foo\".length") 3)
)
)
;; ── core/runtime (7 tests) ──
(defsuite "hs-upstream-core/runtime"
(deftest "arrays args are handled properly wrt Promises"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def invokesArrayPromise() return { foo: stringPromise(), bar: stringPromise(), baz: stringPromise() }end def stringPromise() wait 20ms return 'foo' end"))))
)
(deftest "async hypertrace is reasonable"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end def baz(str) wait 20ms throw str end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end def baz(str) wait 20ms throw str end"))))
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call bar()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "has proper stack"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return bar() end def bar() return meta.caller end"))))
)
(deftest "has proper stack from event handler"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() log meta.caller return meta.caller end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() log meta.caller return meta.caller end"))))
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put bar().meta.feature.type into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "onFeature")
))
(deftest "hypertrace from javascript is reasonable"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end"))))
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call bar()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "hypertrace is reasonable"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end def baz(str) throw str end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def bar() call baz('nope') end def baz(str) throw str end"))))
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call bar()")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
))
(deftest "scalar args are handled properly wrt Promises"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def invokesScalarPromise() return stringPromise()end def stringPromise() wait 20ms return 'foo' end"))))
)
)
;; ── core/runtimeErrors (18 tests) ──
(defsuite "hs-upstream-core/runtimeErrors"
(deftest "reports basic function invocation null errors properly"
(error "SKIP (untranslated): reports basic function invocation null errors properly"))
(deftest "reports basic function invocation null errors properly w/ of"
(error "SKIP (untranslated): reports basic function invocation null errors properly w/ of"))
(deftest "reports basic function invocation null errors properly w/ possessives"
(error "SKIP (untranslated): reports basic function invocation null errors properly w/ possessives"))
(deftest "reports null errors on add command properly"
(error "SKIP (untranslated): reports null errors on add command properly"))
(deftest "reports null errors on decrement command properly"
(error "SKIP (untranslated): reports null errors on decrement command properly"))
(deftest "reports null errors on default command properly"
(error "SKIP (untranslated): reports null errors on default command properly"))
(deftest "reports null errors on hide command properly"
(error "SKIP (untranslated): reports null errors on hide command properly"))
(deftest "reports null errors on increment command properly"
(error "SKIP (untranslated): reports null errors on increment command properly"))
(deftest "reports null errors on measure command properly"
(error "SKIP (untranslated): reports null errors on measure command properly"))
(deftest "reports null errors on put command properly"
(error "SKIP (untranslated): reports null errors on put command properly"))
(deftest "reports null errors on remove command properly"
(error "SKIP (untranslated): reports null errors on remove command properly"))
(deftest "reports null errors on send command properly"
(error "SKIP (untranslated): reports null errors on send command properly"))
(deftest "reports null errors on sets properly"
(error "SKIP (untranslated): reports null errors on sets properly"))
(deftest "reports null errors on settle command properly"
(error "SKIP (untranslated): reports null errors on settle command properly"))
(deftest "reports null errors on show command properly"
(error "SKIP (untranslated): reports null errors on show command properly"))
(deftest "reports null errors on toggle command properly"
(error "SKIP (untranslated): reports null errors on toggle command properly"))
(deftest "reports null errors on transition command properly"
(error "SKIP (untranslated): reports null errors on transition command properly"))
(deftest "reports null errors on trigger command properly"
(error "SKIP (untranslated): reports null errors on trigger command properly"))
)
;; ── core/scoping (20 tests) ──
(defsuite "hs-upstream-core/scoping"
(deftest "basic behavior scoping works"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(foo) on click set @out to foo")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "install Behave(foo:10)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "behavior scoping is at the element level"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 2 set @out to foo")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "install Behave(foo:10)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
))
(deftest "behavior scoping is isolated from other behaviors"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 3 set @out to foo behavior BehaveTwo(foo) on click 2 set element foo to 1 on click 4 set @out2 to foo")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "install Behave(foo:10) install BehaveTwo(foo:42)")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
(assert= (dom-get-attr (dom-query-by-id "d1") "out2") "1")
))
(deftest "behavior scoping is isolated from the core element scope"
(hs-cleanup!)
(let ((_el-script (dom-create-element "script")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-script "type" "text/hyperscript")
(dom-set-inner-html _el-script "behavior Behave(foo) on click 1 set foo to foo + 10 on click 3 set @out to foo")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "install Behave(foo:10) on click 2 set element foo to 1 on click 4 set @out2 to foo")
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
(assert= (dom-get-attr (dom-query-by-id "d1") "out2") "1")
))
(deftest "element scoped variables are local only to element"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set element x to 10")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "_" "on click set @out to x")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-d2)
(hs-activate! _el-d1)
(hs-activate! _el-d2)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d2") "click" nil)
))
(deftest "element scoped variables span features"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 set element x to 10 on click 2 set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "element scoped variables span features w/short syntax"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 set :x to 10 on click 2 set @out to :x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "element scoped variables support pseudo-possessive syntax"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set the element's x to 10 then set @out to the element's x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "element scoped variables work"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set element x to 10 then set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "element scoped variables work w/short syntax"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set :x to 10 then set @out to :x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "global scoped variables span features"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 set $x to 10 on click 2 set @out to $x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "global scoped variables work"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set global x to 10 then set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "global scoped variables work w/ short syntax"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set $x to 10 then set @out to $x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "locally scoped variables do not span features"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 set x to 10 on click 2 set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
))
(deftest "locally scoped variables don't clash with built-in variables"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click repeat for meta in [1, 2, 3] set @out to meta end")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "[object Object]")
))
(deftest "locally scoped variables work"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set x to 10 then set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "set favors local variables over global variables"
(hs-cleanup!)
(host-set! (host-global "window") "foo" 12)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 set foo to 20 then set @out to foo")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "20")
))
(deftest "setting a global scoped variable spans features"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 default global x to 0 on click 2 set global x to 10 on click 3 set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "setting an element scoped variable spans features"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click 1 default element x to 0 on click 2 set element x to 10 on click 3 set @out to x")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
(deftest "variables are hoisted"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click if true set foo to 10 end set @out to foo")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-get-attr (dom-query-by-id "d1") "out") "10")
))
)
;; ── core/security (1 tests) ──
(defsuite "hs-upstream-core/security"
(deftest "on a single div"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click add .foo")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert (not (dom-has-class? (dom-query-by-id "d1") "foo")))
))
)
;; ── core/sourceInfo (4 tests) ──
(defsuite "hs-upstream-core/sourceInfo"
(deftest "debug"
(error "SKIP (untranslated): debug"))
(deftest "get line works for statements"
(error "SKIP (untranslated): get line works for statements"))
(deftest "get source works for expressions"
(error "SKIP (untranslated): get source works for expressions"))
(deftest "get source works for statements"
(error "SKIP (untranslated): get source works for statements"))
)
;; ── core/tokenizer (17 tests) ──
(defsuite "hs-upstream-core/tokenizer"
(deftest "handles $ in template properly"
(error "SKIP (untranslated): handles $ in template properly"))
(deftest "handles all special escapes properly"
(error "SKIP (untranslated): handles all special escapes properly"))
(deftest "handles basic token types"
(error "SKIP (untranslated): handles basic token types"))
(deftest "handles class identifiers properly"
(error "SKIP (untranslated): handles class identifiers properly"))
(deftest "handles comments properly"
(error "SKIP (untranslated): handles comments properly"))
(deftest "handles hex escapes properly"
(error "SKIP (untranslated): handles hex escapes properly"))
(deftest "handles id references properly"
(error "SKIP (untranslated): handles id references properly"))
(deftest "handles identifiers properly"
(error "SKIP (untranslated): handles identifiers properly"))
(deftest "handles identifiers with numbers properly"
(error "SKIP (untranslated): handles identifiers with numbers properly"))
(deftest "handles look ahead property"
(error "SKIP (untranslated): handles look ahead property"))
(deftest "handles numbers properly"
(error "SKIP (untranslated): handles numbers properly"))
(deftest "handles operators properly"
(error "SKIP (untranslated): handles operators properly"))
(deftest "handles strings properly"
(error "SKIP (untranslated): handles strings properly"))
(deftest "handles strings properly 2"
(error "SKIP (untranslated): handles strings properly 2"))
(deftest "handles template bootstrap properly"
(error "SKIP (untranslated): handles template bootstrap properly"))
(deftest "handles whitespace properly"
(error "SKIP (untranslated): handles whitespace properly"))
(deftest "string interpolation isnt surprising"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set x to 42 then put `test${x} test ${x} test$x test $x test $x test ${x} test$x test_$x test_${x} test-$x test.$x` into my.innerHTML")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "test${x} test 42 test$x test 42 test $x test ${x} test42 test_42 test_42 test-42 test.42")
))
)
;; ── def (27 tests) ──
(defsuite "hs-upstream-def"
(deftest "async finally blocks run normally"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10 finally set window.bar to 20 end"))))
)
(deftest "async finally blocks run when an exception occurs"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait a tick then set window.bar to 10 throw \"foo\" finally set window.bar to 20 end"))))
)
(deftest "can call asynchronously"
(error "SKIP (skip-list): can call asynchronously"))
(deftest "can catch async exceptions"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\" end def foo() call doh() catch e set window.bar to e end"))))
)
(deftest "can catch exceptions"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e set window.bar to e end"))))
)
(deftest "can catch nested async exceptions"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def doh() wait 10ms throw \"bar\" end def foo() call doh() catch e set window.bar to e end"))))
)
(deftest "can define a basic no arg function"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() add .called to #d1 end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() add .called to #d1 end"))))
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call foo()")
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-div)
))
(deftest "can define a basic one arg function"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo(str) put str into #d1.innerHTML end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo(str) put str into #d1.innerHTML end"))))
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call foo(\"called\")")
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-div)
))
(deftest "can exit"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() exit end"))))
)
(deftest "can install a function on an element and use in children w/ no leak"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-div "_" "def func() put 42 into #d3")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click call func()")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d3 "id" "d3")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(dom-append _el-div _el-d2)
(dom-append _el-div _el-d3)
(hs-activate! _el-div)
(hs-activate! _el-d1)
))
(deftest "can install a function on an element and use in children w/ return value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-div "_" "def func() return 42")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click put func() into me")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d3 "id" "d3")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(dom-append _el-div _el-d2)
(dom-append _el-div _el-d3)
(hs-activate! _el-div)
(hs-activate! _el-d1)
))
(deftest "can install a function on an element and use me symbol correctly"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-outer "_" "def func() put 42 into me")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click call func()")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d3 "id" "d3")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-d1)
(dom-append _el-outer _el-d2)
(dom-append _el-outer _el-d3)
(hs-activate! _el-outer)
(hs-activate! _el-d1)
))
(deftest "can interop with javascript"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \"foo\" end"))))
)
(deftest "can interop with javascript asynchronously"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \"foo\" end"))))
)
(deftest "can rethrow in async catch blocks"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e wait 10ms throw e end"))))
)
(deftest "can rethrow in catch blocks"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e throw e end"))))
)
(deftest "can return a value asynchronously"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \"foo\" end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() wait 1ms return \\\"foo\\\" end"))))
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-div)
))
(deftest "can return a value synchronously"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \"foo\" end"))))
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return \\\"foo\\\" end"))))
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click call foo() then put it into #d1.innerText")
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-d1)
(hs-activate! _el-div)
))
(deftest "can return in async catch blocks"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e wait 10ms return 42 end"))))
)
(deftest "can return in catch blocks"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() throw \"bar\" catch e return 42 end"))))
)
(deftest "can return without a value"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() return end"))))
)
(deftest "exit stops execution mid-function"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set x to 1 then exit then set x to 2 then return x end"))))
)
(deftest "finally blocks run normally"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 finally set window.bar to 20 end"))))
)
(deftest "finally blocks run when an exception expr occurs"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 call throwsAsyncException() finally set window.bar to 20 end"))))
)
(deftest "finally blocks run when an exception occurs"
(hs-cleanup!)
(guard (_e (true nil)) (eval-expr-cek (hs-to-sx (hs-compile "def foo() set window.bar to 10 throw \"foo\" finally set window.bar to 20 end"))))
)
(deftest "functions can be namespaced"
(error "SKIP (skip-list): functions can be namespaced"))
(deftest "is called synchronously"
(error "SKIP (skip-list): is called synchronously"))
)
;; ── default (15 tests) ──
(defsuite "hs-upstream-default"
(deftest "can default array elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set arr to [null, null] then default arr[0] to 'yes' then put arr[0] into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "yes")
))
(deftest "can default attributes"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default @foo to \"foo\" then put @foo into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "foo")
))
(deftest "can default of-expression properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default foo of me to 'bar' then put my foo into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
))
(deftest "can default possessive properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default #d1's foo to 'bar' then put #d1's foo into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
))
(deftest "can default properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default me.foo to \"foo\" then put me.foo into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "foo")
))
(deftest "can default style ref when unset"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click default *background-color to 'red'")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-style _el-div "background-color") "red")
))
(deftest "can default variables"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default x to \"foo\" then put x into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "foo")
))
(deftest "default array element respects existing value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set arr to ['existing', null] then default arr[0] to 'new' then put arr[0] into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "existing")
))
(deftest "default attributes respect existing values"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click default @foo to \"foo\" then put @foo into me")
(dom-set-attr _el-d1 "foo" "bar")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
))
(deftest "default overwrites empty string"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set x to \"\" then default x to \"fallback\" then put x into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "fallback")
))
(deftest "default preserves false"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set x to false then default x to true then put x into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "false")
))
(deftest "default preserves zero"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set x to 0 then default x to 10 then put x into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "0")
))
(deftest "default properties respect existing values"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set me.foo to \"bar\" then default me.foo to \"foo\" then put me.foo into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
))
(deftest "default style ref preserves existing value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click default *color to 'red'")
(dom-set-attr _el-div "style" "color: blue")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-get-style _el-div "color") "blue")
))
(deftest "default variables respect existing values"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set x to \"bar\" then default x to \"foo\" then put x into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
))
)
;; ── dialog (12 tests) ──
(defsuite "hs-upstream-dialog"
(deftest "close closes a details element"
(hs-cleanup!)
(let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-attr _el-d "open" "")
(dom-set-inner-html _el-summary "More")
(dom-set-inner-html _el-p "Content")
(dom-set-attr _el-button "_" "on click close #d")
(dom-set-inner-html _el-button "Close")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-summary)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
(dom-dispatch _el-button "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
))
(deftest "close closes a dialog"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-close (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-close "id" "close")
(dom-set-attr _el-close "_" "on click close #d")
(dom-set-inner-html _el-close "Close")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append _el-d _el-close)
(hs-activate! _el-close)
(host-call (dom-query-by-id "d") "showModal")
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
(dom-dispatch (dom-query-by-id "close") "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
))
(deftest "close hides a popover"
(hs-cleanup!)
(let ((_el-p (dom-create-element "div")) (_el-p1 (dom-create-element "p")) (_el-close (dom-create-element "button")))
(dom-set-attr _el-p "id" "p")
(dom-set-inner-html _el-p1 "Popover content")
(dom-set-attr _el-close "id" "close")
(dom-set-attr _el-close "_" "on click close #p")
(dom-set-inner-html _el-close "Close")
(dom-append (dom-body) _el-p)
(dom-append _el-p _el-p1)
(dom-append _el-p _el-close)
(hs-activate! _el-close)
(dom-dispatch (dom-query-by-id "close") "click" nil)
))
(deftest "hide closes a dialog"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-close (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-close "id" "close")
(dom-set-attr _el-close "_" "on click hide #d")
(dom-set-inner-html _el-close "Close")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append _el-d _el-close)
(hs-activate! _el-close)
(host-call (dom-query-by-id "d") "showModal")
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
(dom-dispatch (dom-query-by-id "close") "click" nil)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
))
(deftest "open on implicit me"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-attr _el-d "_" "on myOpen open")
(dom-set-attr _el-button "_" "on click send myOpen to #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append (dom-body) _el-button)
(hs-activate! _el-d)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
(deftest "open opens a details element"
(hs-cleanup!)
(let ((_el-d (dom-create-element "details")) (_el-summary (dom-create-element "summary")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-summary "More")
(dom-set-inner-html _el-p "Content")
(dom-set-attr _el-button "_" "on click open #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-summary)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
(deftest "open opens a dialog"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-button "_" "on click open #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
(deftest "open opens a modal dialog (matches :modal)"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-button "_" "on click open #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "open shows a popover"
(hs-cleanup!)
(let ((_el-p (dom-create-element "div")) (_el-p1 (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-p "id" "p")
(dom-set-inner-html _el-p1 "Popover content")
(dom-set-attr _el-button "_" "on click open #p")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-p)
(dom-append _el-p _el-p1)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "show on already-open dialog is a no-op"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-button "_" "on click show #d")
(dom-set-inner-html _el-button "Show Again")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append _el-d _el-button)
(hs-activate! _el-button)
(host-call (dom-query-by-id "d") "showModal")
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
(deftest "show opens a dialog (non-modal)"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-button "_" "on click show #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert (not (dom-has-attr? (dom-query-by-id "d") "open")))
(dom-dispatch _el-button "click" nil)
(assert (dom-has-attr? (dom-query-by-id "d") "open"))
))
(deftest "show opens a non-modal dialog (no ::backdrop)"
(hs-cleanup!)
(let ((_el-d (dom-create-element "dialog")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d "id" "d")
(dom-set-inner-html _el-p "Hello")
(dom-set-attr _el-button "_" "on click show #d")
(dom-set-inner-html _el-button "Open")
(dom-append (dom-body) _el-d)
(dom-append _el-d _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
)
;; ── empty (13 tests) ──
(defsuite "hs-upstream-empty"
(deftest "can empty a checkbox"
(hs-cleanup!)
(let ((_el-cb1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-cb1 "id" "cb1")
(dom-set-attr _el-cb1 "type" "checkbox")
(dom-set-attr _el-cb1 "checked" "")
(dom-set-attr _el-button "_" "on click empty #cb1")
(dom-set-inner-html _el-button "Empty")
(dom-append (dom-body) _el-cb1)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert (dom-get-prop (dom-query-by-id "cb1") "checked"))
(dom-dispatch _el-button "click" nil)
(assert (not (dom-get-prop (dom-query-by-id "cb1") "checked")))
))
(deftest "can empty a form (clears all inputs)"
(hs-cleanup!)
(let ((_el-f1 (dom-create-element "form")) (_el-t2 (dom-create-element "input")) (_el-ta2 (dom-create-element "textarea")) (_el-cb2 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-f1 "id" "f1")
(dom-set-attr _el-t2 "id" "t2")
(dom-set-attr _el-t2 "type" "text")
(dom-set-attr _el-t2 "value" "val")
(dom-set-attr _el-ta2 "id" "ta2")
(dom-set-inner-html _el-ta2 "text")
(dom-set-attr _el-cb2 "id" "cb2")
(dom-set-attr _el-cb2 "type" "checkbox")
(dom-set-attr _el-cb2 "checked" "")
(dom-set-attr _el-button "_" "on click empty #f1")
(dom-set-inner-html _el-button "Empty")
(dom-append (dom-body) _el-f1)
(dom-append _el-f1 _el-t2)
(dom-append _el-f1 _el-ta2)
(dom-append _el-f1 _el-cb2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-get-prop (dom-query-by-id "t2") "value") "")
(assert= (dom-get-prop (dom-query-by-id "ta2") "value") "")
(assert (not (dom-get-prop (dom-query-by-id "cb2") "checked")))
))
(deftest "can empty a map"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :m to {a:1, b:2} as Map then empty :m then put :m.size into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "0")
))
(deftest "can empty a select"
(hs-cleanup!)
(let ((_el-sel1 (dom-create-element "select")) (_el-option (dom-create-element "option")) (_el-option2 (dom-create-element "option")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-sel1 "id" "sel1")
(dom-set-attr _el-option "value" "a")
(dom-set-inner-html _el-option "A")
(dom-set-attr _el-option2 "value" "b")
(dom-set-attr _el-option2 "selected" "")
(dom-set-inner-html _el-option2 "B")
(dom-set-attr _el-button "_" "on click empty #sel1")
(dom-set-inner-html _el-button "Empty")
(dom-append (dom-body) _el-sel1)
(dom-append _el-sel1 _el-option)
(dom-append _el-sel1 _el-option2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "can empty a set"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :s to [1,2,3] as Set then empty :s then put :s.size into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "0")
))
(deftest "can empty a text input"
(hs-cleanup!)
(let ((_el-t1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-t1 "id" "t1")
(dom-set-attr _el-t1 "type" "text")
(dom-set-attr _el-t1 "value" "hello")
(dom-set-attr _el-button "_" "on click empty #t1")
(dom-set-inner-html _el-button "Empty")
(dom-append (dom-body) _el-t1)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert= (dom-get-prop (dom-query-by-id "t1") "value") "hello")
(dom-dispatch _el-button "click" nil)
(assert= (dom-get-prop (dom-query-by-id "t1") "value") "")
))
(deftest "can empty a textarea"
(hs-cleanup!)
(let ((_el-ta1 (dom-create-element "textarea")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-ta1 "id" "ta1")
(dom-set-inner-html _el-ta1 "some text")
(dom-set-attr _el-button "_" "on click empty #ta1")
(dom-set-inner-html _el-button "Empty")
(dom-append (dom-body) _el-ta1)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-get-prop (dom-query-by-id "ta1") "value") "")
))
(deftest "can empty an array"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click set :arr to [1,2,3] then empty :arr then put :arr.length into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "0")
))
(deftest "can empty an element"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-inner-html _el-p "hello")
(dom-set-inner-html _el-p2 "world")
(dom-set-attr _el-button "_" "on click empty #d1")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-p)
(dom-append _el-d1 _el-p2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert= (dom-text-content (dom-query-by-id "d1")) "helloworld")
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "")
))
(deftest "can empty multiple elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-div2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-add-class _el-div "clearme")
(dom-set-inner-html _el-p "a")
(dom-add-class _el-div2 "clearme")
(dom-set-inner-html _el-p3 "b")
(dom-set-attr _el-button "_" "on click empty .clearme")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-p)
(dom-append (dom-body) _el-div2)
(dom-append _el-div2 _el-p3)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (nth (dom-query-all (dom-body) ".clearme") 0)) "")
(assert= (dom-text-content (let ((_all (dom-query-all (dom-body) ".clearme"))) (nth _all (- (len _all) 1)))) "")
))
(deftest "clear is an alias for empty"
(hs-cleanup!)
(let ((_el-t3 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-t3 "id" "t3")
(dom-set-attr _el-t3 "type" "text")
(dom-set-attr _el-t3 "value" "hello")
(dom-set-attr _el-button "_" "on click clear #t3")
(dom-set-inner-html _el-button "Clear")
(dom-append (dom-body) _el-t3)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert= (dom-get-prop (dom-query-by-id "t3") "value") "hello")
(dom-dispatch _el-button "click" nil)
(assert= (dom-get-prop (dom-query-by-id "t3") "value") "")
))
(deftest "clear works on elements"
(hs-cleanup!)
(let ((_el-d2 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-d2 "id" "d2")
(dom-set-inner-html _el-p "content")
(dom-set-attr _el-button "_" "on click clear #d2")
(dom-append (dom-body) _el-d2)
(dom-append _el-d2 _el-p)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(assert= (dom-text-content (dom-query-by-id "d2")) "content")
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "d2")) "")
))
(deftest "empty with no target empties me"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click empty")
(dom-set-inner-html _el-div "content")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(assert= (dom-text-content _el-div) "content")
(dom-dispatch _el-div "click" nil)
(assert= (dom-text-content _el-div) "")
))
)
;; ── expressions/arrayIndex (14 tests) ──
(defsuite "hs-upstream-expressions/arrayIndex"
(deftest "can create an array literal"
(assert= (eval-hs "[1, 2, 3]") (list 1 2 3))
)
(deftest "can get the range of first values in an array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[..3] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can get the range of last values in an array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[3 ..] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can get the range of last values in an array WITHOUT EXTRA SPACES"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[3..] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can get the range of middle values in an array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[2 .. 3] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can get the range of middle values in an array WITHOUT EXTRA SPACES"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set var to [0,1,2,3,4,5] then put var[2..3] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can get the range of middle values in an array using an expression"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set index to 3 then set var to [0,1,2,3,4,5] then put var[(index-1)..(index+1)] as String into #d1")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can index an array value"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can index an array value at the beginning of the array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[0] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can index an array value at the end of the array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[2] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can index an array value in the middle of the array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[1] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "can index an array value with an expression"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [\"A\", \"B\", \"C\"] then put newVar[1+1] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
))
(deftest "errors when index exceeds array length"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to [10, 20, 30] then put newVar[10] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
))
(deftest "errors when indexed value is not an array"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set newVar to \"not-an-array\" then put newVar[0] into #d1.innerHTML")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
))
)
;; ── expressions/arrayLiteral (8 tests) ──
(defsuite "hs-upstream-expressions/arrayLiteral"
(deftest "arrays can contain expressions"
(assert= (eval-hs "[1 + 1, 2 * 3, 10 - 5]") (list 2 6 5))
)
(deftest "arrays containing objects work"
(assert= (eval-hs "[{a: 1}, {b: 2}]") (list {:a 1} {:b 2}))
)
(deftest "deeply nested array literals work"
(assert= (eval-hs "[[[1]], [[2, 3]]]") (list (list (list 1)) (list (list 2 3))))
)
(deftest "empty array literals work"
(assert= (eval-hs "[]") (list))
)
(deftest "mixed-type array literal works"
(assert= (eval-hs "[1, 'two', true, null]") (list 1 "two" true nil))
)
(deftest "multi element array literal works"
(assert= (eval-hs "[true, false]") (list true false))
)
(deftest "nested array literals work"
(assert= (eval-hs "[[1, 2], [3, 4]]") (list (list 1 2) (list 3 4)))
)
(deftest "one element array literal works"
(assert= (eval-hs "[true]") (list true))
)
)
;; ── expressions/asExpression (42 tests) ──
(defsuite "hs-upstream-expressions/asExpression"
(deftest "can accept custom conversions"
(error "SKIP (untranslated): can accept custom conversions"))
(deftest "can accept custom dynamic conversions"
(error "SKIP (untranslated): can accept custom dynamic conversions"))
(deftest "can use the a modifier if you like"
(error "SKIP (untranslated): can use the a modifier if you like"))
(deftest "can use the an modifier if you'd like"
(assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as an Object") "foo") "bar")
)
(deftest "collects duplicate text inputs into an array"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node " ")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (host-get _result "tag") (list "alpha" "beta" "gamma"))
(assert= (host-get _result "tag") (list "alpha" "beta" "gamma"))
))
)
(deftest "converts a NodeList into HTML"
(error "SKIP (untranslated): converts a NodeList into HTML"))
(deftest "converts a complete form into Values"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node " Catches elements nested deeply within the DOM tree
Works with Textareas Works with Single Select Boxes Works with Multi-Select Boxes Works with Radio Buttons Works with Checkboxes ")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (host-get _result "firstName") "John")
(assert= (host-get _result "lastName") "Connor")
(assert= (host-get _result "phone") "555-1212")
(assert= (host-get _result "aboutMe") "It began on a warm summer day in 1969...")
(assert= (host-get _result "animal") "dog")
(assert= (nth (host-get _result "spiritAnimal") 0) "dog")
(assert= (nth (host-get _result "spiritAnimal") 1) "raccoon")
(assert= (host-get _result "coolOrNaw") "Cool")
(assert= (nth (host-get _result "gender") 0) "Male")
(assert= (nth (host-get _result "gender") 1) "Female")
(assert= (nth (host-get _result "gender") 2) "Other")
))
)
(deftest "converts a form element into Values"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "
")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (host-get _result "firstName") "John")
(assert= (host-get _result "lastName") "Connor")
(assert= (host-get _result "areaCode") "213")
(assert= (host-get _result "phone") "555-1212")
))
)
(deftest "converts a form element into Values | FormEncoded"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "
")
(let ((_result (eval-hs-locals "x as Values | FormEncoded" (list (list (quote x) _node)))))
(assert= _result "firstName=John&lastName=Connor&areaCode=213&phone=555-1212")
))
)
(deftest "converts a form element into Values | JSONString"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "
")
(let ((_result (eval-hs-locals "x as Values | JSONString" (list (list (quote x) _node)))))
(assert= _result "{\"firstName\":\"John\",\"lastName\":\"Connor\",\"areaCode\":\"213\",\"phone\":\"555-1212\"}")
))
)
(deftest "converts a query selector into Values"
(hs-cleanup!)
(let ((_el-qsdiv (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-br (dom-create-element "br")) (_el-input3 (dom-create-element "input")) (_el-br4 (dom-create-element "br")) (_el-input5 (dom-create-element "input")) (_el-input6 (dom-create-element "input")))
(dom-set-attr _el-qsdiv "id" "qsdiv")
(dom-set-attr _el-qsdiv "_" "on click put as Values into my.customData")
(dom-add-class _el-input "include")
(dom-set-attr _el-input "name" "firstName")
(dom-set-attr _el-input "value" "John")
(dom-add-class _el-input3 "include")
(dom-set-attr _el-input3 "name" "lastName")
(dom-set-attr _el-input3 "value" "Connor")
(dom-add-class _el-input5 "include")
(dom-set-attr _el-input5 "name" "areaCode")
(dom-set-attr _el-input5 "value" "213")
(dom-add-class _el-input6 "dont-include")
(dom-set-attr _el-input6 "name" "phone")
(dom-set-attr _el-input6 "value" "555-1212")
(dom-append (dom-body) _el-qsdiv)
(dom-append _el-qsdiv _el-input)
(dom-append _el-input _el-br)
(dom-append _el-br _el-input3)
(dom-append _el-input3 _el-br4)
(dom-append _el-br4 _el-input5)
(dom-append _el-input5 _el-input6)
(hs-activate! _el-qsdiv)
))
(deftest "converts an array into HTML"
(assert= (eval-hs-locals "d as HTML" (list (list (quote d) (list "this-" "is-" "html")))) "this-is-html")
)
(deftest "converts an element into HTML"
(let ((_node (dom-create-element "div")))
(host-set! _node "id" "myDiv")
(host-set! _node "innerText" "With Text")
(let ((_result (eval-hs-locals "d as HTML" (list (list (quote d) _node)))))
(assert= _result "With Text
")
))
)
(deftest "converts an input element into Values"
(let ((_node (dom-create-element "input")))
(host-set! _node "name" "test-name")
(host-set! _node "value" "test-value")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (host-get _result "test-name") "test-value")
))
)
(deftest "converts array as Reversed"
(assert= (eval-hs "[1,2,3] as Reversed") (list 3 2 1))
)
(deftest "converts array as Set"
(error "SKIP (untranslated): converts array as Set"))
(deftest "converts array as Unique"
(assert= (eval-hs "[1,2,2,3,3] as Unique") (list 1 2 3))
)
(deftest "converts arrays into fragments"
(error "SKIP (untranslated): converts arrays into fragments"))
(deftest "converts checkboxes into a Value correctly"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "
")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (nth (host-get _result "gender") 0) "Male")
(assert= (nth (host-get _result "gender") 1) "Female")
(assert= (nth (host-get _result "gender") 2) "Other")
))
)
(deftest "converts elements into fragments"
(error "SKIP (untranslated): converts elements into fragments"))
(deftest "converts multiple selects into a Value correctly"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (nth (host-get _result "animal") 0) "dog")
(assert= (nth (host-get _result "animal") 1) "raccoon")
))
)
(deftest "converts multiple selects with programmatically changed selections"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (nth (host-get _result "animal") 0) "cat")
(assert= (nth (host-get _result "animal") 1) "raccoon")
))
)
(deftest "converts nested array as Flat"
(assert= (eval-hs "[[1,2],[3,4]] as Flat") (list 1 2 3 4))
)
(deftest "converts null as null"
(eval-hs "null as String")
)
(deftest "converts numbers things 'HTML'"
(assert= (eval-hs-locals "value as HTML" (list (list (quote value) 123))) "123")
)
(deftest "converts object as Entries"
(assert= (eval-hs "{a:1} as Entries") (list (list "a" 1)))
)
(deftest "converts object as Keys"
(assert= (eval-hs "{a:1, b:2} as Keys") (list "a" "b"))
)
(deftest "converts object as Map"
(error "SKIP (untranslated): converts object as Map"))
(deftest "converts radio buttons into a Value correctly"
(let ((_node (dom-create-element "form")))
(dom-set-inner-html _node "
")
(let ((_result (eval-hs-locals "x as Values" (list (list (quote x) _node)))))
(assert= (host-get _result "gender") "Male")
))
)
(deftest "converts string as Object"
(assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as Object") "foo") "bar")
)
(deftest "converts strings into fragments"
(error "SKIP (untranslated): converts strings into fragments"))
(deftest "converts value as Boolean"
(assert= (eval-hs "1 as Boolean") true)
(assert= (eval-hs "0 as Boolean") false)
(assert= (eval-hs "'' as Boolean") false)
(assert= (eval-hs "'hello' as Boolean") true)
)
(deftest "converts value as Date"
(error "SKIP (untranslated): converts value as Date"))
(deftest "converts value as Fixed"
(assert= (eval-hs "'10.4' as Fixed") "10")
(assert= (eval-hs "'10.4899' as Fixed:2") "10.49")
)
(deftest "converts value as Float"
(assert= (eval-hs "'10' as Float") 10)
(assert= (eval-hs "'10.4' as Float") 10.4)
)
(deftest "converts value as Int"
(assert= (eval-hs "'10' as Int") 10)
(assert= (eval-hs "'10.4' as Int") 10)
)
(deftest "converts value as JSONString"
(assert= (eval-hs "{foo:'bar'} as JSONString") "{\"foo\":\"bar\"}")
)
(deftest "converts value as Number"
(assert= (eval-hs "'10' as Number") 10)
(assert= (eval-hs "'10.4' as Number") 10.4)
)
(deftest "converts value as Object"
(assert= (host-get (eval-hs-locals "x as Object" (list (list (quote x) {:foo "bar"}))) "foo") "bar")
)
(deftest "converts value as String"
(assert= (eval-hs "10 as String") "10")
(assert= (eval-hs "true as String") "true")
)
(deftest "parses string as JSON to object"
(assert= (host-get (eval-hs "'{\"foo\":\"bar\"}' as JSON") "foo") "bar")
)
(deftest "pipe operator chains conversions"
(assert= (host-get (eval-hs "{foo:'bar'} as JSONString | JSON") "foo") "bar")
)
)
;; ── expressions/assignableElements (8 tests) ──
(defsuite "hs-upstream-expressions/assignableElements"
(deftest "hyperscript in replacement content is initialized"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-set-inner-html _el-target "old")
(dom-set-attr _el-go "id" "go")
(dom-set-attr _el-go "_" "on click set #target to 'new
'")
(dom-set-inner-html _el-go "go")
(dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-go)
(hs-activate! _el-go)
(dom-dispatch (dom-query-by-id "go") "click" nil)
(assert= (dom-text-content (dom-query-by-id "target")) "new")
(dom-dispatch (dom-query-by-id "target") "click" nil)
(assert= (dom-text-content (dom-query-by-id "target")) "clicked")
))
(deftest "put into still works as innerHTML"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-set-inner-html _el-target "old")
(dom-set-attr _el-button "_" "on click put \"new\" into #target")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "target")) "new")
))
(deftest "set #id replaces element with HTML string"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-set-inner-html _el-target "old")
(dom-set-attr _el-button "_" "on click set #target to \"new\"")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "target")) "new")
))
(deftest "set #id replaces element with another element"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-set-inner-html _el-target "old")
(dom-set-attr _el-button "_" "on click make a then put \"moved\" into it then set #target to it")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-target)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "set .class replaces all matching elements"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-list "id" "list")
(dom-add-class _el-li "item")
(dom-set-inner-html _el-li "a")
(dom-add-class _el-li2 "item")
(dom-set-inner-html _el-li2 "b")
(dom-add-class _el-li3 "item")
(dom-set-inner-html _el-li3 "c")
(dom-set-attr _el-button "_" "on click set .item to \"replaced\"")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-list)
(dom-append _el-list _el-li)
(dom-append _el-list _el-li2)
(dom-append _el-list _el-li3)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "set replaces all matching elements"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-set-inner-html _el-p "one")
(dom-set-inner-html _el-p2 "two")
(dom-set-attr _el-button "_" "on click set in #box to \"done
\"")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-p)
(dom-append _el-box _el-p2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
(deftest "set closest replaces ancestor"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-add-class _el-div "wrapper")
(dom-set-attr _el-button "_" "on click set (closest ) to \"replaced
\"")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query ".wrapper")) "replaced")
))
(deftest "swap #a with #b swaps DOM positions"
(hs-cleanup!)
(let ((_el-container (dom-create-element "div")) (_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-container "id" "container")
(dom-set-attr _el-a "id" "a")
(dom-set-inner-html _el-a "A")
(dom-set-attr _el-b "id" "b")
(dom-set-inner-html _el-b "B")
(dom-set-attr _el-button "_" "on click swap #a with #b")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-container)
(dom-append _el-container _el-a)
(dom-append _el-container _el-b)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
))
)
;; ── expressions/attributeRef (22 tests) ──
(defsuite "hs-upstream-expressions/attributeRef"
(deftest "attributeRef can be put as symbol"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click put \"blue\" into [@data-foo]")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can be put as symbol w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click put \"blue\" into @data-foo")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can be put indirectly"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be put indirectly w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be set as prop"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be set as prop w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be set as symbol"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click set [@data-foo] to \"blue\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can be set as symbol w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click set @data-foo to \"blue\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can be set indirectly"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be set indirectly w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef can be set through possessive"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click set my [@data-foo] to \"blue\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can be set through possessive w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click set my @data-foo to \"blue\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value in quotes used in add commands"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add [@data-foo=\"blue\"]")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value in quotes used in add commands w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add @data-foo=\"blue\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value in quotes with spaces used in add commands"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add [@data-foo=\"blue green\"]")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value in quotes with spaces used in add commands w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add @data-foo=\"blue green\"")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value used in add commands"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add [@data-foo=blue]")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef can have value used in add commands w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "_" "on click add @data-foo=blue")
(dom-set-attr _el-arDiv "data-foo" "red")
(dom-append (dom-body) _el-arDiv)
(hs-activate! _el-arDiv)
))
(deftest "attributeRef with dashes name works"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "c1")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef with dashes name works w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "data-foo" "c1")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef with no value works"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "foo" "c1")
(dom-append (dom-body) _el-arDiv)
))
(deftest "attributeRef with no value works w/ short syntax"
(hs-cleanup!)
(let ((_el-arDiv (dom-create-element "div")))
(dom-set-attr _el-arDiv "id" "arDiv")
(dom-set-attr _el-arDiv "foo" "c1")
(dom-append (dom-body) _el-arDiv)
))
)
;; ── expressions/beep! (6 tests) ──
(defsuite "hs-upstream-expressions/beep!"
(deftest "beeps a basic value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click get beep! 10")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "beeps a formatted string value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click get beep! \"foo\"")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "beeps a null value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click get beep! null")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "beeps the result of an ElementCollection"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "foo")
(dom-set-attr _el-div "_" "on click get beep! .foo")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can be cancelled"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on hyperscript:beep halt on click get beep! \"foo\"")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can capture information from event"
(hs-cleanup!)
(let ((_el-beepDiv (dom-create-element "div")))
(dom-set-attr _el-beepDiv "id" "beepDiv")
(dom-set-attr _el-beepDiv "_" "on hyperscript:beep(value) set my @data-value to the value on click get beep! \"foo\"")
(dom-append (dom-body) _el-beepDiv)
(hs-activate! _el-beepDiv)
))
)
;; ── expressions/blockLiteral (4 tests) ──
(defsuite "hs-upstream-expressions/blockLiteral"
(deftest "basic block literals work"
(error "SKIP (untranslated): basic block literals work"))
(deftest "basic identity works"
(error "SKIP (untranslated): basic identity works"))
(deftest "basic two arg identity works"
(error "SKIP (untranslated): basic two arg identity works"))
(deftest "can map an array"
(error "SKIP (untranslated): can map an array"))
)
;; ── expressions/boolean (2 tests) ──
(defsuite "hs-upstream-expressions/boolean"
(deftest "false boolean literals work"
(assert= (eval-hs "false") false)
)
(deftest "true boolean literals work"
(assert= (eval-hs "true") true)
)
)
;; ── expressions/classRef (9 tests) ──
(defsuite "hs-upstream-expressions/classRef"
(deftest "basic classRef works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-append (dom-body) _el-div)
))
(deftest "basic classRef works w no match"
(error "SKIP (untranslated): basic classRef works w no match"))
(deftest "colon class ref works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1:foo")
(dom-append (dom-body) _el-div)
))
(deftest "dashed class ref works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1-foo")
(dom-append (dom-body) _el-div)
))
(deftest "leading minus class ref works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "-c1")
(dom-append (dom-body) _el-div)
))
(deftest "multiple colon class ref works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1:foo:bar")
(dom-append (dom-body) _el-div)
))
(deftest "slashes in class references work"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "-c1/22")
(dom-append (dom-body) _el-div)
))
(deftest "tailwind insanity in class references work"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "group-[:nth-of-type(3)_&]:block")
(dom-append (dom-body) _el-div)
))
(deftest "template classRef works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-append (dom-body) _el-div)
))
)
;; ── expressions/closest (10 tests) ──
(defsuite "hs-upstream-expressions/closest"
(deftest "attributes can be looked up and referred to in same expression"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-div "foo" "bar")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click put closest @foo into me")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-d1)
(hs-activate! _el-d1)
))
(deftest "attributes can be set via the closest expression"
(hs-cleanup!)
(let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")))
(dom-set-attr _el-outerDiv "id" "outerDiv")
(dom-set-attr _el-outerDiv "foo" "bar")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set closest @foo to \"doh\"")
(dom-append (dom-body) _el-outerDiv)
(dom-append _el-outerDiv _el-d1)
(hs-activate! _el-d1)
))
(deftest "attributes can be set via the closest expression 2"
(hs-cleanup!)
(let ((_el-outerDiv2 (dom-create-element "div")) (_el-d1b (dom-create-element "div")))
(dom-set-attr _el-outerDiv2 "id" "outerDiv2")
(dom-set-attr _el-outerDiv2 "foo" "bar")
(dom-set-attr _el-d1b "id" "d1b")
(dom-set-attr _el-d1b "_" "on click set closest @foo to \"doh\"")
(dom-append (dom-body) _el-outerDiv2)
(dom-append _el-outerDiv2 _el-d1b)
(hs-activate! _el-d1b)
))
(deftest "attributes resolve as attributes"
(hs-cleanup!)
(let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d3 "id" "d3")
(dom-set-attr _el-d3 "foo" "bar")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d3)
(dom-append _el-d3 _el-d1)
(dom-append _el-d3 _el-d2)
))
(deftest "basic query return values"
(hs-cleanup!)
(let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d3 "id" "d3")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d3)
(dom-append _el-d3 _el-d1)
(dom-append _el-d3 _el-d2)
))
(deftest "closest does not consume a following where clause"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-input4 (dom-create-element "input")) (_el-master (dom-create-element "input")) (_el-out (dom-create-element "div")))
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-add-class _el-input4 "cb")
(dom-set-attr _el-input4 "type" "checkbox")
(dom-set-attr _el-master "id" "master")
(dom-set-attr _el-master "_" "set :others to in the closest where it is not me on click put :others.length into #out")
(dom-set-attr _el-master "type" "checkbox")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-table)
(dom-append _el-table _el-tr)
(dom-append _el-tr _el-td)
(dom-append _el-td _el-input)
(dom-append _el-input _el-input4)
(dom-append _el-input4 _el-master)
(dom-append _el-master _el-out)
(hs-activate! _el-master)
(dom-dispatch (dom-query-by-id "master") "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "2")
))
(deftest "closest with to modifier still works after parse change"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-inner "id" "inner")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-inner)
))
(deftest "parent modifier works"
(hs-cleanup!)
(let ((_el-d3 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d3 "id" "d3")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d3)
(dom-append _el-d3 _el-d1)
(dom-append _el-d3 _el-d2)
))
(deftest "parenthesizing allows you to nest to modifiers properly"
(hs-cleanup!)
(let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-outerDiv "id" "outerDiv")
(dom-set-attr _el-outerDiv "foo" "bar")
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-div2 "id" "div2")
(dom-set-attr _el-div2 "_" "on click set (closest @foo to #d1) to \"doh\"")
(dom-append (dom-body) _el-outerDiv)
(dom-append _el-outerDiv _el-d1)
(dom-append (dom-body) _el-div2)
(hs-activate! _el-div2)
))
(deftest "returns an array where appropriate"
(hs-cleanup!)
(let ((_el-d2 (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d3 (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "bar")
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "foo")
(dom-set-attr _el-d1 "_" "on click add .doh to closest .bar to .foo")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "bar")
(dom-add-class _el-div "foo")
(dom-append (dom-body) _el-d2)
(dom-append _el-d2 _el-d1)
(dom-append (dom-body) _el-d3)
(dom-append _el-d3 _el-div)
(hs-activate! _el-d1)
))
)
;; ── expressions/collectionExpressions (28 tests) ──
(defsuite "hs-upstream-expressions/collectionExpressions"
(deftest "filters an array by condition"
(assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}] then return arr where its active")) (list "a" "c"))
)
(deftest "filters with comparison"
(assert= (eval-hs "set arr to [1, 2, 3, 4, 5] then return arr where it > 3") (list 4 5))
)
(deftest "full select-all pattern with multiple on features"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-tr4 (dom-create-element "tr")) (_el-td5 (dom-create-element "td")) (_el-input6 (dom-create-element "input")) (_el-tr7 (dom-create-element "tr")) (_el-td8 (dom-create-element "td")) (_el-input9 (dom-create-element "input")) (_el-tr10 (dom-create-element "tr")) (_el-td11 (dom-create-element "td")) (_el-master (dom-create-element "input")))
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-input "checked" "")
(dom-add-class _el-input6 "cb")
(dom-set-attr _el-input6 "type" "checkbox")
(dom-add-class _el-input9 "cb")
(dom-set-attr _el-input9 "type" "checkbox")
(dom-set-attr _el-input9 "checked" "")
(dom-set-attr _el-master "id" "master")
(dom-set-attr _el-master "_" "set :checkboxes to in the closest where it is not me then on change set checked of the :checkboxes to my checked then on change from the closest then if no :checkboxes where it is checked then set my indeterminate to false then set my checked to false else if no :checkboxes where it is not checked then set my indeterminate to false then set my checked to true else set my indeterminate to true end")
(dom-set-attr _el-master "type" "checkbox")
(dom-append (dom-body) _el-table)
(dom-append _el-table _el-tr)
(dom-append _el-tr _el-td)
(dom-append _el-td _el-input)
(dom-append _el-input _el-tr4)
(dom-append _el-tr4 _el-td5)
(dom-append _el-td5 _el-input6)
(dom-append _el-input6 _el-tr7)
(dom-append _el-tr7 _el-td8)
(dom-append _el-td8 _el-input9)
(dom-append _el-input9 _el-tr10)
(dom-append _el-tr10 _el-td11)
(dom-append _el-td11 _el-master)
(hs-activate! _el-master)
(dom-dispatch (dom-query-by-id "master") "click" nil)
(dom-dispatch (nth (dom-query-all (dom-body) ".cb") 0) "click" nil)
))
(deftest "joined by on null returns null"
(eval-hs "set x to null then return x joined by ','")
)
(deftest "mapped to on null returns null"
(eval-hs "set x to null then return x mapped to (it * 2)")
)
(deftest "maps to a property"
(assert= (eval-hs "set arr to [{name: \"Alice\"}, {name: \"Bob\"}] then return arr mapped to its name") (list "Alice" "Bob"))
)
(deftest "maps with an expression"
(assert= (eval-hs "set arr to [1, 2, 3] then return arr mapped to (it * 2)") (list 2 4 6))
)
(deftest "sorted by binds after in without parens"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")))
(dom-set-attr _el-list "id" "list")
(dom-set-inner-html _el-li "C")
(dom-set-inner-html _el-li2 "A")
(dom-set-inner-html _el-li3 "B")
(dom-append (dom-body) _el-list)
(dom-append _el-list _el-li)
(dom-append _el-list _el-li2)
(dom-append _el-list _el-li3)
))
(deftest "sorted by on null returns null"
(eval-hs "set x to null then return x sorted by it")
)
(deftest "sorted by then mapped to"
(assert= (eval-hs "set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name") (list "Alice" "Charlie"))
)
(deftest "sorts by a property"
(assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")) (list "Alice" "Bob" "Charlie"))
)
(deftest "sorts descending"
(assert= (eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending") (list 3 2 1))
)
(deftest "sorts numbers by a computed key"
(assert= (map (fn (x) (get x "name")) (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")) (list "a" "c" "b"))
)
(deftest "split by on null returns null"
(eval-hs "set x to null then return x split by ','\\n")
)
(deftest "the result inside where refers to previous command result, not current element"
(assert= (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result") (list 4 5))
)
(deftest "where after in with mapped to"
(hs-cleanup!)
(let ((_el-items (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")))
(dom-set-attr _el-items "id" "items")
(dom-add-class _el-li "yes")
(dom-set-inner-html _el-li "A")
(dom-set-inner-html _el-li2 "B")
(dom-add-class _el-li3 "yes")
(dom-set-inner-html _el-li3 "C")
(dom-append (dom-body) _el-items)
(dom-append _el-items _el-li)
(dom-append _el-items _el-li2)
(dom-append _el-items _el-li3)
))
(deftest "where binds after in on closest"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-span3 (dom-create-element "span")) (_el-button (dom-create-element "button")) (_el-b2 (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-add-class _el-span "a")
(dom-set-inner-html _el-span "A")
(dom-add-class _el-span2 "b")
(dom-set-inner-html _el-span2 "B")
(dom-add-class _el-span3 "a")
(dom-set-inner-html _el-span3 "C")
(dom-set-attr _el-button "_" "on click set result to ( in #box) where it matches .a then put result.length into me")
(dom-set-inner-html _el-button "go (parens)")
(dom-set-attr _el-b2 "id" "b2")
(dom-set-attr _el-b2 "_" "on click set result to in #box where it matches .a then put result.length into me")
(dom-set-inner-html _el-b2 "go")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-span)
(dom-append _el-box _el-span2)
(dom-append _el-box _el-span3)
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-b2)
(hs-activate! _el-button)
(hs-activate! _el-b2)
(dom-dispatch (nth (dom-query-all (dom-body) "button") 0) "click" nil)
(assert= (dom-text-content (nth (dom-query-all (dom-body) "button") 0)) "2")
(dom-dispatch (dom-query-by-id "b2") "click" nil)
(assert= (dom-text-content (dom-query-by-id "b2")) "2")
))
(deftest "where binds after in without parens"
(hs-cleanup!)
(let ((_el-container (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-span3 (dom-create-element "span")))
(dom-set-attr _el-container "id" "container")
(dom-add-class _el-span "a")
(dom-set-inner-html _el-span "A")
(dom-add-class _el-span2 "b")
(dom-set-inner-html _el-span2 "B")
(dom-add-class _el-span3 "a")
(dom-set-inner-html _el-span3 "C")
(dom-append (dom-body) _el-container)
(dom-append _el-container _el-span)
(dom-append _el-container _el-span2)
(dom-append _el-container _el-span3)
))
(deftest "where binds after property access"
(assert= (eval-hs-locals "obj.items where it > 2" (list (list (quote obj) {:items (list 1 2 3 4)}))) (list 3 4))
)
(deftest "where in component init followed by on feature"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-script (dom-create-element "script")) (_el-test-where-comp (dom-create-element "test-where-comp")))
(dom-set-attr _el-box "id" "box")
(dom-add-class _el-span "a")
(dom-set-inner-html _el-span "A")
(dom-add-class _el-span2 "b")
(dom-set-inner-html _el-span2 "B")
(dom-set-attr _el-script "_" "set :items to in #box where it matches .a then on click put :items.length into me")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-attr _el-script "component" "test-where-comp")
(dom-set-inner-html _el-script "")
(dom-set-inner-html _el-test-where-comp "go")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-span)
(dom-append _el-box _el-span2)
(dom-append (dom-body) _el-script)
(dom-append (dom-body) _el-test-where-comp)
(hs-activate! _el-script)
(dom-dispatch _el-test-where-comp "click" nil)
(assert= (dom-text-content _el-test-where-comp) "1")
))
(deftest "where in init followed by on feature"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-add-class _el-span "a")
(dom-set-inner-html _el-span "A")
(dom-add-class _el-span2 "b")
(dom-set-inner-html _el-span2 "B")
(dom-set-attr _el-button "_" "set :items to in #box where it matches .a on click put :items.length into me")
(dom-set-inner-html _el-button "go")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-span)
(dom-append _el-box _el-span2)
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content _el-button) "1")
))
(deftest "where on null returns null"
(eval-hs "set x to null then return x where it > 1")
)
(deftest "where on undefined returns undefined"
(eval-hs "return doesNotExist where it > 1")
)
(deftest "where then mapped to"
(assert= (eval-hs "set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name") (list "Alice" "Charlie"))
)
(deftest "where then sorted by then mapped to"
(assert= (eval-hs "set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name") (list "Bob" "Charlie"))
)
(deftest "where with is not me followed by on feature"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")) (_el-tr (dom-create-element "tr")) (_el-td (dom-create-element "td")) (_el-input (dom-create-element "input")) (_el-tr4 (dom-create-element "tr")) (_el-td5 (dom-create-element "td")) (_el-input6 (dom-create-element "input")) (_el-tr7 (dom-create-element "tr")) (_el-td8 (dom-create-element "td")) (_el-input9 (dom-create-element "input")) (_el-tr10 (dom-create-element "tr")) (_el-td11 (dom-create-element "td")) (_el-master (dom-create-element "input")))
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-input "checked" "")
(dom-add-class _el-input6 "cb")
(dom-set-attr _el-input6 "type" "checkbox")
(dom-add-class _el-input9 "cb")
(dom-set-attr _el-input9 "type" "checkbox")
(dom-set-attr _el-input9 "checked" "")
(dom-set-attr _el-master "id" "master")
(dom-set-attr _el-master "_" "set :checkboxes to in the closest where it is not me then on change set checked of the :checkboxes to my checked")
(dom-set-attr _el-master "type" "checkbox")
(dom-append (dom-body) _el-table)
(dom-append _el-table _el-tr)
(dom-append _el-tr _el-td)
(dom-append _el-td _el-input)
(dom-append _el-input _el-tr4)
(dom-append _el-tr4 _el-td5)
(dom-append _el-td5 _el-input6)
(dom-append _el-input6 _el-tr7)
(dom-append _el-tr7 _el-td8)
(dom-append _el-td8 _el-input9)
(dom-append _el-input9 _el-tr10)
(dom-append _el-tr10 _el-td11)
(dom-append _el-td11 _el-master)
(hs-activate! _el-master)
(dom-dispatch (dom-query-by-id "master") "click" nil)
))
(deftest "where with is not me in component template"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-input2 (dom-create-element "input")) (_el-script (dom-create-element "script")) (_el-test-where-me (dom-create-element "test-where-me")))
(dom-set-attr _el-box "id" "box")
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-add-class _el-input2 "cb")
(dom-set-attr _el-input2 "type" "checkbox")
(dom-set-attr _el-script "type" "text/hyperscript-template")
(dom-set-attr _el-script "component" "test-where-me")
(dom-set-inner-html _el-script " in #box where it is not me on change set checked of the :checkboxes to my checked\">")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-input)
(dom-append _el-input _el-input2)
(dom-append _el-input2 _el-script)
(dom-append _el-input2 _el-test-where-me)
(dom-dispatch (dom-query "test-where-me input") "click" nil)
))
(deftest "works with DOM elements"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-li (dom-create-element "li")) (_el-li2 (dom-create-element "li")) (_el-li3 (dom-create-element "li")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-list "id" "list")
(dom-add-class _el-li "yes")
(dom-set-inner-html _el-li "A")
(dom-set-inner-html _el-li2 "B")
(dom-add-class _el-li3 "yes")
(dom-set-inner-html _el-li3 "C")
(dom-set-attr _el-button "_" "on click set items to in #list set matches to items where it matches .yes then put matches mapped to its textContent into #out")
(dom-set-inner-html _el-button "Go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-list)
(dom-append _el-list _el-li)
(dom-append _el-list _el-li2)
(dom-append _el-list _el-li3)
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "AC")
))
)
;; ── expressions/comparisonOperator (83 tests) ──
(defsuite "hs-upstream-expressions/comparisonOperator"
(deftest "I am between works"
(assert= (eval-hs-with-me "I am between 1 and 10" 5) true)
(assert= (eval-hs-with-me "I am between 1 and 10" 0) false)
)
(deftest "I am in works"
(assert= (eval-hs-with-me "I am in [1, 2]" 1) true)
(assert= (eval-hs-with-me "I am in [1, 2]" 2) true)
(assert= (eval-hs-with-me "I am in [1, 2]" 3) false)
(assert= (eval-hs "I am in null") false)
)
(deftest "I am not between works"
(assert= (eval-hs-with-me "I am not between 1 and 10" 5) false)
(assert= (eval-hs-with-me "I am not between 1 and 10" 0) true)
)
(deftest "I am not in works"
(assert= (eval-hs-with-me "I am not in [1, 2]" 1) false)
(assert= (eval-hs-with-me "I am not in [1, 2]" 2) false)
(assert= (eval-hs-with-me "I am not in [1, 2]" 3) true)
(assert= (eval-hs "I am not in null") true)
)
(deftest "I precede works"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "on click if I precede #b put 'yes' into me")
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
(hs-activate! _el-a)
))
(deftest "am works as alias for is"
(assert= (eval-hs "2 am 2") true)
(assert= (eval-hs "2 am 1") false)
)
(deftest "between works with strings"
(assert= (eval-hs "'b' is between 'a' and 'c'") true)
(assert= (eval-hs "'d' is between 'a' and 'c'") false)
)
(deftest "contains ignoring case works"
(assert= (eval-hs "'Hello World' contains 'hello' ignoring case") true)
(assert= (eval-hs "'Hello World' contains 'WORLD' ignoring case") true)
(assert= (eval-hs "'Hello World' contains 'missing' ignoring case") false)
)
(deftest "contains works with arrays"
(assert= (eval-hs-locals "I contain that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true)
(assert= (eval-hs-locals "that contains me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true)
)
(deftest "contains works with css literals"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "outer")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-d2)
))
(deftest "contains works with elts"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-inner "id" "inner")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-inner)
))
(deftest "does not contain works"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")) (_el-inner (dom-create-element "div")))
(dom-set-attr _el-outer "id" "outer")
(dom-set-attr _el-inner "id" "inner")
(dom-append (dom-body) _el-outer)
(dom-append _el-outer _el-inner)
))
(deftest "does not end with works"
(assert= (eval-hs "'hello world' does not end with 'world'") false)
(assert= (eval-hs "'hello world' does not end with 'hello'") true)
)
(deftest "does not exist works"
(assert= (eval-hs "undefined does not exist") true)
(assert= (eval-hs "null does not exist") true)
(assert= (eval-hs "#doesNotExist does not exist") true)
(assert= (eval-hs ".aClassThatDoesNotExist does not exist") true)
(assert= (eval-hs "<.aClassThatDoesNotExist/> does not exist") true)
(assert= (eval-hs " does not exist") false)
)
(deftest "does not follow works"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
))
(deftest "does not match works"
(hs-cleanup!)
(let ((_el-mDiv (dom-create-element "div")))
(dom-set-attr _el-mDiv "id" "mDiv")
(dom-add-class _el-mDiv "foo")
(dom-append (dom-body) _el-mDiv)
))
(deftest "does not match works w/ strings"
(assert= (eval-hs "'a' does not match '.*'") false)
(assert= (eval-hs "'a' does not match 'b'") true)
)
(deftest "does not precede works"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
))
(deftest "does not start with works"
(assert= (eval-hs "'hello world' does not start with 'hello'") false)
(assert= (eval-hs "'hello world' does not start with 'world'") true)
)
(deftest "ends with coerces to string"
(assert= (eval-hs "123 ends with '23'") true)
(assert= (eval-hs "123 ends with '12'") false)
)
(deftest "ends with ignoring case works"
(assert= (eval-hs "'Hello World' ends with 'world' ignoring case") true)
(assert= (eval-hs "'Hello World' ends with 'WORLD' ignoring case") true)
(assert= (eval-hs "'Hello World' ends with 'hello' ignoring case") false)
)
(deftest "ends with null is false"
(assert= (eval-hs "null ends with 'x'") false)
(assert= (eval-hs "null does not end with 'x'") true)
)
(deftest "ends with works"
(assert= (eval-hs "'hello world' ends with 'world'") true)
(assert= (eval-hs "'hello world' ends with 'hello'") false)
(assert= (eval-hs "'hello' ends with 'hello'") true)
(assert= (eval-hs "'' ends with 'x'") false)
)
(deftest "english greater than or equal works"
(assert= (eval-hs "1 is greater than or equal to 2") false)
(assert= (eval-hs "2 is greater than or equal to 1") true)
(assert= (eval-hs "2 is greater than or equal to 2") true)
)
(deftest "english greater than works"
(assert= (eval-hs "1 is greater than 2") false)
(assert= (eval-hs "2 is greater than 1") true)
(assert= (eval-hs "2 is greater than 2") false)
)
(deftest "english less than or equal works"
(assert= (eval-hs "1 is less than or equal to 2") true)
(assert= (eval-hs "2 is less than or equal to 1") false)
(assert= (eval-hs "2 is less than or equal to 2") true)
)
(deftest "english less than works"
(assert= (eval-hs "1 is less than 2") true)
(assert= (eval-hs "2 is less than 1") false)
(assert= (eval-hs "2 is less than 2") false)
)
(deftest "equal works"
(assert= (eval-hs "1 == 2") false)
(assert= (eval-hs "2 == 1") false)
(assert= (eval-hs "2 == 2") true)
)
(deftest "equals works"
(assert= (eval-hs "1 equals 2") false)
(assert= (eval-hs "2 equals 1") false)
(assert= (eval-hs "2 equals 2") true)
)
(deftest "exists works"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "follows works"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-c "id" "c")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
(dom-append (dom-body) _el-c)
))
(deftest "greater than or equal works"
(assert= (eval-hs "1 >= 2") false)
(assert= (eval-hs "2 >= 1") true)
(assert= (eval-hs "2 >= 2") true)
)
(deftest "greater than works"
(assert= (eval-hs "1 > 2") false)
(assert= (eval-hs "2 > 1") true)
(assert= (eval-hs "2 > 2") false)
)
(deftest "include works"
(assert= (eval-hs-locals "foo includes foobar" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) false)
(assert= (eval-hs-locals "foobar includes foo" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) true)
(assert= (eval-hs-locals "foo does not include foobar" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) true)
(assert= (eval-hs-locals "foobar does not include foo" (list (list (quote foo) "foo") (list (quote foobar) "foobar"))) false)
)
(deftest "includes works with arrays"
(assert= (eval-hs-locals "I include that" (list (list (quote that) 1) (list (quote me) (list 1 2 3)))) true)
(assert= (eval-hs-locals "that includes me" (list (list (quote that) (list 1 2 3)) (list (quote me) 1))) true)
)
(deftest "includes works with css literals"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "outer")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-d2)
))
(deftest "is a Node works via instanceof"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click if I am a Node put \"yes\" into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "yes")
))
(deftest "is a works"
(assert= (eval-hs "null is a String") true)
(assert= (eval-hs "null is a String!") false)
(assert= (eval-hs "'' is a String!") true)
)
(deftest "is a works with instanceof fallback"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click if I am a Element put \"yes\" into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "yes")
))
(deftest "is an works"
(assert= (eval-hs "null is an String") true)
(assert= (eval-hs "null is an String!") false)
(assert= (eval-hs "'' is an String!") true)
)
(deftest "is between works"
(assert= (eval-hs "5 is between 1 and 10") true)
(assert= (eval-hs "1 is between 1 and 10") true)
(assert= (eval-hs "10 is between 1 and 10") true)
(assert= (eval-hs "0 is between 1 and 10") false)
(assert= (eval-hs "11 is between 1 and 10") false)
)
(deftest "is boolean property works in where clause"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-input2 (dom-create-element "input")))
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-input "checked" "checked")
(dom-add-class _el-input1 "cb")
(dom-set-attr _el-input1 "type" "checkbox")
(dom-add-class _el-input2 "cb")
(dom-set-attr _el-input2 "type" "checkbox")
(dom-set-attr _el-input2 "checked" "checked")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
(dom-append (dom-body) _el-input2)
))
(deftest "is boolean property works with disabled"
(hs-cleanup!)
(let ((_el-b1 (dom-create-element "button")) (_el-b2 (dom-create-element "button")))
(dom-set-attr _el-b1 "id" "b1")
(dom-set-attr _el-b1 "disabled" "")
(dom-set-inner-html _el-b1 "Disabled")
(dom-set-attr _el-b2 "id" "b2")
(dom-set-inner-html _el-b2 "Enabled")
(dom-append (dom-body) _el-b1)
(dom-append (dom-body) _el-b2)
))
(deftest "is empty works"
(assert= (eval-hs "undefined is empty") true)
(assert= (eval-hs "'' is empty") true)
(assert= (eval-hs "[] is empty") true)
(assert= (eval-hs "'not empty' is empty") false)
(assert= (eval-hs "1000 is empty") false)
(assert= (eval-hs "[1,2,3] is empty") false)
(assert= (eval-hs ".aClassThatDoesNotExist is empty") true)
)
(deftest "is equal to works"
(assert= (eval-hs "1 is equal to 2") false)
(assert= (eval-hs "2 is equal to 1") false)
(assert= (eval-hs "2 is equal to 2") true)
)
(deftest "is equal works without to"
(assert= (eval-hs "2 is equal 2") true)
(assert= (eval-hs "2 is equal 1") false)
)
(deftest "is falls back to boolean property when rhs is undefined"
(hs-cleanup!)
(let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
(dom-set-attr _el-c1 "id" "c1")
(dom-set-attr _el-c1 "type" "checkbox")
(dom-set-attr _el-c1 "checked" "checked")
(dom-set-attr _el-c2 "id" "c2")
(dom-set-attr _el-c2 "type" "checkbox")
(dom-append (dom-body) _el-c1)
(dom-append (dom-body) _el-c2)
))
(deftest "is ignoring case works"
(assert= (eval-hs "'Hello' is 'hello' ignoring case") true)
(assert= (eval-hs "'Hello' is 'HELLO' ignoring case") true)
(assert= (eval-hs "'Hello' is 'world' ignoring case") false)
)
(deftest "is in works"
(assert= (eval-hs "1 is in [1, 2]") true)
(assert= (eval-hs "2 is in [1, 2]") true)
(assert= (eval-hs "3 is in [1, 2]") false)
(assert= (eval-hs "3 is in null") false)
)
(deftest "is not a works"
(assert= (eval-hs "null is not a String") false)
(assert= (eval-hs "null is not a String!") true)
(assert= (eval-hs "'' is not a String!") false)
)
(deftest "is not a works with instanceof fallback"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click if \"hello\" is not a Element put \"yes\" into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-dispatch (dom-query-by-id "d1") "click" nil)
(assert= (dom-text-content (dom-query-by-id "d1")) "yes")
))
(deftest "is not an works"
(assert= (eval-hs "null is not an String") false)
(assert= (eval-hs "null is not an String!") true)
(assert= (eval-hs "'' is not an String!") false)
)
(deftest "is not between works"
(assert= (eval-hs "5 is not between 1 and 10") false)
(assert= (eval-hs "0 is not between 1 and 10") true)
(assert= (eval-hs "11 is not between 1 and 10") true)
(assert= (eval-hs "1 is not between 1 and 10") false)
(assert= (eval-hs "10 is not between 1 and 10") false)
)
(deftest "is not empty works"
(assert= (eval-hs "undefined is not empty") false)
(assert= (eval-hs "'' is not empty") false)
(assert= (eval-hs "[] is not empty") false)
(assert= (eval-hs "'not empty' is not empty") true)
(assert= (eval-hs "1000 is not empty") true)
(assert= (eval-hs "[1,2,3] is not empty") true)
)
(deftest "is not equal to works"
(assert= (eval-hs "1 is not equal to 2") true)
(assert= (eval-hs "2 is not equal to 1") true)
(assert= (eval-hs "2 is not equal to 2") false)
)
(deftest "is not equal works without to"
(assert= (eval-hs "2 is not equal 2") false)
(assert= (eval-hs "2 is not equal 1") true)
)
(deftest "is not falls back to boolean property when rhs is undefined"
(hs-cleanup!)
(let ((_el-c1 (dom-create-element "input")) (_el-c2 (dom-create-element "input")))
(dom-set-attr _el-c1 "id" "c1")
(dom-set-attr _el-c1 "type" "checkbox")
(dom-set-attr _el-c1 "checked" "checked")
(dom-set-attr _el-c2 "id" "c2")
(dom-set-attr _el-c2 "type" "checkbox")
(dom-append (dom-body) _el-c1)
(dom-append (dom-body) _el-c2)
))
(deftest "is not ignoring case works"
(assert= (eval-hs "'Hello' is not 'world' ignoring case") true)
(assert= (eval-hs "'Hello' is not 'hello' ignoring case") false)
)
(deftest "is not in works"
(assert= (eval-hs "1 is not in [1, 2]") false)
(assert= (eval-hs "2 is not in [1, 2]") false)
(assert= (eval-hs "3 is not in [1, 2]") true)
(assert= (eval-hs "3 is not in null") true)
)
(deftest "is not null still works as equality"
(assert= (eval-hs "5 is not null") true)
(assert= (eval-hs "null is not null") false)
)
(deftest "is not really equal to works"
(assert= (eval-hs "1 is not really equal to 2") true)
(assert= (eval-hs "2 is not really equal to 1") true)
(assert= (eval-hs "2 is not really equal to '2'") true)
(assert= (eval-hs "2 is not really equal to 2") false)
)
(deftest "is not really works without equal to"
(assert= (eval-hs "2 is not really '2'") true)
(assert= (eval-hs "2 is not really 2") false)
)
(deftest "is not undefined still works as equality"
(assert= (eval-hs "5 is not undefined") true)
(assert= (eval-hs "null is not undefined") false)
)
(deftest "is not works"
(assert= (eval-hs "1 is not 2") true)
(assert= (eval-hs "2 is not 1") true)
(assert= (eval-hs "2 is not 2") false)
)
(deftest "is really equal to works"
(assert= (eval-hs "1 is really equal to 2") false)
(assert= (eval-hs "2 is really equal to 1") false)
(assert= (eval-hs "2 is really equal to '2'") false)
(assert= (eval-hs "2 is really equal to 2") true)
)
(deftest "is really works without equal to"
(assert= (eval-hs "2 is really 2") true)
(assert= (eval-hs "2 is really '2'") false)
)
(deftest "is still does equality when rhs variable exists"
(assert= (eval-hs-locals "x is y" (list (list (quote x) 5) (list (quote y) 5))) true)
(assert= (eval-hs-locals "x is y" (list (list (quote x) 5) (list (quote y) 6))) false)
)
(deftest "is works"
(assert= (eval-hs "1 is 2") false)
(assert= (eval-hs "2 is 1") false)
(assert= (eval-hs "2 is 2") true)
)
(deftest "less than or equal works"
(assert= (eval-hs "1 <= 2") true)
(assert= (eval-hs "2 <= 1") false)
(assert= (eval-hs "2 <= 2") true)
)
(deftest "less than works"
(assert= (eval-hs "1 < 2") true)
(assert= (eval-hs "2 < 1") false)
(assert= (eval-hs "2 < 2") false)
)
(deftest "match works"
(hs-cleanup!)
(let ((_el-mDiv (dom-create-element "div")))
(dom-set-attr _el-mDiv "id" "mDiv")
(dom-add-class _el-mDiv "foo")
(dom-append (dom-body) _el-mDiv)
))
(deftest "match works w/ strings"
(assert= (eval-hs "'a' matches '.*'") true)
(assert= (eval-hs "'a' matches 'b'") false)
)
(deftest "matches ignoring case works"
(assert= (eval-hs "'Hello' matches 'hello' ignoring case") true)
(assert= (eval-hs "'Hello' matches 'HELLO' ignoring case") true)
)
(deftest "not equal works"
(assert= (eval-hs "1 != 2") true)
(assert= (eval-hs "2 != 1") true)
(assert= (eval-hs "2 != 2") false)
)
(deftest "precedes with null is false"
(assert= (eval-hs "null precedes null") false)
(assert= (eval-hs "null does not precede null") true)
)
(deftest "precedes works"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")) (_el-c (dom-create-element "div")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-c "id" "c")
(dom-append (dom-body) _el-a)
(dom-append (dom-body) _el-b)
(dom-append (dom-body) _el-c)
))
(deftest "really equals works"
(assert= (eval-hs "1 really equals 2") false)
(assert= (eval-hs "2 really equals 1") false)
(assert= (eval-hs "2 really equals 2") true)
)
(deftest "starts with coerces to string"
(assert= (eval-hs "123 starts with '12'") true)
(assert= (eval-hs "123 starts with '23'") false)
)
(deftest "starts with ignoring case works"
(assert= (eval-hs "'Hello World' starts with 'hello' ignoring case") true)
(assert= (eval-hs "'Hello World' starts with 'HELLO' ignoring case") true)
(assert= (eval-hs "'Hello World' starts with 'world' ignoring case") false)
)
(deftest "starts with null is false"
(assert= (eval-hs "null starts with 'x'") false)
(assert= (eval-hs "null does not start with 'x'") true)
)
(deftest "starts with works"
(assert= (eval-hs "'hello world' starts with 'hello'") true)
(assert= (eval-hs "'hello world' starts with 'world'") false)
(assert= (eval-hs "'hello' starts with 'hello'") true)
(assert= (eval-hs "'' starts with 'x'") false)
)
(deftest "triple equal works"
(assert= (eval-hs "1 === 2") false)
(assert= (eval-hs "2 === 1") false)
(assert= (eval-hs "2 === 2") true)
)
(deftest "triple not equal works"
(assert= (eval-hs "1 !== 2") true)
(assert= (eval-hs "2 !== 1") true)
(assert= (eval-hs "2 !== 2") false)
)
)
;; ── expressions/cookies (5 tests) ──
(defsuite "hs-upstream-expressions/cookies"
(deftest "basic clear cookie values work"
(error "SKIP (untranslated): basic clear cookie values work"))
(deftest "basic set cookie values work"
(error "SKIP (untranslated): basic set cookie values work"))
(deftest "iterate cookies values work"
(error "SKIP (untranslated): iterate cookies values work"))
(deftest "length is 0 when no cookies are set"
(error "SKIP (untranslated): length is 0 when no cookies are set"))
(deftest "update cookie values work"
(error "SKIP (untranslated): update cookie values work"))
)
;; ── expressions/dom-scope (20 tests) ──
(defsuite "hs-upstream-expressions/dom-scope"
(deftest "always reacts to ^var changes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")))
(dom-set-attr _el-div "_" "init set ^name to 'alice'")
(dom-set-attr _el-button "_" "on click set ^name to 'bob'")
(dom-set-inner-html _el-button "rename")
(dom-set-attr _el-output "_" "live put 'Hello ' + ^name into me")
(dom-set-inner-html _el-output "loading")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-output)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-output)
))
(deftest "bind works with ^var"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-input (dom-create-element "input")) (_el-output (dom-create-element "output")))
(dom-set-attr _el-div "_" "init set ^search to ''")
(dom-set-attr _el-input "_" "bind ^search and my value")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-output "_" "when ^search changes put it into me")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-input)
(dom-append _el-div _el-output)
(hs-activate! _el-div)
(hs-activate! _el-input)
(hs-activate! _el-output)
))
(deftest "child reads ^var set by parent"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^count to 42")
(dom-set-attr _el-span "_" "on click put ^count into me")
(dom-set-inner-html _el-span "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-span)
))
(deftest "child write updates the ancestor, not a local copy"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^shared to 0")
(dom-set-attr _el-button "_" "on click set ^shared to 10")
(dom-set-inner-html _el-button "set")
(dom-set-attr _el-span "_" "on click put ^shared into me")
(dom-set-inner-html _el-span "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-span)
))
(deftest "child writes ^var and parent sees it"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^count to 0")
(dom-set-attr _el-button "_" "on click set ^count to 99")
(dom-set-inner-html _el-button "set")
(dom-set-attr _el-span "_" "on click put ^count into me")
(dom-set-inner-html _el-span "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-span)
))
(deftest "closest ancestor wins (shadowing)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^color to 'red'")
(dom-set-attr _el-div1 "_" "init set ^color to 'blue'")
(dom-set-attr _el-span "_" "on click put ^color into me")
(dom-set-inner-html _el-span "empty")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-span)
))
(deftest "dedup prevents re-fire on same ^var value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")) (_el-diff (dom-create-element "button")))
(dom-set-attr _el-div "_" "init set ^val to 'same'")
(dom-set-attr _el-output "_" "when ^val changes increment :runs then put :runs into me")
(dom-set-attr _el-button "_" "on click set ^val to 'same'")
(dom-set-inner-html _el-button "same")
(dom-set-attr _el-diff "id" "diff")
(dom-set-attr _el-diff "_" "on click set ^val to 'different'")
(dom-set-inner-html _el-diff "diff")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-output)
(dom-append _el-div _el-button)
(dom-append _el-div _el-diff)
(hs-activate! _el-div)
(hs-activate! _el-output)
(hs-activate! _el-button)
(hs-activate! _el-diff)
))
(deftest "deeply nested child reads ^var from grandparent"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^name to 'alice'")
(dom-set-attr _el-span "_" "on click put ^name into me")
(dom-set-inner-html _el-span "empty")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-div2)
(dom-append _el-div2 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-span)
))
(deftest "derived ^var chains reactively"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-output (dom-create-element "output")) (_el-price-btn (dom-create-element "button")) (_el-qty-btn (dom-create-element "button")))
(dom-set-attr _el-div "_" "init set ^price to 10 then set ^qty to 2 then set ^total to 20")
(dom-set-attr _el-span "_" "when ^price changes set ^total to (^price * ^qty)")
(dom-set-attr _el-span2 "_" "when ^qty changes set ^total to (^price * ^qty)")
(dom-set-attr _el-output "_" "when ^total changes put it into me")
(dom-set-attr _el-price-btn "id" "price-btn")
(dom-set-attr _el-price-btn "_" "on click set ^price to 25")
(dom-set-inner-html _el-price-btn "set price")
(dom-set-attr _el-qty-btn "id" "qty-btn")
(dom-set-attr _el-qty-btn "_" "on click set ^qty to 5")
(dom-set-inner-html _el-qty-btn "set qty")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-span)
(dom-append _el-div _el-span2)
(dom-append _el-div _el-output)
(dom-append _el-div _el-price-btn)
(dom-append _el-div _el-qty-btn)
(hs-activate! _el-div)
(hs-activate! _el-span)
(hs-activate! _el-span2)
(hs-activate! _el-output)
(hs-activate! _el-price-btn)
(hs-activate! _el-qty-btn)
))
(deftest "dom keyword works as scope modifier"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set dom count to 42")
(dom-set-attr _el-span "_" "on click put dom count into me")
(dom-set-inner-html _el-span "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-span)
))
(deftest "effect stops when element is removed"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "output")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-div "_" "init set ^msg to 'hello'")
(dom-set-attr _el-output "_" "when ^msg changes put it into me")
(dom-set-attr _el-button "_" "on click set ^msg to 'updated'")
(dom-set-inner-html _el-button "update")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-output)
(dom-append _el-div _el-button)
(hs-activate! _el-div)
(hs-activate! _el-output)
(hs-activate! _el-button)
))
(deftest "increment works on inherited var"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^count to 0")
(dom-set-attr _el-button "_" "on click increment ^count")
(dom-set-inner-html _el-button "+1")
(dom-set-attr _el-span "_" "on click put ^count into me")
(dom-set-inner-html _el-span "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-span)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-span)
))
(deftest "multiple children react to same ^var"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-a (dom-create-element "span")) (_el-b (dom-create-element "span")))
(dom-set-attr _el-div "_" "init set ^color to 'red'")
(dom-set-attr _el-button "_" "on click set ^color to 'blue'")
(dom-set-inner-html _el-button "change")
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "when ^color changes put it into me")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-b "_" "when ^color changes put it into me")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-a)
(dom-append _el-div _el-b)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-a)
(hs-activate! _el-b)
))
(deftest "on clause targets a specific ancestor"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-span (dom-create-element "span")))
(dom-add-class _el-div "outer")
(dom-set-attr _el-div "_" "init set ^outerVal to 'outer'")
(dom-add-class _el-div1 "inner")
(dom-set-attr _el-div1 "_" "init set ^innerVal to 'inner'")
(dom-set-attr _el-span "_" "on click put ^outerVal on closest .outer into me")
(dom-set-inner-html _el-span "read")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-div1)
(dom-append _el-div1 _el-span)
(hs-activate! _el-div)
(hs-activate! _el-div1)
(hs-activate! _el-span)
))
(deftest "on clause with id reference"
(hs-cleanup!)
(let ((_el-state-holder (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-state-holder "id" "state-holder")
(dom-set-attr _el-button "_" "on click set ^count on #state-holder to 99")
(dom-set-inner-html _el-button "set")
(dom-set-attr _el-span "_" "on click put ^count on #state-holder into me")
(dom-set-inner-html _el-span "read")
(dom-append (dom-body) _el-state-holder)
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-span)
(hs-activate! _el-button)
(hs-activate! _el-span)
))
(deftest "set ^var on explicit element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-add-class _el-div "store")
(dom-set-attr _el-button "_" "on click set ^data on closest .store to 'hello'")
(dom-set-inner-html _el-button "set")
(dom-set-attr _el-span "_" "on click put ^data on closest .store into me")
(dom-set-inner-html _el-span "read")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-span)
(hs-activate! _el-button)
(hs-activate! _el-span)
))
(deftest "sibling subtrees have independent ^vars"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-b (dom-create-element "div")) (_el-span3 (dom-create-element "span")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "init set ^val to 'A'")
(dom-set-attr _el-span "_" "on click put ^val into me")
(dom-set-inner-html _el-span "empty")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-b "_" "init set ^val to 'B'")
(dom-set-attr _el-span3 "_" "on click put ^val into me")
(dom-set-inner-html _el-span3 "empty")
(dom-append (dom-body) _el-a)
(dom-append _el-a _el-span)
(dom-append (dom-body) _el-b)
(dom-append _el-b _el-span3)
(hs-activate! _el-a)
(hs-activate! _el-span)
(hs-activate! _el-b)
(hs-activate! _el-span3)
))
(deftest "sibling subtrees react independently with ^var"
(hs-cleanup!)
(let ((_el-a (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")) (_el-b (dom-create-element "div")) (_el-button4 (dom-create-element "button")) (_el-output5 (dom-create-element "output")))
(dom-set-attr _el-a "id" "a")
(dom-set-attr _el-a "_" "init set ^val to 0")
(dom-set-attr _el-button "_" "on click increment ^val")
(dom-set-inner-html _el-button "+1")
(dom-set-attr _el-output "_" "when ^val changes put it into me")
(dom-set-inner-html _el-output "0")
(dom-set-attr _el-b "id" "b")
(dom-set-attr _el-b "_" "init set ^val to 0")
(dom-set-attr _el-button4 "_" "on click increment ^val")
(dom-set-inner-html _el-button4 "+1")
(dom-set-attr _el-output5 "_" "when ^val changes put it into me")
(dom-set-inner-html _el-output5 "0")
(dom-append (dom-body) _el-a)
(dom-append _el-a _el-button)
(dom-append _el-a _el-output)
(dom-append (dom-body) _el-b)
(dom-append _el-b _el-button4)
(dom-append _el-b _el-output5)
(hs-activate! _el-a)
(hs-activate! _el-button)
(hs-activate! _el-output)
(hs-activate! _el-b)
(hs-activate! _el-button4)
(hs-activate! _el-output5)
))
(deftest "when reacts to ^var changes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-output (dom-create-element "output")))
(dom-set-attr _el-div "_" "init set ^count to 0")
(dom-set-attr _el-button "_" "on click increment ^count")
(dom-set-inner-html _el-button "+1")
(dom-set-attr _el-output "_" "when ^count changes put it into me")
(dom-set-inner-html _el-output "0")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-output)
(hs-activate! _el-div)
(hs-activate! _el-button)
(hs-activate! _el-output)
))
(deftest "write to ^var not found anywhere creates on current element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-button "_" "on click set ^newvar to 'created' then put ^newvar into next ")
(dom-set-inner-html _el-button "go")
(dom-set-inner-html _el-span "empty")
(dom-append (dom-body) _el-div)
(dom-append _el-div _el-button)
(dom-append _el-div _el-span)
(hs-activate! _el-button)
))
)
;; ── expressions/functionCalls (12 tests) ──
(defsuite "hs-upstream-expressions/functionCalls"
(deftest "can access a property of a call's result"
(assert= (eval-hs-locals "makePoint(3, 4).x" (list (list (quote makePoint) (fn (x y) {:x x :y y})))) 3)
(assert= (eval-hs-locals "makePoint(3, 4).y" (list (list (quote makePoint) (fn (x y) {:x x :y y})))) 4)
)
(deftest "can chain calls on the result of a call"
(assert= (eval-hs-locals "getObj().greet()" (list (list (quote getObj) (fn () {:greet (fn () "hi")})))) "hi")
)
(deftest "can invoke function on object"
(assert= (eval-hs-locals "obj.getValue()" (list (list (quote obj) {:value "foo" :getValue (fn () (host-get this "value"))}))) "foo")
)
(deftest "can invoke function on object w/ async arg"
(error "SKIP (untranslated): can invoke function on object w/ async arg"))
(deftest "can invoke function on object w/ async root & arg"
(error "SKIP (untranslated): can invoke function on object w/ async root & arg"))
(deftest "can invoke global function"
(assert= (eval-hs-locals "identity(\"foo\")" (list (list (quote identity) (fn (x) x)))) "foo")
)
(deftest "can invoke global function w/ async arg"
(error "SKIP (untranslated): can invoke global function w/ async arg"))
(deftest "can pass an array literal as an argument"
(assert= (eval-hs-locals "sum([1, 2, 3, 4])" (list (list (quote sum) (fn (arr) (host-call arr "reduce" (fn (a b) (+ a b)) 0))))) 10)
)
(deftest "can pass an expression as an argument"
(assert= (eval-hs-locals "double(3 + 4)" (list (list (quote double) (fn (n) (* n 2))))) 14)
)
(deftest "can pass an object literal as an argument"
(assert= (eval-hs-locals "getName({name: 'Alice'})" (list (list (quote getName) (fn (o) (host-get o "name"))))) "Alice")
)
(deftest "can pass multiple arguments"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "on click put add(1, 2, 3) into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "can pass no arguments"
(assert= (eval-hs-locals "getFortyTwo()" (list (list (quote getFortyTwo) (fn () 42)))) 42)
)
)
;; ── expressions/idRef (4 tests) ──
(defsuite "hs-upstream-expressions/idRef"
(deftest "basic id ref works"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-d1)
))
(deftest "basic id ref works w no match"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-append (dom-body) _el-div)
))
(deftest "id ref works from a disconnected element"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-d1)
))
(deftest "template id ref works"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-d1)
))
)
;; ── expressions/in (10 tests) ──
(defsuite "hs-upstream-expressions/in"
(deftest "basic no query return values"
(assert= (eval-hs "1 in [1, 2, 3]") (list 1))
(assert= (eval-hs "[1, 3] in [1, 2, 3]") (list 1 3))
(assert= (eval-hs "[1, 3, 4] in [1, 2, 3]") (list 1 3))
(assert= (eval-hs "[4, 5, 6] in [1, 2, 3]") (list))
)
(deftest "basic query return values"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")))
(dom-set-attr _el-d1 "id" "d1")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-p)
(dom-append _el-d1 _el-p2)
))
(deftest "class returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get the first .p1 in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-add-class _el-p1 "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
(deftest "class template returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get the first .{\"p1\"} in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-add-class _el-p1 "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
(deftest "id returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get #p1 in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
(deftest "id template returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get #{\"p1\"} in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
(deftest "in expression binds to unaryOperators"
(hs-cleanup!)
(let ((_el-d2 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")))
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-p "foo")
(dom-set-inner-html _el-p "bar")
(dom-append (dom-body) _el-d2)
(dom-append _el-d2 _el-p)
(dom-append _el-d2 _el-p2)
))
(deftest "null value in array returns empty"
(assert= (eval-hs "null in [1, 2, 3]") (list))
)
(deftest "query returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get the first
in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-add-class _el-p1 "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
(deftest "query template returns values"
(hs-cleanup!)
(let ((_el-inDiv (dom-create-element "div")) (_el-p1 (dom-create-element "p")))
(dom-set-attr _el-inDiv "id" "inDiv")
(dom-set-attr _el-inDiv "_" "on click get the first <${\"p\"}/> in me put its id into @result")
(dom-set-attr _el-p1 "id" "p1")
(dom-add-class _el-p1 "p1")
(dom-append (dom-body) _el-inDiv)
(dom-append _el-inDiv _el-p1)
(hs-activate! _el-inDiv)
))
)
;; ── expressions/logicalOperator (10 tests) ──
(defsuite "hs-upstream-expressions/logicalOperator"
(deftest "and short-circuits when lhs promise resolves to false"
(error "SKIP (untranslated): and short-circuits when lhs promise resolves to false"))
(deftest "and works"
(assert= (eval-hs "true and false") false)
)
(deftest "and works w/ more than one value"
(assert= (eval-hs "true and true and false") false)
)
(deftest "or evaluates rhs when lhs promise resolves to false"
(error "SKIP (untranslated): or evaluates rhs when lhs promise resolves to false"))
(deftest "or short-circuits when lhs promise resolves to true"
(error "SKIP (untranslated): or short-circuits when lhs promise resolves to true"))
(deftest "or works"
(assert= (eval-hs "true or false") true)
)
(deftest "parenthesized expressions with multiple operators work"
(assert= (eval-hs "true and (false or true)") true)
)
(deftest "should short circuit with and expression"
(error "SKIP (untranslated): should short circuit with and expression"))
(deftest "should short circuit with or expression"
(error "SKIP (untranslated): should short circuit with or expression"))
(deftest "unparenthesized expressions with multiple operators cause an error"
(error "SKIP (untranslated): unparenthesized expressions with multiple operators cause an error"))
)
;; ── expressions/mathOperator (15 tests) ──
(defsuite "hs-upstream-expressions/mathOperator"
(deftest "addition works"
(assert= (eval-hs "1 + 1") 2)
)
(deftest "addition works w/ more than one value"
(assert= (eval-hs "1 + 2 + 3") 6)
)
(deftest "array + array concats"
(assert= (eval-hs "[1, 2] + [3, 4]") (list 1 2 3 4))
)
(deftest "array + array does not mutate original"
(assert= (eval-hs "set a to [1, 2] then set b to a + [3] then return a") (list 1 2))
)
(deftest "array + single value appends"
(assert= (eval-hs "[1, 2] + 3") (list 1 2 3))
)
(deftest "array concat chains"
(assert= (eval-hs "[1] + [2] + [3]") (list 1 2 3))
)
(deftest "can use mixed expressions"
(error "SKIP (untranslated): can use mixed expressions"))
(deftest "division works"
(assert= (eval-hs "1 / 2") 0.5)
)
(deftest "empty array + array works"
(assert= (eval-hs "[] + [1, 2]") (list 1 2))
)
(deftest "mod works"
(assert= (eval-hs "3 mod 2") 1)
)
(deftest "multiplication works"
(assert= (eval-hs "1 * 2") 2)
)
(deftest "parenthesized expressions with multiple operators work"
(assert= (eval-hs "1 + (2 * 3)") 7)
)
(deftest "string concat works"
(assert= (eval-hs "'a' + 'b'") "ab")
)
(deftest "subtraction works"
(assert= (eval-hs "1 - 1") 0)
)
(deftest "unparenthesized expressions with multiple operators cause an error"
(error "SKIP (untranslated): unparenthesized expressions with multiple operators cause an error"))
)
;; ── expressions/no (9 tests) ──
(defsuite "hs-upstream-expressions/no"
(deftest "no returns false for non-empty array"
(assert= (eval-hs "no ['thing']") false)
)
(deftest "no returns false for non-null"
(assert= (eval-hs "no 'thing'") false)
)
(deftest "no returns true for empty array"
(assert= (eval-hs "no []") true)
)
(deftest "no returns true for empty selector"
(assert= (eval-hs "no .aClassThatDoesNotExist") true)
)
(deftest "no returns true for null"
(assert= (eval-hs "no null") true)
)
(deftest "no with where and is not"
(assert= (eval-hs "no [1, 2, 3] where it is not 2") false)
)
(deftest "no with where filters then checks emptiness"
(assert= (eval-hs "no [1, 2, 3] where it > 5") true)
)
(deftest "no with where on DOM elements"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-span (dom-create-element "span")) (_el-span2 (dom-create-element "span")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-add-class _el-span "a")
(dom-set-inner-html _el-span "A")
(dom-add-class _el-span2 "b")
(dom-set-inner-html _el-span2 "B")
(dom-set-attr _el-button "_" "on click if no in #box where it matches .c then put 'none' into #out else put 'found' into #out")
(dom-set-inner-html _el-button "go")
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-box)
(dom-append _el-box _el-span)
(dom-append _el-box _el-span2)
(dom-append (dom-body) _el-button)
(dom-append (dom-body) _el-out)
(hs-activate! _el-button)
(dom-dispatch _el-button "click" nil)
(assert= (dom-text-content (dom-query-by-id "out")) "none")
))
(deftest "no with where returns false when matches exist"
(assert= (eval-hs "no [1, 2, 3] where it > 1") false)
)
)
;; ── expressions/not (9 tests) ──
(defsuite "hs-upstream-expressions/not"
(deftest "not has higher precedence than and"
(assert= (eval-hs "not false and true") true)
(assert= (eval-hs "not true and true") false)
)
(deftest "not has higher precedence than or"
(assert= (eval-hs "not true or true") true)
(assert= (eval-hs "not false or false") true)
)
(deftest "not inverts equality comparisons"
(assert= (eval-hs "not (1 is 2)") true)
(assert= (eval-hs "not (1 is 1)") false)
)
(deftest "not inverts false"
(assert= (eval-hs "not false") true)
)
(deftest "not inverts true"
(assert= (eval-hs "not true") false)
)
(deftest "not null and not undefined"
(assert= (eval-hs "not null") true)
(assert= (eval-hs "not undefined") true)
)
(deftest "not with numeric truthy/falsy values"
(assert= (eval-hs "not 0") true)
(assert= (eval-hs "not 1") false)
(assert= (eval-hs "not 42") false)
)
(deftest "not with string truthy/falsy values"
(assert= (eval-hs "not ''") true)
(assert= (eval-hs "not 'hello'") false)
)
(deftest "two nots make a true"
(assert= (eval-hs "not not true") true)
)
)
;; ── expressions/null (1 tests) ──
(defsuite "hs-upstream-expressions/null"
(deftest "null literal work"
(eval-hs "null")
)
)
;; ── expressions/numbers (1 tests) ──
(defsuite "hs-upstream-expressions/numbers"
(deftest "handles numbers properly"
(assert= (eval-hs "-1") -1)
(assert= (eval-hs "1") 1)
(assert= (eval-hs "1.1") 1.1)
(assert= (eval-hs "1e6") 1e6)
(assert= (eval-hs "1e-6") 1e-6)
(assert= (eval-hs "1.1e6") 1.1e6)
(assert= (eval-hs "1.1e-6") 1.1e-6)
(assert= (eval-hs "1234567890.1234567890") 1234567890.123456789)
)
)
;; ── expressions/objectLiteral (12 tests) ──
(defsuite "hs-upstream-expressions/objectLiteral"
(deftest "allows trailing commas"
;; TODO: assert= (eval-hs "{foo:true, bar-baz:false,}") against { "foo": true, "bar-baz": false }
)
(deftest "deeply nested object literals work"
;; TODO: assert= (eval-hs "{a: {b: {c: 'deep'}}}") against { a: { b: { c: 'deep' } } }
)
(deftest "empty object literals work"
;; TODO: assert= (eval-hs "{}") against {}
)
(deftest "expressions work in object literal field names"
(error "SKIP (untranslated): expressions work in object literal field names"))
(deftest "hyphens work in object literal field names"
;; TODO: assert= (eval-hs "{-foo:true, bar-baz:false}") against { "-foo": true, "bar-baz": false }
)
(deftest "mixed field name styles in one literal"
;; TODO: assert= (eval-hs "{plain: 1, \"quoted\": 2, -dashed: 3}") against { plain: 1, quoted: 2, "-dashed": 3 }
)
(deftest "multi-field object literal works"
;; TODO: assert= (eval-hs "{foo:true, bar:false}") against { foo: true, bar: false }
)
(deftest "nested object literals work"
;; TODO: assert= (eval-hs "{outer: {inner: 1}}") against { outer: { inner: 1 } }
)
(deftest "object literal values can be expressions"
;; TODO: assert= (eval-hs "{sum: 1 + 2, product: 3 * 4}") against { sum: 3, product: 12 }
)
(deftest "object literals can contain arrays"
;; TODO: assert= (eval-hs "{items: [1, 2, 3], count: 3}") against { items: [1, 2, 3], count: 3 }
)
(deftest "one field object literal works"
;; TODO: assert= (eval-hs "{foo:true}") against { foo: true }
)
(deftest "strings work in object literal field names"
;; TODO: assert= (eval-hs "{\"foo\":true, \"bar\":false}") against { foo: true, bar: false }
)
)
;; ── expressions/positionalExpression (7 tests) ──
(defsuite "hs-upstream-expressions/positionalExpression"
(deftest "first works"
(assert= (eval-hs "the first of [1, 2, 3]") 1)
)
(deftest "first works w/ array-like"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "first works w/ node"
(hs-cleanup!)
(let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-outerDiv "id" "outerDiv")
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-outerDiv)
(dom-append _el-outerDiv _el-d1)
(dom-append _el-outerDiv _el-d2)
(dom-append _el-outerDiv _el-d3)
))
(deftest "is null safe"
(eval-hs "the first of null")
)
(deftest "last works"
(assert= (eval-hs "the last of [1, 2, 3]") 3)
)
(deftest "last works w/ array-like"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "last works w/ node"
(hs-cleanup!)
(let ((_el-outerDiv (dom-create-element "div")) (_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-outerDiv "id" "outerDiv")
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-outerDiv)
(dom-append _el-outerDiv _el-d1)
(dom-append _el-outerDiv _el-d2)
(dom-append _el-outerDiv _el-d3)
))
)
;; ── expressions/possessiveExpression (23 tests) ──
(defsuite "hs-upstream-expressions/possessiveExpression"
(deftest "can access basic attribute"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "data-foo" "bar")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can access basic properties"
(assert= (eval-hs-locals "foo's foo" (list (list (quote foo) {:foo "foo"}))) "foo")
)
(deftest "can access basic style"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "style" "color:red")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can access its properties"
(assert= (eval-hs-locals "its foo" (list (list (quote it) {:foo "foo"}))) "foo")
)
(deftest "can access multiple basic attributes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-set-attr _el-div "data-foo" "bar")
(dom-add-class _el-div1 "c1")
(dom-set-attr _el-div1 "data-foo" "bar")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
))
(deftest "can access multiple basic styles"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-set-attr _el-div "style" "color:red")
(dom-add-class _el-div1 "c1")
(dom-set-attr _el-div1 "style" "color:red")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
))
(deftest "can access my attribute"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "data-foo" "bar")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can access my properties"
(assert= (eval-hs-locals "my foo" (list (list (quote me) {:foo "foo"}))) "foo")
)
(deftest "can access my style"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "style" "color:red")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can access properties on classrefs"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "foo")
(dom-set-attr _el-div "style" "display: inline")
(dom-append (dom-body) _el-div)
))
(deftest "can access properties on classrefs 2"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "foo")
(dom-set-attr _el-div "style" "display: inline")
(dom-append (dom-body) _el-div)
))
(deftest "can access properties on idrefs"
(hs-cleanup!)
(let ((_el-foo (dom-create-element "div")))
(dom-set-attr _el-foo "id" "foo")
(dom-set-attr _el-foo "style" "display: inline")
(dom-append (dom-body) _el-foo)
))
(deftest "can access properties on idrefs 2"
(hs-cleanup!)
(let ((_el-foo (dom-create-element "div")))
(dom-set-attr _el-foo "id" "foo")
(dom-set-attr _el-foo "style" "display: inline")
(dom-append (dom-body) _el-foo)
))
(deftest "can access properties on queryrefs"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "foo")
(dom-set-attr _el-div "style" "display: inline")
(dom-append (dom-body) _el-div)
))
(deftest "can access properties on queryrefs 2"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "foo")
(dom-set-attr _el-div "style" "display: inline")
(dom-append (dom-body) _el-div)
))
(deftest "can set basic attributes"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "data-foo" "bar")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can set basic styles"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "style" "color:red")
(dom-append (dom-body) _el-pDiv)
))
(deftest "can set multiple basic attributes"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d1 "data-foo" "bar")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d2 "data-foo" "bar")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
))
(deftest "can set multiple basic styles"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d1 "style" "color:red")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d2 "style" "color:red")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
))
(deftest "can set root styles"
(hs-cleanup!)
(let ((_el-pDiv (dom-create-element "div")))
(dom-set-attr _el-pDiv "id" "pDiv")
(dom-set-attr _el-pDiv "style" "color:red")
(dom-append (dom-body) _el-pDiv)
))
(deftest "is null safe"
(eval-hs "foo's foo")
)
(deftest "its property is null safe"
(eval-hs "its foo")
)
(deftest "my property is null safe"
(eval-hs "my foo")
)
)
;; ── expressions/propertyAccess (12 tests) ──
(defsuite "hs-upstream-expressions/propertyAccess"
(deftest "can access basic properties"
(assert= (eval-hs-locals "foo.foo" (list (list (quote foo) {:foo "foo"}))) "foo")
)
(deftest "chained property access (four levels)"
(assert= (eval-hs-locals "a.b.c.d" (list (list (quote a) {:b {:c {:d 42}}}))) 42)
)
(deftest "chained property access (three levels)"
(assert= (eval-hs-locals "a.b.c" (list (list (quote a) {:b {:c "deep"}}))) "deep")
)
(deftest "is null safe"
(eval-hs "foo.foo")
)
(deftest "mixing dot and of forms"
(assert= (eval-hs-locals "c of a.b" (list (list (quote a) {:b {:c "mixed"}}))) "mixed")
)
(deftest "null-safe access through an undefined intermediate"
(eval-hs "a.b.c")
)
(deftest "of form chains through multiple levels"
(assert= (eval-hs-locals "c of b of a" (list (list (quote a) {:b {:c "deep"}}))) "deep")
)
(deftest "of form works"
(assert= (eval-hs-locals "foo of foo" (list (list (quote foo) {:foo "foo"}))) "foo")
)
(deftest "of form works w/ complex left side"
(assert= (eval-hs-locals "bar.doh of foo" (list (list (quote foo) {:bar {:doh "foo"}}))) "foo")
)
(deftest "of form works w/ complex right side"
(assert= (eval-hs-locals "doh of foo.bar" (list (list (quote foo) {:bar {:doh "foo"}}))) "foo")
)
(deftest "property access on function result"
(assert= (eval-hs-locals "makeObj().name" (list (list (quote makeObj) (fn () {:name "hi"})))) "hi")
)
(deftest "works properly w/ boolean properties"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")))
(dom-add-class _el-input "cb")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-input "checked" "checked")
(dom-add-class _el-input1 "cb")
(dom-set-attr _el-input1 "type" "checkbox")
(dom-append (dom-body) _el-input)
(dom-append (dom-body) _el-input1)
))
)
;; ── expressions/queryRef (13 tests) ──
(defsuite "hs-upstream-expressions/queryRef"
(deftest "basic queryRef works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-append (dom-body) _el-div)
))
(deftest "basic queryRef works w no match"
(error "SKIP (untranslated): basic queryRef works w no match"))
(deftest "basic queryRef works w properties w/ strings"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-add-class _el-div1 "c2")
(dom-set-attr _el-div1 "foo" "bar")
(dom-add-class _el-div2 "c3")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "basic queryRef works w/ div selector"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-add-class _el-div1 "c2")
(dom-add-class _el-div2 "c3")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "basic queryRef works w/ funny selector"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-div "title" "t1")
(dom-set-attr _el-div1 "title" "t2")
(dom-set-attr _el-div2 "title" "t3")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "basic queryRef works w/ multiple matches"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-add-class _el-div1 "c1")
(dom-add-class _el-div2 "c1")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "basic queryRef works w/ properties"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-div "title" "t1")
(dom-set-attr _el-div1 "title" "t2")
(dom-set-attr _el-div2 "title" "t3")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "can interpolate elements into queries"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-add-class _el-div "a")
(dom-add-class _el-div1 "b")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
))
(deftest "queryRef w/ $ no curlies works"
(hs-cleanup!)
(let ((_el-t1 (dom-create-element "div")) (_el-t2 (dom-create-element "div")) (_el-t3 (dom-create-element "div")))
(dom-set-attr _el-t1 "id" "t1")
(dom-set-attr _el-t2 "id" "t2")
(dom-set-attr _el-t3 "id" "t3")
(dom-append (dom-body) _el-t1)
(dom-append (dom-body) _el-t2)
(dom-append (dom-body) _el-t3)
))
(deftest "queryRef w/ $ works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-add-class _el-div "c1")
(dom-add-class _el-div1 "c2")
(dom-set-attr _el-div1 "foo" "bar")
(dom-add-class _el-div2 "c3")
(dom-append (dom-body) _el-div)
(dom-append (dom-body) _el-div1)
(dom-append (dom-body) _el-div2)
))
(deftest "queryRefs support colons properly"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")))
(dom-add-class _el-input "foo")
(dom-set-attr _el-input "type" "checkbox")
(dom-set-attr _el-input "checked" "checked")
(dom-append (dom-body) _el-input)
))
(deftest "queryRefs support dollar properly"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "title" "little flower")
(dom-append (dom-body) _el-div)
))
(deftest "queryRefs support tildes properly"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "title" "little flower")
(dom-append (dom-body) _el-div)
))
)
;; ── expressions/relativePositionalExpression (23 tests) ──
(defsuite "hs-upstream-expressions/relativePositionalExpression"
(deftest "can access property of next element with possessive"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-inner-html _el-d2 "hello")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
))
(deftest "can access property of previous element with possessive"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-inner-html _el-d1 "world")
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
))
(deftest "can access style of next element with possessive"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "style" "color: red")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
))
(deftest "can write to next element with put command"
(error "SKIP (untranslated): can write to next element with put command"))
(deftest "next works properly among siblings"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "next works properly among siblings with wrapping"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "next works properly with array-like"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "next works properly with array-like and wrap"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "next works properly with array-like no match"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "next works properly with array-like no match and wrap"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly among siblings"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly among siblings with wrapping"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly with array-like"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly with array-like and wrap"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly with array-like no match"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "previous works properly with array-like no match and wrap"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-d2 (dom-create-element "div")) (_el-p3 (dom-create-element "p")) (_el-d3 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-add-class _el-p "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-add-class _el-p3 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-p)
(dom-append (dom-body) _el-d2)
(dom-append (dom-body) _el-p3)
(dom-append (dom-body) _el-d3)
))
(deftest "properly constrains via the within modifier"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")) (_el-d3 (dom-create-element "div")) (_el-d4 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d3 "id" "d3")
(dom-add-class _el-d3 "c1")
(dom-set-attr _el-d4 "id" "d4")
(dom-add-class _el-d4 "c1")
(dom-append (dom-body) _el-d1)
(dom-append _el-d1 _el-d2)
(dom-append _el-d1 _el-d3)
(dom-append (dom-body) _el-d4)
))
(deftest "relative next works properly among siblings w/ class"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d1 "_" "on click add .foo to next .c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d1)
))
(deftest "relative next works properly among siblings w/ query"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d1 "_" "on click add .foo to next ")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d1)
))
(deftest "relative next works properly among siblings w/ query & class"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d1 "_" "on click add .foo to next ")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d1)
))
(deftest "relative previous works properly among siblings w/ class"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d2 "_" "on click add .foo to previous .c1")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d2)
))
(deftest "relative previous works properly among siblings w/ query"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d2 "_" "on click add .foo to previous ")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d2)
))
(deftest "relative previous works properly among siblings w/ query & class"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-d2 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-add-class _el-d1 "c1")
(dom-set-attr _el-d2 "id" "d2")
(dom-add-class _el-d2 "c1")
(dom-set-attr _el-d2 "_" "on click add .foo to previous ")
(dom-append (dom-body) _el-d1)
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d2)
))
)
;; ── expressions/some (6 tests) ──
(defsuite "hs-upstream-expressions/some"
(deftest "some returns false for empty array"
(assert= (eval-hs "some []") false)
)
(deftest "some returns false for empty selector"
(assert= (eval-hs "some .aClassThatDoesNotExist") false)
)
(deftest "some returns false for null"
(assert= (eval-hs "some null") false)
)
(deftest "some returns true for filled array"
(assert= (eval-hs "some ['thing']") true)
)
(deftest "some returns true for non-null"
(assert= (eval-hs "some 'thing'") true)
)
(deftest "some returns true for nonempty selector"
(assert= (eval-hs "some