# Plan: SX-native engine tests (browser-independent) ## Goal Move the host's *interactive* test coverage from Playwright (`.spec.js`, drives a real Chromium) into **SX harness tests** that drive the hypermedia engine against a **mock platform** — no browser. Reserve Playwright for the one irreducible real-browser fact: "the WASM kernel actually compiles, boots, and loads modules content-addressed." **Why (the principle):** the SX engine (`web/engine.sx` + `web/orchestration.sx`) has no hard browser dependency — it talks to a *platform* (fetch, DOM ops, timers) that is injected. The harness supplies a mock platform, so engine behaviour (fetch → swap → DOM mutation) is asserted with zero browser. The same engine could therefore drive *something else* (a server-side DOM, a native UI) — the SX tests prove that independence by running without one. This is consistent with `[[project_zero_dependencies]]` and `[[feedback_runtime_control]]` (build IN the runtime). ## Current state (2026-06-29) - **Already SX:** the 272 host conformance tests (`lib/host/tests/*.sx`, `spec/harness.sx` mock-IO). The picker's *server contract* is SX too (`lib/host/tests/blog.sx`: `picker form declaratively wired`, `load-more sentinel`, `no-sentinel-on-short-page`). - **Still Playwright (`.spec.js`):** `lib/host/playwright/relate-picker.spec.js` (7 tests) and `spa-check.spec.js` (4) — real-browser checks of populate / filter / paging / relate-delete / remove-button / boosted-nav / error-retry / WASM boot. ## Infrastructure that already exists (the enabler — verified) - `spec/harness.sx` — `make-harness`, `default-platform` with **`:fetch` overridable** (`(fn (url &rest opts) {:status 200 :body "" :ok true})`), plus DOM ops, `:now`, etc. - `web/harness-web.sx` — `(define-library (sx harness-web))` exports: `mock-element`, `mock-set-attr!`, `mock-append-child!`, `mock-get-attr`, `mock-add-listener!`, **`simulate-click` / `simulate-input` / `simulate-event`**, `assert-text`, `assert-attr`, `assert-class`, `assert-no-class`, `assert-child-count`, `assert-event-fired`, `make-web-harness`, render-audit helpers. - `web/tests/` — existing SX engine tests: `test-orchestration.sx` (17 deftests), `test-forms.sx` (25), `test-swap-integration.sx` (43, mock-response → swap → assert), `test-engine.sx`, `test-handlers.sx`. **`test-swap-integration.sx` is the reference pattern** (it sets `_mock-body`/`_mock-headers`/`_mock-content-type`, drives a swap, asserts the result). - Runner: `hosts/ocaml/bin/run_tests.ml` scans `spec/tests/`, `lib/tests/`, `web/tests/` and loads `harness-web.sx` + `harness-reactive.sx`. Run via the `sx_test host="ocaml"` MCP tool (or `./scripts/sx-build-all.sh`). JS runner: `hosts/javascript/run_tests.js` also loads the web harnesses. ## Phases ### Phase 0 — Proof of concept (small): one behavior, SX Port **relate → delete row** to an SX harness test (new `web/tests/test-relate-picker.sx`): 1. Build a mock DOM: a `.rp-results` `