#![allow(clippy::uninlined_format_args)]
mod common;
use common::harness::E2EHarness;
use common::input_sim::{InputSequence, sequence_to_ansi};
use opentui::input::{InputParser, KeyCode, KeyModifiers, MouseButton};
use opentui_rust as opentui;
#[test]
fn test_e2e_workflow_type_delete_undo() {
let mut harness = E2EHarness::new("workflows", "type_delete_undo", 80, 24);
harness
.log()
.info("init", "Testing type → delete → undo workflow");
let mut parser = InputParser::new();
let seq = InputSequence::type_text("hello")
.key(KeyCode::Backspace)
.ctrl_key(KeyCode::Char('z'));
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("workflow", format!("Sequence has {} events", seq.len()));
let mut events = Vec::new();
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
events.push(event);
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(events.len(), 7);
harness
.log()
.info("verify", format!("Parsed {} events", events.len()));
harness.finish(true);
eprintln!("[TEST] PASS: Type → delete → undo workflow works");
}
#[test]
fn test_e2e_workflow_navigation() {
let mut harness = E2EHarness::new("workflows", "navigation", 80, 24);
harness.log().info("init", "Testing navigation workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.key(KeyCode::Right)
.key(KeyCode::Right)
.key(KeyCode::Down)
.key(KeyCode::Home)
.key(KeyCode::End);
let ansi = sequence_to_ansi(&seq);
let mut count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
let key = event.key().expect("Should be key event");
assert!(
key.code.is_navigation() || matches!(key.code, KeyCode::Home | KeyCode::End)
);
count += 1;
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(count, 5);
harness
.log()
.info("verify", "Navigation workflow parsed correctly");
harness.finish(true);
eprintln!("[TEST] PASS: Navigation workflow works");
}
#[test]
fn test_e2e_workflow_selection() {
let mut harness = E2EHarness::new("workflows", "selection", 80, 24);
harness.log().info("init", "Testing selection workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.shift_key(KeyCode::Right)
.shift_key(KeyCode::Right)
.shift_key(KeyCode::Right)
.shift_key(KeyCode::Down);
let ansi = sequence_to_ansi(&seq);
let mut events = Vec::new();
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
events.push(event);
offset += consumed;
}
Err(_) => break,
}
}
for event in &events {
let key = event.key().expect("Should be key event");
assert!(key.shift(), "All events should have Shift modifier");
}
assert_eq!(events.len(), 4);
harness
.log()
.info("verify", "Selection workflow with Shift modifier works");
harness.finish(true);
eprintln!("[TEST] PASS: Selection workflow works");
}
#[test]
fn test_e2e_workflow_copy_paste() {
let mut harness = E2EHarness::new("workflows", "copy_paste", 80, 24);
harness.log().info("init", "Testing copy-paste workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.key_with_mods(KeyCode::End, KeyModifiers::SHIFT) .ctrl_key(KeyCode::Char('c')) .key(KeyCode::End) .ctrl_key(KeyCode::Char('v'));
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("workflow", format!("Sequence: {} bytes", ansi.len()));
let mut count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
let key = event.key().expect("Should be key event");
harness.log().info(
"event",
format!("{}: {:?} mods:{:?}", count, key.code, key.modifiers),
);
count += 1;
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(count, 4);
harness.finish(true);
eprintln!("[TEST] PASS: Copy-paste workflow works");
}
#[test]
fn test_e2e_workflow_undo_redo() {
let mut harness = E2EHarness::new("workflows", "undo_redo", 80, 24);
harness.log().info("init", "Testing undo/redo workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.ctrl_key(KeyCode::Char('z'))
.ctrl_key(KeyCode::Char('z'))
.ctrl_key(KeyCode::Char('z'))
.ctrl_key(KeyCode::Char('y')) .ctrl_key(KeyCode::Char('y'));
let ansi = sequence_to_ansi(&seq);
let mut count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
let key = event.key().expect("Should be key event");
assert!(key.ctrl(), "All should have Ctrl modifier");
count += 1;
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(count, 5);
harness
.log()
.info("verify", "Undo/redo workflow parsed correctly");
harness.finish(true);
eprintln!("[TEST] PASS: Undo/redo workflow works");
}
#[test]
fn test_e2e_workflow_scroll_click_drag() {
let mut harness = E2EHarness::new("workflows", "scroll_click_drag", 80, 24);
harness
.log()
.info("init", "Testing scroll → click → drag workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.scroll_down(40, 12)
.scroll_down(40, 12)
.left_click(10, 15)
.drag((10, 15), (30, 15));
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("workflow", format!("Sequence: {} events", seq.len()));
let mut mouse_events = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
if event.is_mouse() {
mouse_events += 1;
}
offset += consumed;
}
Err(_) => break,
}
}
assert!(
mouse_events >= 10,
"Should have multiple mouse events: got {}",
mouse_events
);
harness
.log()
.info("verify", format!("Parsed {} mouse events", mouse_events));
harness.finish(true);
eprintln!("[TEST] PASS: Scroll → click → drag workflow works");
}
#[test]
fn test_e2e_workflow_context_menu() {
let mut harness = E2EHarness::new("workflows", "context_menu", 80, 24);
harness
.log()
.info("init", "Testing right-click context menu workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.right_click(20, 10)
.key(KeyCode::Down)
.key(KeyCode::Down)
.key(KeyCode::Enter);
let ansi = sequence_to_ansi(&seq);
let mut events = Vec::new();
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
events.push(event);
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(events.len(), 5);
let first_mouse = events[0].mouse().expect("First should be mouse");
assert_eq!(first_mouse.button, MouseButton::Right);
let last_key = events[4].key().expect("Last should be key");
assert!(
last_key.code == KeyCode::Enter || (last_key.code == KeyCode::Char('m') && last_key.ctrl()),
"Last key should be Enter or Ctrl+M (both are 0x0D)"
);
harness
.log()
.info("verify", "Context menu workflow parsed correctly");
harness.finish(true);
eprintln!("[TEST] PASS: Context menu workflow works");
}
#[test]
fn test_e2e_workflow_focus_change() {
let mut harness = E2EHarness::new("workflows", "focus_change", 80, 24);
harness.log().info("init", "Testing focus change workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.focus_lost()
.focus_gained()
.key(KeyCode::Char('a'));
let ansi = sequence_to_ansi(&seq);
let mut events = Vec::new();
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
events.push(event);
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(events.len(), 3);
assert!(matches!(events[0], opentui::input::Event::FocusLost));
assert!(matches!(events[1], opentui::input::Event::FocusGained));
assert!(events[2].is_key());
harness.log().info("verify", "Focus change workflow works");
harness.finish(true);
eprintln!("[TEST] PASS: Focus change workflow works");
}
#[test]
fn test_e2e_workflow_search() {
let mut harness = E2EHarness::new("workflows", "search", 80, 24);
harness.log().info("init", "Testing search workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.ctrl_key(KeyCode::Char('f'))
.then(InputSequence::type_text("test"))
.key(KeyCode::Enter)
.key(KeyCode::F(3)) .shift_key(KeyCode::F(3));
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("workflow", format!("Sequence: {} events", seq.len()));
let mut count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((_, consumed)) => {
count += 1;
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(count, 8);
harness
.log()
.info("verify", format!("Search workflow: {} events", count));
harness.finish(true);
eprintln!("[TEST] PASS: Search workflow works");
}
#[test]
fn test_e2e_workflow_resize() {
let mut harness = E2EHarness::new("workflows", "resize", 80, 24);
harness.log().info("init", "Testing resize workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new().resize(120, 50).key(KeyCode::Char('x'));
let ansi = sequence_to_ansi(&seq);
let mut events = Vec::new();
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
events.push(event);
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(events.len(), 2);
let resize = events[0].resize().expect("First should be resize");
assert_eq!(resize.width, 120);
assert_eq!(resize.height, 50);
harness.log().info(
"verify",
format!("Resize to {}x{}", resize.width, resize.height),
);
harness.finish(true);
eprintln!("[TEST] PASS: Resize workflow works");
}
#[test]
fn test_e2e_workflow_file_editing() {
let mut harness = E2EHarness::new("workflows", "file_editing", 80, 24);
harness.log().info("init", "Testing file editing workflow");
let mut parser = InputParser::new();
let seq = InputSequence::new()
.key(KeyCode::Home) .key_with_mods(KeyCode::Right, KeyModifiers::CTRL | KeyModifiers::SHIFT) .ctrl_key(KeyCode::Char('x')) .key(KeyCode::End) .ctrl_key(KeyCode::Char('v'));
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("workflow", format!("File editing: {} events", seq.len()));
let mut count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((_, consumed)) => {
count += 1;
offset += consumed;
}
Err(_) => break,
}
}
assert_eq!(count, 5);
harness.log().info("verify", "File editing workflow works");
harness.finish(true);
eprintln!("[TEST] PASS: File editing workflow works");
}
#[test]
fn test_e2e_workflow_rapid_burst() {
let mut harness = E2EHarness::new("workflows", "rapid_burst", 80, 24);
harness
.log()
.info("init", "Testing rapid burst input (stress test)");
let mut parser = InputParser::new();
let mut seq = InputSequence::type_text(&"x".repeat(50)).stress_mode();
for i in 0..10 {
seq = seq.left_click(i * 5, 10);
}
for _ in 0..5 {
seq = seq.scroll_down(40, 12);
}
let ansi = sequence_to_ansi(&seq);
harness
.log()
.info("stress", format!("Burst: {} bytes", ansi.len()));
let mut key_count = 0;
let mut mouse_count = 0;
let mut offset = 0;
while offset < ansi.len() {
match parser.parse(&ansi[offset..]) {
Ok((event, consumed)) => {
if event.is_key() {
key_count += 1;
} else if event.is_mouse() {
mouse_count += 1;
}
offset += consumed;
}
Err(_) => break,
}
}
harness.log().info(
"verify",
format!("Parsed: {} keys, {} mouse events", key_count, mouse_count),
);
assert_eq!(key_count, 50, "Should parse all key events");
assert!(mouse_count >= 20, "Should parse mouse events");
harness.finish(true);
eprintln!("[TEST] PASS: Rapid burst workflow works");
}