Integration tests covering 6 main scenarios

Covers
- Add
- Select
- Select Top/Bottom
- Move
- Edit
- Delete
This commit is contained in:
Joseph Ferano 2023-06-17 12:35:17 +07:00
parent d23f458444
commit 514c600bb1
5 changed files with 334 additions and 25 deletions

View File

@ -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,

View File

@ -1 +0,0 @@
// use super::*;

View File

@ -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 {

View File

@ -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
View 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(())
}
}