diff --git a/plans/sx-native-engine-tests.md b/plans/sx-native-engine-tests.md new file mode 100644 index 00000000..6f4049c6 --- /dev/null +++ b/plans/sx-native-engine-tests.md @@ -0,0 +1,110 @@ +# 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` `