;; 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── bind (1 tests) ──
(defsuite "hs-dev-bind"
  (deftest "unsupported element: bind to plain div errors"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── when (2 tests) ──
(defsuite "hs-dev-when"
  (deftest "local variable in when expression produces a parse error"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "attribute observers are persistent (not recreated on re-run)"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "throws on symbol references"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "throws on math expressions"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── 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" {:locals {:obj {:items (list 1 2 3 4)}}}))))

;; ── 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "can pick first n items"
    (assert= (list 10 20 30) (eval-hs "pick first 3 of arr" {:locals {:arr (list 10 20 30 40 50)}})))
  (deftest "can pick last n items"
    (assert= (list 40 50) (eval-hs "pick last 2 of arr" {:locals {:arr (list 10 20 30 40 50)}})))
  (deftest "can pick random item"
    (assert-true (some (fn (x) (= x (eval-hs "pick random of arr" {:locals {:arr (list 10 20 30)}}))) (list 10 20 30))))
  (deftest "can pick random n items"
    (assert= 2 (len (eval-hs "pick random 2 of arr" {:locals {:arr (list 10 20 30 40 50)}}))))
  (deftest "can pick items using 'of' syntax"
    (assert= (list 11 12) (eval-hs "pick items 1 to 3 of arr" {:locals {:arr (list 10 11 12 13 14 15 16)}})))
  (deftest "can pick match using 'of' syntax"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── transition (1 tests) ──
(defsuite "hs-dev-transition"
  (deftest "can transition on query ref with possessive"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── socket (4 tests) ──
(defsuite "hs-dev-socket"
  (deftest "parses socket with absolute ws:// URL"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts relative URL to wss:// on https pages"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts relative URL to ws:// on http pages"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "namespaced sockets work"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── bootstrap (3 tests) ──
(defsuite "hs-dev-bootstrap"
  (deftest "fires hyperscript:before:init and hyperscript:after:init"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "hyperscript:before:init can cancel initialization"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "logAll config logs events to console"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── parser (3 tests) ──
(defsuite "hs-dev-parser"
  (deftest "fires hyperscript:parse-error event with all errors"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "_hyperscript() evaluate API still throws on first error"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "parse error at EOF on trailing newline does not crash"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts multiple selects with programmatically changed selections"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts a form element into Values | JSONString"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts a form element into Values | FormEncoded"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "converts array as Set"
    ;; expect(result.isSet).toBe(true)
    ;; STUB: needs JS bridge — eval-only
    (assert true))
  (deftest "converts object as Map"
    ;; expect(result.isMap).toBe(true)
    ;; STUB: needs JS bridge — eval-only
    (assert true))
  (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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "or short-circuits when lhs promise resolves to true"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
  (deftest "or evaluates rhs when lhs promise resolves to false"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── 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"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)

;; ── relativePositionalExpression (1 tests) ──
(defsuite "hs-dev-relativePositionalExpression"
  (deftest "can write to next element with put command"
    ;; needs DOM/browser — covered by Playwright suite
    (assert true))
)
