use std::time::Duration;
mod common;
use common::Js;
const GOOD_APP: &[u8] = br#"
(function () {
if (globalThis.__started) {
Deno.core.ops.op_emit("phase", "update:" + globalThis.__n);
globalThis.__loop();
return;
}
globalThis.__started = true;
globalThis.__n = 41;
globalThis.__loop = () => (async () => {
for (;;) {
const m = await Deno.core.ops.op_next_event();
if (m == null) return; // shutdown
if (m.t === "reload") return; // yield to Rust so it can re-exec the app
if (m.t === "uiEvent") {
globalThis.__n++;
Deno.core.ops.op_emit("phase", "click:" + globalThis.__n);
}
}
})();
Deno.core.ops.op_emit("phase", "init:" + globalThis.__n);
globalThis.__loop();
})();
"#;
const BROKEN_APP: &[u8] = b"aa16;\n";
#[test]
fn broken_reload_is_rejected_and_recovers() {
let js = Js::spawn("broken_reload", GOOD_APP);
let recv_prefix = |prefix: &str| -> u32 {
loop {
let phase = js
.emitted("phase", Duration::from_secs(10))
.as_str()
.unwrap()
.to_string();
if let Some(n) = phase.strip_prefix(prefix) {
return n.parse().expect("counter");
}
}
};
assert_eq!(recv_prefix("init:"), 41);
js.click();
assert_eq!(recv_prefix("click:"), 42);
std::fs::write(&js.app, BROKEN_APP).expect("write broken app");
js.reload.send(()).expect("send reload");
assert_eq!(
recv_prefix("update:"),
42,
"a broken reload tore down the working app instead of being rejected"
);
js.click();
assert!(
recv_prefix("click:") > 42,
"event loop did not survive a rejected (broken) hot reload"
);
std::fs::write(&js.app, GOOD_APP).expect("rewrite good app");
js.reload.send(()).expect("send reload");
recv_prefix("update:"); js.click();
assert!(
recv_prefix("click:") > 42,
"event loop broken after recovery"
);
eprintln!("OK broken reload rejected; working UI preserved and recovered on next good edit");
}