use crossterm::event::{
Event as CtEvent, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers,
};
use rdom_core::{ListenerOptions, NodeId};
use std::cell::Cell;
use std::rc::Rc;
use crate::TuiDom;
use crate::render::{Terminal, TestBackend};
use crate::runtime::app::App;
use crate::style::Stylesheet;
fn test_app(dom: TuiDom) -> App<TestBackend> {
let backend = TestBackend::new(20, 5);
let terminal = Terminal::new(backend).unwrap();
App::with_backend(dom, Stylesheet::bare(), terminal).unwrap()
}
fn button_focused() -> (App<TestBackend>, NodeId) {
let mut dom: TuiDom = TuiDom::new();
let root = dom.root();
let btn = dom.create_element("button");
dom.append_child(root, btn).unwrap();
dom.set_focused(Some(btn));
(test_app(dom), btn)
}
fn key_press(code: KeyCode, modifiers: KeyModifiers) -> CtEvent {
CtEvent::Key(KeyEvent {
code,
modifiers,
kind: KeyEventKind::Press,
state: KeyEventState::empty(),
})
}
fn record_click_count(app: &mut App<TestBackend>, btn: NodeId) -> Rc<Cell<u32>> {
let count = Rc::new(Cell::new(0));
let c = count.clone();
app.dom_mut()
.add_event_listener(btn, "click", ListenerOptions::default(), move |_| {
c.set(c.get() + 1);
})
.unwrap();
count
}
#[test]
fn enter_on_focused_button_synthesizes_click() {
let (mut app, btn) = button_focused();
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
#[test]
fn space_on_focused_button_synthesizes_click() {
let (mut app, btn) = button_focused();
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Char(' '), KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
#[test]
fn enter_on_non_button_does_not_synthesize_click() {
let mut dom: TuiDom = TuiDom::new();
let root = dom.root();
let div = dom.create_element("div");
dom.set_attribute(div, "tabindex", "0").unwrap();
dom.append_child(root, div).unwrap();
dom.set_focused(Some(div));
let mut app = test_app(dom);
let count = Rc::new(Cell::new(0));
let c = count.clone();
app.dom_mut()
.add_event_listener(div, "click", ListenerOptions::default(), move |_| {
c.set(c.get() + 1);
})
.unwrap();
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 0);
}
#[test]
fn ctrl_enter_is_not_activation() {
let (mut app, btn) = button_focused();
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::CONTROL));
assert_eq!(count.get(), 0);
}
#[test]
fn keydown_prevent_default_suppresses_click() {
let (mut app, btn) = button_focused();
app.dom_mut()
.add_event_listener(btn, "keydown", ListenerOptions::default(), |ctx| {
ctx.event.prevent_default();
})
.unwrap();
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 0);
}
#[test]
fn synthesized_click_is_marked_synthetic() {
let (mut app, btn) = button_focused();
let saw_synthetic = Rc::new(Cell::new(false));
let s = saw_synthetic.clone();
app.dom_mut()
.add_event_listener(btn, "click", ListenerOptions::default(), move |ctx| {
s.set(ctx.event.is_synthetic());
})
.unwrap();
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert!(saw_synthetic.get());
}
#[test]
fn disabled_button_does_not_activate() {
let mut dom: TuiDom = TuiDom::new();
let root = dom.root();
let btn = dom.create_element("button");
dom.set_attribute(btn, "disabled", "").unwrap();
dom.append_child(root, btn).unwrap();
dom.set_focused(Some(btn));
let mut app = test_app(dom);
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
fn input_button_focused(ty: &str) -> (App<TestBackend>, NodeId) {
let mut dom: TuiDom = TuiDom::new();
let root = dom.root();
let inp = dom.create_element("input");
dom.set_attribute(inp, "type", ty).unwrap();
dom.append_child(root, inp).unwrap();
dom.set_focused(Some(inp));
(test_app(dom), inp)
}
#[test]
fn enter_on_input_type_submit_synthesizes_click() {
let (mut app, btn) = input_button_focused("submit");
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
#[test]
fn space_on_input_type_reset_synthesizes_click() {
let (mut app, btn) = input_button_focused("reset");
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Char(' '), KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
#[test]
fn enter_on_input_type_button_synthesizes_click() {
let (mut app, btn) = input_button_focused("button");
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 1);
}
#[test]
fn enter_on_text_input_does_not_synthesize_click() {
let (mut app, btn) = input_button_focused("text");
let count = record_click_count(&mut app, btn);
app.handle_event(key_press(KeyCode::Enter, KeyModifiers::empty()));
assert_eq!(count.get(), 0);
}