Refactored table filtering and searching so that they are now relative to the table being filtered/searched on. Also created two new widgets for error messages and popups to make life easier moving forward. Going to refactor table sorting into StatefulTable's as well so all tables can be searched, filtered, and sorted moving forwards.

This commit is contained in:
2024-02-11 19:02:18 -07:00
parent 5973f4d685
commit adda82f7f3
38 changed files with 1561 additions and 1165 deletions
+754 -8
View File
@@ -3,14 +3,15 @@ mod tests {
use std::cell::RefCell;
use pretty_assertions::{assert_eq, assert_str_eq};
use ratatui::widgets::{ListState, TableState};
use serde::de::value::Error as ValueError;
use serde::de::value::F64Deserializer;
use serde::de::value::I64Deserializer;
use serde::de::IntoDeserializer;
use serde_json::to_string;
use crate::models::from_i64;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::{from_i64, strip_non_search_characters};
use crate::models::{
BlockSelectionState, HorizontallyScrollableText, Scrollable, ScrollableText, StatefulList,
StatefulTable, TabRoute, TabState,
@@ -46,6 +47,59 @@ mod tests {
stateful_table.scroll_to_bottom();
}
#[test]
fn test_stateful_table_filtered_scrolling_on_empty_table_performs_no_op() {
let mut filtered_stateful_table: StatefulTable<String> = StatefulTable {
filtered_items: Some(Vec::new()),
filtered_state: Some(TableState::default()),
..StatefulTable::default()
};
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_table.scroll_up();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_table.scroll_down();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_table.scroll_to_top();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_table.scroll_to_bottom();
}
#[test]
fn test_stateful_table_scroll() {
let mut stateful_table = create_test_stateful_table();
@@ -77,6 +131,86 @@ mod tests {
assert_eq!(stateful_table.state.selected(), Some(0));
}
#[test]
fn test_stateful_table_filtered_items_scroll() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_table.scroll_down();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_table.scroll_down();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_table.scroll_up();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_table.scroll_up();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_table.scroll_to_bottom();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_table.scroll_to_top();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
}
#[test]
fn test_stateful_table_set_items() {
let items_vec = vec!["Test 1", "Test 2", "Test 3"];
@@ -97,6 +231,27 @@ mod tests {
assert_eq!(stateful_table.state.selected(), Some(2));
}
#[test]
fn test_stateful_table_set_filtered_items() {
let filtered_items_vec = vec!["Test 1", "Test 2", "Test 3"];
let mut filtered_stateful_table: StatefulTable<&str> = StatefulTable::default();
filtered_stateful_table.set_filtered_items(filtered_items_vec.clone());
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
assert_eq!(
filtered_stateful_table.filtered_items,
Some(filtered_items_vec.clone())
);
}
#[test]
fn test_stateful_table_current_selection() {
let mut stateful_table = create_test_stateful_table();
@@ -108,6 +263,27 @@ mod tests {
assert_str_eq!(stateful_table.current_selection(), &stateful_table.items[1]);
}
#[test]
fn test_filtered_stateful_table_current_selection() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
assert_str_eq!(
filtered_stateful_table.current_selection(),
&filtered_stateful_table.filtered_items.as_ref().unwrap()[0]
);
filtered_stateful_table
.filtered_state
.as_mut()
.unwrap()
.select(Some(1));
assert_str_eq!(
filtered_stateful_table.current_selection(),
&filtered_stateful_table.filtered_items.as_ref().unwrap()[1]
);
}
#[test]
fn test_stateful_table_select_index() {
let mut stateful_table = create_test_stateful_table();
@@ -123,6 +299,42 @@ mod tests {
assert_eq!(stateful_table.state.selected(), None);
}
#[test]
fn test_filtered_stateful_table_select_index() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_table.select_index(Some(1));
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_table.select_index(None);
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
}
#[test]
fn test_stateful_table_scroll_up() {
let mut stateful_table = create_test_stateful_table();
@@ -139,7 +351,150 @@ mod tests {
}
#[test]
fn test_stateful_list_scrolling_on_empty_table_performs_no_op() {
fn test_filtered_stateful_table_scroll_up() {
let mut filtered_stateful_table = create_test_filtered_stateful_table();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_table.scroll_up();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_table.scroll_up();
assert_eq!(
filtered_stateful_table
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
}
#[test]
fn test_stateful_table_apply_filter() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_items(vec!["this", "is", "a", "test"]);
stateful_table.filter = Some("i".into());
let expected_items = vec!["this", "is"];
let mut expected_state = TableState::default();
expected_state.select(Some(0));
let has_matches = stateful_table.apply_filter(|&item| item);
assert_eq!(stateful_table.filter, None);
assert_eq!(stateful_table.filtered_items, Some(expected_items));
assert_eq!(stateful_table.filtered_state, Some(expected_state));
assert!(has_matches);
}
#[test]
fn test_stateful_table_apply_filter_no_matches() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_items(vec!["this", "is", "a", "test"]);
stateful_table.filter = Some("z".into());
let has_matches = stateful_table.apply_filter(|&item| item);
assert_eq!(stateful_table.filter, None);
assert_eq!(stateful_table.filtered_items, None);
assert_eq!(stateful_table.filtered_state, None);
assert!(!has_matches);
}
#[test]
fn test_stateful_table_reset_filter() {
let mut stateful_table = create_test_filtered_stateful_table();
stateful_table.reset_filter();
assert_eq!(stateful_table.filter, None);
assert_eq!(stateful_table.filtered_items, None);
assert_eq!(stateful_table.filtered_state, None);
}
#[test]
fn test_stateful_table_apply_search() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_items(vec!["this", "is", "a", "test"]);
stateful_table.search = Some("test".into());
let mut expected_state = TableState::default();
expected_state.select(Some(3));
let has_match = stateful_table.apply_search(|&item| item);
assert_eq!(stateful_table.search, None);
assert_eq!(stateful_table.state, expected_state);
assert!(has_match);
}
#[test]
fn test_stateful_table_apply_search_no_match() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_items(vec!["this", "is", "a", "test"]);
stateful_table.search = Some("shi-mon-a!".into());
let has_match = stateful_table.apply_search(|&item| item);
assert_eq!(stateful_table.search, None);
assert!(!has_match);
}
#[test]
fn test_filtered_stateful_table_apply_search() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_filtered_items(vec!["this", "is", "a", "test"]);
stateful_table.search = Some("test".into());
let mut expected_state = TableState::default();
expected_state.select(Some(3));
let has_match = stateful_table.apply_search(|&item| item);
assert_eq!(stateful_table.search, None);
assert_eq!(stateful_table.filtered_state, Some(expected_state));
assert!(has_match);
}
#[test]
fn test_filtered_stateful_table_apply_search_no_match() {
let mut stateful_table: StatefulTable<&str> = StatefulTable::default();
stateful_table.set_filtered_items(vec!["this", "is", "a", "test"]);
stateful_table.search = Some("shi-mon-a!".into());
let mut expected_state = TableState::default();
expected_state.select(Some(0));
let has_match = stateful_table.apply_search(|&item| item);
assert_eq!(stateful_table.search, None);
assert_eq!(stateful_table.filtered_state, Some(expected_state));
assert!(!has_match);
}
#[test]
fn test_stateful_table_reset_search() {
let mut stateful_table = create_test_stateful_table();
stateful_table.search = Some("test".into());
stateful_table.reset_search();
assert_eq!(stateful_table.search, None);
}
#[test]
fn test_stateful_list_scrolling_on_empty_list_performs_no_op() {
let mut stateful_list: StatefulList<String> = StatefulList::default();
assert_eq!(stateful_list.state.selected(), None);
@@ -159,6 +514,59 @@ mod tests {
stateful_list.scroll_to_bottom();
}
#[test]
fn test_filtered_stateful_list_scrolling_on_empty_list_performs_no_op() {
let mut filtered_stateful_list: StatefulList<String> = StatefulList {
filtered_items: Some(Vec::new()),
filtered_state: Some(ListState::default()),
..StatefulList::default()
};
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_list.scroll_up();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_list.scroll_down();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_list.scroll_to_top();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
filtered_stateful_list.scroll_to_bottom();
}
#[test]
fn test_stateful_list_scroll() {
let mut stateful_list = create_test_stateful_list();
@@ -190,6 +598,86 @@ mod tests {
assert_eq!(stateful_list.state.selected(), Some(0));
}
#[test]
fn test_filtered_stateful_list_scroll() {
let mut filtered_stateful_list = create_test_filtered_stateful_list();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_list.scroll_down();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_list.scroll_down();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_list.scroll_up();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_list.scroll_up();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_list.scroll_to_bottom();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_list.scroll_to_top();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
}
#[test]
fn test_stateful_list_set_items() {
let items_vec = vec!["Test 1", "Test 2", "Test 3"];
@@ -210,6 +698,27 @@ mod tests {
assert_eq!(stateful_list.state.selected(), Some(2));
}
#[test]
fn test_stateful_list_set_filtered_items() {
let filtered_items_vec = vec!["Test 1", "Test 2", "Test 3"];
let mut filtered_stateful_list: StatefulList<&str> = StatefulList::default();
filtered_stateful_list.set_filtered_items(filtered_items_vec.clone());
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
assert_eq!(
filtered_stateful_list.filtered_items,
Some(filtered_items_vec.clone())
);
}
#[test]
fn test_stateful_list_current_selection() {
let mut stateful_list = create_test_stateful_list();
@@ -221,19 +730,234 @@ mod tests {
assert_str_eq!(stateful_list.current_selection(), &stateful_list.items[1]);
}
#[test]
fn test_filtered_stateful_list_current_selection() {
let mut filtered_stateful_list = create_test_filtered_stateful_list();
assert_str_eq!(
filtered_stateful_list.current_selection(),
&filtered_stateful_list.filtered_items.as_ref().unwrap()[0]
);
filtered_stateful_list
.filtered_state
.as_mut()
.unwrap()
.select(Some(1));
assert_str_eq!(
filtered_stateful_list.current_selection(),
&filtered_stateful_list.filtered_items.as_ref().unwrap()[1]
);
}
#[test]
fn test_stateful_list_select_index() {
let mut stateful_list = create_test_stateful_list();
assert_eq!(stateful_list.state.selected(), Some(0));
stateful_list.select_index(Some(1));
assert_eq!(stateful_list.state.selected(), Some(1));
stateful_list.select_index(None);
assert_eq!(stateful_list.state.selected(), None);
}
#[test]
fn test_filtered_stateful_list_select_index() {
let mut filtered_stateful_list = create_test_filtered_stateful_list();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_list.select_index(Some(1));
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_list.select_index(None);
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
None
);
}
#[test]
fn test_stateful_list_scroll_up() {
let mut stateful_table = create_test_stateful_table();
let mut stateful_list = create_test_stateful_list();
assert_eq!(stateful_table.state.selected(), Some(0));
assert_eq!(stateful_list.state.selected(), Some(0));
stateful_table.scroll_up();
stateful_list.scroll_up();
assert_eq!(stateful_table.state.selected(), Some(1));
assert_eq!(stateful_list.state.selected(), Some(1));
stateful_table.scroll_up();
stateful_list.scroll_up();
assert_eq!(stateful_table.state.selected(), Some(0));
assert_eq!(stateful_list.state.selected(), Some(0));
}
#[test]
fn test_filtered_stateful_list_scroll_up() {
let mut filtered_stateful_list = create_test_filtered_stateful_list();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
filtered_stateful_list.scroll_up();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(1)
);
filtered_stateful_list.scroll_up();
assert_eq!(
filtered_stateful_list
.filtered_state
.as_ref()
.unwrap()
.selected(),
Some(0)
);
}
#[test]
fn test_stateful_list_apply_filter() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_items(vec!["this", "is", "a", "test"]);
stateful_list.filter = Some("i".into());
let expected_items = vec!["this", "is"];
let mut expected_state = ListState::default();
expected_state.select(Some(0));
let has_matches = stateful_list.apply_filter(|&item| item);
assert_eq!(stateful_list.filter, None);
assert_eq!(stateful_list.filtered_items, Some(expected_items));
assert_eq!(stateful_list.filtered_state, Some(expected_state));
assert!(has_matches);
}
#[test]
fn test_stateful_list_apply_filter_no_matches() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_items(vec!["this", "is", "a", "test"]);
stateful_list.filter = Some("z".into());
let has_matches = stateful_list.apply_filter(|&item| item);
assert_eq!(stateful_list.filter, None);
assert_eq!(stateful_list.filtered_items, None);
assert_eq!(stateful_list.filtered_state, None);
assert!(!has_matches);
}
#[test]
fn test_stateful_list_reset_filter() {
let mut stateful_list = create_test_filtered_stateful_list();
stateful_list.reset_filter();
assert_eq!(stateful_list.filter, None);
assert_eq!(stateful_list.filtered_items, None);
assert_eq!(stateful_list.filtered_state, None);
}
#[test]
fn test_stateful_list_apply_search() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_items(vec!["this", "is", "a", "test"]);
stateful_list.search = Some("test".into());
let mut expected_state = ListState::default();
expected_state.select(Some(3));
let has_match = stateful_list.apply_search(|&item| item);
assert_eq!(stateful_list.search, None);
assert_eq!(stateful_list.state, expected_state);
assert!(has_match);
}
#[test]
fn test_stateful_list_apply_search_no_match() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_items(vec!["this", "is", "a", "test"]);
stateful_list.search = Some("shi-mon-a!".into());
let has_match = stateful_list.apply_search(|&item| item);
assert_eq!(stateful_list.search, None);
assert!(!has_match);
}
#[test]
fn test_filtered_stateful_list_apply_search() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_filtered_items(vec!["this", "is", "a", "test"]);
stateful_list.search = Some("test".into());
let mut expected_state = ListState::default();
expected_state.select(Some(3));
let has_match = stateful_list.apply_search(|&item| item);
assert_eq!(stateful_list.search, None);
assert_eq!(stateful_list.filtered_state, Some(expected_state));
assert!(has_match);
}
#[test]
fn test_filtered_stateful_list_apply_search_no_match() {
let mut stateful_list: StatefulList<&str> = StatefulList::default();
stateful_list.set_filtered_items(vec!["this", "is", "a", "test"]);
stateful_list.search = Some("shi-mon-a!".into());
let mut expected_state = ListState::default();
expected_state.select(Some(0));
let has_match = stateful_list.apply_search(|&item| item);
assert_eq!(stateful_list.search, None);
assert_eq!(stateful_list.filtered_state, Some(expected_state));
assert!(!has_match);
}
#[test]
fn test_stateful_list_reset_search() {
let mut stateful_list = create_test_stateful_list();
stateful_list.search = Some("test".into());
stateful_list.reset_search();
assert_eq!(stateful_list.search, None);
}
#[test]
@@ -771,10 +1495,32 @@ mod tests {
stateful_table
}
fn create_test_filtered_stateful_table() -> StatefulTable<&'static str> {
let mut stateful_table = StatefulTable::default();
stateful_table.set_filtered_items(vec!["Test 1", "Test 2"]);
stateful_table
}
fn create_test_stateful_list() -> StatefulList<&'static str> {
let mut stateful_list = StatefulList::default();
stateful_list.set_items(vec!["Test 1", "Test 2"]);
stateful_list
}
fn create_test_filtered_stateful_list() -> StatefulList<&'static str> {
let mut stateful_list = StatefulList::default();
stateful_list.set_filtered_items(vec!["Test 1", "Test 2"]);
stateful_list
}
#[test]
fn test_strip_non_alphanumeric_characters() {
assert_eq!(
strip_non_search_characters("Te$t S7r!ng::'~-@_`,(.)/*}^&%#+="),
"tet s7rng::'-,./".to_owned()
)
}
}