WIP: pre-existing changes from WASM browser work + test infrastructure

Accumulated changes from WASM browser development sessions:
- sx_runtime.ml: signal subscription + notify, env unwrap tolerance
- sx_browser.bc.js: rebuilt js_of_ocaml browser kernel
- sx_browser.bc.wasm.js + assets: WASM browser kernel build
- sx-platform.js browser tests (test_js, test_platform, test_wasm)
- Playwright sx-inspect.js: interactive page inspector tool
- harness-web.sx: DOM assertion updates
- deploy.sh, Dockerfile, dune-project: build config updates
- test-stepper.sx: stepper unit tests
- reader-macro-demo plan update

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 16:40:38 +00:00
parent 10576f86d1
commit c72a5af04d
47 changed files with 5485 additions and 1728 deletions

186
lib/tests/test-stepper.sx Normal file
View File

@@ -0,0 +1,186 @@
(defsuite
"stepper-lib"
(deftest
"split-tag: string literal becomes leaf step"
(let
((result (list)))
(split-tag "hello" result)
(assert-equal 1 (len result))
(assert-equal "leaf" (get (first result) "type"))
(assert-equal "hello" (get (first result) "expr"))))
(deftest
"split-tag: number literal becomes leaf step"
(let
((result (list)))
(split-tag 42 result)
(assert-equal 1 (len result))
(assert-equal "leaf" (get (first result) "type"))
(assert-equal 42 (get (first result) "expr"))))
(deftest
"split-tag: empty list produces nothing"
(let
((result (list)))
(split-tag (list) result)
(assert-equal 0 (len result))))
(deftest
"split-tag: simple div becomes open+close"
(let
((result (list)) (parsed (sx-parse "(div)")))
(split-tag (first parsed) result)
(assert-equal 2 (len result))
(assert-equal "open" (get (first result) "type"))
(assert-equal "div" (get (first result) "tag"))
(assert-equal "close" (get (nth result 1) "type"))))
(deftest
"split-tag: div with text child becomes open+leaf+close"
(let
((result (list)) (parsed (sx-parse "(div \"hello\")")))
(split-tag (first parsed) result)
(assert-equal 3 (len result))
(assert-equal "open" (get (first result) "type"))
(assert-equal "leaf" (get (nth result 1) "type"))
(assert-equal "hello" (get (nth result 1) "expr"))
(assert-equal "close" (get (nth result 2) "type"))))
(deftest
"split-tag: keyword attrs captured in open step"
(let
((result (list)) (parsed (sx-parse "(div :class \"foo\" \"text\")")))
(split-tag (first parsed) result)
(assert-equal 3 (len result))
(assert-equal "open" (get (first result) "type"))
(let
((attrs (get (first result) "attrs")))
(assert-equal 2 (len attrs)))))
(deftest
"split-tag: nested tags produce nested open/close pairs"
(let
((result (list)) (parsed (sx-parse "(div (span \"hi\"))")))
(split-tag (first parsed) result)
(assert-equal 5 (len result))
(assert-equal "open" (get (first result) "type"))
(assert-equal "div" (get (first result) "tag"))
(assert-equal "open" (get (nth result 1) "type"))
(assert-equal "span" (get (nth result 1) "tag"))
(assert-equal "leaf" (get (nth result 2) "type"))
(assert-equal "close" (get (nth result 3) "type"))
(assert-equal "close" (get (nth result 4) "type"))))
(deftest
"split-tag: component call becomes expr step"
(let
((result (list)) (parsed (sx-parse "(~my/comp :title \"hi\")")))
(split-tag (first parsed) result)
(assert-equal 1 (len result))
(assert-equal "expr" (get (first result) "type"))))
(deftest
"split-tag: component inside div becomes spread"
(let
((result (list))
(parsed (sx-parse "(div (~cssx/tw :tokens \"foo\") \"text\")")))
(split-tag (first parsed) result)
(let
((open-step (first result)))
(assert-equal "open" (get open-step "type"))
(assert-equal 1 (len (get open-step "spreads"))))))
(deftest
"build-code-tokens: string expr produces quoted token"
(let
((tokens (list)) (ref (dict "v" 0)))
(build-code-tokens "hello" tokens ref 0)
(assert-equal 1 (len tokens))
(assert-equal "\"hello\"" (get (first tokens) "text"))
(assert-equal "text-emerald-700" (get (first tokens) "cls"))
(assert-equal 0 (get (first tokens) "step"))))
(deftest
"build-code-tokens: number expr produces token"
(let
((tokens (list)) (ref (dict "v" 0)))
(build-code-tokens 42 tokens ref 0)
(assert-equal 1 (len tokens))
(assert-equal "42" (get (first tokens) "text"))
(assert-equal "text-amber-700" (get (first tokens) "cls"))))
(deftest
"build-code-tokens: symbol produces token with name"
(let
((tokens (list)) (ref (dict "v" 0)))
(build-code-tokens (make-symbol "foo") tokens ref 0)
(assert-equal 1 (len tokens))
(assert-equal "foo" (get (first tokens) "text"))
(assert-equal "text-stone-700" (get (first tokens) "cls"))))
(deftest
"build-code-tokens: html tag symbol gets sky color"
(let
((tokens (list)) (ref (dict "v" 0)))
(build-code-tokens (make-symbol "div") tokens ref 0)
(assert-equal "text-sky-700 font-semibold" (get (first tokens) "cls"))))
(deftest
"build-code-tokens: component symbol gets rose color"
(let
((tokens (list)) (ref (dict "v" 0)))
(build-code-tokens (make-symbol "~my/comp") tokens ref 0)
(assert-equal "text-rose-600 font-semibold" (get (first tokens) "cls"))))
(deftest
"build-code-tokens: list produces open-paren + children + close-paren"
(let
((tokens (list))
(ref (dict "v" 0))
(parsed (sx-parse "(div \"hi\")")))
(build-code-tokens (first parsed) tokens ref 0)
(assert-equal "(" (get (first tokens) "text"))
(assert-equal ")" (get (last tokens) "text"))))
(deftest
"build-code-tokens: step counter increments for strings"
(let
((tokens (list)) (ref (dict "v" 5)))
(build-code-tokens "hello" tokens ref 0)
(assert-equal 5 (get (first tokens) "step"))
(assert-equal 6 (get ref "v"))))
(deftest
"steps-to-preview: empty steps returns nil"
(assert-equal nil (steps-to-preview (list) 5)))
(deftest
"steps-to-preview: target 0 returns nil"
(let
((result (list)))
(split-tag "hello" result)
(assert-equal nil (steps-to-preview result 0))))
(deftest
"steps-to-preview: single leaf step reconstructs value"
(let
((result (list)))
(split-tag "hello" result)
(assert-equal "hello" (steps-to-preview result 1))))
(deftest
"steps-to-preview: open+close reconstructs empty tag"
(let
((result (list)) (parsed (sx-parse "(div)")))
(split-tag (first parsed) result)
(let
((preview (steps-to-preview result 2)))
(assert-equal "div" (symbol-name (first preview))))))
(deftest
"steps-to-preview: partial steps build partial tree"
(let
((result (list)) (parsed (sx-parse "(div (span \"a\") (span \"b\"))")))
(split-tag (first parsed) result)
(let
((preview (steps-to-preview result 4)))
(assert-equal "div" (symbol-name (first preview)))
(assert-equal true (> (len preview) 1)))))
(deftest
"steps-to-preview: full steps build complete tree"
(let
((result (list)) (parsed (sx-parse "(div \"hello\")")))
(split-tag (first parsed) result)
(let
((preview (steps-to-preview result (len result))))
(assert-equal "div" (symbol-name (first preview)))
(assert-equal "hello" (nth preview 1))))))
(deftest
"split-tag result is a render leak (list of dicts)"
(let
((result (list)) (parsed (sx-parse "(div \"hello\")")))
(split-tag (first parsed) result)
(assert-equal true (is-render-leak? result))
(assert-equal true (> (len result) 0))
(assert-equal true (dict? (first result)))))