HS parser/compiler/mock: fix 31 test failures across 7 issues
Parser: - Relax (number? v) to v in parse-one-transition so (expr)unit works - Add (match-kw "then") before parse-cmd-list in parse-for-cmd - Handle "indexed by" syntax alongside "index" in for loops - Add "indexed" to hs-keywords to prevent unit-suffix consumption Compiler: - Use map-indexed instead of for-each for indexed for-loops Test generator: - Preserve \" escapes in process_hs_val via placeholder/restore Mock DOM: - Coerce insertAdjacentHTML values via dom_stringify (match browser) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1985,9 +1985,10 @@ let run_spec_tests env test_files =
|
|||||||
Hashtbl.replace r "right" (Number 100.0); Hashtbl.replace r "bottom" (Number 100.0);
|
Hashtbl.replace r "right" (Number 100.0); Hashtbl.replace r "bottom" (Number 100.0);
|
||||||
Dict r
|
Dict r
|
||||||
| "insertAdjacentHTML" ->
|
| "insertAdjacentHTML" ->
|
||||||
(* Simplified: just append text to innerHTML *)
|
(* Simplified: coerce value to string and append to innerHTML *)
|
||||||
(match rest with
|
(match rest with
|
||||||
| [String _pos; String html] ->
|
| [String _pos; value] ->
|
||||||
|
let html = match dom_stringify value with String s -> s | _ -> "" in
|
||||||
let cur = match Hashtbl.find_opt d "innerHTML" with Some (String s) -> s | _ -> "" in
|
let cur = match Hashtbl.find_opt d "innerHTML" with Some (String s) -> s | _ -> "" in
|
||||||
Hashtbl.replace d "innerHTML" (String (cur ^ html)); Nil
|
Hashtbl.replace d "innerHTML" (String (cur ^ html)); Nil
|
||||||
| _ -> Nil)
|
| _ -> Nil)
|
||||||
|
|||||||
@@ -267,10 +267,10 @@
|
|||||||
(if
|
(if
|
||||||
(and (> (len ast) 4) (= (nth ast 4) :index))
|
(and (> (len ast) 4) (= (nth ast 4) :index))
|
||||||
(list
|
(list
|
||||||
(quote for-each)
|
(quote map-indexed)
|
||||||
(list
|
(list
|
||||||
(quote fn)
|
(quote fn)
|
||||||
(list (make-symbol var-name) (make-symbol (nth ast 5)))
|
(list (make-symbol (nth ast 5)) (make-symbol var-name))
|
||||||
body)
|
body)
|
||||||
collection)
|
collection)
|
||||||
(list
|
(list
|
||||||
|
|||||||
@@ -1223,10 +1223,10 @@
|
|||||||
(let
|
(let
|
||||||
((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value")))))
|
((prop (cond ((= (tp-type) "style") (get (adv!) "value")) ((= (tp-val) "my") (do (adv!) (if (= (tp-type) "style") (get (adv!) "value") (get (adv!) "value")))) (true (get (adv!) "value")))))
|
||||||
(let
|
(let
|
||||||
((from-val (if (match-kw "from") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil)))
|
((from-val (if (match-kw "from") (let ((v (parse-atom))) (if (and v (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil)))
|
||||||
(expect-kw! "to")
|
(expect-kw! "to")
|
||||||
(let
|
(let
|
||||||
((value (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v))))
|
((value (let ((v (parse-atom))) (if (and v (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v))))
|
||||||
(let
|
(let
|
||||||
((dur (if (match-kw "over") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil)))
|
((dur (if (match-kw "over") (let ((v (parse-atom))) (if (and (number? v) (= (tp-type) "ident") (not (hs-keyword? (tp-val)))) (let ((unit (get (adv!) "value"))) (list (quote string-postfix) v unit)) v)) nil)))
|
||||||
(let
|
(let
|
||||||
@@ -1521,9 +1521,9 @@
|
|||||||
(let
|
(let
|
||||||
((collection (parse-expr)))
|
((collection (parse-expr)))
|
||||||
(let
|
(let
|
||||||
((idx (if (match-kw "index") (let ((iname (tp-val))) (adv!) iname) nil)))
|
((idx (cond ((match-kw "index") (let ((iname (tp-val))) (adv!) iname)) ((match-kw "indexed") (do (match-kw "by") (let ((iname (tp-val))) (adv!) iname))) (true nil))))
|
||||||
(let
|
(let
|
||||||
((body (parse-cmd-list)))
|
((body (do (match-kw "then") (parse-cmd-list))))
|
||||||
(match-kw "end")
|
(match-kw "end")
|
||||||
(if
|
(if
|
||||||
idx
|
idx
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
"detail"
|
"detail"
|
||||||
"sender"
|
"sender"
|
||||||
"index"
|
"index"
|
||||||
|
"indexed"
|
||||||
"increment"
|
"increment"
|
||||||
"decrement"
|
"decrement"
|
||||||
"append"
|
"append"
|
||||||
|
|||||||
@@ -638,10 +638,11 @@
|
|||||||
(deftest "can toggle between two attribute values"
|
(deftest "can toggle between two attribute values"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-div "data-state" "active")
|
(dom-set-attr _el-div "data-state" "active")
|
||||||
;; SKIP attr [@data-state (contains special chars)
|
;; SKIP attr [@data-state (contains special chars)
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-get-attr _el-div "data-state") "inactive")
|
(assert= (dom-get-attr _el-div "data-state") "inactive")
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -650,11 +651,12 @@
|
|||||||
(deftest "can toggle between different attributes"
|
(deftest "can toggle between different attributes"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-div "enabled" "true")
|
(dom-set-attr _el-div "enabled" "true")
|
||||||
;; SKIP attr [@enabled (contains special chars)
|
;; SKIP attr [@enabled (contains special chars)
|
||||||
;; SKIP attr [@disabled (contains special chars)
|
;; SKIP attr [@disabled (contains special chars)
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-get-attr _el-div "disabled") "true")
|
(assert= (dom-get-attr _el-div "disabled") "true")
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -722,9 +724,10 @@
|
|||||||
(deftest "can toggle *display between two values"
|
(deftest "can toggle *display between two values"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-div "style" "display:none")
|
(dom-set-attr _el-div "style" "display:none")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-get-style _el-div "display") "flex")
|
(assert= (dom-get-style _el-div "display") "flex")
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -733,9 +736,10 @@
|
|||||||
(deftest "can toggle *opacity between three values"
|
(deftest "can toggle *opacity between three values"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-div "style" "opacity:0")
|
(dom-set-attr _el-div "style" "opacity:0")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-get-style _el-div "opacity") "0.5")
|
(assert= (dom-get-style _el-div "opacity") "0.5")
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -746,8 +750,9 @@
|
|||||||
(deftest "can toggle a global variable between two values"
|
(deftest "can toggle a global variable between two values"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -755,8 +760,9 @@
|
|||||||
(deftest "can toggle a global variable between three values"
|
(deftest "can toggle a global variable between three values"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -1351,7 +1357,7 @@
|
|||||||
(deftest "properly processes hyperscript in new content in a symbol write"
|
(deftest "properly processes hyperscript in new content in a symbol write"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" into me")
|
(dom-set-attr _el-div "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" into me")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -1362,7 +1368,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" into <div#d1/>")
|
(dom-set-attr _el-d1 "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" into <div#d1/>")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch _el-d1 "click" nil)
|
(dom-dispatch _el-d1 "click" nil)
|
||||||
@@ -1373,7 +1379,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" before me")
|
(dom-set-attr _el-d1 "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" before me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch _el-d1 "click" nil)
|
(dom-dispatch _el-d1 "click" nil)
|
||||||
@@ -1384,7 +1390,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" at the start of me")
|
(dom-set-attr _el-d1 "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" at the start of me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch _el-d1 "click" nil)
|
(dom-dispatch _el-d1 "click" nil)
|
||||||
@@ -1395,7 +1401,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" at the end of me")
|
(dom-set-attr _el-d1 "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" at the end of me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch _el-d1 "click" nil)
|
(dom-dispatch _el-d1 "click" nil)
|
||||||
@@ -1406,7 +1412,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click put \"<button id=\"b1\" _=\"on click put 42 into me\">40</button>\" after me")
|
(dom-set-attr _el-d1 "_" "on click put \"<button id=\\\"b1\\\" _=\\\"on click put 42 into me\\\">40</button>\" after me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch _el-d1 "click" nil)
|
(dom-dispatch _el-d1 "click" nil)
|
||||||
@@ -2047,7 +2053,7 @@
|
|||||||
(deftest "for loop over undefined skips without error"
|
(deftest "for loop over undefined skips without error"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click repeat for x in doesNotExist put x at end of me end put \"done\" into me")
|
(dom-set-attr _el-div "_" "on click repeat for x in doesNotExist put x at end of me end put \\\"done\\\" into me")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -2704,9 +2710,10 @@
|
|||||||
(deftest "can transition on query ref with of syntax"
|
(deftest "can transition on query ref with of syntax"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
|
(let ((_el-div (dom-create-element "div")) (_el-span (dom-create-element "span")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(dom-append (dom-body) _el-span)
|
(dom-append (dom-body) _el-span)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-get-style _el-span "width") "100px")
|
(assert= (dom-get-style _el-span "width") "100px")
|
||||||
))
|
))
|
||||||
@@ -2980,7 +2987,7 @@
|
|||||||
(deftest "throws on non-2xx response by default"
|
(deftest "throws on non-2xx response by default"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click fetch /test catch e put \"caught\" into me")
|
(dom-set-attr _el-div "_" "on click fetch /test catch e put \\\"caught\\\" into me")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -3016,8 +3023,9 @@
|
|||||||
(deftest "Response can be converted to JSON via as JSON"
|
(deftest "Response can be converted to JSON via as JSON"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-text-content _el-div) "Joe")
|
(assert= (dom-text-content _el-div) "Joe")
|
||||||
))
|
))
|
||||||
@@ -4184,7 +4192,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on myEvent(foo) if foo put foo into me else put \"no-detail\" into me")
|
(dom-set-attr _el-d1 "_" "on myEvent(foo) if foo put foo into me else put \\\"no-detail\\\" into me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
))
|
))
|
||||||
@@ -4203,7 +4211,7 @@
|
|||||||
(deftest "caught exceptions do not trigger 'exception' event"
|
(deftest "caught exceptions do not trigger 'exception' event"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")))
|
(let ((_el-button (dom-create-element "button")))
|
||||||
(dom-set-attr _el-button "_" "on click put \"foo\" into me then throw \"bar\" catch e log e on exception(error) put error into me")
|
(dom-set-attr _el-button "_" "on click put \\\"foo\\\" into me then throw \\\"bar\\\" catch e log e on exception(error) put error into me")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
(hs-activate! _el-button)
|
(hs-activate! _el-button)
|
||||||
(dom-dispatch _el-button "click" nil)
|
(dom-dispatch _el-button "click" nil)
|
||||||
@@ -4212,7 +4220,7 @@
|
|||||||
(deftest "rethrown exceptions trigger 'exception' event"
|
(deftest "rethrown exceptions trigger 'exception' event"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")))
|
(let ((_el-button (dom-create-element "button")))
|
||||||
(dom-set-attr _el-button "_" "on click put \"foo\" into me then throw \"bar\" catch e throw e on exception(error) put error into me")
|
(dom-set-attr _el-button "_" "on click put \\\"foo\\\" into me then throw \\\"bar\\\" catch e throw e on exception(error) put error into me")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
(hs-activate! _el-button)
|
(hs-activate! _el-button)
|
||||||
(dom-dispatch _el-button "click" nil)
|
(dom-dispatch _el-button "click" nil)
|
||||||
@@ -4221,7 +4229,7 @@
|
|||||||
(deftest "can ignore when target doesn\'t exist"
|
(deftest "can ignore when target doesn\'t exist"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click from #doesntExist then throw \"bar\" on click put \"clicked\" into me")
|
(dom-set-attr _el-div "_" "on click from #doesntExist then throw \\\"bar\\\" on click put \\\"clicked\\\" into me")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -4423,7 +4431,7 @@
|
|||||||
(deftest "prompts and puts result in it"
|
(deftest "prompts and puts result in it"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
||||||
(dom-set-attr _el-button "_" "on click ask \"What is your name?\" then put it into #out")
|
(dom-set-attr _el-button "_" "on click ask \\\"What is your name?\\\" then put it into #out")
|
||||||
(dom-set-inner-html _el-button "Ask")
|
(dom-set-inner-html _el-button "Ask")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -4435,7 +4443,7 @@
|
|||||||
(deftest "returns null on cancel"
|
(deftest "returns null on cancel"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
||||||
(dom-set-attr _el-button "_" "on click ask \"Name?\" then put it into #out")
|
(dom-set-attr _el-button "_" "on click ask \\\"Name?\\\" then put it into #out")
|
||||||
(dom-set-inner-html _el-button "Ask")
|
(dom-set-inner-html _el-button "Ask")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -4447,7 +4455,7 @@
|
|||||||
(deftest "shows an alert"
|
(deftest "shows an alert"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
||||||
(dom-set-attr _el-button "_" "on click answer \"Hello!\" then put \"done\" into #out")
|
(dom-set-attr _el-button "_" "on click answer \\\"Hello!\\\" then put \\\"done\\\" into #out")
|
||||||
(dom-set-inner-html _el-button "Go")
|
(dom-set-inner-html _el-button "Go")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -4459,7 +4467,7 @@
|
|||||||
(deftest "confirm returns first choice on OK"
|
(deftest "confirm returns first choice on OK"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
||||||
(dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
|
(dom-set-attr _el-button "_" "on click answer \\\"Save?\\\" with \\\"Yes\\\" or \\\"No\\\" then put it into #out")
|
||||||
(dom-set-inner-html _el-button "Go")
|
(dom-set-inner-html _el-button "Go")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -4471,7 +4479,7 @@
|
|||||||
(deftest "confirm returns second choice on cancel"
|
(deftest "confirm returns second choice on cancel"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
(let ((_el-button (dom-create-element "button")) (_el-out (dom-create-element "div")))
|
||||||
(dom-set-attr _el-button "_" "on click answer \"Save?\" with \"Yes\" or \"No\" then put it into #out")
|
(dom-set-attr _el-button "_" "on click answer \\\"Save?\\\" with \\\"Yes\\\" or \\\"No\\\" then put it into #out")
|
||||||
(dom-set-inner-html _el-button "Go")
|
(dom-set-inner-html _el-button "Go")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -4850,7 +4858,7 @@
|
|||||||
(deftest "can parse go to with string URL"
|
(deftest "can parse go to with string URL"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click go to \"#test-hash\"")
|
(dom-set-attr _el-div "_" "on click go to \\\"#test-hash\\\"")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
))
|
))
|
||||||
@@ -4946,11 +4954,12 @@
|
|||||||
(dom-set-attr _el-outer "id" "outer")
|
(dom-set-attr _el-outer "id" "outer")
|
||||||
(dom-set-attr _el-outer "_" "on click add .outer-clicked")
|
(dom-set-attr _el-outer "_" "on click add .outer-clicked")
|
||||||
(dom-set-attr _el-inner "id" "inner")
|
(dom-set-attr _el-inner "id" "inner")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-inner "_" "\\\"on")
|
||||||
(dom-set-inner-html _el-inner "click me")
|
(dom-set-inner-html _el-inner "click me")
|
||||||
(dom-append (dom-body) _el-outer)
|
(dom-append (dom-body) _el-outer)
|
||||||
(dom-append _el-outer _el-inner)
|
(dom-append _el-outer _el-inner)
|
||||||
(hs-activate! _el-outer)
|
(hs-activate! _el-outer)
|
||||||
|
(hs-activate! _el-inner)
|
||||||
(dom-dispatch (dom-query-by-id "inner") "click" nil)
|
(dom-dispatch (dom-query-by-id "inner") "click" nil)
|
||||||
(assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
|
(assert (not (dom-has-class? (dom-query-by-id "outer") "outer-clicked")))
|
||||||
(assert (dom-has-class? (dom-query-by-id "inner") "continued"))
|
(assert (dom-has-class? (dom-query-by-id "inner") "continued"))
|
||||||
@@ -5036,7 +5045,7 @@
|
|||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-inner-html _el-span "first")
|
(dom-set-inner-html _el-span "first")
|
||||||
(dom-set-attr _el-target2 "id" "target")
|
(dom-set-attr _el-target2 "id" "target")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-target2 "_" "\\\"on")
|
||||||
(dom-set-inner-html _el-span3 "first")
|
(dom-set-inner-html _el-span3 "first")
|
||||||
(dom-set-inner-html _el-span4 "second")
|
(dom-set-inner-html _el-span4 "second")
|
||||||
(dom-append (dom-body) _el-target)
|
(dom-append (dom-body) _el-target)
|
||||||
@@ -5044,6 +5053,7 @@
|
|||||||
(dom-append (dom-body) _el-target2)
|
(dom-append (dom-body) _el-target2)
|
||||||
(dom-append _el-target2 _el-span3)
|
(dom-append _el-target2 _el-span3)
|
||||||
(dom-append _el-target2 _el-span4)
|
(dom-append _el-target2 _el-span4)
|
||||||
|
(hs-activate! _el-target2)
|
||||||
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
||||||
))
|
))
|
||||||
(deftest "morph removes old children"
|
(deftest "morph removes old children"
|
||||||
@@ -5053,13 +5063,14 @@
|
|||||||
(dom-set-inner-html _el-span "first")
|
(dom-set-inner-html _el-span "first")
|
||||||
(dom-set-inner-html _el-span2 "second")
|
(dom-set-inner-html _el-span2 "second")
|
||||||
(dom-set-attr _el-target3 "id" "target")
|
(dom-set-attr _el-target3 "id" "target")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-target3 "_" "\\\"on")
|
||||||
(dom-set-inner-html _el-span4 "first")
|
(dom-set-inner-html _el-span4 "first")
|
||||||
(dom-append (dom-body) _el-target)
|
(dom-append (dom-body) _el-target)
|
||||||
(dom-append _el-target _el-span)
|
(dom-append _el-target _el-span)
|
||||||
(dom-append _el-target _el-span2)
|
(dom-append _el-target _el-span2)
|
||||||
(dom-append (dom-body) _el-target3)
|
(dom-append (dom-body) _el-target3)
|
||||||
(dom-append _el-target3 _el-span4)
|
(dom-append _el-target3 _el-span4)
|
||||||
|
(hs-activate! _el-target3)
|
||||||
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
||||||
))
|
))
|
||||||
(deftest "morph initializes hyperscript on new elements"
|
(deftest "morph initializes hyperscript on new elements"
|
||||||
@@ -5068,7 +5079,7 @@
|
|||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-inner-html _el-p "old")
|
(dom-set-inner-html _el-p "old")
|
||||||
(dom-set-attr _el-target2 "id" "target")
|
(dom-set-attr _el-target2 "id" "target")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-target2 "_" "\\\"on")
|
||||||
(dom-set-attr _el-inner "id" "inner")
|
(dom-set-attr _el-inner "id" "inner")
|
||||||
;; HS source has bare quotes or embedded HTML
|
;; HS source has bare quotes or embedded HTML
|
||||||
(dom-set-inner-html _el-inner "new")
|
(dom-set-inner-html _el-inner "new")
|
||||||
@@ -5076,6 +5087,7 @@
|
|||||||
(dom-append _el-target _el-p)
|
(dom-append _el-target _el-p)
|
||||||
(dom-append (dom-body) _el-target2)
|
(dom-append (dom-body) _el-target2)
|
||||||
(dom-append _el-target2 _el-inner)
|
(dom-append _el-target2 _el-inner)
|
||||||
|
(hs-activate! _el-target2)
|
||||||
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
(dom-dispatch (dom-query-by-id "go") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "inner")) "new")
|
(assert= (dom-text-content (dom-query-by-id "inner")) "new")
|
||||||
(dom-dispatch (dom-query-by-id "inner") "click" nil)
|
(dom-dispatch (dom-query-by-id "inner") "click" nil)
|
||||||
@@ -5086,7 +5098,7 @@
|
|||||||
(let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
(let ((_el-target (dom-create-element "div")) (_el-child (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-attr _el-child "id" "child")
|
(dom-set-attr _el-child "id" "child")
|
||||||
(dom-set-attr _el-child "_" "on click put \"alive\" into me")
|
(dom-set-attr _el-child "_" "on click put \\\"alive\\\" into me")
|
||||||
(dom-set-inner-html _el-child "child")
|
(dom-set-inner-html _el-child "child")
|
||||||
;; HS source has bare quotes or embedded HTML
|
;; HS source has bare quotes or embedded HTML
|
||||||
(dom-set-inner-html _el-button "go")
|
(dom-set-inner-html _el-button "go")
|
||||||
@@ -5132,7 +5144,7 @@
|
|||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-inner-html _el-target "original")
|
(dom-set-inner-html _el-target "original")
|
||||||
(dom-set-attr _el-go "id" "go")
|
(dom-set-attr _el-go "id" "go")
|
||||||
(dom-set-attr _el-go "_" "on click set content to \"<div id=target>morphed</div>\" then morph #target to content")
|
(dom-set-attr _el-go "_" "on click set content to \\\"<div id=target>morphed</div>\\\" then morph #target to content")
|
||||||
(dom-set-inner-html _el-go "go")
|
(dom-set-inner-html _el-go "go")
|
||||||
(dom-append (dom-body) _el-target)
|
(dom-append (dom-body) _el-target)
|
||||||
(dom-append (dom-body) _el-go)
|
(dom-append (dom-body) _el-go)
|
||||||
@@ -7255,7 +7267,7 @@
|
|||||||
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-inner-html _el-target "old")
|
(dom-set-inner-html _el-target "old")
|
||||||
(dom-set-attr _el-button "_" "on click make a <span.replaced/> then put \"moved\" into it then set #target to it")
|
(dom-set-attr _el-button "_" "on click make a <span.replaced/> then put \\\"moved\\\" into it then set #target to it")
|
||||||
(dom-set-inner-html _el-button "go")
|
(dom-set-inner-html _el-button "go")
|
||||||
(dom-append (dom-body) _el-target)
|
(dom-append (dom-body) _el-target)
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -7343,7 +7355,7 @@
|
|||||||
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
(let ((_el-target (dom-create-element "div")) (_el-button (dom-create-element "button")))
|
||||||
(dom-set-attr _el-target "id" "target")
|
(dom-set-attr _el-target "id" "target")
|
||||||
(dom-set-inner-html _el-target "old")
|
(dom-set-inner-html _el-target "old")
|
||||||
(dom-set-attr _el-button "_" "on click put \"new\" into #target")
|
(dom-set-attr _el-button "_" "on click put \\\"new\\\" into #target")
|
||||||
(dom-set-inner-html _el-button "go")
|
(dom-set-inner-html _el-button "go")
|
||||||
(dom-append (dom-body) _el-target)
|
(dom-append (dom-body) _el-target)
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
@@ -7464,15 +7476,17 @@
|
|||||||
(dom-set-inner-html _el-span2 "B")
|
(dom-set-inner-html _el-span2 "B")
|
||||||
(dom-add-class _el-span3 "a")
|
(dom-add-class _el-span3 "a")
|
||||||
(dom-set-inner-html _el-span3 "C")
|
(dom-set-inner-html _el-span3 "C")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-button "_" "\\\"on")
|
||||||
(dom-set-attr _el-b2 "id" "b2")
|
(dom-set-attr _el-b2 "id" "b2")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-b2 "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-box)
|
(dom-append (dom-body) _el-box)
|
||||||
(dom-append _el-box _el-span)
|
(dom-append _el-box _el-span)
|
||||||
(dom-append _el-box _el-span2)
|
(dom-append _el-box _el-span2)
|
||||||
(dom-append _el-box _el-span3)
|
(dom-append _el-box _el-span3)
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
(dom-append (dom-body) _el-b2)
|
(dom-append (dom-body) _el-b2)
|
||||||
|
(hs-activate! _el-button)
|
||||||
|
(hs-activate! _el-b2)
|
||||||
(dom-dispatch _el-button "click" nil)
|
(dom-dispatch _el-button "click" nil)
|
||||||
(assert= (dom-text-content _el-button) "2")
|
(assert= (dom-text-content _el-button) "2")
|
||||||
(dom-dispatch (dom-query-by-id "b2") "click" nil)
|
(dom-dispatch (dom-query-by-id "b2") "click" nil)
|
||||||
@@ -7486,11 +7500,12 @@
|
|||||||
(dom-set-inner-html _el-span "A")
|
(dom-set-inner-html _el-span "A")
|
||||||
(dom-add-class _el-span2 "b")
|
(dom-add-class _el-span2 "b")
|
||||||
(dom-set-inner-html _el-span2 "B")
|
(dom-set-inner-html _el-span2 "B")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-button "_" "\\\"set")
|
||||||
(dom-append (dom-body) _el-box)
|
(dom-append (dom-body) _el-box)
|
||||||
(dom-append _el-box _el-span)
|
(dom-append _el-box _el-span)
|
||||||
(dom-append _el-box _el-span2)
|
(dom-append _el-box _el-span2)
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
|
(hs-activate! _el-button)
|
||||||
(dom-dispatch _el-button "click" nil)
|
(dom-dispatch _el-button "click" nil)
|
||||||
(assert= (dom-text-content _el-button) "1")
|
(assert= (dom-text-content _el-button) "1")
|
||||||
))
|
))
|
||||||
@@ -7911,8 +7926,9 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-d1 "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
|
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
|
||||||
))
|
))
|
||||||
@@ -7920,8 +7936,9 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-d1 "_" "\\\"on")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
|
(assert= (dom-text-content (dom-query-by-id "d1")) "bar")
|
||||||
))
|
))
|
||||||
@@ -7996,7 +8013,7 @@
|
|||||||
(deftest "handles rejected promises without hanging"
|
(deftest "handles rejected promises without hanging"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click js return Promise.reject(\"boom\") end catch e put e into my.innerHTML")
|
(dom-set-attr _el-div "_" "on click js return Promise.reject(\\\"boom\\\") end catch e put e into my.innerHTML")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -8078,7 +8095,7 @@
|
|||||||
(deftest "the result in a when clause refers to previous command result, not element being tested"
|
(deftest "the result in a when clause refers to previous command result, not element being tested"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")) (_el-s1 (dom-create-element "span")) (_el-s2 (dom-create-element "span")))
|
(let ((_el-div (dom-create-element "div")) (_el-s1 (dom-create-element "span")) (_el-s2 (dom-create-element "span")))
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-s1 "id" "s1")
|
(dom-set-attr _el-s1 "id" "s1")
|
||||||
(dom-set-attr _el-s1 "style" "display:none")
|
(dom-set-attr _el-s1 "style" "display:none")
|
||||||
(dom-set-inner-html _el-s1 "A")
|
(dom-set-inner-html _el-s1 "A")
|
||||||
@@ -8088,6 +8105,7 @@
|
|||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(dom-append (dom-body) _el-s1)
|
(dom-append (dom-body) _el-s1)
|
||||||
(dom-append (dom-body) _el-s2)
|
(dom-append (dom-body) _el-s2)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert (dom-visible? (dom-query-by-id "s1")))
|
(assert (dom-visible? (dom-query-by-id "s1")))
|
||||||
(assert (dom-visible? (dom-query-by-id "s2")))
|
(assert (dom-visible? (dom-query-by-id "s2")))
|
||||||
@@ -8095,7 +8113,7 @@
|
|||||||
(deftest "the result after show...when is the matched elements"
|
(deftest "the result after show...when is the matched elements"
|
||||||
(hs-cleanup!)
|
(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")))
|
(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 has bare quotes or embedded HTML
|
(dom-set-attr _el-div "_" "\\\"on")
|
||||||
(dom-set-attr _el-p "style" "display:none")
|
(dom-set-attr _el-p "style" "display:none")
|
||||||
(dom-set-inner-html _el-p "yes")
|
(dom-set-inner-html _el-p "yes")
|
||||||
(dom-set-attr _el-p2 "style" "display:none")
|
(dom-set-attr _el-p2 "style" "display:none")
|
||||||
@@ -8106,6 +8124,7 @@
|
|||||||
(dom-append (dom-body) _el-p)
|
(dom-append (dom-body) _el-p)
|
||||||
(dom-append (dom-body) _el-p2)
|
(dom-append (dom-body) _el-p2)
|
||||||
(dom-append (dom-body) _el-out)
|
(dom-append (dom-body) _el-out)
|
||||||
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "out")) "some")
|
(assert= (dom-text-content (dom-query-by-id "out")) "some")
|
||||||
))
|
))
|
||||||
@@ -8234,7 +8253,7 @@
|
|||||||
(deftest "can have comments in attributes (triple dash)"
|
(deftest "can have comments in attributes (triple dash)"
|
||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-div (dom-create-element "div")))
|
(let ((_el-div (dom-create-element "div")))
|
||||||
(dom-set-attr _el-div "_" "on click put \"clicked\" into my.innerHTML ---put some content into the div...")
|
(dom-set-attr _el-div "_" "on click put \\\"clicked\\\" into my.innerHTML ---put some content into the div...")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(hs-activate! _el-div)
|
(hs-activate! _el-div)
|
||||||
(dom-dispatch _el-div "click" nil)
|
(dom-dispatch _el-div "click" nil)
|
||||||
@@ -8244,7 +8263,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter put \"hovered\" into my.innerHTML")
|
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter put \\\"hovered\\\" into my.innerHTML")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
))
|
))
|
||||||
@@ -8252,7 +8271,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad end on focus put \"focused\" into my.innerHTML")
|
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad end on focus put \\\"focused\\\" into my.innerHTML")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
))
|
))
|
||||||
@@ -8264,7 +8283,7 @@
|
|||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad")
|
(dom-set-attr _el-d1 "_" "on click blargh end on mouseenter also_bad")
|
||||||
(dom-set-attr _el-d2 "id" "d2")
|
(dom-set-attr _el-d2 "id" "d2")
|
||||||
(dom-set-attr _el-d2 "_" "on click put \"clicked\" into my.innerHTML")
|
(dom-set-attr _el-d2 "_" "on click put \\\"clicked\\\" into my.innerHTML")
|
||||||
(dom-append (dom-body) _el-div)
|
(dom-append (dom-body) _el-div)
|
||||||
(dom-append _el-div _el-d1)
|
(dom-append _el-div _el-d1)
|
||||||
(dom-append _el-div _el-d2)
|
(dom-append _el-div _el-d2)
|
||||||
@@ -8351,7 +8370,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-arDiv (dom-create-element "div")))
|
(let ((_el-arDiv (dom-create-element "div")))
|
||||||
(dom-set-attr _el-arDiv "id" "arDiv")
|
(dom-set-attr _el-arDiv "id" "arDiv")
|
||||||
(dom-set-attr _el-arDiv "_" "on click set my @data-foo to \"blue\"")
|
(dom-set-attr _el-arDiv "_" "on click set my @data-foo to \\\"blue\\\"")
|
||||||
(dom-set-attr _el-arDiv "data-foo" "red")
|
(dom-set-attr _el-arDiv "data-foo" "red")
|
||||||
(dom-append (dom-body) _el-arDiv)
|
(dom-append (dom-body) _el-arDiv)
|
||||||
(hs-activate! _el-arDiv)
|
(hs-activate! _el-arDiv)
|
||||||
@@ -8367,7 +8386,7 @@
|
|||||||
(dom-set-attr _el-outerDiv2 "id" "outerDiv2")
|
(dom-set-attr _el-outerDiv2 "id" "outerDiv2")
|
||||||
(dom-set-attr _el-outerDiv2 "foo" "bar")
|
(dom-set-attr _el-outerDiv2 "foo" "bar")
|
||||||
(dom-set-attr _el-d1b "id" "d1b")
|
(dom-set-attr _el-d1b "id" "d1b")
|
||||||
(dom-set-attr _el-d1b "_" "on click set closest @foo to \"doh\"")
|
(dom-set-attr _el-d1b "_" "on click set closest @foo to \\\"doh\\\"")
|
||||||
(dom-append (dom-body) _el-outerDiv2)
|
(dom-append (dom-body) _el-outerDiv2)
|
||||||
(dom-append _el-outerDiv2 _el-d1b)
|
(dom-append _el-outerDiv2 _el-d1b)
|
||||||
(hs-activate! _el-d1b)
|
(hs-activate! _el-d1b)
|
||||||
@@ -8381,7 +8400,7 @@
|
|||||||
(dom-add-class _el-input4 "cb")
|
(dom-add-class _el-input4 "cb")
|
||||||
(dom-set-attr _el-input4 "type" "checkbox")
|
(dom-set-attr _el-input4 "type" "checkbox")
|
||||||
(dom-set-attr _el-master "id" "master")
|
(dom-set-attr _el-master "id" "master")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-master "_" "\\\"set")
|
||||||
(dom-set-attr _el-master "type" "checkbox")
|
(dom-set-attr _el-master "type" "checkbox")
|
||||||
(dom-set-attr _el-master "<input[type" "checkbox]/")
|
(dom-set-attr _el-master "<input[type" "checkbox]/")
|
||||||
(dom-set-inner-html _el-master "in the closest")
|
(dom-set-inner-html _el-master "in the closest")
|
||||||
@@ -8394,6 +8413,7 @@
|
|||||||
(dom-append _el-input4 _el-master)
|
(dom-append _el-input4 _el-master)
|
||||||
(dom-append _el-master _el-table6)
|
(dom-append _el-master _el-table6)
|
||||||
(dom-append _el-master _el-out)
|
(dom-append _el-master _el-out)
|
||||||
|
(hs-activate! _el-master)
|
||||||
(dom-dispatch (dom-query-by-id "master") "click" nil)
|
(dom-dispatch (dom-query-by-id "master") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "out")) "2")
|
(assert= (dom-text-content (dom-query-by-id "out")) "2")
|
||||||
))
|
))
|
||||||
@@ -8413,7 +8433,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click if I am a Element put \"yes\" into me")
|
(dom-set-attr _el-d1 "_" "on click if I am a Element put \\\"yes\\\" into me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
||||||
@@ -8423,7 +8443,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click if I am a Node put \"yes\" into me")
|
(dom-set-attr _el-d1 "_" "on click if I am a Node put \\\"yes\\\" into me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
||||||
@@ -8433,7 +8453,7 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-d1 (dom-create-element "div")))
|
(let ((_el-d1 (dom-create-element "div")))
|
||||||
(dom-set-attr _el-d1 "id" "d1")
|
(dom-set-attr _el-d1 "id" "d1")
|
||||||
(dom-set-attr _el-d1 "_" "on click if \"hello\" is not a Element put \"yes\" into me")
|
(dom-set-attr _el-d1 "_" "on click if \\\"hello\\\" is not a Element put \\\"yes\\\" into me")
|
||||||
(dom-append (dom-body) _el-d1)
|
(dom-append (dom-body) _el-d1)
|
||||||
(hs-activate! _el-d1)
|
(hs-activate! _el-d1)
|
||||||
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
(dom-dispatch (dom-query-by-id "d1") "click" nil)
|
||||||
@@ -8573,10 +8593,11 @@
|
|||||||
(hs-cleanup!)
|
(hs-cleanup!)
|
||||||
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
|
(let ((_el-a (dom-create-element "div")) (_el-b (dom-create-element "div")))
|
||||||
(dom-set-attr _el-a "id" "a")
|
(dom-set-attr _el-a "id" "a")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-a "_" "\\\"on")
|
||||||
(dom-set-attr _el-b "id" "b")
|
(dom-set-attr _el-b "id" "b")
|
||||||
(dom-append (dom-body) _el-a)
|
(dom-append (dom-body) _el-a)
|
||||||
(dom-append (dom-body) _el-b)
|
(dom-append (dom-body) _el-b)
|
||||||
|
(hs-activate! _el-a)
|
||||||
(dom-dispatch (dom-query-by-id "a") "click" nil)
|
(dom-dispatch (dom-query-by-id "a") "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "a")) "yes")
|
(assert= (dom-text-content (dom-query-by-id "a")) "yes")
|
||||||
))
|
))
|
||||||
@@ -8725,13 +8746,14 @@
|
|||||||
(dom-set-inner-html _el-span "A")
|
(dom-set-inner-html _el-span "A")
|
||||||
(dom-add-class _el-span2 "b")
|
(dom-add-class _el-span2 "b")
|
||||||
(dom-set-inner-html _el-span2 "B")
|
(dom-set-inner-html _el-span2 "B")
|
||||||
;; HS source has bare quotes or embedded HTML
|
(dom-set-attr _el-button "_" "\\\"on")
|
||||||
(dom-set-attr _el-out "id" "out")
|
(dom-set-attr _el-out "id" "out")
|
||||||
(dom-append (dom-body) _el-box)
|
(dom-append (dom-body) _el-box)
|
||||||
(dom-append _el-box _el-span)
|
(dom-append _el-box _el-span)
|
||||||
(dom-append _el-box _el-span2)
|
(dom-append _el-box _el-span2)
|
||||||
(dom-append (dom-body) _el-button)
|
(dom-append (dom-body) _el-button)
|
||||||
(dom-append (dom-body) _el-out)
|
(dom-append (dom-body) _el-out)
|
||||||
|
(hs-activate! _el-button)
|
||||||
(dom-dispatch _el-button "click" nil)
|
(dom-dispatch _el-button "click" nil)
|
||||||
(assert= (dom-text-content (dom-query-by-id "out")) "none")
|
(assert= (dom-text-content (dom-query-by-id "out")) "none")
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -19,6 +19,57 @@ from collections import OrderedDict
|
|||||||
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
INPUT = os.path.join(PROJECT_ROOT, 'spec/tests/hyperscript-upstream-tests.json')
|
INPUT = os.path.join(PROJECT_ROOT, 'spec/tests/hyperscript-upstream-tests.json')
|
||||||
OUTPUT = os.path.join(PROJECT_ROOT, 'spec/tests/test-hyperscript-behavioral.sx')
|
OUTPUT = os.path.join(PROJECT_ROOT, 'spec/tests/test-hyperscript-behavioral.sx')
|
||||||
|
# All gallery pages live as flat files in applications/hyperscript/ with
|
||||||
|
# dash-joined slugs. The sx_docs routing layer only allows one level of
|
||||||
|
# page-fn dispatch at a time (call-page in web/request-handler.sx), and the
|
||||||
|
# hyperscript page-fn is a single-arg make-page-fn — so URLs have to be
|
||||||
|
# /sx/(applications.(hyperscript.gallery-<theme>-<category>)), not nested.
|
||||||
|
# The directory named "tests" is also in the server's skip_dirs list, so we
|
||||||
|
# couldn't use /tests/ anyway.
|
||||||
|
PAGES_DIR = os.path.join(PROJECT_ROOT, 'sx/sx/applications/hyperscript')
|
||||||
|
GALLERY_SLUG = 'gallery'
|
||||||
|
|
||||||
|
|
||||||
|
def page_slug(parts):
|
||||||
|
"""Build a dash-joined slug from path parts (theme, category, ...)."""
|
||||||
|
return '-'.join([GALLERY_SLUG] + [p for p in parts if p])
|
||||||
|
|
||||||
|
|
||||||
|
def page_url(parts):
|
||||||
|
"""Build the full /sx/... URL for a gallery slug."""
|
||||||
|
return f'/sx/(applications.(hyperscript.{page_slug(parts)}))'
|
||||||
|
|
||||||
|
# Six themes for grouping categories on the live gallery pages.
|
||||||
|
# Any category not listed here gets bucketed into 'misc'.
|
||||||
|
TEST_THEMES = {
|
||||||
|
'dom': ['add', 'remove', 'toggle', 'set', 'put', 'append', 'hide', 'empty',
|
||||||
|
'take', 'morph', 'show', 'measure', 'swap', 'focus', 'scroll', 'reset'],
|
||||||
|
'events': ['on', 'when', 'send', 'tell', 'init', 'bootstrap', 'socket',
|
||||||
|
'dialog', 'wait', 'halt', 'pick', 'fetch', 'asyncError'],
|
||||||
|
'expressions': ['comparisonOperator', 'mathOperator', 'logicalOperator',
|
||||||
|
'asExpression', 'collectionExpressions', 'closest', 'increment',
|
||||||
|
'queryRef', 'attributeRef', 'objectLiteral', 'no', 'default',
|
||||||
|
'in', 'splitJoin', 'select'],
|
||||||
|
'control': ['if', 'repeat', 'go', 'call', 'log', 'settle'],
|
||||||
|
'reactivity': ['bind', 'live', 'liveTemplate', 'reactive-properties',
|
||||||
|
'transition', 'resize'],
|
||||||
|
'language': ['def', 'component', 'parser', 'js', 'scoping', 'evalStatically',
|
||||||
|
'askAnswer', 'assignableElements',
|
||||||
|
'relativePositionalExpression', 'cookies', 'dom-scope'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def theme_for_category(category):
|
||||||
|
for theme, cats in TEST_THEMES.items():
|
||||||
|
if category in cats:
|
||||||
|
return theme
|
||||||
|
return 'misc'
|
||||||
|
|
||||||
|
|
||||||
|
def sx_str(s):
|
||||||
|
"""Escape a Python string for inclusion as an SX string literal."""
|
||||||
|
return '"' + s.replace('\\', '\\\\').replace('"', '\\"') + '"'
|
||||||
|
|
||||||
|
|
||||||
with open(INPUT) as f:
|
with open(INPUT) as f:
|
||||||
raw_tests = json.load(f)
|
raw_tests = json.load(f)
|
||||||
@@ -530,9 +581,12 @@ def parse_dev_body(body, elements, var_names):
|
|||||||
|
|
||||||
def process_hs_val(hs_val):
|
def process_hs_val(hs_val):
|
||||||
"""Process a raw HS attribute value: collapse whitespace, insert 'then' separators."""
|
"""Process a raw HS attribute value: collapse whitespace, insert 'then' separators."""
|
||||||
# Convert escaped newlines/tabs to real whitespace before stripping backslashes
|
# Convert escaped newlines/tabs to real whitespace
|
||||||
hs_val = hs_val.replace('\\n', '\n').replace('\\t', ' ')
|
hs_val = hs_val.replace('\\n', '\n').replace('\\t', ' ')
|
||||||
|
# Preserve escaped quotes (\" → placeholder), strip remaining backslashes, restore
|
||||||
|
hs_val = hs_val.replace('\\"', '\x00QUOT\x00')
|
||||||
hs_val = hs_val.replace('\\', '')
|
hs_val = hs_val.replace('\\', '')
|
||||||
|
hs_val = hs_val.replace('\x00QUOT\x00', '\\"')
|
||||||
cmd_kws = r'(?:set|put|get|add|remove|toggle|hide|show|if|repeat|for|wait|send|trigger|log|call|take|throw|return|append|tell|go|halt|settle|increment|decrement|fetch|make|install|measure|empty|reset|swap|default|morph|render|scroll|focus|select|pick|beep!)'
|
cmd_kws = r'(?:set|put|get|add|remove|toggle|hide|show|if|repeat|for|wait|send|trigger|log|call|take|throw|return|append|tell|go|halt|settle|increment|decrement|fetch|make|install|measure|empty|reset|swap|default|morph|render|scroll|focus|select|pick|beep!)'
|
||||||
hs_val = re.sub(r'\s{2,}(?=' + cmd_kws + r'\b)', ' then ', hs_val)
|
hs_val = re.sub(r'\s{2,}(?=' + cmd_kws + r'\b)', ' then ', hs_val)
|
||||||
hs_val = re.sub(r'\s*[\n\r]\s*', ' then ', hs_val)
|
hs_val = re.sub(r'\s*[\n\r]\s*', ' then ', hs_val)
|
||||||
@@ -545,12 +599,16 @@ def process_hs_val(hs_val):
|
|||||||
return hs_val.strip()
|
return hs_val.strip()
|
||||||
|
|
||||||
|
|
||||||
def emit_element_setup(lines, elements, var_names):
|
def emit_element_setup(lines, elements, var_names, root='(dom-body)', indent=' '):
|
||||||
"""Emit SX for creating elements, setting attributes, appending to DOM, and activating.
|
"""Emit SX for creating elements, setting attributes, appending to DOM, and activating.
|
||||||
|
|
||||||
|
root — where top-level elements get appended. Default (dom-body); for the gallery
|
||||||
|
card, callers pass a sandbox variable name so the HS runs inside the card, not on
|
||||||
|
the page body.
|
||||||
|
|
||||||
Three phases to ensure correct ordering:
|
Three phases to ensure correct ordering:
|
||||||
1. Set attributes/content on all elements
|
1. Set attributes/content on all elements
|
||||||
2. Append elements to their parents (children first, then parents to body)
|
2. Append elements to their parents (children first, then roots to root)
|
||||||
3. Activate HS handlers (all elements in DOM)
|
3. Activate HS handlers (all elements in DOM)
|
||||||
"""
|
"""
|
||||||
hs_elements = [] # indices of elements with valid HS
|
hs_elements = [] # indices of elements with valid HS
|
||||||
@@ -559,41 +617,41 @@ def emit_element_setup(lines, elements, var_names):
|
|||||||
for i, el in enumerate(elements):
|
for i, el in enumerate(elements):
|
||||||
var = var_names[i]
|
var = var_names[i]
|
||||||
if el['id']:
|
if el['id']:
|
||||||
lines.append(f' (dom-set-attr {var} "id" "{el["id"]}")')
|
lines.append(f'{indent}(dom-set-attr {var} "id" "{el["id"]}")')
|
||||||
for cls in el['classes']:
|
for cls in el['classes']:
|
||||||
lines.append(f' (dom-add-class {var} "{cls}")')
|
lines.append(f'{indent}(dom-add-class {var} "{cls}")')
|
||||||
if el['hs']:
|
if el['hs']:
|
||||||
hs_val = process_hs_val(el['hs'])
|
hs_val = process_hs_val(el['hs'])
|
||||||
if not hs_val:
|
if not hs_val:
|
||||||
pass # no HS to set
|
pass # no HS to set
|
||||||
elif hs_val.startswith('"') or (hs_val.endswith('"') and '<' in hs_val):
|
elif hs_val.startswith('"') or (hs_val.endswith('"') and '<' in hs_val):
|
||||||
lines.append(f' ;; HS source has bare quotes or embedded HTML')
|
lines.append(f'{indent};; HS source has bare quotes or embedded HTML')
|
||||||
else:
|
else:
|
||||||
hs_escaped = hs_val.replace('\\', '\\\\').replace('"', '\\"')
|
hs_escaped = hs_val.replace('\\', '\\\\').replace('"', '\\"')
|
||||||
lines.append(f' (dom-set-attr {var} "_" "{hs_escaped}")')
|
lines.append(f'{indent}(dom-set-attr {var} "_" "{hs_escaped}")')
|
||||||
hs_elements.append(i)
|
hs_elements.append(i)
|
||||||
for aname, aval in el['attrs'].items():
|
for aname, aval in el['attrs'].items():
|
||||||
if '\\' in aval or '\n' in aval or aname.startswith('['):
|
if '\\' in aval or '\n' in aval or aname.startswith('['):
|
||||||
lines.append(f' ;; SKIP attr {aname} (contains special chars)')
|
lines.append(f'{indent};; SKIP attr {aname} (contains special chars)')
|
||||||
continue
|
continue
|
||||||
aval_escaped = aval.replace('"', '\\"')
|
aval_escaped = aval.replace('"', '\\"')
|
||||||
lines.append(f' (dom-set-attr {var} "{aname}" "{aval_escaped}")')
|
lines.append(f'{indent}(dom-set-attr {var} "{aname}" "{aval_escaped}")')
|
||||||
if el['inner']:
|
if el['inner']:
|
||||||
inner_escaped = el['inner'].replace('\\', '\\\\').replace('"', '\\"')
|
inner_escaped = el['inner'].replace('\\', '\\\\').replace('"', '\\"')
|
||||||
lines.append(f' (dom-set-inner-html {var} "{inner_escaped}")')
|
lines.append(f'{indent}(dom-set-inner-html {var} "{inner_escaped}")')
|
||||||
|
|
||||||
# Phase 2: Append elements (children to parents, roots to body)
|
# Phase 2: Append elements (children to parents, roots to `root`)
|
||||||
for i, el in enumerate(elements):
|
for i, el in enumerate(elements):
|
||||||
var = var_names[i]
|
var = var_names[i]
|
||||||
if el['parent_idx'] is not None:
|
if el['parent_idx'] is not None:
|
||||||
parent_var = var_names[el['parent_idx']]
|
parent_var = var_names[el['parent_idx']]
|
||||||
lines.append(f' (dom-append {parent_var} {var})')
|
lines.append(f'{indent}(dom-append {parent_var} {var})')
|
||||||
else:
|
else:
|
||||||
lines.append(f' (dom-append (dom-body) {var})')
|
lines.append(f'{indent}(dom-append {root} {var})')
|
||||||
|
|
||||||
# Phase 3: Activate HS handlers (all elements now in DOM)
|
# Phase 3: Activate HS handlers (all elements now in DOM)
|
||||||
for i in hs_elements:
|
for i in hs_elements:
|
||||||
lines.append(f' (hs-activate! {var_names[i]})')
|
lines.append(f'{indent}(hs-activate! {var_names[i]})')
|
||||||
|
|
||||||
|
|
||||||
def generate_test_chai(test, elements, var_names, idx):
|
def generate_test_chai(test, elements, var_names, idx):
|
||||||
@@ -905,6 +963,215 @@ def generate_test(test, idx):
|
|||||||
return generate_test_chai(test, elements, var_names, idx)
|
return generate_test_chai(test, elements, var_names, idx)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Live gallery pages ────────────────────────────────────────────
|
||||||
|
|
||||||
|
PAGE_HEADER = (
|
||||||
|
';; AUTO-GENERATED from spec/tests/hyperscript-upstream-tests.json\n'
|
||||||
|
';; DO NOT EDIT — regenerate with:\n'
|
||||||
|
';; python3 tests/playwright/generate-sx-tests.py --emit-pages\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Actions/checks that we can't yet compile into a runner body emit a placeholder
|
||||||
|
# runner that throws; the card still renders so users can see the source. This
|
||||||
|
# keeps gallery coverage 1:1 with the JSON source of truth.
|
||||||
|
NOT_DEMONSTRABLE = '(error "not yet runnable in gallery — see test suite")'
|
||||||
|
|
||||||
|
|
||||||
|
def emit_runner_body(test, elements, var_names):
|
||||||
|
"""Emit the body of the runner lambda that runs inside a sandbox element.
|
||||||
|
|
||||||
|
Returns an SX expression string or None if the test can't be reproduced
|
||||||
|
(no HTML, unparseable action, etc.)."""
|
||||||
|
if not elements:
|
||||||
|
return None
|
||||||
|
|
||||||
|
ref = make_ref_fn(elements, var_names)
|
||||||
|
actions = parse_action(test.get('action', ''), ref)
|
||||||
|
checks_parsed = parse_checks(test.get('check', ''))
|
||||||
|
|
||||||
|
# Skip-only action list (no real action) → nothing to demonstrate
|
||||||
|
real_actions = [a for a in actions if not a.startswith(';;')]
|
||||||
|
if not real_actions:
|
||||||
|
return None
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
bindings = ' '.join(
|
||||||
|
f'({var_names[i]} (dom-create-element "{el["tag"]}"))'
|
||||||
|
for i, el in enumerate(elements)
|
||||||
|
)
|
||||||
|
lines.append(f'(fn (sandbox)')
|
||||||
|
lines.append(f' (let ({bindings})')
|
||||||
|
emit_element_setup(lines, elements, var_names, root='sandbox', indent=' ')
|
||||||
|
for a in actions:
|
||||||
|
lines.append(f' {a}')
|
||||||
|
for c in checks_parsed:
|
||||||
|
sx = check_to_sx(c, ref)
|
||||||
|
lines.append(f' {sx}')
|
||||||
|
lines.append(' ))')
|
||||||
|
return '\n'.join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_card(test):
|
||||||
|
"""Return an SX (~hyperscript/hs-test-card ...) call for one test."""
|
||||||
|
name_sx = sx_str(test['name'])
|
||||||
|
html_sx = sx_str(test.get('html', '') or '')
|
||||||
|
action_sx = sx_str(test.get('action', '') or '')
|
||||||
|
check_sx = sx_str(test.get('check', '') or '')
|
||||||
|
|
||||||
|
elements = parse_html(test.get('html', ''))
|
||||||
|
var_names = assign_var_names(elements) if elements else []
|
||||||
|
runner = emit_runner_body(test, elements, var_names)
|
||||||
|
if runner is None:
|
||||||
|
runner = f'(fn (sandbox) {NOT_DEMONSTRABLE})'
|
||||||
|
|
||||||
|
# :run-src is SX SOURCE TEXT — a string the island parses + evals at Run
|
||||||
|
# time. Ordinary lambda kwargs (and even bare quoted `(fn ...)` lists)
|
||||||
|
# end up lambda-ified by the prop pipeline and print as "<lambda>"
|
||||||
|
# through aser, which can't round-trip. Strings do.
|
||||||
|
run_src = sx_str(runner)
|
||||||
|
return (
|
||||||
|
f'(~hyperscript/hs-test-card\n'
|
||||||
|
f' :name {name_sx}\n'
|
||||||
|
f' :html {html_sx}\n'
|
||||||
|
f' :action {action_sx}\n'
|
||||||
|
f' :check {check_sx}\n'
|
||||||
|
f' :run-src {run_src})'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_category_page(theme, category, tests):
|
||||||
|
"""Return SX source for one category page (all tests in that category)."""
|
||||||
|
total = len(tests)
|
||||||
|
runnable = sum(
|
||||||
|
1 for t in tests
|
||||||
|
if parse_html(t.get('html', '')) and
|
||||||
|
any(not a.startswith(';;') for a in
|
||||||
|
parse_action(t.get('action', ''),
|
||||||
|
make_ref_fn(parse_html(t.get('html', '')),
|
||||||
|
assign_var_names(parse_html(t.get('html', ''))))))
|
||||||
|
)
|
||||||
|
cards = '\n'.join(emit_card(t) for t in tests)
|
||||||
|
title = f'Hyperscript: {category} ({total} tests — {runnable} runnable)'
|
||||||
|
intro = (
|
||||||
|
f'Live cards for the upstream {category} tests. '
|
||||||
|
f'{runnable} of {total} are reproducible in-browser; '
|
||||||
|
f'the remainder show their source for reference.'
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
PAGE_HEADER + '\n'
|
||||||
|
f'(defcomp ()\n'
|
||||||
|
f' (~docs/page :title {sx_str(title)}\n'
|
||||||
|
f' (p :style "color:#57534e;margin-bottom:1rem" {sx_str(intro)})\n'
|
||||||
|
f' (p :style "color:#78716c;font-size:0.875rem;margin-bottom:1rem"\n'
|
||||||
|
f' "Theme: " (a :href {sx_str(page_url([theme]))}\n'
|
||||||
|
f' :style "color:#7c3aed" {sx_str(theme)}))\n'
|
||||||
|
f' (div :style "display:flex;flex-direction:column"\n'
|
||||||
|
f' {cards})))\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_theme_index(theme, cats_in_theme, cats_to_tests):
|
||||||
|
"""Return SX source for a theme index page (list of its categories)."""
|
||||||
|
total = sum(len(cats_to_tests.get(c, [])) for c in cats_in_theme)
|
||||||
|
links = []
|
||||||
|
for cat in cats_in_theme:
|
||||||
|
if cat not in cats_to_tests:
|
||||||
|
continue
|
||||||
|
n = len(cats_to_tests[cat])
|
||||||
|
href = page_url([theme, cat])
|
||||||
|
links.append(
|
||||||
|
f' (li :style "margin-bottom:0.25rem"\n'
|
||||||
|
f' (a :href {sx_str(href)} :style "color:#7c3aed;text-decoration:underline"\n'
|
||||||
|
f' {sx_str(cat)})\n'
|
||||||
|
f' (span :style "color:#78716c;margin-left:0.5rem;font-size:0.875rem"\n'
|
||||||
|
f' {sx_str(f"({n} tests)")}))'
|
||||||
|
)
|
||||||
|
title = f'Hyperscript tests: {theme} ({total} tests)'
|
||||||
|
return (
|
||||||
|
PAGE_HEADER + '\n'
|
||||||
|
f'(defcomp ()\n'
|
||||||
|
f' (~docs/page :title {sx_str(title)}\n'
|
||||||
|
f' (p :style "color:#57534e;margin-bottom:1rem"\n'
|
||||||
|
f' "Pick a category to see its live test cards.")\n'
|
||||||
|
f' (ul :style "list-style:disc;padding-left:1.5rem"\n'
|
||||||
|
+ '\n'.join(links) + '\n'
|
||||||
|
f' )))\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def emit_top_index(themes_with_counts):
|
||||||
|
"""Return SX source for the top-level /tests index page."""
|
||||||
|
links = []
|
||||||
|
for theme, count in themes_with_counts:
|
||||||
|
href = page_url([theme])
|
||||||
|
links.append(
|
||||||
|
f' (li :style "margin-bottom:0.25rem"\n'
|
||||||
|
f' (a :href {sx_str(href)} :style "color:#7c3aed;text-decoration:underline;font-weight:500"\n'
|
||||||
|
f' {sx_str(theme)})\n'
|
||||||
|
f' (span :style "color:#78716c;margin-left:0.5rem;font-size:0.875rem"\n'
|
||||||
|
f' {sx_str(f"({count} tests)")}))'
|
||||||
|
)
|
||||||
|
grand_total = sum(c for _, c in themes_with_counts)
|
||||||
|
title = f'Hyperscript test gallery ({grand_total} tests)'
|
||||||
|
return (
|
||||||
|
PAGE_HEADER + '\n'
|
||||||
|
f'(defcomp ()\n'
|
||||||
|
f' (~docs/page :title {sx_str(title)}\n'
|
||||||
|
f' (p :style "color:#57534e;margin-bottom:1rem"\n'
|
||||||
|
f' "Live cards for every upstream _hyperscript behavioural test. "\n'
|
||||||
|
f' "Each card renders the HTML into a sandbox, activates the hyperscript, "\n'
|
||||||
|
f' "dispatches the action, and runs the assertion. Pass/fail is shown "\n'
|
||||||
|
f' "with the same runtime path as the SX test suite.")\n'
|
||||||
|
f' (ul :style "list-style:disc;padding-left:1.5rem"\n'
|
||||||
|
+ '\n'.join(links) + '\n'
|
||||||
|
f' )))\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def write_page_files(categories):
|
||||||
|
"""Write gallery files. Everything is flat in applications/hyperscript/ —
|
||||||
|
gallery.sx (top), gallery-<theme>.sx, gallery-<theme>-<cat>.sx —
|
||||||
|
because the /sx/ router only dispatches one level per page-fn call."""
|
||||||
|
# Bucket categories by theme
|
||||||
|
themed = OrderedDict() # theme -> [(cat, tests)]
|
||||||
|
for cat, tests in categories.items():
|
||||||
|
theme = theme_for_category(cat)
|
||||||
|
themed.setdefault(theme, []).append((cat, tests))
|
||||||
|
|
||||||
|
# Remove any previous gallery-*.sx files so stale themes don't linger
|
||||||
|
if os.path.isdir(PAGES_DIR):
|
||||||
|
for fname in os.listdir(PAGES_DIR):
|
||||||
|
if fname == f'{GALLERY_SLUG}.sx' or fname.startswith(f'{GALLERY_SLUG}-'):
|
||||||
|
try: os.remove(os.path.join(PAGES_DIR, fname))
|
||||||
|
except OSError: pass
|
||||||
|
|
||||||
|
themes_with_counts = []
|
||||||
|
written = []
|
||||||
|
for theme, cat_pairs in themed.items():
|
||||||
|
cats_in_theme = [c for c, _ in cat_pairs]
|
||||||
|
cats_to_tests = {c: ts for c, ts in cat_pairs}
|
||||||
|
|
||||||
|
for cat, tests in cat_pairs:
|
||||||
|
fname = f'{page_slug([theme, cat])}.sx'
|
||||||
|
with open(os.path.join(PAGES_DIR, fname), 'w') as f:
|
||||||
|
f.write(emit_category_page(theme, cat, tests))
|
||||||
|
written.append(fname)
|
||||||
|
|
||||||
|
fname = f'{page_slug([theme])}.sx'
|
||||||
|
with open(os.path.join(PAGES_DIR, fname), 'w') as f:
|
||||||
|
f.write(emit_theme_index(theme, cats_in_theme, cats_to_tests))
|
||||||
|
written.append(fname)
|
||||||
|
|
||||||
|
themes_with_counts.append((theme, sum(len(ts) for _, ts in cat_pairs)))
|
||||||
|
|
||||||
|
fname = f'{GALLERY_SLUG}.sx'
|
||||||
|
with open(os.path.join(PAGES_DIR, fname), 'w') as f:
|
||||||
|
f.write(emit_top_index(themes_with_counts))
|
||||||
|
written.append(fname)
|
||||||
|
|
||||||
|
return themed, written
|
||||||
|
|
||||||
|
|
||||||
# ── Output generation ─────────────────────────────────────────────
|
# ── Output generation ─────────────────────────────────────────────
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
@@ -989,3 +1256,15 @@ print(f' Categories: {len(categories)}')
|
|||||||
for cat, (gen, stub) in generated_counts.items():
|
for cat, (gen, stub) in generated_counts.items():
|
||||||
marker = '' if stub == 0 else f' ({stub} stubs)'
|
marker = '' if stub == 0 else f' ({stub} stubs)'
|
||||||
print(f' {cat}: {gen}{marker}')
|
print(f' {cat}: {gen}{marker}')
|
||||||
|
|
||||||
|
|
||||||
|
# ── Optional: live gallery pages ──────────────────────────────────
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if '--emit-pages' in sys.argv:
|
||||||
|
themed, written = write_page_files(categories)
|
||||||
|
print(f'\nGallery pages written under {PAGES_DIR} ({len(written)} files)')
|
||||||
|
for theme, pairs in themed.items():
|
||||||
|
cats = ', '.join(c for c, _ in pairs)
|
||||||
|
total_t = sum(len(ts) for _, ts in pairs)
|
||||||
|
print(f' {theme} ({total_t} tests, {len(pairs)} categories): {cats}')
|
||||||
|
|||||||
Reference in New Issue
Block a user