host/tests: Phase 3 — the engine renders the picker to a CONSOLE (non-browser target)

web/console-render.sx: render-to-console walks a live DOM element tree through the
engine's own dom-* accessors and prints it as terminal text — the results <ul>
becomes a bulleted list, the filter <input> a text field, the load-more sentinel a
"…" line, an .sx-error element a flagged line. It's the console platform's draw
step: the browser PAINTS the engine's tree, the harness ASSERTS it, this PRINTS it
— one tree, three bindings, the proof the engine is a general runtime not a browser
library.

Wired into the picker's SX engine tests (web/tests/test-relate-picker.sx): the load
and error tests now ALSO assert their console rendering — the same tree the engine
built drives both the DOM assertion and the terminal output, so Phase 1's suite is
the console renderer's regression suite for free. Plus a relate-picker:console suite
for the field/bullet/sentinel/error shapes. 7/7 green, no web-suite regressions.

(Class membership reads the live classList via dom-has-class?, not the static class
attribute — the engine adds .sx-error through classList.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 18:08:51 +00:00
parent 2b2073cf56
commit 16f90ffdad
3 changed files with 143 additions and 1 deletions

View File

@@ -117,7 +117,7 @@
" sx-swap=\"innerHTML\""
" sx-retry=\"exponential:1000:30000\">"
"<input type=\"hidden\" name=\"kind\" value=\"" kind "\">"
"<input type=\"text\" name=\"q\" class=\"rp-filter\">"
"<input type=\"text\" name=\"q\" class=\"rp-filter\" placeholder=\"filter…\">"
"<ul id=\"rp-" kind "-results\" class=\"rp-results\"></ul>"
"</form>")))
@@ -200,6 +200,12 @@
(process-elements root)
(assert-true (> _mock-fetch-calls 0))
(assert-equal 5 (count-candidates results))
;; Phase 3: the SAME populated tree renders to the console — the results
;; <ul> becomes a bulleted list of candidate titles. (Console renderer driven
;; for free by the engine tree; web/console-render.sx.)
(let ((txt (render-to-console results)))
(assert-true (contains? txt "• Picker Item 0"))
(assert-true (contains? txt "• Picker Item 4")))
(clear-root! root))))
(defsuite
@@ -272,6 +278,9 @@
;; visible failure state: .sx-error lands on the picker form
(assert-true (dom-has-class? form "sx-error"))
(assert-equal 0 (count-candidates results))
;; Phase 3: the console rendering of the errored picker shows the failure as
;; a flagged line (the terminal's "red line") — same tree, different binding.
(assert-true (contains? (render-to-console form) "✖"))
;; recovery: the endpoint works again, the next input retries and the error
;; clears as the results populate
(set! _mock-fetch-fail false)
@@ -280,3 +289,41 @@
(assert-false (dom-has-class? form "sx-error"))
(assert-equal 3 (count-candidates results))
(clear-root! root))))
;; ── Phase 3: the engine drives a non-browser target (the console) ───
;; render-to-console (web/console-render.sx) prints the live engine tree as text.
;; These assert the picker's terminal rendering directly on a built tree — the
;; console platform's draw step, proven without a terminal.
(defsuite
"relate-picker:console"
(deftest
"the picker form renders as a filter field over a bulleted candidate list"
(let
((root (mk-root (str
"<form class=\"relate-picker\">"
"<input type=\"hidden\" name=\"kind\" value=\"related\">"
"<input type=\"text\" name=\"q\" class=\"rp-filter\" placeholder=\"filter…\">"
"<ul class=\"rp-results\">"
(row-html "host" "related" "item-0" "Picker Item 0")
(row-html "host" "related" "item-1" "Picker Item 1")
(sentinel-html "host" "related" 20)
"</ul>"
"</form>")))
(form (dom-query ".relate-picker")))
(let ((txt (render-to-console form)))
;; the text input becomes a labelled field (placeholder as the label)...
(assert-true (contains? txt "filter…: ["))
;; ...the hidden kind input is not drawn...
(assert-false (contains? txt "related: ["))
;; ...each candidate is a bullet, the sentinel an ellipsis line
(assert-true (contains? txt "• Picker Item 0"))
(assert-true (contains? txt "• Picker Item 1"))
(assert-true (contains? txt "… Loading more…")))
(clear-root! root)))
(deftest
"an empty results list renders no bullets"
(let
((root (mk-root "<ul class=\"rp-results\"></ul>"))
(results (dom-query root ".rp-results"))) ;; scope to this root
(assert-equal "" (render-to-console results))
(clear-root! root))))