;; Testing section — SX tests SX across every host.
;; Modular test specs: eval, parser, router, render.
;; Each page shows spec source, runs server-side, offers browser-side run button.

;; ---------------------------------------------------------------------------
;; Overview page
;; ---------------------------------------------------------------------------

(defcomp ~testing/overview-content (&key server-results framework-source eval-source parser-source router-source render-source deps-source engine-source)
  (~docs/page :title "Testing"
    (div (~tw :tokens "space-y-8")

      ;; Intro
      (div (~tw :tokens "space-y-4")
        (p (~tw :tokens "text-lg text-stone-600")
          "SX tests itself. Test specs are written in SX and executed by SX evaluators on every host. "
          "The same assertions run in Python, Node.js, and in the browser — from the same source files.")
        (p (~tw :tokens "text-stone-600")
          "The framework defines two macros ("
          (code (~tw :tokens "text-violet-700 text-sm") "deftest")
          " and "
          (code (~tw :tokens "text-violet-700 text-sm") "defsuite")
          ") and nine assertion helpers, all in SX. Each host provides only "
          (strong "five platform functions")
          " — everything else is pure SX."))

      ;; Architecture
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Architecture")
        (div (~tw :tokens "not-prose bg-stone-100 rounded-lg p-5 mx-auto max-w-3xl")
          (pre (~tw :tokens "text-sm leading-relaxed whitespace-pre-wrap break-words font-mono text-stone-700")
"test-framework.sx            Macros + assertion helpers (loaded first)
    |
    +-- test-eval.sx           81 tests: evaluator + primitives
    +-- test-parser.sx         39 tests: tokenizer, parser, serializer
    +-- test-router.sx         18 tests: route matching + param extraction
    +-- test-render.sx         23 tests: HTML rendering + components
    +-- test-deps.sx           33 tests: dependency analysis + IO detection
    +-- test-engine.sx         37 tests: trigger/swap/retry parsing

Runners:
    run.js                     Node.js — injects platform fns, runs specs
    run.py                     Python — injects platform fns, runs specs
    sx-test-runner.js          Browser — runs specs in this page

Platform functions (5 total):
    try-call (thunk)          -> {:ok true} | {:ok false :error \"msg\"}
    report-pass (name)        -> output pass
    report-fail (name error)  -> output fail
    push-suite (name)         -> push suite context
    pop-suite ()              -> pop suite context

Per-spec platform functions:
    parser:  sx-parse, sx-serialize, make-symbol, make-keyword, ...
    router:  (none — pure spec, uses bootstrapped functions)
    render:  render-html (wraps parse + render-to-html)
    deps:    test-env (returns current evaluation environment)
    engine:  (none — pure spec, uses bootstrapped functions)")))

      ;; Server results
      (when server-results
        (div (~tw :tokens "space-y-3")
          (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Server: Python evaluator")
          (p (~tw :tokens "text-stone-600")
            "Ran all test specs when this page loaded — "
            (strong (str (get server-results "passed") "/" (get server-results "total") " passed"))
            " in " (str (get server-results "elapsed-ms")) "ms.")
          (pre (~tw :tokens "text-sm font-mono bg-stone-900 text-green-400 rounded-lg p-4 overflow-x-auto max-h-96 overflow-y-auto")
            (get server-results "output"))))

      ;; Browser test runner
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Browser: JavaScript evaluator")
        (p (~tw :tokens "text-stone-600")
          "Run all test specs in the browser using "
          (code (~tw :tokens "text-violet-700 text-sm") "sx-browser.js")
          ":")
        (div (~tw :tokens "flex items-center gap-4")
          (button :id "test-btn-all"
            (~tw :tokens "px-4 py-2 rounded-md bg-violet-600 text-white font-medium text-sm hover:bg-violet-700 cursor-pointer")
            :onclick "sxRunModularTests('all','test-output-all','test-btn-all')"
            "Run all tests"))
        (pre :id "test-output-all"
          (~tw :tokens "text-sm font-mono bg-stone-900 text-green-400 rounded-lg p-4 overflow-x-auto max-h-96 overflow-y-auto")
          :style "display:none"
          "")
        ;; Hidden: all spec sources for the browser runner
        (textarea :id "test-framework-source" :style "display:none" framework-source)
        (textarea :id "test-spec-eval" :style "display:none" eval-source)
        (textarea :id "test-spec-parser" :style "display:none" parser-source)
        (textarea :id "test-spec-router" :style "display:none" router-source)
        (textarea :id "test-spec-render" :style "display:none" render-source)
        (textarea :id "test-spec-deps" :style "display:none" deps-source)
        (textarea :id "test-spec-engine" :style "display:none" engine-source)
        (script :src (asset-url "/scripts/sx-test-runner.js")))

      ;; Test spec index
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Test specs")
        (div (~tw :tokens "grid grid-cols-1 md:grid-cols-2 gap-4")
          (a :href "/sx/(language.(test.eval))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Evaluator")
            (p (~tw :tokens "text-sm text-stone-500") "81 tests — literals, arithmetic, strings, lists, dicts, special forms, lambdas, components, macros")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-eval.sx"))
          (a :href "/sx/(language.(test.parser))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Parser")
            (p (~tw :tokens "text-sm text-stone-500") "39 tests — tokenization, parsing, escape sequences, quote sugar, serialization, round-trips")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-parser.sx"))
          (a :href "/sx/(language.(test.router))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Router")
            (p (~tw :tokens "text-sm text-stone-500") "18 tests — path splitting, pattern parsing, segment matching, parameter extraction")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-router.sx"))
          (a :href "/sx/(language.(test.render))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Renderer")
            (p (~tw :tokens "text-sm text-stone-500") "23 tests — elements, attributes, void elements, fragments, escaping, control flow, components")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-render.sx"))
          (a :href "/sx/(language.(test.deps))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Dependencies")
            (p (~tw :tokens "text-sm text-stone-500") "33 tests — scan-refs, transitive-deps, components-needed, IO detection, purity classification")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-deps.sx"))
          (a :href "/sx/(language.(test.engine))" (~tw :tokens "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors")
            (h3 (~tw :tokens "font-semibold text-stone-800") "Engine")
            (p (~tw :tokens "text-sm text-stone-500") "37 tests — parse-time, trigger specs, swap specs, retry logic, param filtering")
            (p (~tw :tokens "text-xs text-violet-600 mt-1") "test-engine.sx"))))

      ;; What it proves
      (div (~tw :tokens "rounded-lg border border-blue-200 bg-blue-50 p-5 space-y-3")
        (h2 (~tw :tokens "text-lg font-semibold text-blue-900") "What this proves")
        (ol (~tw :tokens "list-decimal list-inside text-blue-800 space-y-2 text-sm")
          (li "Test specs are " (strong "written in SX") " and " (strong "executed by SX") " — no code generation")
          (li "The same tests run on " (strong "Python, Node.js, and in the browser") " from the same files")
          (li "Each host provides only " (strong "5 platform functions") " — everything else is pure SX")
          (li "Modular specs test each part independently — evaluator, parser, router, renderer")
          (li "Per-spec platform functions extend the 5-function contract for module-specific capabilities")
          (li "Platform divergences are " (strong "exposed by the tests") ", not hidden"))))))


;; ---------------------------------------------------------------------------
;; Per-spec test page (reusable for eval, parser, router, render)
;; ---------------------------------------------------------------------------

(defcomp ~testing/spec-content (&key (spec-name :as string) (spec-title :as string) (spec-desc :as string) (spec-source :as string) (framework-source :as string) (server-results :as dict?))
  (~docs/page :title spec-title
    (div (~tw :tokens "space-y-8")

      ;; Description
      (div (~tw :tokens "space-y-4")
        (p (~tw :tokens "text-lg text-stone-600") spec-desc))

      ;; Server-side results
      (when server-results
        (div (~tw :tokens "space-y-3")
          (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Server: Python evaluator")
          (p (~tw :tokens "text-stone-600")
            "Ran "
            (code (~tw :tokens "text-violet-700 text-sm") (str "test-" spec-name ".sx"))
            " when this page loaded — "
            (strong (str (get server-results "passed") "/" (get server-results "total") " passed"))
            " in " (str (get server-results "elapsed-ms")) "ms.")
          (pre (~tw :tokens "text-sm font-mono bg-stone-900 text-green-400 rounded-lg p-4 overflow-x-auto max-h-96 overflow-y-auto")
            (get server-results "output"))))

      ;; Browser test runner
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Browser: JavaScript evaluator")
        (p (~tw :tokens "text-stone-600")
          "Run this spec in the browser:")
        (div (~tw :tokens "flex items-center gap-4")
          (button :id (str "test-btn-" spec-name)
            (~tw :tokens "px-4 py-2 rounded-md bg-violet-600 text-white font-medium text-sm hover:bg-violet-700 cursor-pointer")
            :onclick (str "sxRunModularTests('" spec-name "','test-output-" spec-name "','test-btn-" spec-name "')")
            (str "Run " spec-title)))
        (pre :id (str "test-output-" spec-name)
          (~tw :tokens "text-sm font-mono bg-stone-900 text-green-400 rounded-lg p-4 overflow-x-auto max-h-96 overflow-y-auto")
          :style "display:none"
          "")
        ;; Hidden: spec source + framework source for the browser runner
        (textarea :id (str "test-spec-" spec-name) :style "display:none" spec-source)
        (textarea :id "test-framework-source" :style "display:none" framework-source)
        (script :src (asset-url "/scripts/sx-test-runner.js")))

      ;; Full source
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Specification source")
        (p (~tw :tokens "text-xs text-stone-400 italic")
          (str "test-" spec-name ".sx")
          " — the canonical test specification for this module.")
        (div (~tw :tokens "not-prose bg-stone-100 rounded-lg p-5 mx-auto max-w-3xl")
          (pre (~tw :tokens "text-sm leading-relaxed whitespace-pre-wrap break-words")
            (code (highlight spec-source "sx"))))))))


;; ---------------------------------------------------------------------------
;; Runners page
;; ---------------------------------------------------------------------------

(defcomp ~testing/runners-content ()
  (~docs/page :title "Test Runners"
    (div (~tw :tokens "space-y-8")

      (div (~tw :tokens "space-y-4")
        (p (~tw :tokens "text-lg text-stone-600")
          "Three runners execute the same test specs on different hosts. Each injects the five platform functions and any per-spec extensions, then evaluates the SX source directly.")
        (p (~tw :tokens "text-stone-600")
          "All runners produce "
          (a :href "https://testanything.org/" (~tw :tokens "text-violet-700 underline") "TAP")
          " output and accept module names as arguments."))

      ;; Node.js runner
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Node.js: run.js")
        (div (~tw :tokens "rounded-lg border border-stone-200 bg-white p-5 space-y-3")
          (h3 (~tw :tokens "text-sm font-medium text-stone-500 uppercase tracking-wide") "Usage")
          (~docs/code :src
            (highlight "# Run all specs\nnode shared/sx/tests/run.js\n\n# Run specific specs\nnode shared/sx/tests/run.js eval parser\n\n# Legacy mode (monolithic test.sx)\nnode shared/sx/tests/run.js --legacy" "bash")))
        (p (~tw :tokens "text-stone-600 text-sm")
          "Uses "
          (code (~tw :tokens "text-violet-700 text-sm") "sx-browser.js")
          " as the evaluator. Router tests use bootstrapped functions from "
          (code (~tw :tokens "text-violet-700 text-sm") "Sx.splitPathSegments")
          " etc. Render tests use "
          (code (~tw :tokens "text-violet-700 text-sm") "Sx.renderToHtml")
          "."))

      ;; Python runner
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Python: run.py")
        (div (~tw :tokens "rounded-lg border border-stone-200 bg-white p-5 space-y-3")
          (h3 (~tw :tokens "text-sm font-medium text-stone-500 uppercase tracking-wide") "Usage")
          (~docs/code :src
            (highlight "# Run all specs\npython shared/sx/tests/run.py\n\n# Run specific specs\npython shared/sx/tests/run.py eval parser\n\n# Legacy mode (monolithic test.sx)\npython shared/sx/tests/run.py --legacy" "bash")))
        (p (~tw :tokens "text-stone-600 text-sm")
          "Uses the hand-written Python evaluator ("
          (code (~tw :tokens "text-violet-700 text-sm") "evaluator.py")
          "). Router tests import bootstrapped functions from "
          (code (~tw :tokens "text-violet-700 text-sm") "sx_ref.py")
          " because the hand-written evaluator's "
          (code (~tw :tokens "text-violet-700 text-sm") "set!")
          " doesn't propagate across lambda closure copies."))

      ;; Browser runner
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Browser: sx-test-runner.js")
        (p (~tw :tokens "text-stone-600")
          "Runs in the browser on each test page. The spec source is embedded in a hidden textarea; the runner parses and evaluates it using the same "
          (code (~tw :tokens "text-violet-700 text-sm") "sx-browser.js")
          " that renders the page itself.")
        (p (~tw :tokens "text-stone-600")
          "This is the strongest proof of cross-host parity — the browser evaluator that users depend on is the same one running the tests."))

      ;; Platform functions table
      (div (~tw :tokens "space-y-3")
        (h2 (~tw :tokens "text-2xl font-semibold text-stone-800") "Platform functions")
        (div (~tw :tokens "overflow-x-auto rounded border border-stone-200")
          (table (~tw :tokens "w-full text-left text-sm")
            (thead (tr (~tw :tokens "border-b border-stone-200 bg-stone-100")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Function")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Scope")
              (th (~tw :tokens "px-3 py-2 font-medium text-stone-600") "Purpose")))
            (tbody
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "try-call")
                (td (~tw :tokens "px-3 py-2") "All specs")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Call a thunk, catch errors, return result dict"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "report-pass")
                (td (~tw :tokens "px-3 py-2") "All specs")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Report a passing test"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "report-fail")
                (td (~tw :tokens "px-3 py-2") "All specs")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Report a failing test with error message"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "push-suite")
                (td (~tw :tokens "px-3 py-2") "All specs")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Push suite name onto context stack"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "pop-suite")
                (td (~tw :tokens "px-3 py-2") "All specs")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Pop suite name from context stack"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "sx-parse")
                (td (~tw :tokens "px-3 py-2") "Parser")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Parse SX source to AST"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "sx-serialize")
                (td (~tw :tokens "px-3 py-2") "Parser")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Serialize AST back to SX source text"))
              (tr (~tw :tokens "border-b border-stone-100")
                (td (~tw :tokens "px-3 py-2 font-mono text-sm text-violet-700") "render-html")
                (td (~tw :tokens "px-3 py-2") "Renderer")
                (td (~tw :tokens "px-3 py-2 text-stone-700") "Parse SX source and render to HTML string"))))))

      ;; Adding a new host
      (div (~tw :tokens "rounded-lg border border-blue-200 bg-blue-50 p-5 space-y-3")
        (h2 (~tw :tokens "text-lg font-semibold text-blue-900") "Adding a new host")
        (ol (~tw :tokens "list-decimal list-inside text-blue-800 space-y-2 text-sm")
          (li "Implement the 5 platform functions in your target language")
          (li "Load " (code (~tw :tokens "text-sm") "test-framework.sx") " — it defines deftest/defsuite macros")
          (li "Load the spec file(s) — the evaluator runs the tests automatically")
          (li "Add per-spec platform functions if testing parser or renderer")
          (li "Compare TAP output across hosts — divergences reveal platform-specific semantics"))))))
