;; 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")) )