mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-23 20:16:41 +02:00
fix: Disable event loop for testing 🐛
This commit is contained in:
parent
ad3a88aed2
commit
3ffa5bdc77
3 changed files with 109 additions and 86 deletions
76
src/app.rs
76
src/app.rs
|
@ -238,7 +238,7 @@ pub struct TaskwarriorTui {
|
|||
}
|
||||
|
||||
impl TaskwarriorTui {
|
||||
pub async fn new(report: &str) -> Result<Self> {
|
||||
pub async fn new(report: &str, init_event_loop: bool) -> Result<Self> {
|
||||
let output = std::process::Command::new("task")
|
||||
.arg("rc.color=off")
|
||||
.arg("rc._forcecolor=off")
|
||||
|
@ -280,7 +280,7 @@ impl TaskwarriorTui {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let event_loop = crate::event::EventLoop::new(tick_rate);
|
||||
let event_loop = crate::event::EventLoop::new(tick_rate, init_event_loop);
|
||||
|
||||
let mut app = Self {
|
||||
should_quit: false,
|
||||
|
@ -372,7 +372,14 @@ impl TaskwarriorTui {
|
|||
|
||||
pub async fn abort_event_loop(&mut self) -> Result<()> {
|
||||
self.event_loop.abort.send(())?;
|
||||
self.event_loop.handle.abort();
|
||||
loop {
|
||||
if let Some(event) = self.next().await {
|
||||
match event {
|
||||
Event::Closed => break,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -382,7 +389,7 @@ impl TaskwarriorTui {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
self.event_loop = crate::event::EventLoop::new(tick_rate);
|
||||
self.event_loop = crate::event::EventLoop::new(tick_rate, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -418,6 +425,9 @@ impl TaskwarriorTui {
|
|||
debug!("Tick event");
|
||||
self.update(false).await?;
|
||||
}
|
||||
Event::Closed => {
|
||||
debug!("Event loop closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3782,7 +3792,7 @@ mod tests {
|
|||
}
|
||||
|
||||
async fn test_taskwarrior_tui_history() {
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
// setup();
|
||||
app.mode = Mode::Tasks(Action::Add);
|
||||
app.update_completion_list();
|
||||
|
@ -3857,9 +3867,17 @@ mod tests {
|
|||
// teardown();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
async fn test_taskwarrior_tui() {
|
||||
let app = TaskwarriorTui::new("next").await;
|
||||
#[test]
|
||||
fn test_taskwarrior_tui() {
|
||||
let r = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async { _test_taskwarrior_tui().await });
|
||||
}
|
||||
|
||||
async fn _test_taskwarrior_tui() {
|
||||
let app = TaskwarriorTui::new("next", false).await;
|
||||
if app.is_err() {
|
||||
return;
|
||||
}
|
||||
|
@ -3867,7 +3885,7 @@ mod tests {
|
|||
|
||||
assert!(app.task_by_index(0).is_none(), "Expected task data to be empty but found {} tasks. Delete contents of {:?} and {:?} and run the tests again.", app.tasks.len(), Path::new(env!("TASKDATA")), Path::new(env!("TASKDATA")).parent().unwrap().join(".config"));
|
||||
|
||||
let app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app
|
||||
.task_by_uuid(Uuid::parse_str("3f43831b-88dc-45e2-bf0d-4aea6db634cc").unwrap())
|
||||
.is_none());
|
||||
|
@ -3879,10 +3897,10 @@ mod tests {
|
|||
|
||||
setup();
|
||||
|
||||
let app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.task_by_index(0).is_some());
|
||||
|
||||
let app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app
|
||||
.task_by_uuid(Uuid::parse_str("3f43831b-88dc-45e2-bf0d-4aea6db634cc").unwrap())
|
||||
.is_some());
|
||||
|
@ -3902,7 +3920,7 @@ mod tests {
|
|||
|
||||
async fn test_task_tags() {
|
||||
// testing tags
|
||||
let app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
let task = app.task_by_id(1).unwrap();
|
||||
|
||||
let tags = vec!["PENDING".to_string(), "PRIORITY".to_string()];
|
||||
|
@ -3911,7 +3929,7 @@ mod tests {
|
|||
assert!(task.tags().unwrap().contains(&tag));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
let task = app.task_by_id(11).unwrap();
|
||||
let tags = vec!["finance", "UNBLOCKED", "PENDING", "TAGGED", "UDA"]
|
||||
.iter()
|
||||
|
@ -3954,7 +3972,7 @@ mod tests {
|
|||
}
|
||||
|
||||
async fn test_task_style() {
|
||||
let app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
let task = app.task_by_id(1).unwrap();
|
||||
for r in vec![
|
||||
"active",
|
||||
|
@ -3984,7 +4002,7 @@ mod tests {
|
|||
}
|
||||
|
||||
async fn test_task_context() {
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
assert!(app.update(true).await.is_ok());
|
||||
|
||||
|
@ -4019,7 +4037,7 @@ mod tests {
|
|||
async fn test_task_tomorrow() {
|
||||
let total_tasks: u64 = 26;
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4077,7 +4095,7 @@ mod tests {
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4086,7 +4104,7 @@ mod tests {
|
|||
async fn test_task_earlier_today() {
|
||||
let total_tasks: u64 = 26;
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4136,7 +4154,7 @@ mod tests {
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4145,7 +4163,7 @@ mod tests {
|
|||
async fn test_task_later_today() {
|
||||
let total_tasks: u64 = 26;
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4202,7 +4220,7 @@ mod tests {
|
|||
.output()
|
||||
.unwrap();
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
assert!(app.update(true).await.is_ok());
|
||||
assert_eq!(app.tasks.len(), total_tasks as usize);
|
||||
assert_eq!(app.current_context_filter, "");
|
||||
|
@ -4248,7 +4266,7 @@ mod tests {
|
|||
.set_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
app.task_report_next();
|
||||
app.context_next();
|
||||
|
@ -4308,7 +4326,7 @@ mod tests {
|
|||
.set_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
let total_tasks: u64 = 26;
|
||||
|
||||
|
@ -4513,7 +4531,7 @@ mod tests {
|
|||
.set_style(Style::default().fg(Color::Indexed(1)).bg(Color::Indexed(4)));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
app.task_report_next();
|
||||
app.context_next();
|
||||
|
@ -4659,7 +4677,7 @@ mod tests {
|
|||
.set_style(Style::default().bg(Color::Reset).add_modifier(Modifier::UNDERLINED));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
app.task_report_next();
|
||||
app.context_next();
|
||||
|
@ -4704,7 +4722,7 @@ mod tests {
|
|||
.set_style(Style::default().add_modifier(Modifier::BOLD));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
app.mode = Mode::Tasks(Action::HelpPopup);
|
||||
app.task_report_next();
|
||||
|
@ -4773,7 +4791,7 @@ mod tests {
|
|||
.set_style(Style::default().add_modifier(Modifier::BOLD));
|
||||
}
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
app.mode = Mode::Tasks(Action::ContextMenu);
|
||||
app.task_report_next();
|
||||
|
@ -4798,7 +4816,7 @@ mod tests {
|
|||
dbg!(UnicodeWidthStr::width("写作业"));
|
||||
dbg!(UnicodeWidthStr::width("abc"));
|
||||
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
|
||||
if let Some(task) = app.task_by_id(27) {
|
||||
let i = app.task_index_by_uuid(*task.uuid()).unwrap_or_default();
|
||||
|
@ -4827,7 +4845,7 @@ mod tests {
|
|||
|
||||
// #[test]
|
||||
async fn test_taskwarrior_tui_completion() {
|
||||
let mut app = TaskwarriorTui::new("next").await.unwrap();
|
||||
let mut app = TaskwarriorTui::new("next", false).await.unwrap();
|
||||
app.handle_input(KeyCode::Char('z')).await.unwrap();
|
||||
app.mode = Mode::Tasks(Action::Add);
|
||||
app.update_completion_list();
|
||||
|
|
117
src/event.rs
117
src/event.rs
|
@ -15,6 +15,7 @@ use crossterm::event::{
|
|||
pub enum Event<I> {
|
||||
Input(I),
|
||||
Tick,
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq)]
|
||||
|
@ -48,81 +49,85 @@ pub struct EventLoop {
|
|||
pub rx: mpsc::UnboundedReceiver<Event<KeyCode>>,
|
||||
pub tx: mpsc::UnboundedSender<Event<KeyCode>>,
|
||||
pub abort: mpsc::UnboundedSender<()>,
|
||||
pub handle: JoinHandle<()>,
|
||||
pub tick_rate: std::time::Duration,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub fn new(tick_rate: Option<std::time::Duration>) -> Self {
|
||||
pub fn new(tick_rate: Option<std::time::Duration>, init: bool) -> Self {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
let _tx = tx.clone();
|
||||
let mut reader = crossterm::event::EventStream::new();
|
||||
let should_tick = tick_rate.is_some();
|
||||
let tick_rate = tick_rate.unwrap_or(std::time::Duration::from_millis(250));
|
||||
|
||||
let (abort, mut recv) = mpsc::unbounded_channel();
|
||||
let (abort, mut abort_recv) = mpsc::unbounded_channel();
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
loop {
|
||||
let delay = tokio::time::sleep(tick_rate);
|
||||
let event = reader.next();
|
||||
if init {
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let delay = tokio::time::sleep(tick_rate);
|
||||
let event = reader.next();
|
||||
|
||||
tokio::select! {
|
||||
_ = recv.recv() => break,
|
||||
_ = delay, if should_tick => {
|
||||
_tx.send(Event::Tick).unwrap();
|
||||
},
|
||||
_ = _tx.closed() => break,
|
||||
maybe_event = event => {
|
||||
if let Some(Ok(crossterm::event::Event::Key(key))) = maybe_event {
|
||||
let key = match key.code {
|
||||
Backspace => {
|
||||
match key.modifiers {
|
||||
KeyModifiers::CONTROL => KeyCode::CtrlBackspace,
|
||||
KeyModifiers::ALT => KeyCode::AltBackspace,
|
||||
_ => KeyCode::Backspace,
|
||||
}
|
||||
},
|
||||
Delete => {
|
||||
match key.modifiers {
|
||||
KeyModifiers::CONTROL => KeyCode::CtrlDelete,
|
||||
KeyModifiers::ALT => KeyCode::AltDelete,
|
||||
_ => KeyCode::Delete,
|
||||
}
|
||||
},
|
||||
Enter => KeyCode::Char('\n'),
|
||||
Left => KeyCode::Left,
|
||||
Right => KeyCode::Right,
|
||||
Up => KeyCode::Up,
|
||||
Down => KeyCode::Down,
|
||||
Home => KeyCode::Home,
|
||||
End => KeyCode::End,
|
||||
PageUp => KeyCode::PageUp,
|
||||
PageDown => KeyCode::PageDown,
|
||||
Tab => KeyCode::Tab,
|
||||
BackTab => KeyCode::BackTab,
|
||||
Insert => KeyCode::Insert,
|
||||
F(k) => KeyCode::F(k),
|
||||
Null => KeyCode::Null,
|
||||
Esc => KeyCode::Esc,
|
||||
Char(c) => match key.modifiers {
|
||||
KeyModifiers::NONE | KeyModifiers::SHIFT => KeyCode::Char(c),
|
||||
KeyModifiers::CONTROL => KeyCode::Ctrl(c),
|
||||
KeyModifiers::ALT => KeyCode::Alt(c),
|
||||
_ => KeyCode::Null,
|
||||
},
|
||||
};
|
||||
_tx.send(Event::Input(key)).unwrap();
|
||||
tokio::select! {
|
||||
_ = abort_recv.recv() => {
|
||||
_tx.send(Event::Closed).unwrap();
|
||||
_tx.send(Event::Tick).unwrap();
|
||||
break;
|
||||
},
|
||||
_ = delay, if should_tick => {
|
||||
_tx.send(Event::Tick).unwrap();
|
||||
},
|
||||
_ = _tx.closed() => break,
|
||||
maybe_event = event => {
|
||||
if let Some(Ok(crossterm::event::Event::Key(key))) = maybe_event {
|
||||
let key = match key.code {
|
||||
Backspace => {
|
||||
match key.modifiers {
|
||||
KeyModifiers::CONTROL => KeyCode::CtrlBackspace,
|
||||
KeyModifiers::ALT => KeyCode::AltBackspace,
|
||||
_ => KeyCode::Backspace,
|
||||
}
|
||||
},
|
||||
Delete => {
|
||||
match key.modifiers {
|
||||
KeyModifiers::CONTROL => KeyCode::CtrlDelete,
|
||||
KeyModifiers::ALT => KeyCode::AltDelete,
|
||||
_ => KeyCode::Delete,
|
||||
}
|
||||
},
|
||||
Enter => KeyCode::Char('\n'),
|
||||
Left => KeyCode::Left,
|
||||
Right => KeyCode::Right,
|
||||
Up => KeyCode::Up,
|
||||
Down => KeyCode::Down,
|
||||
Home => KeyCode::Home,
|
||||
End => KeyCode::End,
|
||||
PageUp => KeyCode::PageUp,
|
||||
PageDown => KeyCode::PageDown,
|
||||
Tab => KeyCode::Tab,
|
||||
BackTab => KeyCode::BackTab,
|
||||
Insert => KeyCode::Insert,
|
||||
F(k) => KeyCode::F(k),
|
||||
Null => KeyCode::Null,
|
||||
Esc => KeyCode::Esc,
|
||||
Char(c) => match key.modifiers {
|
||||
KeyModifiers::NONE | KeyModifiers::SHIFT => KeyCode::Char(c),
|
||||
KeyModifiers::CONTROL => KeyCode::Ctrl(c),
|
||||
KeyModifiers::ALT => KeyCode::Alt(c),
|
||||
_ => KeyCode::Null,
|
||||
},
|
||||
};
|
||||
_tx.send(Event::Input(key)).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
tx,
|
||||
rx,
|
||||
handle,
|
||||
tick_rate,
|
||||
abort,
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@ async fn tui_main(report: &str) -> Result<()> {
|
|||
better_panic::Settings::auto().create_panic_handler()(panic_info);
|
||||
}));
|
||||
|
||||
let mut app = app::TaskwarriorTui::new(report).await?;
|
||||
let mut app = app::TaskwarriorTui::new(report, true).await?;
|
||||
|
||||
let mut terminal = app.start_tui().await?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue