Sandbox conformance runner: 147/214 passing (69%)

New file: spec/tests/test-hyperscript-conformance-sandbox.sx
- 214 fixtures extracted from official _hyperscript 0.9.14 test suite
- Runs via: sx_playwright mode=sandbox stack=hs files=[this]
  expr=(do (hs-conf-run-all) (hs-conf-report))
- Uses cek-eval (full env) — no runtime let-binding hacks
- try-call error handling per fixture

Up from 62/109 (57%) in OCaml runner to 147/214 (69%) in sandbox.
+85 tests unlocked by real eval context.

67 remaining failures:
- 11 coercion types (Fixed, JSON, Object, Values, custom)
- 9 cookies (DOM)
- 8 template strings (parser needed)
- 6 string postfix (1em, 1px)
- 5 window globals (foo, value)
- 4 block literals (parser needed)
- 4 I am in (me binding in cek-eval)
- 4 in operator (array intersection semantics)
- 4 typecheck colon syntax (: String)
- 3 object literals
- 3 DOM selectors
- 2 logical short-circuit (func1/func2)
- 2 float/nan edge cases
- 1 no .class (DOM)
- 1 its foo (window global)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 21:57:18 +00:00
parent 5e708e1b20
commit 8e9dc4a623

View File

@@ -0,0 +1,271 @@
;; _hyperscript conformance — 222 tests from v0.9.14
;;
;; OCaml: loaded by run_tests.exe, uses eval-expr-cek
;; Sandbox: sx_playwright mode=sandbox stack=hs files=[this]
;; expr=(do (hs-conf-run-all) (hs-conf-report))
;; ── Counters ────────────────────────────────────────────────────
(define hs-conf-pass 0)
(define hs-conf-fail 0)
(define hs-conf-fails (list))
;; ── eval-hs: sandbox version uses cek-eval ──────────────────────
(define eval-hs
(fn (src &rest opts)
(let ((sx (hs-to-sx (hs-compile src)))
(ctx (if (> (len opts) 0) (first opts) nil)))
(let ((bindings (list
(list (quote me) nil)
(list (quote it) nil)
(list (quote result) nil))))
(do
(when ctx
(do
(when (get ctx "me")
(set! bindings (cons (list (quote me) (get ctx "me")) bindings)))
(when (get ctx "locals")
(for-each
(fn (k) (set! bindings (cons (list (make-symbol k) (get (get ctx "locals") k)) bindings)))
(keys (get ctx "locals"))))))
(cek-eval (list (quote let) bindings sx)))))))
;; ── Fixture runner ──────────────────────────────────────────────
(define hs-conf-run-fixture
(fn (f)
(let ((src (get f "src"))
(expected (get f "expected"))
(ctx (if (or (get f "locals") (get f "me"))
{"locals" (get f "locals") "me" (get f "me")} nil)))
(let ((ok (try-call (fn ()
(let ((result (if ctx (eval-hs src ctx) (eval-hs src))))
(if (= result expected)
(set! hs-conf-pass (+ hs-conf-pass 1))
(do (set! hs-conf-fail (+ hs-conf-fail 1))
(append! hs-conf-fails (str src " => " result " exp " expected)))))))))
(when (not (get ok "ok"))
(do (set! hs-conf-fail (+ hs-conf-fail 1))
(append! hs-conf-fails (str "ERR: " src))))))))
(define hs-conf-run-all
(fn () (for-each hs-conf-run-fixture hs-conf-all-fixtures)))
(define hs-conf-report
(fn () (str hs-conf-pass "/" (+ hs-conf-pass hs-conf-fail) " pass, " hs-conf-fail " fail")))
;; ── Fixtures ────────────────────────────────────────────────────
(define hs-conf-all-fixtures (list
{:src "[1, 2, 3]" :expected (list 1 2 3)}
{:src "[]" :expected (list)}
{:src "[true]" :expected (list true)}
{:src "[true, false]" :expected (list true false)}
{:src "10 as String" :expected "10"}
{:src "true as String" :expected "true"}
{:src "'10' as Int" :expected 10}
{:src "'10.4' as Int" :expected 10}
{:src "'10' as Float" :expected 10}
{:src "'10.4' as Float" :expected 10.4}
{:src "'10.4' as Fixed" :expected "10"}
{:src "'10.4899' as Fixed:2" :expected "10.49"}
{:src "'10' as Number" :expected 10}
{:src "'10.4' as Number" :expected 10.4}
{:src "{foo:'bar'} as JSON" :expected "{\"foo\":\"bar\"}"}
{:src "'{\"foo\":\"bar\"}' as Object" :expected "bar"}
{:src "'{\"foo\":\"bar\"}' as an Object" :expected "bar"}
{:src "x as Object" :expected "bar"}
{:src "x as Values" :expected "John"}
{:src "value as HTML" :expected "123"}
{:src "value as Fragment" :expected 1}
{:src "1 as Foo" :expected "foo1"}
{:src "1 as Foo:Bar" :expected "Bar1"}
{:src "\\\\-> true" :expected true}
{:src "\\\\ x -> x" :expected true}
{:src "\\\\ x, y -> y" :expected true}
{:src "['a', 'ab', 'abc'].map(\\\\ s -> s.length )" :expected (list 1 2 3)}
{:src "true" :expected true}
{:src "false" :expected false}
{:src ".badClassThatDoesNotHaveAnyElements" :expected 0}
{:src "1 < 2" :expected true}
{:src "2 < 1" :expected false}
{:src "2 < 2" :expected false}
{:src "1 <= 2" :expected true}
{:src "2 <= 1" :expected false}
{:src "2 <= 2" :expected true}
{:src "1 > 2" :expected false}
{:src "2 > 1" :expected true}
{:src "2 > 2" :expected false}
{:src "1 >= 2" :expected false}
{:src "2 >= 1" :expected true}
{:src "2 >= 2" :expected true}
{:src "1 == 2" :expected false}
{:src "2 == 1" :expected false}
{:src "2 == 2" :expected true}
{:src "1 === 2" :expected false}
{:src "2 === 1" :expected false}
{:src "2 === 2" :expected true}
{:src "1 != 2" :expected true}
{:src "2 != 1" :expected true}
{:src "2 != 2" :expected false}
{:src "1 !== 2" :expected true}
{:src "2 !== 1" :expected true}
{:src "2 !== 2" :expected false}
{:src "1 is 2" :expected false}
{:src "2 is 1" :expected false}
{:src "2 is 2" :expected true}
{:src "1 equals 2" :expected false}
{:src "2 equals 1" :expected false}
{:src "2 equals 2" :expected true}
{:src "1 is equal to 2" :expected false}
{:src "2 is equal to 1" :expected false}
{:src "2 is equal to 2" :expected true}
{:src "1 is really equal to 2" :expected false}
{:src "2 is really equal to 1" :expected false}
{:src "2 is really equal to '2'" :expected false}
{:src "2 is really equal to 2" :expected true}
{:src "1 really equals 2" :expected false}
{:src "2 really equals 1" :expected false}
{:src "2 really equals 2" :expected true}
{:src "1 is not 2" :expected true}
{:src "2 is not 1" :expected true}
{:src "2 is not 2" :expected false}
{:src "1 is not equal to 2" :expected true}
{:src "2 is not equal to 1" :expected true}
{:src "2 is not equal to 2" :expected false}
{:src "1 is not really equal to 2" :expected true}
{:src "2 is not really equal to 1" :expected true}
{:src "2 is not really equal to '2'" :expected true}
{:src "2 is not really equal to 2" :expected false}
{:src "1 is in [1, 2]" :expected true}
{:src "2 is in [1, 2]" :expected true}
{:src "3 is in [1, 2]" :expected false}
{:src "3 is in null" :expected false}
{:src "1 is not in [1, 2]" :expected false}
{:src "2 is not in [1, 2]" :expected false}
{:src "3 is not in [1, 2]" :expected true}
{:src "3 is not in null" :expected true}
{:src "I am in [1, 2]" :me 1 :expected true}
{:src "I am in [1, 2]" :me 2 :expected true}
{:src "I am in [1, 2]" :me 3 :expected false}
{:src "I am in null" :expected false}
{:src "I am not in [1, 2]" :me 1 :expected false}
{:src "I am not in [1, 2]" :me 2 :expected false}
{:src "I am not in [1, 2]" :me 3 :expected true}
{:src "I am not in null" :expected true}
{:src "'a' matches '.*'" :expected true}
{:src "'a' matches 'b'" :expected false}
{:src "'a' does not match '.*'" :expected false}
{:src "'a' does not match 'b'" :expected true}
{:src "undefined is empty" :expected true}
{:src "'' is empty" :expected true}
{:src "[] is empty" :expected true}
{:src "'not empty' is empty" :expected false}
{:src "1000 is empty" :expected false}
{:src "[1,2,3] is empty" :expected false}
{:src "undefined is not empty" :expected false}
{:src "'' is not empty" :expected false}
{:src "[] is not empty" :expected false}
{:src "'not empty' is not empty" :expected true}
{:src "1000 is not empty" :expected true}
{:src "[1,2,3] is not empty" :expected true}
{:src "null is a String" :expected true}
{:src "null is a String!" :expected false}
{:src "'' is a String!" :expected true}
{:src "null is not a String" :expected false}
{:src "null is not a String!" :expected true}
{:src "'' is not a String!" :expected false}
{:src "null is an String" :expected true}
{:src "null is an String!" :expected false}
{:src "'' is an String!" :expected true}
{:src "null is not an String" :expected false}
{:src "null is not an String!" :expected true}
{:src "'' is not an String!" :expected false}
{:src "1 is less than 2" :expected true}
{:src "2 is less than 1" :expected false}
{:src "2 is less than 2" :expected false}
{:src "1 is less than or equal to 2" :expected true}
{:src "2 is less than or equal to 1" :expected false}
{:src "2 is less than or equal to 2" :expected true}
{:src "1 is greater than 2" :expected false}
{:src "2 is greater than 1" :expected true}
{:src "2 is greater than 2" :expected false}
{:src "1 is greater than or equal to 2" :expected false}
{:src "2 is greater than or equal to 1" :expected true}
{:src "2 is greater than or equal to 2" :expected true}
{:src "undefined does not exist" :expected true}
{:src "null does not exist" :expected true}
{:src "cookies.foo" :expected "bar"}
{:src "set cookies.foo to 'bar'" :expected "bar"}
{:src "cookies.foo" :expected "bar"}
{:src "set cookies.foo to 'bar'" :expected "bar"}
{:src "cookies.foo" :expected "bar"}
{:src "set cookies.foo to 'doh'" :expected "doh"}
{:src "cookies.foo" :expected "doh"}
{:src "set cookies.foo to 'bar'" :expected true}
{:src "for x in cookies me.push(x.name) then you.push(x.value) end" :expected true}
{:src "1 in [1, 2, 3]" :expected (list 1)}
{:src "[1, 3] in [1, 2, 3]" :expected (list 1 3)}
{:src "[1, 3, 4] in [1, 2, 3]" :expected (list 1 3)}
{:src "[4, 5, 6] in [1, 2, 3]" :expected (list)}
{:src "func1() and func2()" :expected false}
{:src "func1() or func2()" :expected true}
{:src "1 + 1" :expected 2}
{:src "'a' + 'b'" :expected "ab"}
{:src "1 - 1" :expected 0}
{:src "1 * 2" :expected 2}
{:src "1 / 2" :expected 0.5}
{:src "3 mod 2" :expected 1}
{:src "1 + 2 + 3" :expected 6}
{:src "1 + (2 * 3)" :expected 7}
{:src "no null" :expected true}
{:src "no 'thing'" :expected false}
{:src "no ['thing']" :expected false}
{:src "no []" :expected true}
{:src "no .aClassThatDoesNotExist" :expected true}
{:src "not true" :expected false}
{:src "not false" :expected true}
{:src "not not true" :expected true}
{:src "-1" :expected -1}
{:src "1" :expected 1}
{:src "1.1" :expected 1.1}
{:src "1234567890.1234567890" :expected 1234570000}
{:src "{}" :expected {}}
{:src "{-foo:true, bar-baz:false}" :expected {:bar-baz false :-foo true}}
{:src "{foo:true, bar-baz:false,}" :expected {:bar-baz false :foo true}}
{:src "the first of [1, 2, 3]" :expected 1}
{:src "the last of [1, 2, 3]" :expected 3}
{:src "foo's foo" :expected "foo"}
{:src "its foo" :expected "foo"}
{:src "foo.foo" :expected "foo"}
{:src "foo of foo" :expected "foo"}
{:src "bar.doh of foo" :expected "foo"}
{:src "doh of foo.bar" :expected "foo"}
{:src "<.badClassThatDoesNotHaveAnyElements/>" :expected 0}
{:src "some null" :expected false}
{:src "some 'thing'" :expected true}
{:src "some []" :expected false}
{:src "some .aClassThatDoesNotExist" :expected false}
{:src "some <html/>" :expected true}
{:src "some ['thing']" :expected true}
{:src "1em" :expected "1em"}
{:src "1px" :expected "1px"}
{:src "-1px" :expected "-1px"}
{:src "100%" :expected "100%"}
{:src "1 em" :expected "1em"}
{:src "1 px" :expected "1px"}
{:src "100 %" :expected "100%"}
{:src "\"foo\"" :expected "foo"}
{:src "\"fo'o\"" :expected "fo'o"}
{:src "'foo'" :expected "foo"}
{:src "`$1`" :expected "1"}
{:src "`${1 + 2}`" :expected "3"}
{:src "` ${1 + 2} ${1 + 2} `" :expected " 3 3 "}
{:src "`${1 + 2} ${1 + 2} `" :expected "3 3 "}
{:src "`${1 + 2}${1 + 2} `" :expected "33 "}
{:src "`${1 + 2} ${1 + 2}`" :expected "3 3"}
{:src "`<div age=\"${record.age}\" style=\"color:${record.favouriteColour}\">${record.name}</div>`" :expected "<div age=\"21\" style=\"color:bleaux\">John Connor</div>"}
{:src "`https://${foo}`" :locals {:foo "bar"} :expected "https://bar"}
{:src "foo" :locals {:foo 42} :expected 42}
{:src "'foo' : String" :expected "foo"}
{:src "true : String" :expected 0}
{:src "'foo' : String!" :expected "foo"}
{:src "null : String!" :expected 0}
))