htmx demos working: activation, fetch, swap, OOB filtering, test runner page
- htmx-boot-subtree! wired into process-elements for auto-activation
- Fixed cond compilation bug in hx-verb-info (Clojure-style flat cond)
- Platform io-fetch upgraded: method/body/headers support, full response dict
- Replaced perform IO ops with browser primitives (set-timeout, browser-confirm, etc)
- SX→HTML rendering in hx-do-swap with OOB section filtering
- hx-collect-params: collects input name/value for all methods
- Handler naming: ex-{slug} convention, removed perform IO dependencies
- Test runner page at (test.(applications.(htmx))) with iframe-based runner
- Header "test" link on every page linking to test URL
- Page file restructure: 285 files moved to URL-matching paths (a/b/c/index.sx)
- page-functions.sx: ~100 component name references updated
- _test added to skip_dirs, test- file prefix convention for test files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
176
sx/sx/applications/htmx/runner.sx
Normal file
176
sx/sx/applications/htmx/runner.sx
Normal file
@@ -0,0 +1,176 @@
|
||||
;; Test runner page for the htmx demo
|
||||
;; URL: /sx/(test.(applications.(htmx)))
|
||||
|
||||
(defcomp
|
||||
()
|
||||
(~docs/page
|
||||
:title "Test: htmx demos"
|
||||
(p
|
||||
(~tw :tokens "text-stone-500 mb-4")
|
||||
"Running tests against "
|
||||
(a
|
||||
:href "/sx/(applications.(htmx))"
|
||||
(~tw :tokens "text-violet-600 underline")
|
||||
"/sx/(applications.(htmx))"))
|
||||
(div
|
||||
(~tw :tokens "flex items-center gap-4 mb-4")
|
||||
(button
|
||||
:id "run-btn"
|
||||
(~tw
|
||||
:tokens "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors")
|
||||
"Run All Tests")
|
||||
(span :id "test-status" (~tw :tokens "text-sm text-stone-500") "Ready"))
|
||||
(div :id "test-summary" (~tw :tokens "mb-4"))
|
||||
(div
|
||||
:id "test-list"
|
||||
(~tw :tokens "space-y-2 mb-6")
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "click-to-load"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "click-to-load")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"Click button, verify content loads"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest click-to-load\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (click \"button[hx-get]\")\n (wait 2000)\n (assert-text \"#click-result\" :contains \"Content loaded!\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "click-no-oob-leak"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "click-no-oob-leak")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"OOB swap sections filtered from content"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest click-no-oob-leak\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (click \"button[hx-get]\")\n (wait 2000)\n (assert-text \"#click-result\" :not-contains \"defcomp\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "search-debounce"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "search-debounce")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"Type in search, results appear after debounce"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest search-debounce\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (fill \"input[hx-get]\" \"hx-get\")\n (wait 1500)\n (assert-text \"#search-results\" :contains \"GET request\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "search-no-results"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "search-no-results")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"Non-matching query shows no results"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest search-no-results\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (fill \"input[hx-get]\" \"xyznonexistent\")\n (wait 1500)\n (assert-text \"#search-results\" :contains \"No results\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "tab-overview"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "tab-overview")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"Click overview tab, verify content"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest tab-overview\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (click \"button[hx-get*='tab=overview']\")\n (wait 2000)\n (assert-text \"#htmx-tab-content\" :contains \"htmx gives you access\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "tab-features"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "tab-features")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"Click features tab, verify list"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest tab-features\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (click \"button[hx-get*='tab=features']\")\n (wait 2000)\n (assert-text \"#htmx-tab-content\" :contains \"Any element\"))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "append-item"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "append-item")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"POST appends new item to list"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest append-item\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (click \"button[hx-post*='api.append']\")\n (wait 2000)\n (assert-count \"#item-list > *\" :gte 1))")))
|
||||
(details
|
||||
:class "sx-test-item"
|
||||
:data-test "form-submit"
|
||||
(~tw :tokens "border border-stone-200 rounded-lg overflow-hidden")
|
||||
(summary
|
||||
(~tw
|
||||
:tokens "px-4 py-2 cursor-pointer hover:bg-stone-50 flex items-center gap-2")
|
||||
(span :data-role "test-icon" (~tw :tokens "font-bold") "○")
|
||||
(span (~tw :tokens "font-medium") "form-submit")
|
||||
(span
|
||||
(~tw :tokens "text-sm text-stone-400 ml-auto")
|
||||
"POST form data, verify name in response"))
|
||||
(div
|
||||
(~tw :tokens "px-4 py-3 bg-stone-50 border-t border-stone-200")
|
||||
(pre
|
||||
(~tw
|
||||
:tokens "text-xs font-mono whitespace-pre-wrap text-stone-600")
|
||||
"(deftest form-submit\n :runner :playwright\n :url \"/sx/(applications.(htmx))\"\n (fill \"form[hx-post] input[name='name']\" \"Alice\")\n (fill \"form[hx-post] input[name='email']\" \"alice@test.com\")\n (click \"form[hx-post] button[type='submit']\")\n (wait 2000)\n (assert-text \"#form-result\" :contains \"Alice\"))"))))
|
||||
(iframe
|
||||
:id "test-iframe"
|
||||
:src "/sx/(applications.(htmx))"
|
||||
(~tw :tokens "w-full border border-stone-200 rounded-lg")
|
||||
:style "height:600px")
|
||||
(script :src "/static/scripts/sx-test-runner.js?v=7")))
|
||||
Reference in New Issue
Block a user