use color_eyre::eyre::Result;
use ratatui::{Frame, Terminal, backend::TestBackend};
use tears::prelude::*;
use tokio::time::{Duration, timeout};
#[derive(Debug)]
struct CounterApp {
count: u32,
max_count: u32,
}
#[derive(Debug, Clone)]
enum CounterMessage {
#[allow(dead_code)]
Increment,
}
impl Application for CounterApp {
type Message = CounterMessage;
type Flags = u32;
fn new(max_count: u32) -> (Self, Command<Self::Message>) {
let cmd = if max_count == 0 {
Command::effect(Action::Quit)
} else {
Command::none()
};
(
Self {
count: 0,
max_count,
},
cmd,
)
}
fn update(&mut self, msg: Self::Message) -> Command<Self::Message> {
match msg {
CounterMessage::Increment => {
self.count += 1;
if self.count >= self.max_count {
Command::effect(Action::Quit)
} else {
Command::none()
}
}
}
}
fn view(&self, _frame: &mut Frame<'_>) {}
fn subscriptions(&self) -> Vec<Subscription<Self::Message>> {
vec![]
}
}
struct SubApp {
tick_count: u32,
}
impl Application for SubApp {
type Message = ();
type Flags = ();
fn new((): ()) -> (Self, Command<()>) {
(Self { tick_count: 0 }, Command::none())
}
fn update(&mut self, (): ()) -> Command<()> {
self.tick_count += 1;
if self.tick_count >= 3 {
Command::effect(Action::Quit)
} else {
Command::none()
}
}
fn view(&self, _frame: &mut Frame<'_>) {}
fn subscriptions(&self) -> Vec<Subscription<()>> {
use tears::subscription::time::Timer;
vec![Subscription::new(Timer::new(10)).map(|_| ())]
}
}
#[tokio::test]
async fn test_runtime_run_end_to_end_basic() -> Result<()> {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend)?;
let runtime = Runtime::<CounterApp>::new(0, 60);
let result = timeout(Duration::from_secs(1), runtime.run(&mut terminal)).await?;
assert!(result.is_ok(), "Runtime should not error");
Ok(())
}
#[tokio::test]
async fn test_runtime_run_end_to_end_with_commands() -> Result<()> {
struct MessageApp {
received: Vec<String>,
}
impl Application for MessageApp {
type Message = String;
type Flags = ();
fn new((): ()) -> (Self, Command<String>) {
let cmd = Command::batch(vec![
Command::future(async { "msg1".to_string() }),
Command::future(async { "msg2".to_string() }),
Command::future(async { "msg3".to_string() }),
]);
(Self { received: vec![] }, cmd)
}
fn update(&mut self, msg: String) -> Command<String> {
self.received.push(msg);
if self.received.len() >= 3 {
Command::effect(Action::Quit)
} else {
Command::none()
}
}
fn view(&self, _frame: &mut Frame<'_>) {}
fn subscriptions(&self) -> Vec<Subscription<String>> {
vec![]
}
}
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend)?;
let runtime = Runtime::<MessageApp>::new((), 60);
let result = timeout(Duration::from_secs(1), runtime.run(&mut terminal)).await?;
assert!(result.is_ok());
Ok(())
}
#[tokio::test]
async fn test_runtime_run_end_to_end_with_subscriptions() -> Result<()> {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend)?;
let runtime = Runtime::<SubApp>::new((), 60);
let result = timeout(Duration::from_secs(1), runtime.run(&mut terminal)).await?;
assert!(result.is_ok());
Ok(())
}