feat(wasm): validate client event and state ABI

req: client_local/005\nreq: client_local/006\nreq: client_local/007\nreq: client_local/008\nreq: client_local/009\nreq: client_local/010\nreq: client_local/014
This commit is contained in:
slhx agent
2026-07-13 12:42:31 +02:00
parent 40643e360d
commit 38eae79a2f
9 changed files with 246 additions and 24 deletions
+46 -4
View File
@@ -45,7 +45,7 @@ async fn client_handler_applies_effect_batch_without_network() -> WebDriverResul
wait_for_text(
&driver,
"[data-hemx-slot='counter_panel']",
"updated by Rust/WASM",
"updated by Rust/WASM (click, count=3)",
)
.await?;
@@ -54,6 +54,46 @@ async fn client_handler_applies_effect_batch_without_network() -> WebDriverResul
network_before,
"client handler made a network request"
);
driver
.execute(
"document.querySelector('[data-hemx-root]').setAttribute('data-hemx-client-state-version', '2'); return true",
Vec::new(),
)
.await?;
driver
.find(By::Css("[data-hemx-client='increment']"))
.await?
.click()
.await?;
wait_until(&driver, "return window.__clientErrors.length === 1").await?;
assert!(
driver
.execute("return window.__clientErrors[0].message", Vec::new())
.await?
.json()
.as_str()
.unwrap_or_default()
.contains("unsupported client-local state ABI version 2; expected 1"),
"invalid state must produce an actionable client-local diagnostic"
);
assert_eq!(
resource_count(&driver).await?,
network_before + 1,
"declared server fallback was not requested"
);
assert!(
driver
.execute(
"return !document.querySelector('[data-hemx-client]').classList.contains('is-pending')",
Vec::new(),
)
.await?
.json()
.as_bool()
.unwrap_or(false),
"invalid input must restore pending UI"
);
Ok::<(), WebDriverError>(())
}
.await;
@@ -160,9 +200,9 @@ fn serve(mut stream: TcpStream, package: &Path, runtime: &Path) {
fn fixture_html() -> String {
r#"<!doctype html><html><body>
<main data-hemx-root="client_local">
<main data-hemx-root="client_local" data-hemx-st="count=3" data-hemx-client-state-version="1">
<section data-hemx-slot="counter_panel">idle</section>
<button type="button" data-hid="1" data-hemx-client="increment">Increment locally</button>
<button type="button" data-hid="1" data-hemx-on="click" data-hemx-client="increment" data-hemx-client-fallback data-hemx-pending-class="is-pending">Increment locally</button>
</main>
<script src="/hemx.js"></script>
<script type="module">
@@ -170,11 +210,13 @@ import init, { __hemx_client_increment } from "/client_local.js";
await init();
const root = document.querySelector("[data-hemx-root]");
const slot = root.querySelector("[data-hemx-slot]");
const probe = __hemx_client_increment();
const probe = __hemx_client_increment(1, "click", undefined, undefined, undefined, 1, "count=3");
const batch = window.hemx.decodeBatch(probe);
root.dataset.hemxBuild = String(batch.fingerprint);
slot.dataset.sid = String(batch.ops[0].target.resource.id);
window.hemx.registerClientHandler("increment", __hemx_client_increment);
window.__clientErrors = [];
root.addEventListener("hemx:client-error", (event) => window.__clientErrors.push(event.detail));
window.__clientReady = true;
</script></body></html>"#
.to_owned()