Files
rose-ash/spec/tests/test-hyperscript-conformance-dev.sx
giles 6e27442d57 Step 17: streaming render — hyperscript enhancements, WASM builds, live server tests
Streaming chunked transfer with shell-first suspense and resolve scripts.
Hyperscript parser/compiler/runtime expanded for conformance. WASM static
assets added to OCaml host. Playwright streaming and page-level test suites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 08:41:38 +00:00

466 lines
19 KiB
Plaintext

;; Dev-branch hyperscript conformance tests — expression evaluation
;; Source: spec/tests/hyperscript-upstream-tests.json (no-HTML tests from v0.9.90-dev)
;; DO NOT EDIT — regenerate with: python3 tests/playwright/generate-sx-conformance-dev.py
;; ── halt (1 tests) ──
(defsuite "hs-dev-halt"
(deftest "halt works outside of event context"
;; expect(error).toBeNull();
(error "STUB: needs JS bridge — promise"))
)
;; ── bind (1 tests) ──
(defsuite "hs-dev-bind"
(deftest "unsupported element: bind to plain div errors"
;; expect(await evaluate(() => window.$nope)).toBeUndefined()
(error "STUB: needs JS bridge — promise"))
)
;; ── when (2 tests) ──
(defsuite "hs-dev-when"
(deftest "local variable in when expression produces a parse error"
;; expect(error).not.toBeNull()
(error "STUB: needs JS bridge — eval-only"))
(deftest "attribute observers are persistent (not recreated on re-run)"
;; expect(observersCreated).toBe(0)
(error "STUB: needs JS bridge — promise"))
)
;; ── evalStatically (8 tests) ──
(defsuite "hs-dev-evalStatically"
(deftest "works on number literals"
(assert= 42 (eval-hs "42"))
(assert= 3.14 (eval-hs "3.14"))
)
(deftest "works on boolean literals"
(assert= true (eval-hs "true"))
(assert= false (eval-hs "false"))
)
(deftest "works on null literal"
(assert= nil (eval-hs "null"))
)
(deftest "works on plain string literals"
(assert= "hello" (eval-hs "\"hello\""))
(assert= "world" (eval-hs "'world'"))
)
(deftest "works on time expressions"
(assert= 200 (eval-hs "200ms"))
(assert= 2000 (eval-hs "2s"))
)
(deftest "throws on template strings"
;; expect(msg).toMatch(/cannot be evaluated statically/);
(error "STUB: needs JS bridge — eval-only"))
(deftest "throws on symbol references"
;; expect(msg).toMatch(/cannot be evaluated statically/);
(error "STUB: needs JS bridge — eval-only"))
(deftest "throws on math expressions"
;; expect(msg).toMatch(/cannot be evaluated statically/);
(error "STUB: needs JS bridge — eval-only"))
)
;; ── collectionExpressions (12 tests) ──
(defsuite "hs-dev-collectionExpressions"
(deftest "filters an array by condition"
(let ((result (eval-hs "set arr to [{name: \"a\", active: true}, {name: \"b\", active: false}, {name: \"c\", active: true}] then return arr where its active")))
(assert= (list "a" "c") (map (fn (x) (get x "name")) result))))
(deftest "filters with comparison"
(assert= (list 4 5) (eval-hs "set arr to [1, 2, 3, 4, 5] then return arr where it > 3"))
)
(deftest "sorts by a property"
(let ((result (eval-hs "set arr to [{name: \"Charlie\"}, {name: \"Alice\"}, {name: \"Bob\"}] then return arr sorted by its name")))
(assert= (list "Alice" "Bob" "Charlie") (map (fn (x) (get x "name")) result))))
(deftest "sorts descending"
(assert= (list 3 2 1) (eval-hs "set arr to [3, 1, 2] then return arr sorted by it descending"))
)
(deftest "sorts numbers by a computed key"
(let ((result (eval-hs "set arr to [{name: \"b\", age: 30}, {name: \"a\", age: 20}, {name: \"c\", age: 25}] then return arr sorted by its age")))
(assert= (list "a" "c" "b") (map (fn (x) (get x "name")) result))))
(deftest "maps to a property"
(assert= (list "Alice" "Bob") (eval-hs "set arr to [{name: \"Alice\"}, {name: \"Bob\"}] then return arr mapped to its name"))
)
(deftest "maps with an expression"
(assert= (list 2 4 6) (eval-hs "set arr to [1, 2, 3] then return arr mapped to (it * 2)"))
)
(deftest "where then mapped to"
(assert= (list "Alice" "Charlie") (eval-hs "set arr to [{name: \"Alice\", active: true}, {name: \"Bob\", active: false}, {name: \"Charlie\", active: true}] then return arr where its active mapped to its name"))
)
(deftest "sorted by then mapped to"
(assert= (list "Alice" "Charlie") (eval-hs "set arr to [{name: \"Charlie\", age: 30}, {name: \"Alice\", age: 20}] then return arr sorted by its age mapped to its name"))
)
(deftest "where then sorted by then mapped to"
(assert= (list "Bob" "Charlie") (eval-hs "set arr to [{name: \"Charlie\", active: true, age: 30}, {name: \"Alice\", active: false, age: 20}, {name: \"Bob\", active: true, age: 25}] then return arr where its active sorted by its age mapped to its name"))
)
(deftest "the result inside where refers to previous command result, not current element"
(assert= (list 4 5) (eval-hs "get 3 then set arr to [1, 2, 3, 4, 5] then return arr where it > the result"))
)
(deftest "where binds after property access"
(assert= (list 3 4) (eval-hs "obj.items where it > 2"))
)
)
;; ── splitJoin (7 tests) ──
(defsuite "hs-dev-splitJoin"
(deftest "splits a string by delimiter"
(assert= (list "a" "b" "c") (eval-hs "return \"a,b,c\" split by \",\""))
)
(deftest "splits by whitespace"
(assert= (list "hello" "world") (eval-hs "return \"hello world\" split by \" \""))
)
(deftest "joins an array with delimiter"
(assert= "a, b, c" (eval-hs "return [\"a\", \"b\", \"c\"] joined by \", \""))
)
(deftest "joins with empty string"
(assert= "xyz" (eval-hs "return [\"x\", \"y\", \"z\"] joined by \"\""))
)
(deftest "split then where then joined"
(assert= "a-b-c" (eval-hs "return \"a,,b,,c\" split by \",\" where it is not \"\" joined by \"-\""))
)
(deftest "split then sorted then joined"
(assert= "apple, banana, cherry" (eval-hs "return \"banana,apple,cherry\" split by \",\" sorted by it joined by \", \""))
)
(deftest "split then mapped then joined"
(assert= "5,5" (eval-hs "return \"hello world\" split by \" \" mapped to its length joined by \",\""))
)
)
;; ── pick (7 tests) ──
(defsuite "hs-dev-pick"
(deftest "does not hang on zero-length regex matches"
;; await run(String.raw`pick matches of "\\d*" from haystack
(error "STUB: needs JS bridge — eval-only"))
(deftest "can pick first n items"
(assert= (list 10 20 30) (eval-hs "pick first 3 of arr set $test to it"))
)
(deftest "can pick last n items"
(assert= (list 40 50) (eval-hs "pick last 2 of arr set $test to it"))
)
(deftest "can pick random item"
;; await run(`pick random of arr
(error "STUB: needs JS bridge — eval-only"))
(deftest "can pick random n items"
;; await run(`pick random 2 of arr
(error "STUB: needs JS bridge — eval-only"))
(deftest "can pick items using 'of' syntax"
(assert= (list 11 12) (eval-hs "pick items 1 to 3 of arr set $test to it"))
)
(deftest "can pick match using 'of' syntax"
;; await run(String.raw`pick match of "\\d+" of haystack
(error "STUB: needs JS bridge — eval-only"))
)
;; ── transition (1 tests) ──
(defsuite "hs-dev-transition"
(deftest "can transition on query ref with possessive"
;; await expect(find('div').nth(1)).toHaveCSS('width', '100px');
(error "STUB: needs JS bridge — eval-only"))
)
;; ── socket (4 tests) ──
(defsuite "hs-dev-socket"
(deftest "parses socket with absolute ws:// URL"
;; expect(result.error).toBeNull();
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts relative URL to wss:// on https pages"
;; expect(result.error).toBeNull();
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts relative URL to ws:// on http pages"
;; expect(result.error).toBeNull();
(error "STUB: needs JS bridge — eval-only"))
(deftest "namespaced sockets work"
;; expect(result.error).toBeNull();
(error "STUB: needs JS bridge — eval-only"))
)
;; ── bootstrap (3 tests) ──
(defsuite "hs-dev-bootstrap"
(deftest "fires hyperscript:before:init and hyperscript:after:init"
;; expect(events).toEqual(['before:init', 'after:init']);
(error "STUB: needs JS bridge — eval-only"))
(deftest "hyperscript:before:init can cancel initialization"
;; expect(result.initialized).toBe(false);
(error "STUB: needs JS bridge — eval-only"))
(deftest "logAll config logs events to console"
;; expect(logged).toBe(true);
(error "STUB: needs JS bridge — eval-only"))
)
;; ── parser (3 tests) ──
(defsuite "hs-dev-parser"
(deftest "fires hyperscript:parse-error event with all errors"
;; expect(errorCount).toBe(2);
(error "STUB: needs JS bridge — eval-only"))
(deftest "_hyperscript() evaluate API still throws on first error"
;; expect(msg).toMatch(/^Expected either a class reference or attribute expression/
(error "STUB: needs JS bridge — simple"))
(deftest "parse error at EOF on trailing newline does not crash"
;; expect(result).toMatch(/^ok:/);
(error "STUB: needs JS bridge — eval-only"))
)
;; ── asExpression (17 tests) ──
(defsuite "hs-dev-asExpression"
(deftest "converts value as Boolean"
(assert= true (eval-hs "1 as Boolean"))
(assert= false (eval-hs "0 as Boolean"))
(assert= false (eval-hs "'' as Boolean"))
(assert= true (eval-hs "'hello' as Boolean"))
)
(deftest "can use the a modifier if you like"
;; expect(result).toBe(new Date(1).getTime())
(error "STUB: needs JS bridge — eval-only"))
(deftest "parses string as JSON to object"
(let ((result (eval-hs "\\'{\"foo\":\"bar\"}\\' as JSON")))
(assert= "bar" (get result "foo"))
))
(deftest "converts value as JSONString"
(assert= "{\"foo\":\"bar\"}" (eval-hs "{foo:'bar'} as JSONString"))
)
(deftest "pipe operator chains conversions"
(let ((result (eval-hs "{foo:'bar'} as JSONString | JSON")))
(assert= "bar" (get result "foo"))
))
(deftest "can use the an modifier if you'd like"
(let ((result (eval-hs "\\'{\"foo\":\"bar\"}\\' as an Object")))
(assert= "bar" (get result "foo"))
))
(deftest "collects duplicate text inputs into an array"
;; expect(result.tag).toEqual(["alpha", "beta", "gamma"])
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts multiple selects with programmatically changed selections"
;; expect(result.animal[0]).toBe("cat")
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts a form element into Values | JSONString"
;; expect(result).toBe('{"firstName":"John","lastName":"Connor","areaCode":"213","p
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts a form element into Values | FormEncoded"
;; expect(result).toBe('firstName=John&lastName=Connor&areaCode=213&phone=555-1212'
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts array as Set"
;; expect(result.isSet).toBe(true)
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts object as Map"
;; expect(result.isMap).toBe(true)
(error "STUB: needs JS bridge — eval-only"))
(deftest "converts object as Keys"
(assert= (list "a" "b") (eval-hs "{a:1, b:2} as Keys"))
)
(deftest "converts object as Entries"
(assert= (list (list "a" 1)) (eval-hs "{a:1} as Entries"))
)
(deftest "converts array as Reversed"
(assert= (list 3 2 1) (eval-hs "[1,2,3] as Reversed"))
)
(deftest "converts array as Unique"
(assert= (list 1 2 3) (eval-hs "[1,2,2,3,3] as Unique"))
)
(deftest "converts nested array as Flat"
(assert= (list 1 2 3 4) (eval-hs "[[1,2],[3,4]] as Flat"))
)
)
;; ── comparisonOperator (28 tests) ──
(defsuite "hs-dev-comparisonOperator"
(deftest "is ignoring case works"
(assert= true (eval-hs "'Hello' is 'hello' ignoring case"))
(assert= true (eval-hs "'Hello' is 'HELLO' ignoring case"))
(assert= false (eval-hs "'Hello' is 'world' ignoring case"))
)
(deftest "is not ignoring case works"
(assert= true (eval-hs "'Hello' is not 'world' ignoring case"))
(assert= false (eval-hs "'Hello' is not 'hello' ignoring case"))
)
(deftest "contains ignoring case works"
(assert= true (eval-hs "'Hello World' contains 'hello' ignoring case"))
(assert= true (eval-hs "'Hello World' contains 'WORLD' ignoring case"))
(assert= false (eval-hs "'Hello World' contains 'missing' ignoring case"))
)
(deftest "matches ignoring case works"
(assert= true (eval-hs "'Hello' matches 'hello' ignoring case"))
(assert= true (eval-hs "'Hello' matches 'HELLO' ignoring case"))
)
(deftest "starts with works"
(assert= true (eval-hs "'hello world' starts with 'hello'"))
(assert= false (eval-hs "'hello world' starts with 'world'"))
(assert= true (eval-hs "'hello' starts with 'hello'"))
(assert= false (eval-hs "'' starts with 'x'"))
)
(deftest "ends with works"
(assert= true (eval-hs "'hello world' ends with 'world'"))
(assert= false (eval-hs "'hello world' ends with 'hello'"))
(assert= true (eval-hs "'hello' ends with 'hello'"))
(assert= false (eval-hs "'' ends with 'x'"))
)
(deftest "does not start with works"
(assert= false (eval-hs "'hello world' does not start with 'hello'"))
(assert= true (eval-hs "'hello world' does not start with 'world'"))
)
(deftest "does not end with works"
(assert= false (eval-hs "'hello world' does not end with 'world'"))
(assert= true (eval-hs "'hello world' does not end with 'hello'"))
)
(deftest "starts with null is false"
(assert= false (eval-hs "null starts with 'x'"))
(assert= true (eval-hs "null does not start with 'x'"))
)
(deftest "ends with null is false"
(assert= false (eval-hs "null ends with 'x'"))
(assert= true (eval-hs "null does not end with 'x'"))
)
(deftest "starts with ignoring case works"
(assert= true (eval-hs "'Hello World' starts with 'hello' ignoring case"))
(assert= true (eval-hs "'Hello World' starts with 'HELLO' ignoring case"))
(assert= false (eval-hs "'Hello World' starts with 'world' ignoring case"))
)
(deftest "ends with ignoring case works"
(assert= true (eval-hs "'Hello World' ends with 'world' ignoring case"))
(assert= true (eval-hs "'Hello World' ends with 'WORLD' ignoring case"))
(assert= false (eval-hs "'Hello World' ends with 'hello' ignoring case"))
)
(deftest "starts with coerces to string"
(assert= true (eval-hs "123 starts with '12'"))
(assert= false (eval-hs "123 starts with '23'"))
)
(deftest "ends with coerces to string"
(assert= true (eval-hs "123 ends with '23'"))
(assert= false (eval-hs "123 ends with '12'"))
)
(deftest "is between works"
(assert= true (eval-hs "5 is between 1 and 10"))
(assert= true (eval-hs "1 is between 1 and 10"))
(assert= true (eval-hs "10 is between 1 and 10"))
(assert= false (eval-hs "0 is between 1 and 10"))
(assert= false (eval-hs "11 is between 1 and 10"))
)
(deftest "is not between works"
(assert= false (eval-hs "5 is not between 1 and 10"))
(assert= true (eval-hs "0 is not between 1 and 10"))
(assert= true (eval-hs "11 is not between 1 and 10"))
(assert= false (eval-hs "1 is not between 1 and 10"))
(assert= false (eval-hs "10 is not between 1 and 10"))
)
(deftest "between works with strings"
(assert= true (eval-hs "'b' is between 'a' and 'c'"))
(assert= false (eval-hs "'d' is between 'a' and 'c'"))
)
(deftest "I am between works"
(assert= true (eval-hs "I am between 1 and 10" {:me 5}))
(assert= false (eval-hs "I am between 1 and 10" {:me 0}))
)
(deftest "I am not between works"
(assert= false (eval-hs "I am not between 1 and 10" {:me 5}))
(assert= true (eval-hs "I am not between 1 and 10" {:me 0}))
)
(deftest "precedes with null is false"
(assert= false (eval-hs "null precedes null"))
(assert= true (eval-hs "null does not precede null"))
)
(deftest "is really works without equal to"
(assert= true (eval-hs "2 is really 2"))
(assert= false (eval-hs "2 is really '2'"))
)
(deftest "is not really works without equal to"
(assert= true (eval-hs "2 is not really '2'"))
(assert= false (eval-hs "2 is not really 2"))
)
(deftest "is equal works without to"
(assert= true (eval-hs "2 is equal 2"))
(assert= false (eval-hs "2 is equal 1"))
)
(deftest "is not equal works without to"
(assert= false (eval-hs "2 is not equal 2"))
(assert= true (eval-hs "2 is not equal 1"))
)
(deftest "am works as alias for is"
(assert= true (eval-hs "2 am 2"))
(assert= false (eval-hs "2 am 1"))
)
(deftest "is not undefined still works as equality"
(assert= true (eval-hs "5 is not undefined"))
(assert= false (eval-hs "null is not undefined"))
)
(deftest "is not null still works as equality"
(assert= true (eval-hs "5 is not null"))
(assert= false (eval-hs "null is not null"))
)
(deftest "is still does equality when rhs variable exists"
(assert= true (eval-hs "x is y" {:locals {:x 5 :y 5}}))
(assert= false (eval-hs "x is y" {:locals {:x 5 :y 6}}))
)
)
;; ── cookies (1 tests) ──
(defsuite "hs-dev-cookies"
(deftest "length is 0 when no cookies are set"
;; expect(result).toBe(0)
(error "STUB: needs JS bridge — eval-only"))
)
;; ── in (1 tests) ──
(defsuite "hs-dev-in"
(deftest "null value in array returns empty"
(assert= (list) (eval-hs "null in [1, 2, 3]"))
)
)
;; ── logicalOperator (3 tests) ──
(defsuite "hs-dev-logicalOperator"
(deftest "and short-circuits when lhs promise resolves to false"
;; expect(result.result).toBe(false)
(error "STUB: needs JS bridge — eval-only"))
(deftest "or short-circuits when lhs promise resolves to true"
;; expect(result.result).toBe(true)
(error "STUB: needs JS bridge — eval-only"))
(deftest "or evaluates rhs when lhs promise resolves to false"
;; expect(result.result).toBe("fallback")
(error "STUB: needs JS bridge — eval-only"))
)
;; ── mathOperator (5 tests) ──
(defsuite "hs-dev-mathOperator"
(deftest "array + array concats"
(assert= (list 1 2 3 4) (eval-hs "[1, 2] + [3, 4]"))
)
(deftest "array + single value appends"
(assert= (list 1 2 3) (eval-hs "[1, 2] + 3"))
)
(deftest "array + array does not mutate original"
(assert= (list 1 2) (eval-hs "set a to [1, 2] then set b to a + [3] then return a"))
)
(deftest "array concat chains"
(assert= (list 1 2 3) (eval-hs "[1] + [2] + [3]"))
)
(deftest "empty array + array works"
(assert= (list 1 2) (eval-hs "[] + [1, 2]"))
)
)
;; ── no (4 tests) ──
(defsuite "hs-dev-no"
(deftest "no returns false for non-empty array"
(assert= false (eval-hs "no ['thing']"))
)
(deftest "no with where filters then checks emptiness"
(assert= true (eval-hs "no [1, 2, 3] where it > 5"))
)
(deftest "no with where returns false when matches exist"
(assert= false (eval-hs "no [1, 2, 3] where it > 1"))
)
(deftest "no with where and is not"
(assert= false (eval-hs "no [1, 2, 3] where it is not 2"))
)
)
;; ── objectLiteral (1 tests) ──
(defsuite "hs-dev-objectLiteral"
(deftest "allows trailing commas"
;; expect(await run("{foo:true, bar-baz:false,}")).toEqual({ "foo": true, "bar-baz"
(error "STUB: needs JS bridge — run-eval"))
)
;; ── relativePositionalExpression (1 tests) ──
(defsuite "hs-dev-relativePositionalExpression"
(deftest "can write to next element with put command"
;; await expect(find('#d2')).toHaveText('updated');
(error "STUB: needs JS bridge — eval-only"))
)