new<
;; SKIP action: find('#go').dispatchEvent('click')
;; SKIP check: skip toBe(true); toBe("new") None None
))
(deftest "morph with variable content"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-go (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-append (dom-body) _el-target)
(dom-set-attr _el-go "id" "go")
;; HS source contains quotes: on click set content to "
morphed
" then
;; SKIP action: find('#go').dispatchEvent('click')
;; SKIP check: skip toHaveText("morphed") None None
))
)
;; ── reset (8 tests) ──
(defsuite "hs-upstream-reset"
(deftest "can reset a form"
(hs-cleanup!)
(let ((_el-f1 (dom-create-element "form")))
(dom-set-attr _el-f1 "id" "f1")
(dom-append (dom-body) _el-f1)
;; SKIP action: find('#t1').fill('changed')
;; SKIP action: find('#rst').dispatchEvent('click')
;; SKIP check: skip toHaveValue('changed'); toHaveValue('original') None None
))
(deftest "reset with no target resets me (form)"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")))
(dom-set-attr _el-form "_" "on custom reset")
(dom-append (dom-body) _el-form)
(hs-activate! _el-form)
;; SKIP action: find('#t2').fill('modified')
;; SKIP action: find('form').dispatchEvent('custom')
;; SKIP check: skip toHaveValue('modified'); toHaveValue('default') None None
))
(deftest "can reset a text input to defaultValue"
(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-append (dom-body) _el-t3)
(dom-set-attr _el-button "_" "on click reset #t3")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('#t3').fill('goodbye')
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveValue('goodbye'); toHaveValue('hello') None None
))
(deftest "can reset 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-append (dom-body) _el-cb1)
(dom-set-attr _el-button "_" "on click reset #cb1")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').dispatchEvent('click')
))
(deftest "can reset an unchecked checkbox"
(hs-cleanup!)
(let ((_el-cb2 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-cb2 "id" "cb2")
(dom-set-attr _el-cb2 "type" "checkbox")
(dom-append (dom-body) _el-cb2)
(dom-set-attr _el-button "_" "on click reset #cb2")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').dispatchEvent('click')
))
(deftest "can reset 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-append (dom-body) _el-ta1)
(dom-set-attr _el-button "_" "on click reset #ta1")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('#ta1').fill('new text')
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveValue('new text'); toHaveValue('original text') None None
))
(deftest "can reset a select"
(hs-cleanup!)
(let ((_el-sel1 (dom-create-element "select")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-sel1 "id" "sel1")
(dom-append (dom-body) _el-sel1)
(dom-set-attr _el-button "_" "on click reset #sel1")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveValue('c'); toHaveValue('b') None None
))
(deftest "can reset multiple inputs"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-input1 (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-add-class _el-input "resettable")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "one")
(dom-append (dom-body) _el-input)
(dom-add-class _el-input1 "resettable")
(dom-set-attr _el-input1 "type" "text")
(dom-set-attr _el-input1 "value" "two")
(dom-append (dom-body) _el-input1)
(dom-set-attr _el-button "_" "on click reset .resettable")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').dispatchEvent('click')
))
)
;; ── scroll (8 tests) ──
(defsuite "hs-upstream-scroll"
(deftest "can scroll to an element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-div "style" "height: 2000px")
(dom-append (dom-body) _el-div)
(dom-set-attr _el-target "id" "target")
(dom-append (dom-body) _el-target)
(dom-set-attr _el-div2 "_" "on click scroll to #target")
(dom-append (dom-body) _el-div2)
(hs-activate! _el-div2)
;; SKIP check: skip toBe(true) None None
))
(deftest "can scroll to top of element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-target (dom-create-element "div")) (_el-div2 (dom-create-element "div")))
(dom-set-attr _el-div "style" "height: 2000px")
(dom-append (dom-body) _el-div)
(dom-set-attr _el-target "id" "target")
(dom-set-attr _el-target "style" "height: 200px")
(dom-append (dom-body) _el-target)
(dom-set-attr _el-div2 "_" "on click scroll to the top of #target")
(dom-append (dom-body) _el-div2)
(hs-activate! _el-div2)
;; SKIP check: skip toBe(true) None None
))
(deftest "can scroll down by amount"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-set-attr _el-div "style" "height: 5000px")
(dom-append (dom-body) _el-div)
(dom-set-attr _el-div1 "_" "on click scroll down by 300px")
(dom-append (dom-body) _el-div1)
(hs-activate! _el-div1)
))
(deftest "can scroll up by amount"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-set-attr _el-div "style" "height: 5000px")
(dom-append (dom-body) _el-div)
(dom-set-attr _el-div1 "_" "on click scroll up by 100px")
(dom-append (dom-body) _el-div1)
(hs-activate! _el-div1)
))
(deftest "can scroll by without direction (defaults to down)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")))
(dom-set-attr _el-div "style" "height: 5000px")
(dom-append (dom-body) _el-div)
(dom-set-attr _el-div1 "_" "on click scroll by 200px")
(dom-append (dom-body) _el-div1)
(hs-activate! _el-div1)
))
(deftest "can scroll container by amount"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-go (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "style" "height: 100px; overflow: auto")
(dom-append (dom-body) _el-box)
(dom-set-attr _el-go "id" "go")
(dom-set-attr _el-go "_" "on click scroll #box down by 200px")
(dom-append (dom-body) _el-go)
(hs-activate! _el-go)
;; SKIP action: find('#go').dispatchEvent('click')
))
(deftest "can scroll to element in container"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-go (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "style" "height: 100px; overflow: auto")
(dom-append (dom-body) _el-box)
(dom-set-attr _el-go "id" "go")
(dom-set-attr _el-go "_" "on click scroll to #item in #box")
(dom-append (dom-body) _el-go)
(hs-activate! _el-go)
;; SKIP action: find('#go').dispatchEvent('click')
))
(deftest "can scroll left by amount"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-go (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "style" "width: 100px; overflow: auto; white-space: nowrap")
(dom-append (dom-body) _el-box)
(dom-set-attr _el-go "id" "go")
(dom-set-attr _el-go "_" "on click scroll #box right by 300px")
(dom-append (dom-body) _el-go)
(hs-activate! _el-go)
;; SKIP action: find('#go').dispatchEvent('click')
))
)
;; ── select (4 tests) ──
(defsuite "hs-upstream-select"
(deftest "selects text in an input"
(hs-cleanup!)
(let ((_el-inp (dom-create-element "input")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-inp "id" "inp")
(dom-set-attr _el-inp "value" "hello world")
(dom-append (dom-body) _el-inp)
(dom-set-attr _el-button "_" "on click select #inp")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').click()
;; SKIP check: skip toBe("hello world") None None
))
(deftest "selects text in a textarea"
(hs-cleanup!)
(let ((_el-ta (dom-create-element "textarea")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-ta "id" "ta")
(dom-append (dom-body) _el-ta)
(dom-set-attr _el-button "_" "on click select #ta")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').click()
;; SKIP check: skip toBe("some text") None None
))
(deftest "selects implicit me"
(hs-cleanup!)
(let ((_el-inp (dom-create-element "input")))
(dom-set-attr _el-inp "id" "inp")
(dom-set-attr _el-inp "_" "on click select")
(dom-set-attr _el-inp "value" "test")
(dom-append (dom-body) _el-inp)
(hs-activate! _el-inp)
;; SKIP action: find('#inp').click()
;; SKIP check: skip toBe("test") None None
))
(deftest "returns selected text"
(hs-cleanup!)
(let ((_el-text (dom-create-element "p")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-text "id" "text")
(dom-append (dom-body) _el-text)
(dom-set-attr _el-button "_" "on click put the selection into #out")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP action: find('button').click()
;; SKIP check: skip toHaveText("Hello") None None
))
)
;; ── swap (4 tests) ──
(defsuite "hs-upstream-swap"
(deftest "can swap two variables"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click set x to "a" then set y to "b" then swap x with y t
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("ba") None None
))
(deftest "can swap two properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-a (dom-create-element "span")) (_el-b (dom-create-element "span")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click set #a.textContent to "hello" then set #b.textConte
(dom-set-attr _el-a "id" "a")
(dom-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("world"); toHaveText("hello") None None
))
(deftest "can swap array elements"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "on click set arr to [1,2,3] then swap arr[0] with arr[2] then put arr as String into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("3,2,1") None None
))
(deftest "can swap a variable with a property"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")) (_el-target (dom-create-element "span")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click set x to "old" then set #target.dataset.val to "new
(dom-set-attr _el-target "id" "target")
(dom-set-attr _el-target "data-val" "x")
(dom-append (dom-body) _el-target)
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("new"); toHaveAttribute('data-val', 'old') None None
))
)
;; ── bind (44 tests) ──
(defsuite "hs-upstream-bind"
(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-append (dom-body) _el-name-input)
(dom-set-attr _el-span "_" "bind $name and #name-input.value end when $name changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: evaluate({...})
;; SKIP action: await run("set $name to '
;; SKIP check: skip toHaveText('Alice'); toHaveText('Bob') None None
))
(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)
;; SKIP action: await run("set $theme to '
;; SKIP action: await run("set $theme to '
;; SKIP check: skip toHaveAttribute('data-theme', 'light'); toHaveAttribute('dat None None
))
(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)
;; SKIP action: await run("set $color to '
;; SKIP action: await run("set $color to '
;; SKIP check: skip toHaveAttribute('data-color', 'red'); toHaveAttribute('data- None None
))
(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-append (dom-body) _el-city-input)
(dom-set-attr _el-span "_" "bind $city to #city-input.value end when $city changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: await run("set $city to '
;; SKIP check: skip toHaveText('Paris') None None
))
(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)
(hs-activate! _el-input)
(dom-append (dom-body) _el-span)
;; SKIP action: find('input').fill('goodbye')
;; SKIP action: await run("set $greeting to '
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(dom-set-attr _el-span "_" "when $isDarkMode changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: await run("set $isDarkMode to false"
;; SKIP action: await run("set $isDarkMode to false"
))
(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-append (dom-body) _el-textarea)
(hs-activate! _el-textarea)
(dom-set-attr _el-span "_" "when $bio changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: find('textarea').fill('New bio')
))
(deftest "shorthand on select binds to value"
(hs-cleanup!)
(let ((_el-select (dom-create-element "select")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-select "_" "bind $country to me")
(dom-append (dom-body) _el-select)
(hs-activate! _el-select)
(dom-set-attr _el-span "_" "when $country changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
))
(deftest "unsupported element: bind to plain div errors"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(dom-set-attr _el-span "_" "when $price changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: await run("set $price to 42"
;; SKIP check: skip toHaveText('42') None None
))
(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)
;; SKIP action: await run("set $isEnabled to true"
;; SKIP action: await run("set $isEnabled to false"
;; SKIP check: skip toHaveAttribute('data-active', '') None None
))
(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)
;; SKIP action: await run("set $isHidden to true"
;; SKIP action: await run("set $isHidden to false"
;; SKIP check: skip toHaveAttribute('aria-hidden', 'true'); toHaveAttribute('ari None None
))
(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-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $opacity to 1"
;; SKIP action: await run("set $opacity to 0.3"
))
(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)
;; SKIP check: skip toBe(false) None None
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(dom-set-attr _el-span "_" "when $searchTerm changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: evaluate({...})
))
(deftest "form.reset() syncs variable back to default value"
(hs-cleanup!)
(let ((_el-test-form (dom-create-element "form")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-test-form "id" "test-form")
(dom-append (dom-body) _el-test-form)
(dom-set-attr _el-span "_" "when $formField changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: find('input').fill('user typed this')
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(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-input1)
(hs-activate! _el-input1)
(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-append (dom-body) _el-input2)
(hs-activate! _el-input2)
(dom-set-attr _el-span "_" "when $color changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: find('input[value="blue"]').click()
;; SKIP action: find('input[value="green"]').click()
;; SKIP action: await run("set $color to '
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(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-append (dom-body) _el-input1)
(hs-activate! _el-input1)
(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-input2)
(hs-activate! _el-input2)
;; SKIP action: await run("set $size to '
;; SKIP action: await run("set $size to '
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(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-append (dom-body) _el-input1)
(hs-activate! _el-input1)
(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-input2)
(hs-activate! _el-input2)
;; SKIP action: await run("set $fruit to '
))
(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-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $darkMode to false"
;; SKIP action: await run("set $darkMode to true"
;; SKIP action: await run("set $darkMode to false"
;; SKIP check: skip toHaveClass('dark') None None
))
(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-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $darkMode to false"
))
(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-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $highlighted to true"
;; SKIP check: skip toHaveClass('highlight') None None
))
(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)
;; SKIP action: await run("set $name to '
))
(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)
;; SKIP action: await run("set $name to '
))
(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 — 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)
;; SKIP action: await run("set $theme to '
;; SKIP check: skip toHaveAttribute('data-theme', 'dark') None None
))
(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)
;; SKIP action: await run("set $isDark to true"
))
(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)
;; SKIP action: await run("set $isDark to false"
))
(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)
;; SKIP action: find('input').fill('world')
))
(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)
;; SKIP action: await run("set $label to '
;; SKIP action: await run("set $label to '
;; SKIP check: skip toHaveAttribute('data-label', 'important') None None
))
(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-append (dom-body) _el-of-input)
(dom-set-attr _el-div "_" "bind $search to value of #of-input")
(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-append (dom-body) _el-dark-toggle)
(dom-set-attr _el-div "_" "bind .dark and #dark-toggle's checked")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toHaveClass('dark') None None
))
(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-append (dom-body) _el-title-input)
(dom-set-attr _el-h1 "_" "bind @data-title and #title-input's value")
(dom-append (dom-body) _el-h1)
(hs-activate! _el-h1)
;; SKIP action: find('#title-input').fill('World')
))
(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-append (dom-body) _el-slider)
(dom-set-attr _el-input "_" "bind my value and #slider's value")
(dom-set-attr _el-input "type" "number")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
;; SKIP action: evaluate({...})
))
(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-append (dom-body) _el-name-field)
(dom-set-attr _el-div "_" "bind $name to #name-field")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: evaluate({...})
;; SKIP action: await run("set $name to '
))
(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-append (dom-body) _el-agree-cb)
(dom-set-attr _el-div "_" "bind $agreed to #agree-cb")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $agreed to false"
;; SKIP action: await run("set $agreed to true"
))
(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-append (dom-body) _el-qty-input)
(dom-set-attr _el-div "_" "bind $qty to #qty-input")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $qty to 5"
))
(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-append (dom-body) _el-range-slider)
(dom-set-attr _el-input "_" "bind me to #range-slider")
(dom-set-attr _el-input "type" "number")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
;; SKIP action: evaluate({...})
))
(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)
;; SKIP action: await run("set $name to '
))
(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 "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-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $text to '
))
(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)
;; SKIP action: evaluate({...})
;; SKIP action: await run("set $custom to '
))
(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-append (dom-body) _el-input)
(hs-activate! _el-input)
(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-input1)
(hs-activate! _el-input1)
;; SKIP action: evaluate({...})
;; SKIP action: evaluate({...})
;; SKIP action: await run("set $color to '
;; SKIP action: await run("$color"
;; SKIP check: skip toBe('red') None None
))
(deftest "form reset listener is removed on cleanup"
(hs-cleanup!)
(let ((_el-form (dom-create-element "form")))
(dom-append (dom-body) _el-form)
;; SKIP action: await run("set $val to '
;; SKIP action: await run("set $val to '
;; SKIP action: await run("$val"
;; SKIP check: skip toBe('changed') None None
))
)
;; ── live (23 tests) ──
(defsuite "hs-upstream-live"
(deftest "derives a variable from a computed expression"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set $total to ($price * $qty) end when $total changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $price to 10"
;; SKIP action: await run("set $qty to 3"
;; SKIP action: await run("set $price to 25"
;; SKIP check: skip toHaveText('30') None None
))
(deftest "updates DOM text reactively with put"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put 'hello ' + $greeting into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $greeting to '
;; SKIP action: await run("set $greeting to '
))
(deftest "sets an attribute reactively"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set my @data-theme to $theme")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $theme to '
;; SKIP action: await run("set $theme to '
;; SKIP check: skip toHaveAttribute('data-theme', 'light') None None
))
(deftest "sets a style reactively"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set *opacity to $opacity")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $opacity to 1"
;; SKIP action: await run("set $opacity to 0.5"
))
(deftest "puts a computed dollar amount into the DOM"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put '$' + ($price * $qty) into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $price to 10"
;; SKIP action: await run("set $qty to 2"
;; SKIP action: await run("set $qty to 5"
))
(deftest "block form re-runs all commands when any dependency changes"
(hs-cleanup!)
(let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-w "id" "w")
(dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
(dom-append (dom-body) _el-w)
(hs-activate! _el-w)
(dom-set-attr _el-h "id" "h")
(dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
(dom-append (dom-body) _el-h)
(hs-activate! _el-h)
(dom-set-attr _el-div "_" "live set $doubleWidth to ($width * 2) set $doubleHeight to ($height * 2) end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $width to 100"
;; SKIP action: await run("set $height to 200"
;; SKIP action: await run("set $height to 300"
;; SKIP check: skip toHaveText('200') None None
))
(deftest "separate live statements create independent effects"
(hs-cleanup!)
(let ((_el-w (dom-create-element "span")) (_el-h (dom-create-element "span")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-w "id" "w")
(dom-set-attr _el-w "_" "when $doubleWidth changes put it into me")
(dom-append (dom-body) _el-w)
(hs-activate! _el-w)
(dom-set-attr _el-h "id" "h")
(dom-set-attr _el-h "_" "when $doubleHeight changes put it into me")
(dom-append (dom-body) _el-h)
(hs-activate! _el-h)
(dom-set-attr _el-div "_" "live set $doubleWidth to ($width * 2) end live set $doubleHeight to ($height * 2)")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $width to 100"
;; SKIP action: await run("set $height to 200"
;; SKIP action: await run("set $height to 300"
;; SKIP check: skip toHaveText('200') None None
))
(deftest "block form cascades inter-dependent commands"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set $subtotal to ($price * $qty) set $total to ($subtotal + $tax) end when $total changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $price to 10"
;; SKIP action: await run("set $qty to 3"
;; SKIP action: await run("set $tax to 5"
;; SKIP action: await run("set $price to 20"
;; SKIP action: await run("set $tax to 10"
))
(deftest "toggles a class based on a boolean variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live if $isActive add .active to me else remove .active from me end end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $isActive to false"
;; SKIP action: await run("set $isActive to true"
;; SKIP action: await run("set $isActive to false"
;; SKIP check: skip toHaveClass('active') None None
))
(deftest "toggles display style based on a boolean variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live if $isVisible set *display to 'block' else set *display to 'none' end end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $isVisible to true"
;; SKIP action: await run("set $isVisible to false"
;; SKIP action: await run("set $isVisible to true"
))
(deftest "effects stop when element is removed from DOM"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $message into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $message to '
;; SKIP action: await run("set $message to '
))
(deftest "conditional branch only tracks the active dependency"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live if $showFirst put $firstName into me else put $lastName into me end end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $showFirst to true"
;; SKIP action: await run("set $firstName to '
;; SKIP action: await run("set $lastName to '
;; SKIP action: await run("set $firstName to '
;; SKIP action: await run("set $lastName to '
;; SKIP check: skip toHaveText('Bob'); toHaveText('Jones') None None
))
(deftest "multiple live on same element work independently"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set my @data-name to $firstName end live set my @data-age to $age")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $firstName to '
;; SKIP action: await run("set $age to 30"
;; SKIP action: await run("set $firstName to '
;; SKIP check: skip toHaveAttribute('data-name', 'Alice'); toHaveAttribute('data None None
))
(deftest "live and when on same element do not interfere"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live set my @data-status to $status end when $status changes put 'Status: ' + it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $status to '
;; SKIP action: await run("set $status to '
;; SKIP check: skip toHaveAttribute('data-status', 'online'); toHaveText('Status None None
))
(deftest "bind and live on same element do not interfere"
(hs-cleanup!)
(let ((_el-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-input "_" "bind $username to me end live set my @data-mirror to $username")
(dom-set-attr _el-input "type" "text")
(dom-set-attr _el-input "value" "alice")
(dom-append (dom-body) _el-input)
(hs-activate! _el-input)
(dom-set-attr _el-span "_" "when $username changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: find('input').fill('bob')
;; SKIP action: await run("set $username to '
))
(deftest "reactive effects are stopped on cleanup"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
(dom-set-attr _el-d1 "_" "live put $count into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
;; SKIP action: await run("set $count to 0"
;; SKIP action: await run("set $count to 99"
;; SKIP check: skip toHaveText('cleaned') None None
))
(deftest "append triggers live block"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $items.join(', ') into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $items to ['
;; SKIP action: await run("append '
))
(deftest "push via pseudo-command triggers live block"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $items.join(', ') into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $items to ['
;; SKIP action: await run("$items.push('
))
(deftest "push via call triggers live block"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $items.join(', ') into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $items to ['
;; SKIP action: await run("call $items.push('
))
(deftest "array + still works with live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $items.join(', ') into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $items to ['
;; SKIP action: await run("set $items to $items + ['
))
(deftest "set property still works with live"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "live put $obj.name into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $obj to {name: '
;; SKIP action: await run("set $obj.name to '
))
(deftest "property change on object in array triggers live re-render"
(hs-cleanup!)
(let ((_el-people-tmpl (dom-create-element "template")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-people-tmpl "id" "people-tmpl")
(dom-append (dom-body) _el-people-tmpl)
(dom-set-attr _el-div "_" "live render #people-tmpl with people: $people then put it into my.innerHTML end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $people to [{name: '
;; SKIP action: await run("set $people[0].name to '
))
(deftest "push object then modify its property both trigger live"
(hs-cleanup!)
(let ((_el-items-tmpl (dom-create-element "template")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-items-tmpl "id" "items-tmpl")
(dom-append (dom-body) _el-items-tmpl)
(dom-set-attr _el-div "_" "live render #items-tmpl with items: $items then put it into my.innerHTML end")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $items to [{label: '
;; SKIP action: await run("call $items.push({label: '
;; SKIP action: await run("set $items[1].label to '
))
)
;; ── reactive-properties (4 tests) ──
(defsuite "hs-upstream-reactive-properties"
(deftest "setting a property on a plain object triggers reactivity"
(hs-cleanup!)
(let ((_el-output (dom-create-element "output")))
(dom-set-attr _el-output "_" "when ($obj's x + $obj's y) changes put it into me")
(dom-append (dom-body) _el-output)
(hs-activate! _el-output)
;; SKIP action: await run("set $obj to {x: 1, y: 2}"
;; SKIP action: await run("set $obj'
;; SKIP check: skip toHaveText('3') None None
))
(deftest "nested property chain triggers on intermediate reassignment"
(hs-cleanup!)
(let ((_el-output (dom-create-element "output")))
(dom-set-attr _el-output "_" "when $data's inner's val changes put it into me")
(dom-append (dom-body) _el-output)
(hs-activate! _el-output)
;; SKIP action: await run("set $data to {inner: {val: '
;; SKIP action: await run("set $data'
;; SKIP check: skip toHaveText('hello') None None
))
(deftest "property change on DOM element triggers reactivity via setProperty"
(hs-cleanup!)
(let ((_el-prop-input (dom-create-element "input")) (_el-output (dom-create-element "output")))
(dom-set-attr _el-prop-input "id" "prop-input")
(dom-set-attr _el-prop-input "type" "text")
(dom-set-attr _el-prop-input "value" "start")
(dom-append (dom-body) _el-prop-input)
(dom-set-attr _el-output "_" "when #prop-input's value changes put it into me")
(dom-append (dom-body) _el-output)
(hs-activate! _el-output)
;; SKIP action: await run("set #prop-input'
;; SKIP check: skip toHaveText('start') None None
))
(deftest "live block tracks property reads on plain objects"
(hs-cleanup!)
(let ((_el-output (dom-create-element "output")))
(dom-set-attr _el-output "_" "live put $config's label into me")
(dom-append (dom-body) _el-output)
(hs-activate! _el-output)
;; SKIP action: await run("set $config to {label: '
;; SKIP action: await run("set $config'
))
)
;; ── resize (3 tests) ──
(defsuite "hs-upstream-resize"
(deftest "fires when element is resized"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "_" "on resize put detail.width into #out")
(dom-set-attr _el-box "style" "width:100px; height:100px")
(dom-append (dom-body) _el-box)
(hs-activate! _el-box)
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP check: skip toHaveText("200") None None
))
(deftest "provides height in detail"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "_" "on resize put detail.height into #out")
(dom-set-attr _el-box "style" "width:100px; height:100px")
(dom-append (dom-body) _el-box)
(hs-activate! _el-box)
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP check: skip toHaveText("300") None None
))
(deftest "works with from clause"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-set-attr _el-box "style" "width:100px; height:100px")
(dom-append (dom-body) _el-box)
(dom-set-attr _el-out "id" "out")
(dom-set-attr _el-out "_" "on resize from #box put detail.width into me")
(dom-append (dom-body) _el-out)
(hs-activate! _el-out)
;; SKIP check: skip toHaveText("150") None None
))
)
;; ── when (41 tests) ──
(defsuite "hs-upstream-when"
(deftest "provides access to `it` and syncs initial value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $global changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $global to '
;; SKIP action: await run("set $global to '
;; SKIP action: await run("set $global to 42"
;; SKIP check: skip toHaveText('initial'); toHaveText('hello world'); toHaveText None None
))
(deftest "detects changes from $global variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $global changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $global to '
;; SKIP check: skip toHaveText('Changed!') None None
))
(deftest "detects changes from :element variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "init set :count to 0 end when :count changes put it into me end on click increment :count")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: find('div').click()
;; SKIP action: find('div').click()
;; SKIP check: skip toHaveText('0'); toHaveText('1'); toHaveText('2') None None
))
(deftest "triggers multiple elements watching same variable"
(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 "_" "when $shared changes put 'first' into me")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "_" "when $shared changes put 'second' into me")
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d2)
;; SKIP action: await run("set $shared to '
;; SKIP check: skip toHaveText('first'); toHaveText('second') None None
))
(deftest "executes multiple commands"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $multi changes put 'first' into me then add .executed to me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $multi to '
;; SKIP check: skip toHaveText('first'); toHaveClass(/executed/) None None
))
(deftest "does not execute when variable is undefined initially"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $neverSet changes put 'synced' into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toHaveText('original') None None
))
(deftest "only triggers when variable actually changes value"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $dedup changes increment :callCount then put :callCount into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $dedup to '
;; SKIP action: await run("set $dedup to '
;; SKIP action: await run("set $dedup to '
;; SKIP check: skip toHaveText('1'); toHaveText('1'); toHaveText('2') None None
))
(deftest "auto-tracks compound expressions"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when ($a + $b) changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $a to 1"
;; SKIP action: await run("set $b to 2"
;; SKIP action: await run("set $a to 10"
;; SKIP action: await run("set $b to 20"
;; SKIP check: skip toHaveText('3'); toHaveText('12'); toHaveText('30') None None
))
(deftest "detects attribute changes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when @data-title changes put it into me")
(dom-set-attr _el-div "data-title" "original")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toHaveText('original') None None
))
(deftest "detects form input value changes via user interaction"
(hs-cleanup!)
(let ((_el-reactive-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-reactive-input "id" "reactive-input")
(dom-set-attr _el-reactive-input "type" "text")
(dom-set-attr _el-reactive-input "value" "start")
(dom-append (dom-body) _el-reactive-input)
(dom-set-attr _el-span "_" "when #reactive-input.value changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: evaluate({...})
;; SKIP check: skip toHaveText('start'); toHaveText('typed') None None
))
(deftest "detects property change via hyperscript set"
(hs-cleanup!)
(let ((_el-prog-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-prog-input "id" "prog-input")
(dom-set-attr _el-prog-input "type" "text")
(dom-set-attr _el-prog-input "value" "initial")
(dom-append (dom-body) _el-prog-input)
(dom-set-attr _el-span "_" "when #prog-input.value changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: await run("set #prog-input.value to '
;; SKIP check: skip toHaveText('initial') None None
))
(deftest "disposes effect when element is removed from DOM"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $dispose changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $dispose to '
;; SKIP action: await run("set $dispose to '
;; SKIP check: skip toHaveText('before'); toBe('before') None None
))
(deftest "batches multiple synchronous writes into one effect run"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when ($batchA + $batchB) changes increment :runCount then put :runCount into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $batchA to 0"
;; SKIP action: await run("set $batchB to 0"
;; SKIP check: skip toHaveText('1'); toHaveText('2') None None
))
(deftest "handles chained reactivity across elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-output (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $source changes set $derived to (it * 2)")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
(dom-set-attr _el-output "id" "output")
(dom-set-attr _el-output "_" "when $derived changes put it into me")
(dom-append (dom-body) _el-output)
(hs-activate! _el-output)
;; SKIP action: await run("set $source to 5"
;; SKIP action: await run("set $source to 20"
;; SKIP check: skip toHaveText('10'); toHaveText('40') None None
))
(deftest "supports multiple when features on the same element"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $left changes put it into my @data-left end when $right changes put it into my @data-right")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $left to '
;; SKIP action: await run("set $right to '
;; SKIP action: await run("set $left to '
;; SKIP check: skip toHaveAttribute('data-left', 'L'); toHaveAttribute('data-rig None None
))
(deftest "works with on handlers that modify the watched variable"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "init set :label to 'initial' end when :label changes put it into me end on click set :label to 'clicked'")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: find('div').click()
;; SKIP check: skip toHaveText('initial'); toHaveText('clicked') None None
))
(deftest "does not cross-trigger on unrelated variable writes"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $trigger changes increment :count put :count into me set $other to 'side-effect'")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $trigger to '
;; SKIP check: skip toHaveText('1'); toHaveText('1') None None
))
(deftest "handles rapid successive changes correctly"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $rapid changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $rapid to "
;; SKIP check: skip toHaveText('9') None None
))
(deftest "isolates element-scoped variables between elements"
(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 "_" "init set :value to 'A' end when :value changes put it into me end on click set :value to 'A-clicked'")
(dom-append (dom-body) _el-d1)
(hs-activate! _el-d1)
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "_" "init set :value to 'B' end when :value changes put it into me end on click set :value to 'B-clicked'")
(dom-append (dom-body) _el-d2)
(hs-activate! _el-d2)
;; SKIP action: find('#d1').click()
;; SKIP action: find('#d2').click()
;; SKIP check: skip toHaveText('A'); toHaveText('B'); toHaveText('A-clicked'); t None None
))
(deftest "handles NaN without infinite re-firing"
(hs-cleanup!)
(let ((_el-nan-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-nan-input "id" "nan-input")
(dom-set-attr _el-nan-input "type" "text")
(dom-set-attr _el-nan-input "value" "not a number")
(dom-append (dom-body) _el-nan-input)
(dom-set-attr _el-span "_" "when (#nan-input.value * 1) changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: evaluate({...})
;; SKIP check: skip toHaveText('NaN'); toHaveText('NaN') None None
))
(deftest "fires when either expression changes using or"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $x or $y changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $x to '
;; SKIP action: await run("set $y to '
;; SKIP check: skip toHaveText('from-x'); toHaveText('from-y') None None
))
(deftest "supports three or more expressions with or"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $r or $g or $b changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $r to '
;; SKIP action: await run("set $g to '
;; SKIP action: await run("set $b to '
;; SKIP check: skip toHaveText('red'); toHaveText('green'); toHaveText('blue') None None
))
(deftest "#element.checked is tracked"
(hs-cleanup!)
(let ((_el-cb-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-cb-input "id" "cb-input")
(dom-set-attr _el-cb-input "type" "checkbox")
(dom-append (dom-body) _el-cb-input)
(dom-set-attr _el-span "_" "when #cb-input.checked changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP check: skip toHaveText('false') None None
))
(deftest "my @attr is tracked"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when my @data-x changes put it into me")
(dom-set-attr _el-div "data-x" "one")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "value of #element is tracked"
(hs-cleanup!)
(let ((_el-of-input (dom-create-element "input")) (_el-span (dom-create-element "span")))
(dom-set-attr _el-of-input "id" "of-input")
(dom-set-attr _el-of-input "type" "text")
(dom-set-attr _el-of-input "value" "init")
(dom-append (dom-body) _el-of-input)
(dom-set-attr _el-span "_" "when (value of #of-input) changes put it into me")
(dom-append (dom-body) _el-span)
(hs-activate! _el-span)
;; SKIP action: find('#of-input').fill('changed')
;; SKIP check: skip toHaveText('init') None None
))
(deftest "math on tracked symbols works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when ($mA * $mB) changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $mA to 3"
;; SKIP action: await run("set $mB to 4"
;; SKIP action: await run("set $mA to 10"
;; SKIP check: skip toHaveText('12') None None
))
(deftest "comparison on tracked symbol works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when ($cmpVal > 5) changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $cmpVal to 3"
;; SKIP action: await run("set $cmpVal to 10"
;; SKIP check: skip toHaveText('false') None None
))
(deftest "string template with tracked symbol works"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when `hello ${$tplName}` changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $tplName to '
;; SKIP action: await run("set $tplName to '
;; SKIP check: skip toHaveText('hello world') None None
))
(deftest "function call on tracked value works (Math.round)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when (Math.round($rawNum)) changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $rawNum to 3.7"
;; SKIP action: await run("set $rawNum to 9.2"
))
(deftest "inline style change via JS is NOT detected"
(hs-cleanup!)
(let ((_el-style-target (dom-create-element "div")))
(dom-set-attr _el-style-target "id" "style-target")
(dom-set-attr _el-style-target "_" "when (*opacity) changes put it into me")
(dom-set-attr _el-style-target "style" "opacity: 1")
(dom-append (dom-body) _el-style-target)
(hs-activate! _el-style-target)
))
(deftest "reassigning whole array IS detected"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $arrWhole changes put it.join(',') into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $arrWhole to [1, 2, 3]"
;; SKIP action: await run("set $arrWhole to [4, 5, 6]"
))
(deftest "mutating array element in place is NOT detected"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when $arrMut[0] changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $arrMut to [1, 2, 3]"
))
(deftest "local variable in when expression produces a parse error"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "attribute observers are persistent (not recreated on re-run)"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "boolean short-circuit does not track unread branch"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "when ($x and $y) changes put it into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $x to false"
;; SKIP action: await run("set $y to '
;; SKIP action: await run("set $y to '
))
(deftest "diamond: cascaded derived values produce correct final value"
(hs-cleanup!)
(let ((_el-d-b (dom-create-element "span")) (_el-d-c (dom-create-element "span")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-d-b "id" "d-b")
(dom-set-attr _el-d-b "_" "when $a changes set $b to (it * 2)")
(dom-append (dom-body) _el-d-b)
(hs-activate! _el-d-b)
(dom-set-attr _el-d-c "id" "d-c")
(dom-set-attr _el-d-c "_" "when $a changes set $c to (it * 3)")
(dom-append (dom-body) _el-d-c)
(hs-activate! _el-d-c)
(dom-set-attr _el-div "_" "live increment :runs then put ($ (runs:)' into me")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: await run("set $a to 1"
;; SKIP action: await run("set $a to 10"
))
(deftest "error in one effect does not break other effects in the same batch"
(hs-cleanup!)
(let ((_el-err-a (dom-create-element "span")))
(dom-set-attr _el-err-a "id" "err-a")
(dom-set-attr _el-err-a "_" "when $trigger changes put null.boom into me")
(dom-append (dom-body) _el-err-a)
(hs-activate! _el-err-a)
;; SKIP action: await run("set $trigger to 0"
;; SKIP action: await run("set $trigger to 42"
;; SKIP check: skip toHaveText('ok:42') None None
))
(deftest "circular guard resets after cascade settles"
(hs-cleanup!)
(let ((_el-span (dom-create-element "span")))
(dom-set-attr _el-span "_" "when $ping changes set $ping to (i
new"
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveText("new"); toBe("SPAN") None None
))
(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-append (dom-body) _el-target)
;; HS source contains quotes: on click make a
then put "moved" into it th
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toBe("moved") None None
))
(deftest "set .class replaces all matching elements"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-list "id" "list")
(dom-append (dom-body) _el-list)
;; HS source contains quotes: on click set .item to "
replaced"
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toBe(3); toEqual(["replaced", "replaced", "replaced"]) None None
))
(deftest "set
replaces all matching elements"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
;; HS source contains quotes: on click set
in #box to "
done
"
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toEqual(["done", "done"]) None None
))
(deftest "set closest replaces ancestor"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-add-class _el-div "wrapper")
(dom-append (dom-body) _el-div)
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveText("replaced") None None
))
(deftest "hyperscript in replacement content is initialized"
(hs-cleanup!)
(let ((_el-target (dom-create-element "div")) (_el-target1 (dom-create-element "button")))
(dom-set-attr _el-target "id" "target")
(dom-append (dom-body) _el-target)
(dom-set-attr _el-target1 "id" "target")
;; HS source contains quotes: "on
;; SKIP action: find('#go').dispatchEvent('click')
;; SKIP action: find('#target').dispatchEvent('click')
;; SKIP check: skip toHaveText("new"); toHaveText("clicked") None None
))
(deftest "swap #a with #b swaps DOM positions"
(hs-cleanup!)
(let ((_el-container (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-container "id" "container")
(dom-append (dom-body) _el-container)
(dom-set-attr _el-button "_" "on click swap #a with #b")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toEqual(["B", "A"]) None None
))
(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-append (dom-body) _el-target)
;; HS source contains quotes: on click put "new" into #target
;; SKIP action: find('button').dispatchEvent('click')
;; SKIP check: skip toHaveText("new") None None
))
)
;; ── collectionExpressions (22 tests) ──
(defsuite "hs-upstream-collectionExpressions"
(deftest "filters an array by condition"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "filters with comparison"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "works with DOM elements"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-list "id" "list")
(dom-append (dom-body) _el-list)
(dom-set-attr _el-button "_" "on click set items to
in #list then set matches to items where it matches .yes then put matches mapped to its textContent into #out")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP action: find('button').click()
;; SKIP check: skip toHaveText("AC") None None
))
(deftest "sorts by a property"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "sorts descending"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "sorts numbers by a computed key"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "maps to a property"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "maps with an expression"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "where then mapped to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "sorted by then mapped to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "where then sorted by then mapped to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "the result inside where refers to previous command result, not current element"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "where binds after in without parens"
(hs-cleanup!)
(let ((_el-container (dom-create-element "div")))
(dom-set-attr _el-container "id" "container")
(dom-append (dom-body) _el-container)
;; SKIP action: await run("
in #container where it matches .a"
;; SKIP check: skip toBe(2) None None
))
(deftest "sorted by binds after in without parens"
(hs-cleanup!)
(let ((_el-list (dom-create-element "ul")))
(dom-set-attr _el-list "id" "list")
(dom-append (dom-body) _el-list)
;; SKIP action: await run("
in #list where its textContent is not '
;; SKIP check: skip toBe(2) None None
))
(deftest "where binds after property access"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "where after in with mapped to"
(hs-cleanup!)
(let ((_el-items (dom-create-element "ul")))
(dom-set-attr _el-items "id" "items")
(dom-append (dom-body) _el-items)
;; SKIP action: await run(
"
in #items where it matches .yes mapped
;; SKIP check: skip toEqual(["A", "C"]) None None
))
(deftest "where binds after in on closest"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-b2 (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
;; HS source contains quotes: "on
(dom-set-attr _el-b2 "id" "b2")
;; HS source contains quotes: "on
;; SKIP action: find('#b2').click()
;; SKIP check: skip toHaveText("2") None None
))
(deftest "where in init followed by on feature"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
;; HS source contains quotes: "set
;; SKIP action: find('button').click()
;; SKIP check: skip toHaveText("1") None None
))
(deftest "where in component init followed by on feature"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-template (dom-create-element "template")) (_el-test-where-comp (dom-create-element "test-where-comp")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
(dom-set-attr _el-template "_" "set :items to
in #box where it matches .a on click put :items.length into me")
(dom-set-attr _el-template "component" "test-where-comp")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-where-comp)
;; SKIP action: find('test-where-comp').click()
;; SKIP check: skip toHaveText("1") None None
))
(deftest "where with is not me in component template"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
;; SKIP action: find('test-where-me input').click()
))
(deftest "where with is not me followed by on feature"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")))
(dom-append (dom-body) _el-table)
;; SKIP action: find('#master').click()
))
(deftest "full select-all pattern with multiple on features"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")))
(dom-append (dom-body) _el-table)
;; SKIP action: find('#master').click()
;; SKIP check: skip toBe(true) None None
))
)
;; ── splitJoin (7 tests) ──
(defsuite "hs-upstream-splitJoin"
(deftest "splits a string by delimiter"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "splits by whitespace"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "joins an array with delimiter"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "joins with empty string"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "split then where then joined"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "split then sorted then joined"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "split then mapped then joined"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── component (19 tests) ──
(defsuite "hs-upstream-component"
(deftest "registers a custom element from a template"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-hello (dom-create-element "test-hello")))
(dom-set-attr _el-template "component" "test-hello")
(dom-append (dom-body) _el-template)
(dom-append (dom-body) _el-test-hello)
))
(deftest "renders template expressions"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-greet (dom-create-element "test-greet")))
(dom-set-attr _el-template "component" "test-greet")
(dom-append (dom-body) _el-template)
(dom-append (dom-body) _el-test-greet)
;; SKIP action: await run("set $name to '
))
(deftest "applies _ hyperscript to component instance"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-init (dom-create-element "test-init")))
(dom-set-attr _el-template "_" "init set ^msg to 'initialized'")
(dom-set-attr _el-template "component" "test-init")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-init)
))
(deftest "processes _ on inner elements"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-inner (dom-create-element "test-inner")))
(dom-set-attr _el-template "_" "init set ^count to 0")
(dom-set-attr _el-template "component" "test-inner")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-inner)
;; SKIP action: find('test-inner button').click()
))
(deftest "reactively updates template expressions"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-reactive (dom-create-element "test-reactive")))
(dom-set-attr _el-template "_" "init set ^count to 0")
(dom-set-attr _el-template "component" "test-reactive")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-reactive)
;; SKIP action: find('test-reactive button').click()
))
(deftest "supports multiple independent instances"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-a (dom-create-element "test-multi")) (_el-b (dom-create-element "test-multi")))
(dom-set-attr _el-template "_" "init set ^count to 0")
(dom-set-attr _el-template "component" "test-multi")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-a "id" "a")
(dom-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
;; SKIP action: find('#a button').click()
))
(deftest "reads attributes via @"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-attrs (dom-create-element "test-attrs")))
(dom-set-attr _el-template "_" "init set ^val to @data-start as Int")
(dom-set-attr _el-template "component" "test-attrs")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-test-attrs "data-start" "42")
(dom-append (dom-body) _el-test-attrs)
))
(deftest "supports #for loops in template"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-loop (dom-create-element "test-loop")))
(dom-set-attr _el-template "_" "init set ^items to ['a', 'b', 'c']")
(dom-set-attr _el-template "component" "test-loop")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-loop)
))
(deftest "supports #if conditionals in template"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-cond (dom-create-element "test-cond")))
(dom-set-attr _el-template "_" "init set ^show to true")
(dom-set-attr _el-template "component" "test-cond")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-cond)
))
(deftest "substitutes slot content into template"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-card (dom-create-element "test-card")))
(dom-set-attr _el-template "component" "test-card")
(dom-append (dom-body) _el-template)
(dom-append (dom-body) _el-test-card)
))
(deftest "blocks processing of inner hyperscript until render"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-block (dom-create-element "test-block")))
(dom-set-attr _el-template "_" "init set ^msg to 'ready'")
(dom-set-attr _el-template "component" "test-block")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-append (dom-body) _el-test-block)
;; SKIP action: find('test-block span').click()
))
(deftest "supports named slots"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-named-slot (dom-create-element "test-named-slot")))
(dom-set-attr _el-template "component" "test-named-slot")
(dom-append (dom-body) _el-template)
(dom-append (dom-body) _el-test-named-slot)
))
(deftest "does not process slotted _ attributes prematurely"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "init set ^x to 42")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP action: find('test-slot-hs span').click()
))
(deftest "slotted content resolves ^var from outer scope, not component scope"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "init set ^outer to 'from-outside'")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "component isolation prevents ^var leaking inward"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-set-attr _el-div "_" "init set ^leaked to 'should-not-see'")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
))
(deftest "bind keeps ^var in sync with attribute changes"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-bind-attr (dom-create-element "test-bind-attr")))
(dom-set-attr _el-template "_" "bind ^count to @data-count")
(dom-set-attr _el-template "component" "test-bind-attr")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-test-bind-attr "data-count" "5")
(dom-append (dom-body) _el-test-bind-attr)
))
(deftest "attrs evaluates attribute as hyperscript in parent scope"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-args (dom-create-element "test-args")))
(dom-set-attr _el-template "_" "init set ^list to attrs.items")
(dom-set-attr _el-template "component" "test-args")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-test-args "items" "$stuff")
(dom-append (dom-body) _el-test-args)
;; SKIP action: await run("set $stuff to ['
))
(deftest "attrs works with bind for reactive pass-through"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-args-bind (dom-create-element "test-args-bind")) (_el-button (dom-create-element "button")))
(dom-set-attr _el-template "_" "bind ^val to attrs.count")
(dom-set-attr _el-template "component" "test-args-bind")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-test-args-bind "count" "$count")
(dom-append (dom-body) _el-test-args-bind)
(dom-set-attr _el-button "_" "on click increment $count")
(dom-append (dom-body) _el-button)
(hs-activate! _el-button)
;; SKIP action: find('button').click()
;; SKIP action: await run("set $count to 10"
))
(deftest "attrs bind is bidirectional — inner changes flow outward"
(hs-cleanup!)
(let ((_el-template (dom-create-element "template")) (_el-test-args-bidir (dom-create-element "test-args-bidir")) (_el-p (dom-create-element "p")))
(dom-set-attr _el-template "_" "bind ^count to attrs.count")
(dom-set-attr _el-template "component" "test-args-bidir")
(dom-append (dom-body) _el-template)
(hs-activate! _el-template)
(dom-set-attr _el-test-args-bidir "count" "$count")
(dom-append (dom-body) _el-test-args-bidir)
(dom-set-attr _el-p "_" "live put $count into me")
(dom-append (dom-body) _el-p)
(hs-activate! _el-p)
;; SKIP action: find('test-args-bidir button').click()
;; SKIP action: await run("set $count to 10"
))
)
;; ── default (9 tests) ──
(defsuite "hs-upstream-default"
(deftest "can default possessive properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: "on
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("bar") None None
))
(deftest "can default of-expression properties"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: "on
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("bar") None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("yes") None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("existing") None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("0") None None
))
(deftest "default overwrites empty string"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
;; HS source contains quotes: on click set x to "" then default x to "fallback" then put x
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("fallback") None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("false") None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveCSS('background-color', 'rgb(255, 0, 0) None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveCSS('color', 'rgb(0, 0, 255) None None
))
)
;; ── js (1 tests) ──
(defsuite "hs-upstream-js"
(deftest "handles rejected promises without hanging"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
;; HS source contains quotes: on click js return Promise.reject("boom") end catch e put e
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("boom") None None
))
)
;; ── measure (2 tests) ──
(defsuite "hs-upstream-measure"
(deftest "can measure with possessive syntax"
(hs-cleanup!)
(let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-other "id" "other")
(dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
(dom-append (dom-body) _el-other)
(dom-set-attr _el-div "_" "on click measure #other's top then set window.measurement to {top: top}")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toBe(89) None None
))
(deftest "can measure with of syntax"
(hs-cleanup!)
(let ((_el-other (dom-create-element "div")) (_el-div (dom-create-element "div")))
(dom-set-attr _el-other "id" "other")
(dom-set-attr _el-other "style" "all: initial; position: fixed; top: 89px")
(dom-append (dom-body) _el-other)
(dom-set-attr _el-div "_" "on click measure top of #other then set window.measurement to {top: top}")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toBe(89) None None
))
)
;; ── pick (7 tests) ──
(defsuite "hs-upstream-pick"
(deftest "does not hang on zero-length regex matches"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick first n items"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick last n items"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick random item"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick random n items"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick items using 'of' syntax"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can pick match using 'of' syntax"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── settle (1 tests) ──
(defsuite "hs-upstream-settle"
(deftest "can settle a collection of elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-div1 (dom-create-element "div")) (_el-trigger (dom-create-element "div")))
(dom-add-class _el-div "item")
(dom-append (dom-body) _el-div)
(dom-add-class _el-div1 "item")
(dom-append (dom-body) _el-div1)
(dom-set-attr _el-trigger "id" "trigger")
(dom-set-attr _el-trigger "_" "on click settle <.item/> then add .done to <.item/>")
(dom-append (dom-body) _el-trigger)
(hs-activate! _el-trigger)
;; SKIP action: find('#trigger').dispatchEvent('click')
))
)
;; ── show (2 tests) ──
(defsuite "hs-upstream-show"
(deftest "the result in a when clause refers to previous command result, not element being tested"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-s1 (dom-create-element "span")) (_el-s2 (dom-create-element "span")))
;; HS source contains quotes: "on
(dom-set-attr _el-s1 "id" "s1")
(dom-set-attr _el-s1 "style" "display:none")
(dom-append (dom-body) _el-s1)
(dom-set-attr _el-s2 "id" "s2")
(dom-set-attr _el-s2 "style" "display:none")
(dom-append (dom-body) _el-s2)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toBeVisible(); toBeVisible() None None
))
(deftest "the result after show...when is the matched elements"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")) (_el-p (dom-create-element "p")) (_el-p2 (dom-create-element "p")) (_el-out (dom-create-element "span")))
;; HS source contains quotes: "on
(dom-set-attr _el-p "style" "display:none")
(dom-append (dom-body) _el-p)
(dom-set-attr _el-p2 "style" "display:none")
(dom-append (dom-body) _el-p2)
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("some") None None
))
)
;; ── socket (4 tests) ──
(defsuite "hs-upstream-socket"
(deftest "parses socket with absolute ws:// URL"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts relative URL to wss:// on https pages"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts relative URL to ws:// on http pages"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "namespaced sockets work"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── bootstrap (14 tests) ──
(defsuite "hs-upstream-bootstrap"
(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-append (dom-body) _el-bar)
(dom-set-attr _el-div "_" "on click add .foo to #bar then add .blah")
(dom-append (dom-body) _el-div)
(hs-activate! _el-div)
;; SKIP check: skip toHaveClass(/foo/); toHaveClass(/blah/) None None
))
(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)
;; SKIP check: skip toBe(true); toBe(true); toBe(true) None None
))
(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)
;; SKIP action: evaluate({...})
(dom-dispatch _el-div "click" nil)
;; SKIP check: skip toBe(1) None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP action: evaluate({...})
;; SKIP check: skip toHaveClass(/foo/); toHaveClass(/bar/) None None
))
(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)
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP action: evaluate({...})
(dom-dispatch _el-div "click" nil)
;; SKIP check: skip toHaveClass(/foo/); toHaveClass(/foo/) None None
))
(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-append (dom-body) _el-source)
(dom-set-attr _el-target "id" "target")
(dom-set-attr _el-target "_" "on click from #source add .foo")
(dom-append (dom-body) _el-target)
(hs-activate! _el-target)
;; SKIP action: find('#source').dispatchEvent('click')
;; SKIP action: evaluate({...})
(dom-dispatch (dom-query-by-id "source") "click" nil)
;; SKIP check: skip toHaveClass(/foo/); toBe(true) None None
))
(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)
;; SKIP check: skip toBe(true) None None
))
(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)
;; SKIP check: skip toBe(false) None None
))
(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)
;; SKIP check: skip toHaveAttribute('data-hyperscript-powered', 'true') None None
))
(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)
;; SKIP check: skip toHaveAttribute('data-hyperscript-powered', 'true') None None
))
(deftest "fires hyperscript:before:init and hyperscript:after:init"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "hyperscript:before:init can cancel initialization"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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)
;; SKIP check: skip toEqual(['before:cleanup', 'after:cleanup']) None None
))
(deftest "logAll config logs events to console"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── parser (7 tests) ──
(defsuite "hs-upstream-parser"
(deftest "can have comments in attributes (triple dash)"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
;; HS source contains quotes: on click put "clicked" into my.innerHTML ---put some content
;; SKIP action: find('div').dispatchEvent('click')
;; SKIP check: skip toHaveText("clicked") None None
))
(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")
;; HS source contains quotes: on click blargh end on mouseenter put "hovered" into my.inne
;; SKIP check: skip toBe(false) None None
))
(deftest "recovers across multiple feature errors"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click blargh end on mouseenter also_bad end on focus put
;; SKIP check: skip toBe(false) None None
))
(deftest "fires hyperscript:parse-error event with all errors"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "element-level isolation still works with error recovery"
(hs-cleanup!)
(let ((_el-div (dom-create-element "div")))
(dom-append (dom-body) _el-div)
;; SKIP action: find('#d2').dispatchEvent('click')
;; SKIP check: skip toHaveText("clicked") None None
))
(deftest "_hyperscript() evaluate API still throws on first error"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "parse error at EOF on trailing newline does not crash"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── scoping (1 tests) ──
(defsuite "hs-upstream-scoping"
(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)
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveAttribute('out', '10') None None
))
)
;; ── asExpression (17 tests) ──
(defsuite "hs-upstream-asExpression"
(deftest "converts value as Boolean"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can use the a modifier if you like"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "parses string as JSON to object"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts value as JSONString"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "pipe operator chains conversions"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "can use the an modifier if you'd like"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "collects duplicate text inputs into an array"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts multiple selects with programmatically changed selections"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts a form element into Values | JSONString"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts a form element into Values | FormEncoded"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts array as Set"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts object as Map"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts object as Keys"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts object as Entries"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts array as Reversed"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts array as Unique"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "converts nested array as Flat"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── attributeRef (1 tests) ──
(defsuite "hs-upstream-attributeRef"
(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")
;; HS source contains quotes: on click set my @data-foo to "blue"
;; SKIP action: find('#arDiv').dispatchEvent('click')
;; SKIP check: skip toBe("blue") None None
))
)
;; ── closest (3 tests) ──
(defsuite "hs-upstream-closest"
(deftest "attributes can be set via the closest expression 2"
(hs-cleanup!)
(let ((_el-outerDiv2 (dom-create-element "div")))
(dom-set-attr _el-outerDiv2 "id" "outerDiv2")
(dom-set-attr _el-outerDiv2 "foo" "bar")
(dom-append (dom-body) _el-outerDiv2)
;; SKIP action: find('#d1b').dispatchEvent('click')
;; SKIP check: skip toBe("bar") None None
))
(deftest "closest does not consume a following where clause"
(hs-cleanup!)
(let ((_el-table (dom-create-element "table")))
(dom-append (dom-body) _el-table)
;; SKIP action: find('#master').click()
;; SKIP check: skip toHaveText("2") None None
))
(deftest "closest with to modifier still works after parse change"
(hs-cleanup!)
(let ((_el-outer (dom-create-element "div")))
(dom-set-attr _el-outer "id" "outer")
(dom-append (dom-body) _el-outer)
;; SKIP check: skip toBe(true) None None
))
)
;; ── comparisonOperator (40 tests) ──
(defsuite "hs-upstream-comparisonOperator"
(deftest "is a works with instanceof fallback"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click if I am a Element put "yes" into me
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("yes") None None
))
(deftest "is a Node works via instanceof"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click if I am a Node put "yes" into me
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("yes") None None
))
(deftest "is not a works with instanceof fallback"
(hs-cleanup!)
(let ((_el-d1 (dom-create-element "div")))
(dom-set-attr _el-d1 "id" "d1")
;; HS source contains quotes: on click if "hello" is not a Element put "yes" into me
;; SKIP action: find('#d1').dispatchEvent('click')
;; SKIP check: skip toHaveText("yes") None None
))
(deftest "is ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "contains ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "matches ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "starts with works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "ends with works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "does not start with works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "does not end with works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "starts with null is false"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "ends with null is false"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "starts with ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "ends with ignoring case works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "starts with coerces to string"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "ends with coerces to string"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is between works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not between works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "between works with strings"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "I am between works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "I am not between works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
(dom-set-attr _el-c "id" "c")
(dom-append (dom-body) _el-c)
))
(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-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
(dom-set-attr _el-c "id" "c")
(dom-append (dom-body) _el-c)
))
(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-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
))
(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-append (dom-body) _el-a)
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
))
(deftest "precedes with null is false"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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")
;; HS source contains quotes: "on
(dom-set-attr _el-b "id" "b")
(dom-append (dom-body) _el-b)
;; SKIP action: find('#a').dispatchEvent('click')
;; SKIP check: skip toHaveText("yes") None None
))
(deftest "is really works without equal to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not really works without equal to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is equal works without to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not equal works without to"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "am works as alias for is"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not undefined still works as equality"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "is not null still works as equality"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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-append (dom-body) _el-c1)
(dom-set-attr _el-c2 "id" "c2")
(dom-set-attr _el-c2 "type" "checkbox")
(dom-append (dom-body) _el-c2)
;; SKIP action: await run("#c1 is checked"
;; SKIP action: await run("#c2 is checked"
))
(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-append (dom-body) _el-c1)
(dom-set-attr _el-c2 "id" "c2")
(dom-set-attr _el-c2 "type" "checkbox")
(dom-append (dom-body) _el-c2)
;; SKIP action: await run("#c1 is not checked"
;; SKIP action: await run("#c2 is not checked"
))
(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-append (dom-body) _el-b1)
(dom-set-attr _el-b2 "id" "b2")
(dom-append (dom-body) _el-b2)
;; SKIP action: await run("#b1 is disabled"
;; SKIP action: await run("#b2 is disabled"
;; SKIP action: await run("#b2 is not disabled"
))
(deftest "is still does equality when rhs variable exists"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(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-append (dom-body) _el-input)
(dom-add-class _el-input1 "cb")
(dom-set-attr _el-input1 "type" "checkbox")
(dom-append (dom-body) _el-input1)
(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-input2)
;; SKIP action: await run(".cb where it is checked"
;; SKIP check: skip toBe(2) None None
))
)
;; ── cookies (1 tests) ──
(defsuite "hs-upstream-cookies"
(deftest "length is 0 when no cookies are set"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── in (1 tests) ──
(defsuite "hs-upstream-in"
(deftest "null value in array returns empty"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── logicalOperator (3 tests) ──
(defsuite "hs-upstream-logicalOperator"
(deftest "and short-circuits when lhs promise resolves to false"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "or short-circuits when lhs promise resolves to true"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "or evaluates rhs when lhs promise resolves to false"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── mathOperator (5 tests) ──
(defsuite "hs-upstream-mathOperator"
(deftest "array + array concats"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "array + single value appends"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "array + array does not mutate original"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "array concat chains"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "empty array + array works"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── no (5 tests) ──
(defsuite "hs-upstream-no"
(deftest "no returns false for non-empty array"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "no with where filters then checks emptiness"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "no with where returns false when matches exist"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "no with where and is not"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
(deftest "no with where on DOM elements"
(hs-cleanup!)
(let ((_el-box (dom-create-element "div")) (_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
(dom-set-attr _el-box "id" "box")
(dom-append (dom-body) _el-box)
;; HS source contains quotes: "on
(dom-set-attr _el-out "id" "out")
(dom-append (dom-body) _el-out)
;; SKIP action: find('button').click()
;; SKIP check: skip toHaveText("none") None None
))
)
;; ── objectLiteral (1 tests) ──
(defsuite "hs-upstream-objectLiteral"
(deftest "allows trailing commas"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)
;; ── queryRef (1 tests) ──
(defsuite "hs-upstream-queryRef"
(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-append (dom-body) _el-div)
(dom-add-class _el-div1 "c2")
(dom-set-attr _el-div1 "foo" "bar")
(dom-append (dom-body) _el-div1)
(dom-add-class _el-div2 "c3")
(dom-append (dom-body) _el-div2)
;; SKIP check: skip toBe(1) None None
))
)
;; ── relativePositionalExpression (4 tests) ──
(defsuite "hs-upstream-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-append (dom-body) _el-d1)
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d2)
;; SKIP check: skip toBe('hello') None None
))
(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-append (dom-body) _el-d1)
(dom-set-attr _el-d2 "id" "d2")
(dom-append (dom-body) _el-d2)
;; SKIP check: skip toBe('world') None None
))
(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-append (dom-body) _el-d1)
(dom-set-attr _el-d2 "id" "d2")
(dom-set-attr _el-d2 "style" "color: red")
(dom-append (dom-body) _el-d2)
;; SKIP check: skip toBe('red') None None
))
(deftest "can write to next element with put command"
(error "NOT IMPLEMENTED: test HTML could not be parsed into SX"))
)