#![allow(dead_code)]
use std::path::PathBuf;
use std::time::{Duration, Instant};
use bevy_react_core::animations::AnimationCommand;
use bevy_react_core::js_thread::{FlushInfo, HostSenders, spawn_js_thread};
use bevy_react_core::protocol::op::Op;
use bevy_react_core::protocol::outbound::{Outbound, UiEvent};
use bevy_react_core::{RawRequest, ReactMessage};
use crossbeam_channel::{Receiver, RecvTimeoutError};
use tokio::sync::mpsc::UnboundedSender;
pub struct Js {
pub ops: Receiver<Vec<Op>>,
pub emits: Receiver<ReactMessage>,
pub flushes: Receiver<FlushInfo>,
pub reload: UnboundedSender<()>,
pub app: PathBuf,
outbound: UnboundedSender<Outbound>,
_requests: Receiver<RawRequest>,
_anims: Receiver<AnimationCommand>,
}
impl Js {
pub fn spawn(test: &str, app: impl AsRef<[u8]>) -> Self {
let dir = std::env::temp_dir().join(format!("bevy-react-{test}-{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("create temp bundle dir");
let vendor = dir.join("vendor.js");
let app_path = dir.join("app.js");
std::fs::write(&vendor, "").expect("write vendor");
std::fs::write(&app_path, app).expect("write app");
let (ops_tx, ops) = crossbeam_channel::unbounded();
let (flush_tx, flushes) = crossbeam_channel::unbounded();
let (emit_tx, emits) = crossbeam_channel::unbounded();
let (request_tx, _requests) = crossbeam_channel::unbounded();
let (anim_tx, _anims) = crossbeam_channel::unbounded();
let (outbound, outbound_rx) = tokio::sync::mpsc::unbounded_channel();
let (reload, reload_rx) = tokio::sync::mpsc::unbounded_channel();
let senders = HostSenders {
ops: ops_tx,
flush: flush_tx,
emit: emit_tx,
request: request_tx,
anim: anim_tx,
};
spawn_js_thread(
bevy_react_core::ext::ExtRegistrySlot::ready(bevy_react_core::ext::builtin_registry()),
vendor,
app_path.clone(),
senders,
outbound_rx,
reload_rx,
);
Self {
ops,
emits,
flushes,
reload,
app: app_path,
outbound,
_requests,
_anims,
}
}
pub fn click(&self) {
self.outbound
.send(Outbound::UiEvent {
event: UiEvent {
id: 1,
kind: "click".into(),
..Default::default()
},
})
.expect("JS thread gone");
}
pub fn emitted(&self, name: &str, timeout: Duration) -> serde_json::Value {
let deadline = Instant::now() + timeout;
loop {
let left = deadline.saturating_duration_since(Instant::now());
match self.emits.recv_timeout(left) {
Ok(msg) if msg.name == name => return msg.value,
Ok(_) => {}
Err(RecvTimeoutError::Timeout) => panic!("no {name:?} emit within {timeout:?}"),
Err(RecvTimeoutError::Disconnected) => panic!("JS thread died before {name:?}"),
}
}
}
}