use telex::prelude::*;
use telex::{Event, KeyCode, KeyEvent, KeyModifiers};
fn key(code: KeyCode) -> Event {
Event::Key(KeyEvent::new(code, KeyModifiers::NONE))
}
fn key_mod(code: KeyCode, modifiers: KeyModifiers) -> Event {
Event::Key(KeyEvent::new(code, modifiers))
}
fn char_key(c: char) -> Event {
key(KeyCode::Char(c))
}
#[test]
fn test_ctrl_q_quits() {
let output = run_headless(
|_cx: Scope| View::text("Hello"),
40,
10,
vec![key_mod(KeyCode::Char('q'), KeyModifiers::CONTROL)],
);
assert!(output.contains("Hello"), "Should render Hello before quit");
}
#[test]
fn test_empty_events_auto_quits() {
let output = run_headless(
|_cx: Scope| View::text("Auto quit"),
40,
10,
vec![],
);
assert!(
output.contains("Auto quit"),
"Should render and auto-quit"
);
}
#[test]
fn test_tab_moves_focus() {
let output = run_headless(
|_cx: Scope| {
View::vstack()
.child(View::button().label("First").build())
.child(View::button().label("Second").build())
.build()
},
40,
10,
vec![key(KeyCode::Tab)],
);
assert!(output.contains("First"), "First button should be visible");
assert!(output.contains("Second"), "Second button should be visible");
}
#[test]
fn test_enter_activates_button() {
let output = run_headless(
|cx: Scope| {
let count = state!(cx, || 0i32);
let c = count.clone();
View::vstack()
.child(View::text(format!("Count: {}", count.get())))
.child(
View::button()
.label("Inc")
.on_press(with!(c => move || c.update(|n| *n += 1)))
.build(),
)
.build()
},
40,
10,
vec![key(KeyCode::Enter)],
);
assert!(
output.contains("Count: 1"),
"Count should be 1 after pressing Enter. Got:\n{}",
output
);
}
#[test]
fn test_multiple_enter_presses() {
let output = run_headless(
|cx: Scope| {
let count = state!(cx, || 0i32);
let c = count.clone();
View::vstack()
.child(View::text(format!("Count: {}", count.get())))
.child(
View::button()
.label("Inc")
.on_press(with!(c => move || c.update(|n| *n += 1)))
.build(),
)
.build()
},
40,
10,
vec![
key(KeyCode::Enter),
key(KeyCode::Enter),
key(KeyCode::Enter),
],
);
assert!(
output.contains("Count: 3"),
"Count should be 3 after three Enter presses. Got:\n{}",
output
);
}
#[test]
fn test_text_input_typing() {
let output = run_headless(
|cx: Scope| {
let text = state!(cx, || String::new());
let t = text.clone();
View::vstack()
.child(
View::text_input()
.value(text.get().clone())
.on_change(with!(t => move |v: String| t.set(v)))
.placeholder("Type here...")
.build(),
)
.child(View::text(format!("Typed: {}", text.get())))
.build()
},
40,
10,
vec![char_key('H'), char_key('i')],
);
assert!(
output.contains("Typed: Hi"),
"Should show typed text 'Hi'. Got:\n{}",
output
);
}
#[test]
fn test_shift_tab_moves_focus_backward() {
let output = run_headless(
|_cx: Scope| {
View::vstack()
.child(View::button().label("A").build())
.child(View::button().label("B").build())
.child(View::button().label("C").build())
.build()
},
40,
10,
vec![
key(KeyCode::Tab),
key(KeyCode::Tab),
key_mod(KeyCode::BackTab, KeyModifiers::SHIFT),
],
);
assert!(output.contains("A"), "Button A should be visible");
assert!(output.contains("B"), "Button B should be visible");
assert!(output.contains("C"), "Button C should be visible");
}
#[test]
fn test_events_processed_in_order() {
let output = run_headless(
|cx: Scope| {
let count = state!(cx, || 0i32);
let c = count.clone();
View::vstack()
.child(View::text(format!("Count: {}", count.get())))
.child(
View::button()
.label("Inc")
.on_press(with!(c => move || c.update(|n| *n += 1)))
.build(),
)
.build()
},
40,
10,
vec![
key(KeyCode::Enter),
key(KeyCode::Enter),
key(KeyCode::Enter),
key(KeyCode::Enter),
key(KeyCode::Enter),
],
);
assert!(
output.contains("Count: 5"),
"All 5 events should be processed (no input flush). Got:\n{}",
output
);
}
#[test]
fn test_checkbox_toggle() {
let output = run_headless(
|cx: Scope| {
let checked = state!(cx, || false);
let c = checked.clone();
View::vstack()
.child(
View::checkbox()
.label("Accept")
.checked(checked.get())
.on_toggle(with!(c => move |v: bool| c.set(v)))
.build(),
)
.child(View::text(
if checked.get() { "YES" } else { "NO" }.to_string(),
))
.build()
},
40,
10,
vec![key(KeyCode::Enter)],
);
assert!(
output.contains("YES"),
"Checkbox should be toggled on. Got:\n{}",
output
);
}
#[test]
fn test_slider_right_arrow_increments() {
let output = run_headless(
|cx: Scope| {
let val = state!(cx, || 50.0f64);
let v = val.clone();
View::vstack()
.child(
View::slider()
.min(0.0)
.max(100.0)
.step(1.0)
.value(val.get())
.label("Test")
.on_change(with!(v => move |n: f64| v.set(n)))
.build(),
)
.child(View::text(format!("VAL={}", val.get() as i32)))
.build()
},
60,
10,
vec![
key(KeyCode::Right),
key(KeyCode::Right),
key(KeyCode::Right),
],
);
assert!(
output.contains("VAL=53"),
"Slider should increment to 53 after 3 Right presses. Got:\n{}",
output
);
}