Integration tests covering 6 main scenarios
Covers - Add - Select - Select Top/Bottom - Move - Edit - Delete
This commit is contained in:
parent
d23f458444
commit
514c600bb1
@ -7,9 +7,6 @@ use tui_textarea::TextArea;
|
|||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Column {
|
pub struct Column {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
|
@ -1 +0,0 @@
|
|||||||
// use super::*;
|
|
@ -3,7 +3,7 @@ use anyhow::Error;
|
|||||||
use rusqlite::{params, Connection, Result};
|
use rusqlite::{params, Connection, Result};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
pub struct DBConn(Connection);
|
pub struct DBConn(pub Connection);
|
||||||
|
|
||||||
impl DerefMut for DBConn {
|
impl DerefMut for DBConn {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
19
src/ui.rs
19
src/ui.rs
@ -35,8 +35,6 @@ fn draw_tasks<B: Backend>(f: &mut Frame<'_, B>, area: Rect, state: &State<'_>) {
|
|||||||
span = Span::raw(&task.title);
|
span = Span::raw(&task.title);
|
||||||
}
|
}
|
||||||
span.style = style;
|
span.style = style;
|
||||||
// TODO: This is unoptimized, we can actually construct the list
|
|
||||||
// inside a single ListItem it seems
|
|
||||||
ListItem::new(vec![Spans::from(span)])
|
ListItem::new(vec![Spans::from(span)])
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -202,24 +200,17 @@ fn draw_project_stats<B: Backend>(f: &mut Frame<'_, B>, area: Rect, state: &mut
|
|||||||
let c4_len = state.columns[3].tasks.len();
|
let c4_len = state.columns[3].tasks.len();
|
||||||
let tocomplete_total = c1_len + c2_len + c3_len;
|
let tocomplete_total = c1_len + c2_len + c3_len;
|
||||||
let percentage = (c3_len as f32 / tocomplete_total as f32 * 100.0) as u8;
|
let percentage = (c3_len as f32 / tocomplete_total as f32 * 100.0) as u8;
|
||||||
let list = List::new(
|
let list = List::new(vec![ListItem::new(vec![
|
||||||
vec![
|
|
||||||
ListItem::new(vec![
|
|
||||||
Spans::from("Tasks per Column:"),
|
Spans::from("Tasks per Column:"),
|
||||||
Spans::from(format!(" Todo ({})", c1_len)),
|
Spans::from(format!(" Todo ({})", c1_len)),
|
||||||
Spans::from(format!(" In Progress ({})", c2_len)),
|
Spans::from(format!(" In Progress ({})", c2_len)),
|
||||||
Spans::from(format!(" Done ({})", c3_len)),
|
Spans::from(format!(" Done ({})", c3_len)),
|
||||||
Spans::from(format!(" Ideas ({})", c4_len)),
|
Spans::from(format!(" Ideas ({})", c4_len)),
|
||||||
Spans::from(
|
Spans::from(format!(
|
||||||
format!(
|
|
||||||
"Progress: {} / {} - {}%",
|
"Progress: {} / {} - {}%",
|
||||||
c3_len,
|
c3_len, tocomplete_total, percentage
|
||||||
tocomplete_total,
|
)),
|
||||||
percentage
|
])])
|
||||||
))
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
)
|
|
||||||
.block(block);
|
.block(block);
|
||||||
|
|
||||||
f.render_widget(list, area);
|
f.render_widget(list, area);
|
||||||
|
322
tests/app_tests.rs
Normal file
322
tests/app_tests.rs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
// - [X] Adding
|
||||||
|
// - [X] Selecting general
|
||||||
|
// - [X] Selecting top/bottom
|
||||||
|
// - [X] Moving
|
||||||
|
// - [X] Editing
|
||||||
|
// - [X] Deleting
|
||||||
|
#[cfg(test)]
|
||||||
|
mod app_tests {
|
||||||
|
use anyhow::Error;
|
||||||
|
use kanban_tui::State;
|
||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
|
fn create_connection() -> Result<Connection, Error> {
|
||||||
|
let mut conn = Connection::open_in_memory()?;
|
||||||
|
|
||||||
|
let migrations = include_str!("../sql/migrations.sql");
|
||||||
|
let migrations: Vec<&str> = migrations.split(';').collect();
|
||||||
|
let tx = conn.transaction()?;
|
||||||
|
for m in migrations {
|
||||||
|
if !m.trim().is_empty() {
|
||||||
|
tx.execute(m, ())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx.commit()?;
|
||||||
|
Ok(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_adds_tasks_to_different_columns() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
state.add_new_task(String::from("T2"), String::from("D2"))?;
|
||||||
|
state.select_next_column()?;
|
||||||
|
state.select_next_column()?;
|
||||||
|
state.add_new_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
state.select_previous_column()?;
|
||||||
|
state.add_new_task(String::from("T4"), String::from("D4"))?;
|
||||||
|
|
||||||
|
assert_eq!(state.columns[0].tasks.len(), 2);
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 1);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 1);
|
||||||
|
assert_eq!(&state.columns[0].tasks[0].title, "T1");
|
||||||
|
assert_eq!(&state.columns[0].tasks[1].description, "D2");
|
||||||
|
assert_eq!(&state.columns[1].tasks[0].title, "T4");
|
||||||
|
assert_eq!(&state.columns[2].tasks[0].description, "D3");
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert_eq!(state.columns[0].tasks.len(), 2);
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 1);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 1);
|
||||||
|
assert_eq!(&state.columns[0].tasks[0].title, "T1");
|
||||||
|
assert_eq!(&state.columns[0].tasks[1].description, "D2");
|
||||||
|
assert_eq!(&state.columns[1].tasks[0].title, "T4");
|
||||||
|
assert_eq!(&state.columns[2].tasks[0].description, "D3");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_selects_the_correct_tasks_and_stays_inbound() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
state.move_task_up()?;
|
||||||
|
state.move_task_up()?;
|
||||||
|
state.move_task_down()?;
|
||||||
|
state.move_task_down()?;
|
||||||
|
for _ in 0..10 {
|
||||||
|
state.select_next_column()?;
|
||||||
|
}
|
||||||
|
for _ in 0..10 {
|
||||||
|
state.select_previous_column()?;
|
||||||
|
}
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.add_new_task(String::from("T2"), String::from("D2"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
for _ in 0..6 {
|
||||||
|
state.select_next_column()?;
|
||||||
|
}
|
||||||
|
state.select_previous_column()?;
|
||||||
|
state.add_new_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
assert_eq!(state.get_selected_column().name, "Done");
|
||||||
|
for _ in 0..6 {
|
||||||
|
state.select_next_column()?;
|
||||||
|
}
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.select_previous_column()?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_next_column()?;
|
||||||
|
state.select_next_column()?;
|
||||||
|
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let mut state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.select_next_task()?;
|
||||||
|
state.select_next_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.select_previous_column()?;
|
||||||
|
state.select_previous_column()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_next_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_selects_the_correct_first_and_last_task() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
for i in 1..11 {
|
||||||
|
state.add_new_task(format!("T{i}"), format!("D{i}"))?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T10");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T10");
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
for _ in 0..10 {
|
||||||
|
state.move_task_down()?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_last_task()?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T9");
|
||||||
|
for _ in 0..10 {
|
||||||
|
state.move_task_up()?;
|
||||||
|
}
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T9");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let mut state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T9");
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_moves_tasks_up_and_down_and_to_different_columns() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
state.add_new_task(String::from("T2"), String::from("D2"))?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
state.move_task_up()?;
|
||||||
|
state.move_task_down()?;
|
||||||
|
state.move_task_down()?;
|
||||||
|
assert_eq!(&state.columns[0].tasks[1].title, "T1");
|
||||||
|
assert_eq!(&state.columns[0].tasks[0].title, "T2");
|
||||||
|
state.select_next_column()?;
|
||||||
|
state.add_new_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
state.move_task_next_column()?;
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 1);
|
||||||
|
for _ in 0..5 {
|
||||||
|
state.move_task_next_column()?;
|
||||||
|
}
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.move_task_previous_column()?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.columns[0].tasks.len(), 3);
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[3].tasks.len(), 0);
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.select_next_task()?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.select_previous_task()?;
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let mut state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_next_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_next_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.select_first_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
assert_eq!(state.columns[0].tasks.len(), 3);
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[3].tasks.len(), 0);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_edits_a_task_and_updates_it() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D1");
|
||||||
|
state.edit_task(String::from("T1"), String::from("D2"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D2");
|
||||||
|
state.edit_task(String::from("T2"), String::from("D1"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D1");
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.move_task_next_column()?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D1");
|
||||||
|
state.edit_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D3");
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.move_task_previous_column()?;
|
||||||
|
}
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D3");
|
||||||
|
state.edit_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
|
||||||
|
assert_eq!(state.columns[0].tasks.len(), 1);
|
||||||
|
assert_eq!(state.columns[1].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[2].tasks.len(), 0);
|
||||||
|
assert_eq!(state.columns[3].tasks.len(), 0);
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().description, "D1");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_deletes_a_task() -> Result<(), Error> {
|
||||||
|
let mut state = State::new(create_connection()?)?;
|
||||||
|
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
for col in state.columns.iter().skip(1) {
|
||||||
|
assert_eq!(col.tasks.len(), 0);
|
||||||
|
}
|
||||||
|
state.delete_task()?;
|
||||||
|
assert!(state.get_selected_task().is_none());
|
||||||
|
for col in &state.columns {
|
||||||
|
assert_eq!(col.tasks.len(), 0);
|
||||||
|
}
|
||||||
|
state.delete_task()?;
|
||||||
|
|
||||||
|
state.add_new_task(String::from("T1"), String::from("D1"))?;
|
||||||
|
state.add_new_task(String::from("T2"), String::from("D2"))?;
|
||||||
|
state.add_new_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.delete_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.add_new_task(String::from("T3"), String::from("D3"))?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T2");
|
||||||
|
state.delete_task()?;
|
||||||
|
state.select_previous_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_last_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
state.add_new_task(String::from("T2"), String::from("D2"))?;
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.move_task_up()?;
|
||||||
|
}
|
||||||
|
state.delete_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T1");
|
||||||
|
state.select_next_task()?;
|
||||||
|
assert_eq!(state.get_selected_task().unwrap().title, "T3");
|
||||||
|
for _ in 0..4 {
|
||||||
|
state.delete_task()?;
|
||||||
|
}
|
||||||
|
assert!(state.get_selected_task().is_none());
|
||||||
|
for col in &state.columns {
|
||||||
|
assert_eq!(col.tasks.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload the data from the database then rerun the asserts to
|
||||||
|
// make sure everything was saved correctly
|
||||||
|
let state = State::new(state.db_conn.0)?;
|
||||||
|
|
||||||
|
assert!(state.get_selected_task().is_none());
|
||||||
|
for col in state.columns {
|
||||||
|
assert_eq!(col.tasks.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user