use anyhow::Result;
use fission_core::ui::{TextInput, Widget};
use fission_core::{InputEvent, LayoutPoint};
use fission_ir::Role;
use fission_test::{detect_ir_cycle, TestHarness};
#[derive(Debug, Default, Clone)]
struct GlobalState {
_text: String,
checked: bool,
}
impl fission_core::action::GlobalState for GlobalState {}
#[test]
fn text_input_focus_has_no_ir_cycles() -> Result<()> {
#[derive(Clone)]
struct Root;
impl From<Root> for Widget {
fn from(_component: Root) -> Self {
let (_ctx, _view) = fission_core::build::current::<GlobalState>();
TextInput {
value: String::new(),
placeholder: Some("type".into()),
width: Some(200.0),
height: Some(40.0),
..Default::default()
}
.into()
}
}
let mut h = TestHarness::new(GlobalState::default()).with_root_widget(Root);
h.pump()?;
let ir = h.last_ir.as_ref().unwrap();
let mut text_node = None;
for (id, node) in &ir.nodes {
if let fission_ir::Op::Semantics(s) = &node.op {
if s.role == Role::TextInput {
text_node = Some(*id);
break;
}
}
}
let id = text_node.expect("TextInput semantics not found");
let rect = h.last_snapshot.as_ref().unwrap().get_node_rect(id).unwrap();
let center = LayoutPoint::new(
rect.x() + rect.width() / 2.0,
rect.y() + rect.height() / 2.0,
);
h.send_event(InputEvent::Pointer(
fission_core::event::PointerEvent::Down {
point: center,
button: fission_core::event::PointerButton::Primary,
modifiers: 0,
},
))?;
h.pump()?;
let ir2 = h.last_ir.as_ref().unwrap();
assert!(
detect_ir_cycle(ir2).is_none(),
"IR contains cycle after focusing TextInput"
);
Ok(())
}
#[test]
fn checkbox_toggle_has_no_ir_cycles() -> Result<()> {
use fission_core::Widget;
use fission_widgets::Checkbox;
use fission_core::event::{PointerButton, PointerEvent};
#[fission_macros::fission_reducer(Toggle)]
fn on_toggle(state: &mut GlobalState) {
state.checked = !state.checked;
}
#[derive(Clone)]
struct Root;
impl From<Root> for Widget {
fn from(_component: Root) -> Self {
let (ctx, view) = fission_core::build::current::<GlobalState>();
Checkbox {
checked: view.state().checked,
on_toggle: Some(fission_core::with_reducer!(ctx, Toggle, on_toggle)),
label: Some("check".into()),
..Default::default()
}
.into()
}
}
let mut h = TestHarness::new(GlobalState::default()).with_root_widget(Root);
h.pump()?;
let ir = h.last_ir.as_ref().unwrap();
let mut cb_node = None;
for (id, node) in &ir.nodes {
if let fission_ir::Op::Semantics(s) = &node.op {
if s.role == Role::Checkbox {
cb_node = Some(*id);
break;
}
}
}
let id = cb_node.expect("Checkbox semantics not found");
let rect = h.last_snapshot.as_ref().unwrap().get_node_rect(id).unwrap();
let center = LayoutPoint::new(
rect.x() + rect.width() / 2.0,
rect.y() + rect.height() / 2.0,
);
h.send_event(fission_core::InputEvent::Pointer(PointerEvent::Down {
point: center,
button: PointerButton::Primary,
modifiers: 0,
}))?;
h.pump()?;
h.send_event(fission_core::InputEvent::Pointer(PointerEvent::Up {
point: center,
button: PointerButton::Primary,
modifiers: 0,
}))?;
h.pump()?;
let ir2 = h.last_ir.as_ref().unwrap();
if let Some(cycle) = fission_test::detect_ir_cycle(ir2) {
panic!("IR cycle after checkbox toggle: {:?}", cycle);
}
Ok(())
}