From d84e7dfcabaa4f6ff1a1513c1c798e3bc162b8f5 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 17 Jul 2024 19:55:10 -0600 Subject: [PATCH] Refactored to require handlers to specify the components they rely on and to specify when they are ready. This fixes a lot of bugs with the UI when users try to press buttons while the application is still loading. --- Cargo.lock | 2 +- Cargo.toml | 4 +- src/app/app_tests.rs | 1 - src/app/mod.rs | 5 - src/handlers/mod.rs | 43 +- .../blocklist/blocklist_handler_tests.rs | 363 +++++++- src/handlers/radarr_handlers/blocklist/mod.rs | 4 + .../collections/collection_details_handler.rs | 4 + .../collection_details_handler_tests.rs | 217 ++++- .../collections/collections_handler_tests.rs | 491 +++++++++- .../collections/edit_collection_handler.rs | 4 + .../edit_collection_handler_tests.rs | 112 +++ .../radarr_handlers/collections/mod.rs | 4 + .../downloads/downloads_handler_tests.rs | 260 +++++- src/handlers/radarr_handlers/downloads/mod.rs | 4 + .../indexers/edit_indexer_handler.rs | 4 + .../indexers/edit_indexer_handler_tests.rs | 100 +- .../indexers/edit_indexer_settings_handler.rs | 4 + .../edit_indexer_settings_handler_tests.rs | 130 ++- .../indexers/indexers_handler_tests.rs | 355 ++++++- src/handlers/radarr_handlers/indexers/mod.rs | 4 + .../indexers/test_all_indexers_handler.rs | 10 + .../test_all_indexers_handler_tests.rs | 173 +++- .../library/add_movie_handler.rs | 4 + .../library/add_movie_handler_tests.rs | 183 +++- .../library/delete_movie_handler.rs | 4 + .../library/delete_movie_handler_tests.rs | 84 +- .../library/edit_movie_handler.rs | 4 + .../library/edit_movie_handler_tests.rs | 131 +++ .../library/library_handler_tests.rs | 501 +++++++++- src/handlers/radarr_handlers/library/mod.rs | 4 + .../library/movie_details_handler.rs | 15 + .../library/movie_details_handler_tests.rs | 863 +++++++++++++++++- src/handlers/radarr_handlers/mod.rs | 4 + .../radarr_handlers/radarr_handler_tests.rs | 15 + .../radarr_handlers/root_folders/mod.rs | 4 + .../root_folders_handler_tests.rs | 295 +++++- src/handlers/radarr_handlers/system/mod.rs | 7 + .../system/system_details_handler.rs | 6 + .../system/system_details_handler_tests.rs | 589 +++++++++++- .../system/system_handler_tests.rs | 311 ++++++- src/models/mod.rs | 4 + src/models/model_tests.rs | 12 + src/models/stateful_list.rs | 4 + src/models/stateful_list_tests.rs | 11 + src/models/stateful_table.rs | 4 + src/models/stateful_table_tests.rs | 11 + src/ui/radarr_ui/library/mod.rs | 26 +- src/ui/radarr_ui/library/movie_details_ui.rs | 14 +- 49 files changed, 5143 insertions(+), 265 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4c6514..cf7f804 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -853,7 +853,7 @@ dependencies = [ [[package]] name = "managarr" -version = "0.0.34" +version = "0.0.35" dependencies = [ "anyhow", "backtrace", diff --git a/Cargo.toml b/Cargo.toml index 99ae732..4a5ae69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "managarr" -version = "0.0.34" +version = "0.0.35" authors = ["Alex Clarke "] description = "A TUI to manage your Servarrs" keywords = ["managarr", "tui-rs", "dashboard", "servarr", "tui"] @@ -16,7 +16,7 @@ anyhow = "1.0.68" backtrace = "0.3.67" bimap = "0.6.3" chrono = { version = "0.4", features = ["serde"] } -confy = { version = "0.6.0", default_features = false, features = ["yaml_conf"] } +confy = { version = "0.6.0", default-features = false, features = ["yaml_conf"] } crossterm = "0.27.0" derivative = "2.2.0" human-panic = "1.1.3" diff --git a/src/app/app_tests.rs b/src/app/app_tests.rs index 6bd9862..4a09eda 100644 --- a/src/app/app_tests.rs +++ b/src/app/app_tests.rs @@ -40,7 +40,6 @@ mod tests { }, ] ); - assert_str_eq!(app.title, "Managarr"); assert_eq!(app.tick_until_poll, 400); assert_eq!(app.ticks_until_scroll, 4); assert_eq!(app.tick_count, 0); diff --git a/src/app/mod.rs b/src/app/mod.rs index 0cba1c5..57dd16a 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -2,7 +2,6 @@ use anyhow::anyhow; use log::{debug, error}; use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::Sender; -use tokio::time::Instant; use tokio_util::sync::CancellationToken; use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES}; @@ -26,11 +25,9 @@ pub struct App<'a> { cancellation_token: CancellationToken, pub server_tabs: TabState, pub error: HorizontallyScrollableText, - pub title: &'static str, pub tick_until_poll: u64, pub ticks_until_scroll: u64, pub tick_count: u64, - pub last_tick: Instant, pub is_routing: bool, pub is_loading: bool, pub should_refresh: bool, @@ -151,11 +148,9 @@ impl<'a> Default for App<'a> { contextual_help: None, }, ]), - title: "Managarr", tick_until_poll: 400, ticks_until_scroll: 4, tick_count: 0, - last_tick: Instant::now(), is_loading: false, is_routing: false, should_refresh: false, diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 4edce7d..d7064a1 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -19,17 +19,45 @@ pub trait KeyEventHandler<'a, 'b, T: Into> { fn handle_key_event(&mut self) { let key = self.get_key(); match key { - _ if *key == DEFAULT_KEYBINDINGS.up.key => self.handle_scroll_up(), - _ if *key == DEFAULT_KEYBINDINGS.down.key => self.handle_scroll_down(), - _ if *key == DEFAULT_KEYBINDINGS.home.key => self.handle_home(), - _ if *key == DEFAULT_KEYBINDINGS.end.key => self.handle_end(), - _ if *key == DEFAULT_KEYBINDINGS.delete.key => self.handle_delete(), + _ if *key == DEFAULT_KEYBINDINGS.up.key => { + if self.is_ready() { + self.handle_scroll_up(); + } + } + _ if *key == DEFAULT_KEYBINDINGS.down.key => { + if self.is_ready() { + self.handle_scroll_down(); + } + } + _ if *key == DEFAULT_KEYBINDINGS.home.key => { + if self.is_ready() { + self.handle_home(); + } + } + _ if *key == DEFAULT_KEYBINDINGS.end.key => { + if self.is_ready() { + self.handle_end(); + } + } + _ if *key == DEFAULT_KEYBINDINGS.delete.key => { + if self.is_ready() { + self.handle_delete(); + } + } _ if *key == DEFAULT_KEYBINDINGS.left.key || *key == DEFAULT_KEYBINDINGS.right.key => { self.handle_left_right_action() } - _ if *key == DEFAULT_KEYBINDINGS.submit.key => self.handle_submit(), + _ if *key == DEFAULT_KEYBINDINGS.submit.key => { + if self.is_ready() { + self.handle_submit(); + } + } _ if *key == DEFAULT_KEYBINDINGS.esc.key => self.handle_esc(), - _ => self.handle_char_key_event(), + _ => { + if self.is_ready() { + self.handle_char_key_event(); + } + } } } @@ -40,6 +68,7 @@ pub trait KeyEventHandler<'a, 'b, T: Into> { fn accepts(active_block: &'a T) -> bool; fn with(key: &'a Key, app: &'a mut App<'b>, active_block: &'a T, context: &'a Option) -> Self; fn get_key(&self) -> &Key; + fn is_ready(&self) -> bool; fn handle_scroll_up(&mut self); fn handle_scroll_down(&mut self); fn handle_home(&mut self); diff --git a/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs b/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs index a90128e..c982452 100644 --- a/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs +++ b/src/handlers/radarr_handlers/blocklist/blocklist_handler_tests.rs @@ -1,5 +1,11 @@ #[cfg(test)] mod tests { + use std::cmp::Ordering; + + use chrono::DateTime; + use pretty_assertions::{assert_eq, assert_str_eq}; + use strum::IntoEnumIterator; + use crate::app::key_binding::DEFAULT_KEYBINDINGS; use crate::app::App; use crate::event::Key; @@ -10,10 +16,6 @@ mod tests { }; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, BLOCKLIST_BLOCKS}; use crate::models::stateful_table::SortOption; - use chrono::DateTime; - use pretty_assertions::{assert_eq, assert_str_eq}; - use std::cmp::Ordering; - use strum::IntoEnumIterator; mod test_handle_scroll_up_and_down { use pretty_assertions::{assert_eq, assert_str_eq}; @@ -35,6 +37,49 @@ mod tests { to_string ); + #[rstest] + fn test_blocklist_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .blocklist + .set_items(simple_stateful_iterable_vec!( + BlocklistItem, + String, + source_title + )); + + BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .blocklist + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + + BlocklistHandler::with(&key, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .blocklist + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_blocklist_sort_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -92,10 +137,12 @@ mod tests { } mod test_handle_home_end { - use super::*; + use pretty_assertions::{assert_eq, assert_str_eq}; + use crate::models::radarr_models::BlocklistItem; use crate::{extended_stateful_iterable_vec, test_iterable_home_and_end}; - use pretty_assertions::{assert_eq, assert_str_eq}; + + use super::*; test_iterable_home_and_end!( test_blocklist_home_and_end, @@ -108,6 +155,59 @@ mod tests { to_string ); + #[test] + fn test_blocklist_home_and_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .blocklist + .set_items(extended_stateful_iterable_vec!( + BlocklistItem, + String, + source_title + )); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .blocklist + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .blocklist + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + #[test] fn test_blocklist_sort_home_end() { let blocklist_field_vec = sort_options(); @@ -157,30 +257,51 @@ mod tests { } mod test_handle_delete { - use super::*; - use crate::assert_delete_prompt; use pretty_assertions::assert_eq; + use super::*; + const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key; #[test] fn test_delete_blocklist_item_prompt() { - assert_delete_prompt!( - BlocklistHandler, - ActiveRadarrBlock::Blocklist, - ActiveRadarrBlock::DeleteBlocklistItemPrompt + let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + + BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::DeleteBlocklistItemPrompt.into() + ); + } + + #[test] + fn test_delete_blocklist_item_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + + BlocklistHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() ); } } mod test_handle_left_right_action { - use super::*; use pretty_assertions::assert_eq; use rstest::rstest; - #[test] - fn test_blocklist_tab_left() { + use super::*; + + #[rstest] + fn test_blocklist_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(3); BlocklistHandler::with( @@ -201,9 +322,10 @@ mod tests { ); } - #[test] - fn test_blocklist_tab_right() { + #[rstest] + fn test_blocklist_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(3); BlocklistHandler::with( @@ -246,14 +368,44 @@ mod tests { } mod test_handle_submit { - use crate::network::radarr_network::RadarrEvent; use pretty_assertions::assert_eq; use rstest::rstest; + use crate::network::radarr_network::RadarrEvent; + use super::*; const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key; + #[test] + fn test_blocklist_submit() { + let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + + BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::BlocklistItemDetails.into() + ); + } + + #[test] + fn test_blocklist_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + + BlocklistHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Blocklist, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() + ); + } + #[rstest] #[case( ActiveRadarrBlock::Blocklist, @@ -271,6 +423,7 @@ mod tests { #[case] expected_action: RadarrEvent, ) { let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(base_route.into()); app.push_navigation_stack(prompt_block.into()); @@ -294,6 +447,7 @@ mod tests { prompt_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(prompt_block.into()); @@ -337,11 +491,13 @@ mod tests { } mod test_handle_esc { - use super::*; - use crate::handlers::radarr_handlers::downloads::DownloadsHandler; use pretty_assertions::assert_eq; use rstest::rstest; + use crate::handlers::radarr_handlers::downloads::DownloadsHandler; + + use super::*; + const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; #[rstest] @@ -408,9 +564,10 @@ mod tests { ); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); @@ -426,18 +583,57 @@ mod tests { } mod test_handle_key_char { - use super::*; - use crate::assert_refresh_key; use pretty_assertions::assert_eq; + use super::*; + #[test] fn test_refresh_blocklist_key() { - assert_refresh_key!(BlocklistHandler, ActiveRadarrBlock::Blocklist); + let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() + ); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_blocklist_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() + ); + assert!(!app.should_refresh); } #[test] fn test_clear_blocklist_key() { let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); BlocklistHandler::with( &DEFAULT_KEYBINDINGS.clear.key, @@ -453,9 +649,31 @@ mod tests { ); } + #[test] + fn test_clear_blocklist_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.clear.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() + ); + } + #[test] fn test_sort_key() { let mut app = App::default(); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); BlocklistHandler::with( &DEFAULT_KEYBINDINGS.sort.key, @@ -475,6 +693,29 @@ mod tests { ); assert!(!app.data.radarr_data.blocklist.sort_asc); } + + #[test] + fn test_sort_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Blocklist.into()); + app.data.radarr_data.blocklist.set_items(blocklist_vec()); + + BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.sort.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Blocklist.into() + ); + assert!(app.data.radarr_data.blocklist.sort.is_none()); + assert!(!app.data.radarr_data.blocklist.sort_asc); + } } #[test] @@ -612,6 +853,67 @@ mod tests { assert_str_eq!(sort_option.name, "Date"); } + #[test] + fn test_blocklist_handler_accepts() { + ActiveRadarrBlock::iter().for_each(|active_radarr_block| { + if BLOCKLIST_BLOCKS.contains(&active_radarr_block) { + assert!(BlocklistHandler::accepts(&active_radarr_block)); + } else { + assert!(!BlocklistHandler::accepts(&active_radarr_block)); + } + }) + } + + #[test] + fn test_blocklist_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_blocklist_handler_not_ready_when_blocklist_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_blocklist_handler_ready_when_not_loading_and_blocklist_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .blocklist + .set_items(vec![BlocklistItem::default()]); + + let handler = BlocklistHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Blocklist, + &None, + ); + + assert!(handler.is_ready()); + } + fn blocklist_vec() -> Vec { vec![ BlocklistItem { @@ -692,15 +994,4 @@ mod tests { }), }] } - - #[test] - fn test_blocklist_handler_accepts() { - ActiveRadarrBlock::iter().for_each(|active_radarr_block| { - if BLOCKLIST_BLOCKS.contains(&active_radarr_block) { - assert!(BlocklistHandler::accepts(&active_radarr_block)); - } else { - assert!(!BlocklistHandler::accepts(&active_radarr_block)); - } - }) - } } diff --git a/src/handlers/radarr_handlers/blocklist/mod.rs b/src/handlers/radarr_handlers/blocklist/mod.rs index 623a084..fd6e4dc 100644 --- a/src/handlers/radarr_handlers/blocklist/mod.rs +++ b/src/handlers/radarr_handlers/blocklist/mod.rs @@ -43,6 +43,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a, self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.blocklist.is_empty() + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Blocklist => self.app.data.radarr_data.blocklist.scroll_up(), diff --git a/src/handlers/radarr_handlers/collections/collection_details_handler.rs b/src/handlers/radarr_handlers/collections/collection_details_handler.rs index 1e5e75e..c9243d9 100644 --- a/src/handlers/radarr_handlers/collections/collection_details_handler.rs +++ b/src/handlers/radarr_handlers/collections/collection_details_handler.rs @@ -43,6 +43,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionDetailsHan self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.collection_movies.is_empty() + } + fn handle_scroll_up(&mut self) { if ActiveRadarrBlock::CollectionDetails == *self.active_radarr_block { self.app.data.radarr_data.collection_movies.scroll_up() diff --git a/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs b/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs index e91789b..34659d5 100644 --- a/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/collection_details_handler_tests.rs @@ -31,6 +31,53 @@ mod tests { title, to_string ); + + #[rstest] + fn test_collection_details_scroll_no_op_when_not_ready( + #[values( + DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key + )] + key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .collection_movies + .set_items(simple_stateful_iterable_vec!( + CollectionMovie, + HorizontallyScrollableText + )); + + CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collection_movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + + CollectionDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::CollectionDetails, &None) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collection_movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + } } mod test_handle_home_end { @@ -48,6 +95,58 @@ mod tests { title, to_string ); + + #[test] + fn test_collection_details_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .collection_movies + .set_items(extended_stateful_iterable_vec!( + CollectionMovie, + HorizontallyScrollableText + )); + + CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collection_movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + + CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collection_movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + } } mod test_handle_submit { @@ -139,6 +238,32 @@ mod tests { ); } + #[test] + fn test_collection_details_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); + app + .data + .radarr_data + .collection_movies + .set_items(vec![CollectionMovie::default()]); + + CollectionDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::CollectionDetails.into() + ); + assert!(app.data.radarr_data.add_movie_modal.is_none()); + } + #[test] fn test_collection_details_submit_movie_already_in_library() { let mut app = App::default(); @@ -169,15 +294,16 @@ mod tests { } mod test_handle_esc { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; + use rstest::rstest; const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_esc_collection_details() { + #[rstest] + fn test_esc_collection_details(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); app @@ -225,7 +351,6 @@ mod tests { mod test_handle_key_char { use bimap::BiMap; use pretty_assertions::{assert_eq, assert_str_eq}; - use strum::IntoEnumIterator; use crate::models::radarr_models::{Collection, MinimumAvailability}; @@ -233,7 +358,6 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_COLLECTION_SELECTION_BLOCKS, }; - use crate::test_edit_collection_key; use super::*; @@ -246,6 +370,37 @@ mod tests { ActiveRadarrBlock::CollectionDetails ); } + + #[test] + fn test_edit_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::CollectionDetails.into()); + let mut radarr_data = create_test_radarr_data(); + radarr_data.collections.set_items(vec![Collection { + root_folder_path: "/nfs/movies/Test".to_owned().into(), + monitored: true, + search_on_add: true, + quality_profile_id: 2222, + minimum_availability: MinimumAvailability::Released, + ..Collection::default() + }]); + app.data.radarr_data = radarr_data; + + CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.edit.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::CollectionDetails.into() + ); + assert!(app.data.radarr_data.edit_collection_modal.is_none()); + } } #[test] @@ -258,4 +413,54 @@ mod tests { } }); } + + #[test] + fn test_collection_details_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_collection_details_handler_not_ready_when_collection_movies_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_collection_details_handler_ready_when_not_loading_and_collection_movies_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .collection_movies + .set_items(vec![CollectionMovie::default()]); + + let handler = CollectionDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::CollectionDetails, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs index 1fdd360..61c2b6f 100644 --- a/src/handlers/radarr_handlers/collections/collections_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/collections_handler_tests.rs @@ -1,9 +1,10 @@ #[cfg(test)] mod tests { - use pretty_assertions::{assert_eq, assert_str_eq}; - use rstest::rstest; use std::cmp::Ordering; use std::iter; + + use pretty_assertions::{assert_eq, assert_str_eq}; + use rstest::rstest; use strum::IntoEnumIterator; use crate::app::key_binding::DEFAULT_KEYBINDINGS; @@ -22,10 +23,10 @@ mod tests { use crate::{extended_stateful_iterable_vec, test_handler_delegation}; mod test_handle_scroll_up_and_down { + use pretty_assertions::assert_eq; use rstest::rstest; use crate::{simple_stateful_iterable_vec, test_iterable_scroll}; - use pretty_assertions::assert_eq; use super::*; @@ -40,6 +41,51 @@ mod tests { to_string ); + #[rstest] + fn test_collections_scroll_no_op_when_not_ready( + #[values( + DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key + )] + key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .collections + .set_items(simple_stateful_iterable_vec!( + Collection, + HorizontallyScrollableText + )); + + CollectionsHandler::with(&key, &mut app, &ActiveRadarrBlock::Collections, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collections + .current_selection() + .title + .to_string(), + "Test 1" + ); + + CollectionsHandler::with(&key, &mut app, &ActiveRadarrBlock::Collections, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collections + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_collections_sort_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -114,9 +160,66 @@ mod tests { to_string ); + #[test] + fn test_collections_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .collections + .set_items(extended_stateful_iterable_vec!( + Collection, + HorizontallyScrollableText + )); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collections + .current_selection() + .title + .to_string(), + "Test 1" + ); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .collections + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[test] fn test_collection_search_box_home_end_keys() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.search = Some("Test".into()); CollectionsHandler::with( @@ -165,6 +268,11 @@ mod tests { #[test] fn test_collection_filter_box_home_end_keys() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.filter = Some("Test".into()); CollectionsHandler::with( @@ -264,9 +372,10 @@ mod tests { use super::*; - #[test] - fn test_collections_tab_left() { + #[rstest] + fn test_collections_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(1); CollectionsHandler::with( @@ -284,9 +393,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); } - #[test] - fn test_collections_tab_right() { + #[rstest] + fn test_collections_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(1); CollectionsHandler::with( @@ -443,6 +553,11 @@ mod tests { #[test] fn test_collections_submit() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); CollectionsHandler::with( &SUBMIT_KEY, @@ -458,6 +573,31 @@ mod tests { ); } + #[test] + fn test_collections_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + } + #[test] fn test_search_collections_submit() { let mut app = App::default(); @@ -661,6 +801,11 @@ mod tests { #[test] fn test_update_all_collections_prompt_confirm_submit() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into()); @@ -687,6 +832,11 @@ mod tests { #[test] fn test_update_all_collections_prompt_decline_submit() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::UpdateAllCollectionsPrompt.into()); @@ -849,9 +999,10 @@ mod tests { ); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); @@ -881,7 +1032,6 @@ mod tests { mod test_handle_key_char { use bimap::BiMap; use pretty_assertions::{assert_eq, assert_str_eq}; - use strum::IntoEnumIterator; use crate::models::radarr_models::MinimumAvailability; @@ -889,15 +1039,18 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ RadarrData, EDIT_COLLECTION_SELECTION_BLOCKS, }; - - use crate::models::stateful_table::StatefulTable; - use crate::{assert_refresh_key, test_edit_collection_key}; + use crate::test_edit_collection_key; use super::*; #[test] fn test_search_collections_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); CollectionsHandler::with( &DEFAULT_KEYBINDINGS.search.key, @@ -918,9 +1071,41 @@ mod tests { ); } + #[test] + fn test_search_collections_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.search.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(!app.should_ignore_quit_key); + assert_eq!(app.data.radarr_data.collections.search, None); + } + #[test] fn test_filter_collections_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); CollectionsHandler::with( &DEFAULT_KEYBINDINGS.filter.key, @@ -938,13 +1123,44 @@ mod tests { assert!(app.data.radarr_data.collections.filter.is_some()); } + #[test] + fn test_filter_collections_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.filter.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(!app.should_ignore_quit_key); + assert!(app.data.radarr_data.collections.filter.is_none()); + } + #[test] fn test_filter_collections_key_resets_previous_filter() { let mut app = App::default(); + app.data.radarr_data = create_test_radarr_data(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.should_ignore_quit_key = true; app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); - app.data.radarr_data = create_test_radarr_data(); - app.data.radarr_data.collections = StatefulTable::default(); app.data.radarr_data.collections.filter = Some("Test".into()); CollectionsHandler::with( @@ -977,9 +1193,45 @@ mod tests { ); } + #[test] + fn test_collection_edit_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + let mut radarr_data = create_test_radarr_data(); + radarr_data.collections.set_items(vec![Collection { + root_folder_path: "/nfs/movies/Test".to_owned().into(), + monitored: true, + search_on_add: true, + quality_profile_id: 2222, + minimum_availability: MinimumAvailability::Released, + ..Collection::default() + }]); + app.data.radarr_data = radarr_data; + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.edit.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(app.data.radarr_data.edit_collection_modal.is_none()); + } + #[test] fn test_update_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); CollectionsHandler::with( &DEFAULT_KEYBINDINGS.update.key, @@ -995,14 +1247,90 @@ mod tests { ); } + #[test] + fn test_update_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + } + #[test] fn test_refresh_collections_key() { - assert_refresh_key!(CollectionsHandler, ActiveRadarrBlock::Collections); + let mut app = App::default(); + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_collections_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(!app.should_refresh); } #[test] fn test_search_collections_box_backspace_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.search = Some("Test".into()); CollectionsHandler::with( @@ -1029,7 +1357,11 @@ mod tests { #[test] fn test_filter_collections_box_backspace_key() { let mut app = App::default(); - app.data.radarr_data.collections = StatefulTable::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.filter = Some("Test".into()); CollectionsHandler::with( @@ -1056,6 +1388,11 @@ mod tests { #[test] fn test_search_collections_box_char_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.search = Some(HorizontallyScrollableText::default()); CollectionsHandler::with( @@ -1082,7 +1419,11 @@ mod tests { #[test] fn test_filter_collections_box_char_key() { let mut app = App::default(); - app.data.radarr_data.collections = StatefulTable::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); app.data.radarr_data.collections.filter = Some(HorizontallyScrollableText::default()); CollectionsHandler::with( @@ -1109,6 +1450,11 @@ mod tests { #[test] fn test_sort_key() { let mut app = App::default(); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); CollectionsHandler::with( &DEFAULT_KEYBINDINGS.sort.key, @@ -1135,6 +1481,33 @@ mod tests { ); assert!(!app.data.radarr_data.collections.sort_asc); } + + #[test] + fn test_sort_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.sort.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Collections.into() + ); + assert!(app.data.radarr_data.collections.sort.is_none()); + assert!(!app.data.radarr_data.collections.sort_asc); + } } #[rstest] @@ -1279,6 +1652,72 @@ mod tests { assert_str_eq!(sort_option.name, "Monitored"); } + #[test] + fn test_collections_handler_accepts() { + let mut collections_handler_blocks = Vec::new(); + collections_handler_blocks.extend(COLLECTIONS_BLOCKS); + collections_handler_blocks.extend(COLLECTION_DETAILS_BLOCKS); + collections_handler_blocks.extend(EDIT_COLLECTION_BLOCKS); + + ActiveRadarrBlock::iter().for_each(|active_radarr_block| { + if collections_handler_blocks.contains(&active_radarr_block) { + assert!(CollectionsHandler::accepts(&active_radarr_block)); + } else { + assert!(!CollectionsHandler::accepts(&active_radarr_block)); + } + }); + } + + #[test] + fn test_collections_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_collections_handler_not_ready_when_collections_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_collections_handler_ready_when_not_loading_and_collections_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .collections + .set_items(vec![Collection::default()]); + + let handler = CollectionsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Collections, + &None, + ); + + assert!(handler.is_ready()); + } + fn collections_vec() -> Vec { vec![ Collection { @@ -1325,20 +1764,4 @@ mod tests { }), }] } - - #[test] - fn test_collections_handler_accepts() { - let mut collections_handler_blocks = Vec::new(); - collections_handler_blocks.extend(COLLECTIONS_BLOCKS); - collections_handler_blocks.extend(COLLECTION_DETAILS_BLOCKS); - collections_handler_blocks.extend(EDIT_COLLECTION_BLOCKS); - - ActiveRadarrBlock::iter().for_each(|active_radarr_block| { - if collections_handler_blocks.contains(&active_radarr_block) { - assert!(CollectionsHandler::accepts(&active_radarr_block)); - } else { - assert!(!CollectionsHandler::accepts(&active_radarr_block)); - } - }); - } } diff --git a/src/handlers/radarr_handlers/collections/edit_collection_handler.rs b/src/handlers/radarr_handlers/collections/edit_collection_handler.rs index 465d1dd..2cfd8ea 100644 --- a/src/handlers/radarr_handlers/collections/edit_collection_handler.rs +++ b/src/handlers/radarr_handlers/collections/edit_collection_handler.rs @@ -41,6 +41,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditCollectionHandle self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && self.app.data.radarr_data.edit_collection_modal.is_some() + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::EditCollectionSelectMinimumAvailability => self diff --git a/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs b/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs index 692f85d..4790569 100644 --- a/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs +++ b/src/handlers/radarr_handlers/collections/edit_collection_handler_tests.rs @@ -9,6 +9,7 @@ mod tests { use crate::handlers::radarr_handlers::collections::edit_collection_handler::EditCollectionHandler; use crate::handlers::KeyEventHandler; use crate::models::radarr_models::MinimumAvailability; + use crate::models::servarr_data::radarr::modals::EditCollectionModal; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, EDIT_COLLECTION_BLOCKS, }; @@ -146,6 +147,7 @@ mod tests { #[rstest] fn test_edit_collection_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); app.data.radarr_data.selected_block.next(); @@ -170,6 +172,31 @@ mod tests { ); } } + + #[rstest] + fn test_edit_collection_prompt_scroll_no_op_when_not_ready( + #[values(Key::Up, Key::Down)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + EditCollectionHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::EditCollectionSelectMinimumAvailability + ); + } } mod test_handle_home_end { @@ -479,6 +506,7 @@ mod tests { #[test] fn test_edit_collection_prompt_prompt_decline_submit() { let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.data.radarr_data.selected_block = @@ -507,6 +535,7 @@ mod tests { #[test] fn test_edit_collection_confirm_prompt_prompt_confirmation_submit() { let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.data.radarr_data.prompt_confirm = true; @@ -537,6 +566,38 @@ mod tests { assert!(app.should_refresh); } + #[test] + fn test_edit_collection_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); + app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); + app.data.radarr_data.prompt_confirm = true; + app.data.radarr_data.selected_block = + BlockSelectionState::new(&EDIT_COLLECTION_SELECTION_BLOCKS); + app + .data + .radarr_data + .selected_block + .set_index(EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + + EditCollectionHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::EditCollectionPrompt.into() + ); + assert_eq!(app.data.radarr_data.prompt_confirm_action, None); + assert!(!app.should_refresh); + } + #[test] fn test_edit_collection_toggle_monitored_submit() { let current_route = Route::from(( @@ -657,6 +718,7 @@ mod tests { #[case] index: usize, ) { let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.push_navigation_stack( ( ActiveRadarrBlock::EditCollectionPrompt, @@ -697,6 +759,7 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(active_radarr_block.into()); @@ -733,6 +796,7 @@ mod tests { fn test_edit_collection_root_folder_path_input_esc() { let mut app = App::default(); app.data.radarr_data = create_test_radarr_data(); + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); app.should_ignore_quit_key = true; app.push_navigation_stack(ActiveRadarrBlock::EditCollectionPrompt.into()); app.push_navigation_stack(ActiveRadarrBlock::EditCollectionRootFolderPathInput.into()); @@ -784,8 +848,10 @@ mod tests { ActiveRadarrBlock::EditCollectionSelectQualityProfile )] active_radarr_block: ActiveRadarrBlock, + #[values(true, false)] is_ready: bool, ) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data = create_test_radarr_data(); app.push_navigation_stack(ActiveRadarrBlock::Collections.into()); app.push_navigation_stack(active_radarr_block.into()); @@ -869,4 +935,50 @@ mod tests { } }); } + + #[test] + fn test_edit_collection_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = EditCollectionHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_collection_handler_is_not_ready_when_edit_collection_modal_is_none() { + let mut app = App::default(); + app.is_loading = false; + + let handler = EditCollectionHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_collection_handler_is_ready_when_edit_collection_modal_is_some() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.edit_collection_modal = Some(EditCollectionModal::default()); + + let handler = EditCollectionHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditCollectionPrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/collections/mod.rs b/src/handlers/radarr_handlers/collections/mod.rs index 77d6226..942a855 100644 --- a/src/handlers/radarr_handlers/collections/mod.rs +++ b/src/handlers/radarr_handlers/collections/mod.rs @@ -67,6 +67,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<' self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.collections.is_empty() + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Collections => self.app.data.radarr_data.collections.scroll_up(), diff --git a/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs b/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs index c02475a..15a349e 100644 --- a/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs +++ b/src/handlers/radarr_handlers/downloads/downloads_handler_tests.rs @@ -8,6 +8,7 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::downloads::DownloadsHandler; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::DownloadRecord; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS}; mod test_handle_scroll_up_and_down { @@ -27,6 +28,36 @@ mod tests { None, title ); + + #[rstest] + fn test_downloads_scroll_no_op_when_not_ready( + #[values( + DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key + )] + key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .downloads + .set_items(simple_stateful_iterable_vec!(DownloadRecord)); + + DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.downloads.current_selection().title, + "Test 1" + ); + + DownloadsHandler::with(&key, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.downloads.current_selection().title, + "Test 1" + ); + } } mod test_handle_home_end { @@ -44,23 +75,85 @@ mod tests { None, title ); + + #[test] + fn test_downloads_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .downloads + .set_items(extended_stateful_iterable_vec!(DownloadRecord)); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.downloads.current_selection().title, + "Test 1" + ); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.downloads.current_selection().title, + "Test 1" + ); + } } mod test_handle_delete { use pretty_assertions::assert_eq; - use crate::assert_delete_prompt; - use super::*; const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key; #[test] fn test_delete_download_prompt() { - assert_delete_prompt!( - DownloadsHandler, - ActiveRadarrBlock::Downloads, - ActiveRadarrBlock::DeleteDownloadPrompt + let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + + DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::DeleteDownloadPrompt.into() + ); + } + + #[test] + fn test_delete_download_prompt_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + + DownloadsHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Downloads, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Downloads.into() ); } } @@ -71,9 +164,10 @@ mod tests { use super::*; - #[test] - fn test_downloads_tab_left() { + #[rstest] + fn test_downloads_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(2); DownloadsHandler::with( @@ -94,9 +188,10 @@ mod tests { ); } - #[test] - fn test_downloads_tab_right() { + #[rstest] + fn test_downloads_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(2); DownloadsHandler::with( @@ -165,6 +260,11 @@ mod tests { #[case] expected_action: RadarrEvent, ) { let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(base_route.into()); app.push_navigation_stack(prompt_block.into()); @@ -187,6 +287,11 @@ mod tests { #[case] prompt_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); app.push_navigation_stack(base_route.into()); app.push_navigation_stack(prompt_block.into()); @@ -224,9 +329,10 @@ mod tests { assert!(!app.data.radarr_data.prompt_confirm); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); @@ -244,13 +350,16 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_eq; - use crate::assert_refresh_key; - use super::*; #[test] fn test_update_downloads_key() { let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); DownloadsHandler::with( &DEFAULT_KEYBINDINGS.update.key, @@ -266,9 +375,80 @@ mod tests { ); } + #[test] + fn test_update_downloads_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Downloads.into() + ); + } + #[test] fn test_refresh_downloads_key() { - assert_refresh_key!(DownloadsHandler, ActiveRadarrBlock::Downloads); + let mut app = App::default(); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Downloads.into() + ); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_downloads_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + + DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::Downloads.into() + ); + assert!(!app.should_refresh); } } @@ -282,4 +462,54 @@ mod tests { } }) } + + #[test] + fn test_downloads_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_downloads_handler_not_ready_when_downloads_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_downloads_handler_ready_when_not_loading_and_downloads_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + + app + .data + .radarr_data + .downloads + .set_items(vec![DownloadRecord::default()]); + let handler = DownloadsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Downloads, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/downloads/mod.rs b/src/handlers/radarr_handlers/downloads/mod.rs index 57a773d..d23184a 100644 --- a/src/handlers/radarr_handlers/downloads/mod.rs +++ b/src/handlers/radarr_handlers/downloads/mod.rs @@ -41,6 +41,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DownloadsHandler<'a, self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.downloads.is_empty() + } + fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Downloads { self.app.data.radarr_data.downloads.scroll_up() diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs index 63a5c7e..465a639 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_handler.rs @@ -40,6 +40,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditIndexerHandler<' self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && self.app.data.radarr_data.edit_indexer_modal.is_some() + } + fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::EditIndexerPrompt { self.app.data.radarr_data.selected_block.previous(); diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs index 170489a..a76156f 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_handler_tests.rs @@ -1,9 +1,11 @@ #[cfg(test)] mod tests { use crate::app::key_binding::DEFAULT_KEYBINDINGS; + use crate::app::App; use crate::event::Key; use crate::handlers::radarr_handlers::indexers::edit_indexer_handler::EditIndexerHandler; use crate::handlers::KeyEventHandler; + use crate::models::servarr_data::radarr::modals::EditIndexerModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_INDEXER_BLOCKS}; use strum::IntoEnumIterator; @@ -41,6 +43,26 @@ mod tests { ); } } + + #[rstest] + fn test_edit_indexer_prompt_scroll_no_op_when_not_ready( + #[values(Key::Up, Key::Down)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + EditIndexerHandler::with(&key, &mut app, &ActiveRadarrBlock::EditIndexerPrompt, &None) + .handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::EditIndexerToggleEnableRss + ); + } } mod test_handle_home_end { @@ -803,6 +825,32 @@ mod tests { ); } + #[test] + fn test_edit_indexer_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditIndexerPrompt.into()); + app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); + app.data.radarr_data.prompt_confirm = true; + + EditIndexerHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::EditIndexerPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::EditIndexerPrompt.into() + ); + assert!(app.data.radarr_data.edit_indexer_modal.is_some()); + assert!(!app.should_refresh); + assert_eq!(app.data.radarr_data.prompt_confirm_action, None); + } + #[rstest] #[case(0, ActiveRadarrBlock::EditIndexerNameInput)] #[case(5, ActiveRadarrBlock::EditIndexerUrlInput)] @@ -814,6 +862,7 @@ mod tests { #[case] block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); app.push_navigation_stack(ActiveRadarrBlock::EditIndexerPrompt.into()); app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_INDEXER_TORRENT_SELECTION_BLOCKS); @@ -1177,9 +1226,10 @@ mod tests { const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_edit_indexer_prompt_esc() { + #[rstest] + fn test_edit_indexer_prompt_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::EditIndexerPrompt.into()); app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); @@ -1518,4 +1568,50 @@ mod tests { } }) } + + #[test] + fn test_edit_indexer_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = EditIndexerHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditIndexerPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_indexer_handler_is_not_ready_when_edit_indexer_modal_is_none() { + let mut app = App::default(); + app.is_loading = false; + + let handler = EditIndexerHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditIndexerPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_indexer_handler_is_ready_when_edit_indexer_modal_is_some() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.edit_indexer_modal = Some(EditIndexerModal::default()); + + let handler = EditIndexerHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditIndexerPrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs index b1ba34d..2869309 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler.rs @@ -42,6 +42,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexerSettingsHandl self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && self.app.data.radarr_data.indexer_settings.is_some() + } + fn handle_scroll_up(&mut self) { let indexer_settings = self.app.data.radarr_data.indexer_settings.as_mut().unwrap(); match self.active_radarr_block { diff --git a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs index 84bd315..40fd94f 100644 --- a/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/edit_indexer_settings_handler_tests.rs @@ -7,6 +7,7 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::indexers::edit_indexer_settings_handler::IndexerSettingsHandler; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::IndexerSettings; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, INDEXER_SETTINGS_BLOCKS, }; @@ -121,6 +122,31 @@ mod tests { } } + #[rstest] + fn test_edit_indexer_settings_prompt_scroll_no_op_when_not_ready( + #[values(Key::Up, Key::Down)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + IndexerSettingsHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::IndexerSettingsRetentionInput + ); + } + #[rstest] fn test_edit_indexer_settings_minimum_age_scroll(#[values(Key::Up, Key::Down)] key: Key) { test_i64_counter_scroll_value!( @@ -462,6 +488,30 @@ mod tests { assert!(app.should_refresh); } + #[test] + fn test_edit_indexer_settings_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); + app.data.radarr_data.prompt_confirm = true; + + IndexerSettingsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::IndexerSettingsPrompt.into() + ); + assert!(!app.should_refresh); + } + #[rstest] #[case(ActiveRadarrBlock::IndexerSettingsMinimumAgeInput, 0)] #[case(ActiveRadarrBlock::IndexerSettingsRetentionInput, 1)] @@ -473,6 +523,7 @@ mod tests { #[case] index: usize, ) { let mut app = App::default(); + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); app.data.radarr_data.selected_block = BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); @@ -489,9 +540,36 @@ mod tests { assert_eq!(app.get_current_route(), &selected_block.into()); } + #[rstest] + fn test_edit_indexer_settings_prompt_submit_selected_block_no_op_when_not_ready( + #[values(0, 1, 2, 5, 6)] index: usize, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); + app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); + app.data.radarr_data.selected_block = + BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.set_index(index); + + IndexerSettingsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::IndexerSettingsPrompt.into() + ); + } + #[test] fn test_edit_indexer_settings_prompt_submit_whitelisted_subtitle_tags_input() { let mut app = App::default(); + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); app.data.radarr_data.selected_block = BlockSelectionState::new(&INDEXER_SETTINGS_SELECTION_BLOCKS); @@ -669,6 +747,7 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); app.push_navigation_stack(active_radarr_block.into()); @@ -691,9 +770,10 @@ mod tests { const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_edit_indexer_settings_prompt_esc() { + #[rstest] + fn test_edit_indexer_settings_prompt_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::IndexerSettingsPrompt.into()); app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); @@ -837,4 +917,50 @@ mod tests { } }) } + + #[test] + fn test_edit_indexer_settings_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = IndexerSettingsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_indexer_settings_handler_not_ready_when_indexer_settings_is_none() { + let mut app = App::default(); + app.is_loading = false; + + let handler = IndexerSettingsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_indexer_settings_handler_ready_when_not_loading_and_indexer_settings_is_some() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.indexer_settings = Some(IndexerSettings::default()); + + let handler = IndexerSettingsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::IndexerSettingsPrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs index c6076ce..d8e3696 100644 --- a/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/indexers_handler_tests.rs @@ -9,6 +9,7 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::indexers::IndexersHandler; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::Indexer; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, EDIT_INDEXER_BLOCKS, INDEXERS_BLOCKS, INDEXER_SETTINGS_BLOCKS, }; @@ -31,6 +32,36 @@ mod tests { None, protocol ); + + #[rstest] + fn test_indexers_scroll_no_op_when_not_ready( + #[values( + DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key + )] + key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .indexers + .set_items(simple_stateful_iterable_vec!(Indexer, String, protocol)); + + IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.indexers.current_selection().protocol, + "Test 1" + ); + + IndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::Indexers, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.indexers.current_selection().protocol, + "Test 1" + ); + } } mod test_handle_home_end { @@ -48,25 +79,84 @@ mod tests { None, protocol ); + + #[test] + fn test_indexers_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .indexers + .set_items(extended_stateful_iterable_vec!(Indexer, String, protocol)); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.indexers.current_selection().protocol, + "Test 1" + ); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.indexers.current_selection().protocol, + "Test 1" + ); + } } mod test_handle_delete { use pretty_assertions::assert_eq; - use crate::assert_delete_prompt; - use super::*; const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key; #[test] fn test_delete_indexer_prompt() { - assert_delete_prompt!( - IndexersHandler, - ActiveRadarrBlock::Indexers, - ActiveRadarrBlock::DeleteIndexerPrompt + let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::DeleteIndexerPrompt.into() ); } + + #[test] + fn test_delete_indexer_prompt_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } } mod test_handle_left_right_action { @@ -75,9 +165,10 @@ mod tests { use super::*; - #[test] - fn test_indexers_tab_left() { + #[rstest] + fn test_indexers_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(5); IndexersHandler::with( @@ -98,9 +189,10 @@ mod tests { ); } - #[test] - fn test_indexers_tab_right() { + #[rstest] + fn test_indexers_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(5); IndexersHandler::with( @@ -243,9 +335,31 @@ mod tests { } } + #[test] + fn test_edit_indexer_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Indexers, &None).handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert_eq!(app.data.radarr_data.edit_indexer_modal, None); + } + #[test] fn test_delete_indexer_prompt_confirm_submit() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); @@ -269,6 +383,11 @@ mod tests { #[test] fn test_prompt_decline_submit() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); @@ -293,9 +412,10 @@ mod tests { const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_delete_indexer_prompt_block_esc() { + #[rstest] + fn test_delete_indexer_prompt_block_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteIndexerPrompt.into()); app.data.radarr_data.prompt_confirm = true; @@ -312,9 +432,10 @@ mod tests { assert!(!app.data.radarr_data.prompt_confirm); } - #[test] - fn test_test_indexer_esc() { + #[rstest] + fn test_test_indexer_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.indexer_test_error = Some("test result".to_owned()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::TestIndexer.into()); @@ -325,9 +446,10 @@ mod tests { assert_eq!(app.data.radarr_data.indexer_test_error, None); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); @@ -342,7 +464,6 @@ mod tests { mod test_handle_key_char { use pretty_assertions::assert_eq; - use crate::assert_refresh_key; use crate::models::servarr_data::radarr::radarr_data::INDEXER_SETTINGS_SELECTION_BLOCKS; use super::*; @@ -350,6 +471,11 @@ mod tests { #[test] fn test_indexer_add() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); IndexersHandler::with( &DEFAULT_KEYBINDINGS.add.key, @@ -365,14 +491,81 @@ mod tests { ); } + #[test] + fn test_indexer_add_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.add.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } + #[test] fn test_refresh_indexers_key() { - assert_refresh_key!(IndexersHandler, ActiveRadarrBlock::Indexers); + let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_indexers_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + assert!(!app.should_refresh); } #[test] fn test_indexer_settings_key() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); IndexersHandler::with( &DEFAULT_KEYBINDINGS.settings.key, @@ -392,9 +585,36 @@ mod tests { ); } + #[test] + fn test_indexer_settings_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.settings.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } + #[test] fn test_test_key() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); IndexersHandler::with( &DEFAULT_KEYBINDINGS.test.key, @@ -410,9 +630,36 @@ mod tests { ); } + #[test] + fn test_test_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.test.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } + #[test] fn test_test_all_key() { let mut app = App::default(); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); IndexersHandler::with( &DEFAULT_KEYBINDINGS.test_all.key, @@ -427,6 +674,28 @@ mod tests { &ActiveRadarrBlock::TestAllIndexers.into() ); } + + #[test] + fn test_test_all_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + IndexersHandler::with( + &DEFAULT_KEYBINDINGS.test_all.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); + } } #[rstest] @@ -500,4 +769,54 @@ mod tests { } }) } + + #[test] + fn test_indexers_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = IndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_indexers_handler_not_ready_when_indexers_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = IndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_indexers_handler_ready_when_not_loading_and_indexers_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .indexers + .set_items(vec![Indexer::default()]); + + let handler = IndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Indexers, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/indexers/mod.rs b/src/handlers/radarr_handlers/indexers/mod.rs index 793600e..99cccdd 100644 --- a/src/handlers/radarr_handlers/indexers/mod.rs +++ b/src/handlers/radarr_handlers/indexers/mod.rs @@ -72,6 +72,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for IndexersHandler<'a, self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.indexers.is_empty() + } + fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::Indexers { self.app.data.radarr_data.indexers.scroll_up(); diff --git a/src/handlers/radarr_handlers/indexers/test_all_indexers_handler.rs b/src/handlers/radarr_handlers/indexers/test_all_indexers_handler.rs index c68347f..307008a 100644 --- a/src/handlers/radarr_handlers/indexers/test_all_indexers_handler.rs +++ b/src/handlers/radarr_handlers/indexers/test_all_indexers_handler.rs @@ -38,6 +38,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for TestAllIndexersHandl self.key } + fn is_ready(&self) -> bool { + let table_is_ready = if let Some(table) = &self.app.data.radarr_data.indexer_test_all_results { + !table.is_empty() + } else { + false + }; + + !self.app.is_loading && table_is_ready + } + fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::TestAllIndexers { self diff --git a/src/handlers/radarr_handlers/indexers/test_all_indexers_handler_tests.rs b/src/handlers/radarr_handlers/indexers/test_all_indexers_handler_tests.rs index b7a50f9..4ac81cc 100644 --- a/src/handlers/radarr_handlers/indexers/test_all_indexers_handler_tests.rs +++ b/src/handlers/radarr_handlers/indexers/test_all_indexers_handler_tests.rs @@ -5,7 +5,9 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::indexers::test_all_indexers_handler::TestAllIndexersHandler; use crate::handlers::KeyEventHandler; + use crate::models::servarr_data::radarr::modals::IndexerTestResultModalItem; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; + use crate::models::stateful_table::StatefulTable; use strum::IntoEnumIterator; mod test_handle_scroll_up_and_down { @@ -61,6 +63,51 @@ mod tests { "Test 1" ); } + + #[rstest] + fn test_test_all_indexers_results_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut indexer_test_results = StatefulTable::default(); + indexer_test_results.set_items(simple_stateful_iterable_vec!( + IndexerTestResultModalItem, + String, + name + )); + app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results); + + TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .indexer_test_all_results + .as_ref() + .unwrap() + .current_selection() + .name, + "Test 1" + ); + + TestAllIndexersHandler::with(&key, &mut app, &ActiveRadarrBlock::TestAllIndexers, &None) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .indexer_test_all_results + .as_ref() + .unwrap() + .current_selection() + .name, + "Test 1" + ); + } } mod test_handle_home_end { @@ -122,17 +169,71 @@ mod tests { "Test 1" ); } + + #[test] + fn test_test_all_indexers_results_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut indexer_test_results = StatefulTable::default(); + indexer_test_results.set_items(extended_stateful_iterable_vec!( + IndexerTestResultModalItem, + String, + name + )); + app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results); + + TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .indexer_test_all_results + .as_ref() + .unwrap() + .current_selection() + .name, + "Test 1" + ); + + TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .indexer_test_all_results + .as_ref() + .unwrap() + .current_selection() + .name, + "Test 1" + ); + } } mod test_handle_esc { + use super::*; use crate::models::stateful_table::StatefulTable; use pretty_assertions::assert_eq; + use rstest::rstest; - use super::*; - - #[test] - fn test_test_all_indexers_esc() { + #[rstest] + fn test_test_all_indexers_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Indexers.into()); app.push_navigation_stack(ActiveRadarrBlock::TestAllIndexers.into()); app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default()); @@ -161,4 +262,68 @@ mod tests { } }); } + + #[test] + fn test_test_all_indexers_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_test_all_indexers_handler_is_not_ready_when_results_is_none() { + let mut app = App::default(); + app.is_loading = false; + + let handler = TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_test_all_indexers_handler_is_not_ready_when_results_is_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.indexer_test_all_results = Some(StatefulTable::default()); + + let handler = TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_test_all_indexers_handler_is_ready_when_results_is_not_empty_and_is_loaded() { + let mut app = App::default(); + app.is_loading = false; + let mut indexer_test_results = StatefulTable::default(); + indexer_test_results.set_items(vec![IndexerTestResultModalItem::default()]); + app.data.radarr_data.indexer_test_all_results = Some(indexer_test_results); + + let handler = TestAllIndexersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::TestAllIndexers, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/library/add_movie_handler.rs b/src/handlers/radarr_handlers/library/add_movie_handler.rs index ca85ddd..e4d24d0 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler.rs @@ -41,6 +41,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for AddMovieHandler<'a, self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::AddMovieSearchResults => self diff --git a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs index 7f3be59..7718462 100644 --- a/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/add_movie_handler_tests.rs @@ -82,6 +82,62 @@ mod tests { ); } + #[rstest] + fn test_add_movie_search_results_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(simple_stateful_iterable_vec!( + AddMovieSearchResult, + HorizontallyScrollableText + )); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + + AddMovieHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + + AddMovieHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_add_movie_select_monitor_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -342,6 +398,21 @@ mod tests { ); } } + + #[rstest] + fn test_add_movie_prompt_scroll_no_op_when_not_ready(#[values(Key::Up, Key::Down)] key: Key) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.selected_block = BlockSelectionState::new(&ADD_MOVIE_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + AddMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::AddMoviePrompt, &None).handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::AddMovieSelectMonitor + ); + } } mod test_handle_home_end { @@ -406,6 +477,60 @@ mod tests { ); } + #[test] + fn test_add_movie_search_results_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(extended_stateful_iterable_vec!( + AddMovieSearchResult, + HorizontallyScrollableText + )); + app.data.radarr_data.add_searched_movies = Some(add_searched_movies); + + AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + + AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .add_searched_movies + .as_ref() + .unwrap() + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[test] fn test_add_movie_select_monitor_home_end() { let monitor_vec = Vec::from_iter(Monitor::iter()); @@ -970,6 +1095,29 @@ mod tests { ); } + #[test] + fn test_add_movie_search_results_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchResults.into()); + let mut add_searched_movies = StatefulTable::default(); + add_searched_movies.set_items(vec![AddMovieSearchResult::default()]); + + AddMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::AddMovieSearchResults, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::AddMovieSearchResults.into() + ); + assert!(app.data.radarr_data.add_movie_modal.is_none()); + } + #[test] fn test_add_movie_search_results_submit_does_nothing_on_empty_table() { let mut app = App::default(); @@ -1155,9 +1303,10 @@ mod tests { const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_add_movie_search_input_esc() { + #[rstest] + fn test_add_movie_search_input_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data = create_test_radarr_data(); app.should_ignore_quit_key = true; app.push_navigation_stack(ActiveRadarrBlock::AddMovieSearchInput.into()); @@ -1447,4 +1596,34 @@ mod tests { } }); } + + #[test] + fn test_add_movie_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_add_movie_handler_is_ready_when_not_loading() { + let mut app = App::default(); + app.is_loading = false; + + let handler = AddMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::AddMoviePrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/library/delete_movie_handler.rs b/src/handlers/radarr_handlers/library/delete_movie_handler.rs index 8317f4a..036763c 100644 --- a/src/handlers/radarr_handlers/library/delete_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/delete_movie_handler.rs @@ -38,6 +38,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for DeleteMovieHandler<' self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading + } + fn handle_scroll_up(&mut self) { if *self.active_radarr_block == ActiveRadarrBlock::DeleteMoviePrompt { self.app.data.radarr_data.selected_block.previous(); diff --git a/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs index 9619fad..5eeafb8 100644 --- a/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/delete_movie_handler_tests.rs @@ -40,6 +40,25 @@ mod tests { ); } } + + #[rstest] + fn test_delete_movie_prompt_scroll_no_op_when_not_ready( + #[values(Key::Up, Key::Down)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.selected_block = + BlockSelectionState::new(&DELETE_MOVIE_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + DeleteMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::DeleteMoviePrompt, &None) + .handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::DeleteMovieToggleAddListExclusion + ); + } } mod test_handle_left_right_action { @@ -139,6 +158,35 @@ mod tests { assert!(app.data.radarr_data.add_list_exclusion); } + #[test] + fn test_delete_movie_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); + app.data.radarr_data.prompt_confirm = true; + app.data.radarr_data.delete_movie_files = true; + app.data.radarr_data.add_list_exclusion = true; + + DeleteMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::DeleteMoviePrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::DeleteMoviePrompt.into() + ); + assert_eq!(app.data.radarr_data.prompt_confirm_action, None); + assert!(!app.should_refresh); + assert!(app.data.radarr_data.prompt_confirm); + assert!(app.data.radarr_data.delete_movie_files); + assert!(app.data.radarr_data.add_list_exclusion); + } + #[test] fn test_delete_movie_toggle_delete_files_submit() { let current_route = ActiveRadarrBlock::DeleteMoviePrompt.into(); @@ -173,12 +221,14 @@ mod tests { mod test_handle_esc { use super::*; + use rstest::rstest; const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_delete_movie_prompt_esc() { + #[rstest] + fn test_delete_movie_prompt_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteMoviePrompt.into()); app.data.radarr_data.prompt_confirm = true; @@ -210,4 +260,34 @@ mod tests { } }); } + + #[test] + fn test_delete_movie_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = DeleteMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::DeleteMoviePrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_delete_movie_handler_ready_when_not_loading() { + let mut app = App::default(); + app.is_loading = false; + + let handler = DeleteMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::DeleteMoviePrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/library/edit_movie_handler.rs b/src/handlers/radarr_handlers/library/edit_movie_handler.rs index 5dffc26..d583a71 100644 --- a/src/handlers/radarr_handlers/library/edit_movie_handler.rs +++ b/src/handlers/radarr_handlers/library/edit_movie_handler.rs @@ -41,6 +41,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for EditMovieHandler<'a, self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && self.app.data.radarr_data.edit_movie_modal.is_some() + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::EditMovieSelectMinimumAvailability => self diff --git a/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs b/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs index 0163f4c..030f531 100644 --- a/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs @@ -9,6 +9,7 @@ mod tests { use crate::handlers::radarr_handlers::library::edit_movie_handler::EditMovieHandler; use crate::handlers::KeyEventHandler; use crate::models::radarr_models::MinimumAvailability; + use crate::models::servarr_data::radarr::modals::EditMovieModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, EDIT_MOVIE_BLOCKS}; mod test_handle_scroll_up_and_down { @@ -144,6 +145,7 @@ mod tests { #[rstest] fn test_edit_movie_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { let mut app = App::default(); + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); app.data.radarr_data.selected_block.next(); @@ -161,6 +163,22 @@ mod tests { ); } } + + #[rstest] + fn test_edit_movie_prompt_scroll_no_op_when_not_ready(#[values(Key::Up, Key::Down)] key: Key) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); + app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.next(); + + EditMovieHandler::with(&key, &mut app, &ActiveRadarrBlock::EditMoviePrompt, &None).handle(); + + assert_eq!( + app.data.radarr_data.selected_block.get_active_block(), + &ActiveRadarrBlock::EditMovieSelectMinimumAvailability + ); + } } mod test_handle_home_end { @@ -596,6 +614,7 @@ mod tests { #[test] fn test_edit_movie_prompt_prompt_decline_submit() { let mut app = App::default(); + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); @@ -648,6 +667,31 @@ mod tests { assert!(app.should_refresh); } + #[test] + fn test_edit_movie_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); + app.data.radarr_data.prompt_confirm = true; + + EditMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::EditMoviePrompt.into() + ); + assert_eq!(app.data.radarr_data.prompt_confirm_action, None); + assert!(!app.should_refresh); + } + #[test] fn test_edit_movie_toggle_monitored_submit() { let current_route = Route::from(( @@ -710,6 +754,7 @@ mod tests { #[case] index: usize, ) { let mut app = App::default(); + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.push_navigation_stack( ( ActiveRadarrBlock::EditMoviePrompt, @@ -741,6 +786,43 @@ mod tests { } } + #[rstest] + fn test_edit_movie_prompt_selected_block_submit_no_op_when_not_ready( + #[values(1, 2, 3, 4)] index: usize, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); + app.push_navigation_stack( + ( + ActiveRadarrBlock::EditMoviePrompt, + Some(ActiveRadarrBlock::Movies), + ) + .into(), + ); + app.data.radarr_data.selected_block = BlockSelectionState::new(&EDIT_MOVIE_SELECTION_BLOCKS); + app.data.radarr_data.selected_block.set_index(index); + + EditMovieHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &Some(ActiveRadarrBlock::Movies), + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &( + ActiveRadarrBlock::EditMoviePrompt, + Some(ActiveRadarrBlock::Movies), + ) + .into() + ); + assert_eq!(app.data.radarr_data.prompt_confirm_action, None); + assert!(!app.should_ignore_quit_key); + } + #[rstest] fn test_edit_movie_prompt_selecting_preferences_blocks_submit( #[values( @@ -752,6 +834,7 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); app.push_navigation_stack(ActiveRadarrBlock::EditMoviePrompt.into()); app.push_navigation_stack(active_radarr_block.into()); @@ -838,8 +921,10 @@ mod tests { ActiveRadarrBlock::EditMovieSelectQualityProfile )] active_radarr_block: ActiveRadarrBlock, + #[values(true, false)] is_ready: bool, ) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data = create_test_radarr_data(); app.push_navigation_stack(active_radarr_block.into()); @@ -974,4 +1059,50 @@ mod tests { } }); } + + #[test] + fn test_edit_movie_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = EditMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_movie_handler_is_not_ready_when_edit_movie_modal_is_none() { + let mut app = App::default(); + app.is_loading = false; + + let handler = EditMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_movie_handler_is_ready_when_edit_movie_modal_is_some() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.edit_movie_modal = Some(EditMovieModal::default()); + + let handler = EditMovieHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::EditMoviePrompt, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/library/library_handler_tests.rs b/src/handlers/radarr_handlers/library/library_handler_tests.rs index 1ebc3a2..32aee4f 100644 --- a/src/handlers/radarr_handlers/library/library_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/library_handler_tests.rs @@ -36,6 +36,48 @@ mod tests { to_string ); + #[rstest] + fn test_movies_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .movies + .set_items(simple_stateful_iterable_vec!( + Movie, + HorizontallyScrollableText + )); + + LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + + LibraryHandler::with(&key, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_movies_sort_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -100,9 +142,66 @@ mod tests { to_string ); + #[test] + fn test_movies_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .movies + .set_items(extended_stateful_iterable_vec!( + Movie, + HorizontallyScrollableText + )); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movies + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[test] fn test_movie_search_box_home_end_keys() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.search = Some("Test".into()); LibraryHandler::with( @@ -151,6 +250,11 @@ mod tests { #[test] fn test_movie_filter_box_home_end_keys() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.filter = Some("Test".into()); LibraryHandler::with( @@ -257,6 +361,11 @@ mod tests { #[test] fn test_movies_delete() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); assert_delete_prompt!( LibraryHandler, @@ -269,6 +378,22 @@ mod tests { &DELETE_MOVIE_SELECTION_BLOCKS ); } + + #[test] + fn test_movies_delete_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with(&DELETE_KEY, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + } } mod test_handle_left_right_action { @@ -277,9 +402,10 @@ mod tests { use super::*; - #[test] - fn test_movie_tab_left() { + #[rstest] + fn test_movie_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(0); LibraryHandler::with( @@ -297,9 +423,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); } - #[test] - fn test_movie_tab_right() { + #[rstest] + fn test_movie_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(0); LibraryHandler::with( @@ -457,6 +584,11 @@ mod tests { #[test] fn test_movie_details_submit() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); @@ -466,6 +598,22 @@ mod tests { ); } + #[test] + fn test_movie_details_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with(&SUBMIT_KEY, &mut app, &ActiveRadarrBlock::Movies, &None).handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + } + #[test] fn test_search_movie_submit() { let mut app = App::default(); @@ -532,6 +680,11 @@ mod tests { #[test] fn test_search_filtered_movies_submit() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::SearchMovie.into()); app @@ -636,6 +789,11 @@ mod tests { #[test] fn test_update_all_movies_prompt_confirm_submit() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into()); @@ -659,6 +817,11 @@ mod tests { #[test] fn test_update_all_movies_prompt_decline_submit() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::UpdateAllMoviesPrompt.into()); @@ -793,9 +956,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); @@ -831,14 +995,18 @@ mod tests { RadarrData, EDIT_MOVIE_SELECTION_BLOCKS, }; - use crate::models::stateful_table::StatefulTable; - use crate::{assert_refresh_key, test_edit_movie_key}; + use crate::test_edit_movie_key; use super::*; #[test] fn test_search_movies_key() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.search.key, @@ -859,10 +1027,38 @@ mod tests { ); } + #[test] + fn test_search_movies_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.search.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(!app.should_ignore_quit_key); + assert_eq!(app.data.radarr_data.movies.search, None); + } + #[test] fn test_filter_movies_key() { let mut app = App::default(); - app.data.radarr_data.movies = StatefulTable::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.filter.key, @@ -880,13 +1076,41 @@ mod tests { assert!(app.data.radarr_data.movies.filter.is_some()); } + #[test] + fn test_filter_movies_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.filter.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(!app.should_ignore_quit_key); + assert!(app.data.radarr_data.movies.filter.is_none()); + } + #[test] fn test_filter_movies_key_resets_previous_filter() { let mut app = App::default(); app.should_ignore_quit_key = true; app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.data.radarr_data = create_test_radarr_data(); - app.data.radarr_data.movies = StatefulTable::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.filter = Some("Test".into()); LibraryHandler::with( @@ -913,6 +1137,11 @@ mod tests { #[test] fn test_movie_add_key() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.add.key, @@ -930,6 +1159,30 @@ mod tests { assert!(app.data.radarr_data.add_movie_search.is_some()); } + #[test] + fn test_movie_add_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.add.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(!app.should_ignore_quit_key); + assert!(app.data.radarr_data.add_movie_search.is_none()); + } + #[test] fn test_movie_edit_key() { test_edit_movie_key!( @@ -939,9 +1192,37 @@ mod tests { ); } + #[test] + fn test_movie_edit_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.edit.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(app.data.radarr_data.edit_movie_modal.is_none()); + } + #[test] fn test_update_all_movies_key() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.update.key, @@ -957,15 +1238,82 @@ mod tests { ); } + #[test] + fn test_update_all_movies_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + } + #[test] fn test_refresh_movies_key() { - assert_refresh_key!(LibraryHandler, ActiveRadarrBlock::Movies); + let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_movies_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(!app.should_refresh); } #[test] fn test_search_movies_box_backspace_key() { let mut app = App::default(); app.data.radarr_data.movies.search = Some("Test".into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.backspace.key, @@ -984,7 +1332,11 @@ mod tests { #[test] fn test_filter_movies_box_backspace_key() { let mut app = App::default(); - app.data.radarr_data.movies = StatefulTable::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.filter = Some("Test".into()); LibraryHandler::with( @@ -1004,6 +1356,11 @@ mod tests { #[test] fn test_search_movies_box_char_key() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.search = Some(HorizontallyScrollableText::default()); LibraryHandler::with( @@ -1023,7 +1380,11 @@ mod tests { #[test] fn test_filter_movies_box_char_key() { let mut app = App::default(); - app.data.radarr_data.movies = StatefulTable::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); app.data.radarr_data.movies.filter = Some(HorizontallyScrollableText::default()); LibraryHandler::with( @@ -1043,6 +1404,11 @@ mod tests { #[test] fn test_sort_key() { let mut app = App::default(); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); LibraryHandler::with( &DEFAULT_KEYBINDINGS.sort.key, @@ -1062,6 +1428,29 @@ mod tests { ); assert!(!app.data.radarr_data.movies.sort_asc); } + + #[test] + fn test_sort_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + LibraryHandler::with( + &DEFAULT_KEYBINDINGS.sort.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Movies.into()); + assert!(app.data.radarr_data.movies.sort.is_none()); + } } #[rstest] @@ -1314,6 +1703,74 @@ mod tests { assert_str_eq!(sort_option.name, "Tags"); } + #[test] + fn test_library_handler_accepts() { + let mut library_handler_blocks = Vec::new(); + library_handler_blocks.extend(LIBRARY_BLOCKS); + library_handler_blocks.extend(ADD_MOVIE_BLOCKS); + library_handler_blocks.extend(DELETE_MOVIE_BLOCKS); + library_handler_blocks.extend(EDIT_MOVIE_BLOCKS); + library_handler_blocks.extend(MOVIE_DETAILS_BLOCKS); + + ActiveRadarrBlock::iter().for_each(|active_radarr_block| { + if library_handler_blocks.contains(&active_radarr_block) { + assert!(LibraryHandler::accepts(&active_radarr_block)); + } else { + assert!(!LibraryHandler::accepts(&active_radarr_block)); + } + }); + } + + #[test] + fn test_library_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = LibraryHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_library_handler_not_ready_when_movies_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = LibraryHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_library_handler_ready_when_not_loading_and_movies_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .movies + .set_items(vec![Movie::default()]); + + let handler = LibraryHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Movies, + &None, + ); + + assert!(handler.is_ready()); + } + fn movies_vec() -> Vec { vec![ Movie { @@ -1378,22 +1835,4 @@ mod tests { }), }] } - - #[test] - fn test_library_handler_accepts() { - let mut library_handler_blocks = Vec::new(); - library_handler_blocks.extend(LIBRARY_BLOCKS); - library_handler_blocks.extend(ADD_MOVIE_BLOCKS); - library_handler_blocks.extend(DELETE_MOVIE_BLOCKS); - library_handler_blocks.extend(EDIT_MOVIE_BLOCKS); - library_handler_blocks.extend(MOVIE_DETAILS_BLOCKS); - - ActiveRadarrBlock::iter().for_each(|active_radarr_block| { - if library_handler_blocks.contains(&active_radarr_block) { - assert!(LibraryHandler::accepts(&active_radarr_block)); - } else { - assert!(!LibraryHandler::accepts(&active_radarr_block)); - } - }); - } } diff --git a/src/handlers/radarr_handlers/library/mod.rs b/src/handlers/radarr_handlers/library/mod.rs index 9edf6e0..a31eb5b 100644 --- a/src/handlers/radarr_handlers/library/mod.rs +++ b/src/handlers/radarr_handlers/library/mod.rs @@ -80,6 +80,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, ' self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.movies.is_empty() + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::Movies => self.app.data.radarr_data.movies.scroll_up(), diff --git a/src/handlers/radarr_handlers/library/movie_details_handler.rs b/src/handlers/radarr_handlers/library/movie_details_handler.rs index 1e04aff..fc6e798 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler.rs @@ -46,6 +46,21 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for MovieDetailsHandler< self.key } + fn is_ready(&self) -> bool { + let movie_details_modal_is_ready = + if let Some(movie_details_modal) = &self.app.data.radarr_data.movie_details_modal { + !movie_details_modal.movie_details.is_empty() + || !movie_details_modal.movie_history.is_empty() + || !movie_details_modal.movie_cast.is_empty() + || !movie_details_modal.movie_crew.is_empty() + || !movie_details_modal.movie_releases.is_empty() + } else { + false + }; + + !self.app.is_loading && movie_details_modal_is_ready + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::MovieDetails => self diff --git a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs index f4dec9b..e0013b6 100644 --- a/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/movie_details_handler_tests.rs @@ -16,6 +16,7 @@ mod tests { use crate::models::radarr_models::{ Credit, Language, MovieHistoryItem, Quality, QualityWrapper, Release, }; + use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; use crate::models::stateful_table::SortOption; use crate::models::{HorizontallyScrollableText, ScrollableText}; @@ -78,6 +79,56 @@ mod tests { ); } + #[test] + fn test_movie_details_scroll_no_op_if_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("Test 1\nTest 2".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.up.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); + + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.down.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); + + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); + } + #[rstest] fn test_movie_history_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -126,6 +177,55 @@ mod tests { ); } + #[rstest] + fn test_movie_history_scroll_no_op_if_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_history + .set_items(simple_stateful_iterable_vec!( + MovieHistoryItem, + HorizontallyScrollableText, + source_title + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::MovieHistory, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::MovieHistory, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_cast_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -168,6 +268,49 @@ mod tests { ); } + #[rstest] + fn test_cast_scroll_no_op_if_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_cast + .set_items(simple_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Cast, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Cast, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + } + #[rstest] fn test_crew_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -210,6 +353,49 @@ mod tests { ); } + #[rstest] + fn test_crew_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_crew + .set_items(simple_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Crew, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::Crew, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + } + #[rstest] fn test_manual_search_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -257,6 +443,54 @@ mod tests { ); } + #[rstest] + fn test_manual_search_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_releases + .set_items(simple_stateful_iterable_vec!( + Release, + HorizontallyScrollableText + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::ManualSearch, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + + MovieDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::ManualSearch, &None).handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[rstest] fn test_manual_search_sort_scroll( #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, @@ -377,6 +611,57 @@ mod tests { ); } + #[test] + fn test_movie_details_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let movie_details_modal = MovieDetailsModal { + movie_details: ScrollableText::with_string("Test 1\nTest 2".to_owned()), + ..MovieDetailsModal::default() + }; + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); + + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ) + .handle(); + + assert_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_details + .offset, + 0 + ); + } + #[test] fn test_movie_history_home_end() { let mut app = App::default(); @@ -435,6 +720,65 @@ mod tests { ); } + #[test] + fn test_movie_history_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_history + .set_items(extended_stateful_iterable_vec!( + MovieHistoryItem, + HorizontallyScrollableText, + source_title + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::MovieHistory, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::MovieHistory, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_history + .current_selection() + .source_title + .to_string(), + "Test 1" + ); + } + #[test] fn test_cast_home_end() { let mut app = App::default(); @@ -487,6 +831,59 @@ mod tests { ); } + #[test] + fn test_cast_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_cast + .set_items(extended_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Cast, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Cast, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_cast + .current_selection() + .person_name, + "Test 1" + ); + } + #[test] fn test_crew_home_end() { let mut app = App::default(); @@ -539,6 +936,59 @@ mod tests { ); } + #[test] + fn test_crew_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_crew + .set_items(extended_stateful_iterable_vec!(Credit, String, person_name)); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::Crew, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::Crew, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_crew + .current_selection() + .person_name, + "Test 1" + ); + } + #[test] fn test_manual_search_home_end() { let mut app = App::default(); @@ -596,6 +1046,64 @@ mod tests { ); } + #[test] + fn test_manual_search_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + let mut movie_details_modal = MovieDetailsModal::default(); + movie_details_modal + .movie_releases + .set_items(extended_stateful_iterable_vec!( + Release, + HorizontallyScrollableText + )); + app.data.radarr_data.movie_details_modal = Some(movie_details_modal); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_str_eq!( + app + .data + .radarr_data + .movie_details_modal + .as_ref() + .unwrap() + .movie_releases + .current_selection() + .title + .to_string(), + "Test 1" + ); + } + #[test] fn test_manual_search_sort_home_end() { let release_field_vec = sort_options(); @@ -689,8 +1197,10 @@ mod tests { fn test_movie_info_tabs_left_right_action( #[case] left_block: ActiveRadarrBlock, #[case] right_block: ActiveRadarrBlock, + #[values(true, false)] is_ready: bool, ) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(right_block.into()); app.data.radarr_data.movie_info_tabs.index = app .data @@ -735,6 +1245,10 @@ mod tests { #[test] fn test_manual_search_submit() { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); MovieDetailsHandler::with( @@ -751,6 +1265,30 @@ mod tests { ); } + #[test] + fn test_manual_search_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); + + MovieDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::ManualSearch.into() + ); + } + #[rstest] #[case( ActiveRadarrBlock::AutomaticallySearchMoviePrompt, @@ -766,6 +1304,10 @@ mod tests { #[case] expected_action: RadarrEvent, ) { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()); app.push_navigation_stack(prompt_block.into()); @@ -793,6 +1335,10 @@ mod tests { prompt_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); app.push_navigation_stack(ActiveRadarrBlock::MovieDetails.into()); app.push_navigation_stack(prompt_block.into()); @@ -868,8 +1414,10 @@ mod tests { ActiveRadarrBlock::ManualSearch )] active_radarr_block: ActiveRadarrBlock, + #[values(true, false)] is_ready: bool, ) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data = create_test_radarr_data(); app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(active_radarr_block.into()); @@ -889,8 +1437,10 @@ mod tests { ActiveRadarrBlock::ManualSearchSortPrompt )] prompt_block: ActiveRadarrBlock, + #[values(true, false)] is_ready: bool, ) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::Movies.into()); app.push_navigation_stack(prompt_block.into()); @@ -932,6 +1482,10 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.search.key, @@ -947,10 +1501,44 @@ mod tests { ); } + #[rstest] + fn test_search_key_no_op_when_not_ready( + #[values( + ActiveRadarrBlock::MovieDetails, + ActiveRadarrBlock::MovieHistory, + ActiveRadarrBlock::FileInfo, + ActiveRadarrBlock::Cast, + ActiveRadarrBlock::Crew, + ActiveRadarrBlock::ManualSearch + )] + active_radarr_block: ActiveRadarrBlock, + ) { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(active_radarr_block.into()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.search.key, + &mut app, + &active_radarr_block, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &active_radarr_block.into()); + } + #[test] fn test_sort_key() { let mut app = App::default(); - app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.sort.key, @@ -990,6 +1578,30 @@ mod tests { ); } + #[test] + fn test_sort_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::ManualSearch.into()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.sort.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::ManualSearch.into() + ); + } + #[rstest] fn test_edit_key( #[values( @@ -1009,6 +1621,38 @@ mod tests { ); } + #[rstest] + fn test_edit_key_no_op_when_not_ready( + #[values( + ActiveRadarrBlock::MovieDetails, + ActiveRadarrBlock::MovieHistory, + ActiveRadarrBlock::FileInfo, + ActiveRadarrBlock::Cast, + ActiveRadarrBlock::Crew, + ActiveRadarrBlock::ManualSearch + )] + active_radarr_block: ActiveRadarrBlock, + ) { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(active_radarr_block.into()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.edit.key, + &mut app, + &active_radarr_block, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &active_radarr_block.into()); + assert!(app.data.radarr_data.edit_movie_modal.is_none()); + } + #[rstest] fn test_update_key( #[values( @@ -1022,6 +1666,10 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.update.key, @@ -1037,6 +1685,37 @@ mod tests { ); } + #[rstest] + fn test_update_key_no_op_when_not_ready( + #[values( + ActiveRadarrBlock::MovieDetails, + ActiveRadarrBlock::MovieHistory, + ActiveRadarrBlock::FileInfo, + ActiveRadarrBlock::Cast, + ActiveRadarrBlock::Crew, + ActiveRadarrBlock::ManualSearch + )] + active_radarr_block: ActiveRadarrBlock, + ) { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(active_radarr_block.into()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &active_radarr_block, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &active_radarr_block.into()); + } + #[rstest] fn test_refresh_key( #[values( @@ -1050,6 +1729,42 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); + + MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &active_radarr_block, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &active_radarr_block.into()); + assert!(app.is_routing); + } + + #[rstest] + fn test_refresh_key_no_op_when_not_ready( + #[values( + ActiveRadarrBlock::MovieDetails, + ActiveRadarrBlock::MovieHistory, + ActiveRadarrBlock::FileInfo, + ActiveRadarrBlock::Cast, + ActiveRadarrBlock::Crew, + ActiveRadarrBlock::ManualSearch + )] + active_radarr_block: ActiveRadarrBlock, + ) { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(active_radarr_block.into()); + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("test".to_owned()), + ..MovieDetailsModal::default() + }); MovieDetailsHandler::with( &DEFAULT_KEYBINDINGS.refresh.key, @@ -1220,6 +1935,141 @@ mod tests { assert_str_eq!(sort_option.name, "Quality"); } + #[test] + fn test_movie_details_handler_accepts() { + ActiveRadarrBlock::iter().for_each(|active_radarr_block| { + if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) { + assert!(MovieDetailsHandler::accepts(&active_radarr_block)); + } else { + assert!(!MovieDetailsHandler::accepts(&active_radarr_block)); + } + }); + } + + #[test] + fn test_movie_details_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_not_ready_when_no_movie_details_are_in_modal() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal::default()); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_ready_when_movie_details_are_in_modal() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.movie_details_modal = Some(MovieDetailsModal { + movie_details: ScrollableText::with_string("Test".to_owned()), + ..MovieDetailsModal::default() + }); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::MovieDetails, + &None, + ); + + assert!(handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_ready_when_movie_history_is_in_modal() { + let mut app = App::default(); + app.is_loading = false; + let mut modal = MovieDetailsModal::default(); + modal + .movie_history + .set_items(vec![MovieHistoryItem::default()]); + app.data.radarr_data.movie_details_modal = Some(modal); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::MovieHistory, + &None, + ); + + assert!(handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_ready_when_movie_cast_is_in_modal() { + let mut app = App::default(); + app.is_loading = false; + let mut modal = MovieDetailsModal::default(); + modal.movie_cast.set_items(vec![Credit::default()]); + app.data.radarr_data.movie_details_modal = Some(modal); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Cast, + &None, + ); + + assert!(handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_ready_when_movie_crew_is_in_modal() { + let mut app = App::default(); + app.is_loading = false; + let mut modal = MovieDetailsModal::default(); + modal.movie_crew.set_items(vec![Credit::default()]); + app.data.radarr_data.movie_details_modal = Some(modal); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::Crew, + &None, + ); + + assert!(handler.is_ready()); + } + + #[test] + fn test_movie_details_handler_is_ready_when_movie_releases_is_in_modal() { + let mut app = App::default(); + app.is_loading = false; + let mut modal = MovieDetailsModal::default(); + modal.movie_releases.set_items(vec![Release::default()]); + app.data.radarr_data.movie_details_modal = Some(modal); + + let handler = MovieDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::ManualSearch, + &None, + ); + + assert!(handler.is_ready()); + } + fn release_vec() -> Vec { let release_a = Release { protocol: "Protocol A".to_owned(), @@ -1283,15 +2133,4 @@ mod tests { cmp_fn: Some(|a, b| a.age.cmp(&b.age)), }] } - - #[test] - fn test_movie_details_handler_accepts() { - ActiveRadarrBlock::iter().for_each(|active_radarr_block| { - if MOVIE_DETAILS_BLOCKS.contains(&active_radarr_block) { - assert!(MovieDetailsHandler::accepts(&active_radarr_block)); - } else { - assert!(!MovieDetailsHandler::accepts(&active_radarr_block)); - } - }); - } } diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index 2c4042a..843f2c4 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -85,6 +85,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b self.key } + fn is_ready(&self) -> bool { + true + } + fn handle_scroll_up(&mut self) {} fn handle_scroll_down(&mut self) {} diff --git a/src/handlers/radarr_handlers/radarr_handler_tests.rs b/src/handlers/radarr_handlers/radarr_handler_tests.rs index 6cc5d84..375b7ab 100644 --- a/src/handlers/radarr_handlers/radarr_handler_tests.rs +++ b/src/handlers/radarr_handlers/radarr_handler_tests.rs @@ -216,4 +216,19 @@ mod tests { assert!(RadarrHandler::accepts(&active_radarr_block)); }) } + + #[test] + fn test_radarr_handler_is_ready() { + let mut app = App::default(); + app.is_loading = true; + + let handler = RadarrHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/root_folders/mod.rs b/src/handlers/radarr_handlers/root_folders/mod.rs index 2227711..0b34f79 100644 --- a/src/handlers/radarr_handlers/root_folders/mod.rs +++ b/src/handlers/radarr_handlers/root_folders/mod.rs @@ -42,6 +42,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RootFoldersHandler<' self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading && !self.app.data.radarr_data.root_folders.is_empty() + } + fn handle_scroll_up(&mut self) { if self.active_radarr_block == &ActiveRadarrBlock::RootFolders { self.app.data.radarr_data.root_folders.scroll_up() diff --git a/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs b/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs index 1ee2120..9cce9ff 100644 --- a/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs +++ b/src/handlers/radarr_handlers/root_folders/root_folders_handler_tests.rs @@ -8,6 +8,7 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::root_folders::RootFoldersHandler; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::RootFolder; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS}; use crate::models::HorizontallyScrollableText; @@ -28,6 +29,33 @@ mod tests { None, path ); + + #[rstest] + fn test_root_folders_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .root_folders + .set_items(simple_stateful_iterable_vec!(RootFolder, String, path)); + + RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.root_folders.current_selection().path, + "Test 1" + ); + + RootFoldersHandler::with(&key, &mut app, &ActiveRadarrBlock::RootFolders, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.root_folders.current_selection().path, + "Test 1" + ); + } } mod test_handle_home_end { @@ -48,9 +76,51 @@ mod tests { path ); + #[test] + fn test_root_folders_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .root_folders + .set_items(extended_stateful_iterable_vec!(RootFolder, String, path)); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.root_folders.current_selection().path, + "Test 1" + ); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.root_folders.current_selection().path, + "Test 1" + ); + } + #[test] fn test_add_root_folder_prompt_home_end_keys() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.data.radarr_data.edit_root_folder = Some("Test".into()); RootFoldersHandler::with( @@ -98,18 +168,55 @@ mod tests { mod test_handle_delete { use pretty_assertions::assert_eq; - use crate::assert_delete_prompt; - use super::*; const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key; #[test] fn test_delete_root_folder_prompt() { - assert_delete_prompt!( - RootFoldersHandler, - ActiveRadarrBlock::RootFolders, - ActiveRadarrBlock::DeleteRootFolderPrompt + let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + + RootFoldersHandler::with( + &DELETE_KEY, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::DeleteRootFolderPrompt.into() + ); + } + + #[test] + fn test_delete_root_folder_prompt_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + + RootFoldersHandler::with( + &DELETE_KEY, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::RootFolders.into() ); } } @@ -120,9 +227,10 @@ mod tests { use super::*; - #[test] - fn test_root_folders_tab_left() { + #[rstest] + fn test_root_folders_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(4); RootFoldersHandler::with( @@ -143,9 +251,10 @@ mod tests { ); } - #[test] - fn test_root_folders_tab_right() { + #[rstest] + fn test_root_folders_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(4); RootFoldersHandler::with( @@ -249,6 +358,11 @@ mod tests { #[test] fn test_add_root_folder_prompt_confirm_submit() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.data.radarr_data.edit_root_folder = Some("Test".into()); app.data.radarr_data.prompt_confirm = true; app.should_ignore_quit_key = true; @@ -304,6 +418,11 @@ mod tests { #[test] fn test_delete_root_folder_prompt_confirm_submit() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); @@ -330,6 +449,11 @@ mod tests { #[test] fn test_delete_root_folder_prompt_decline_submit() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::DeleteRootFolderPrompt.into()); @@ -351,9 +475,9 @@ mod tests { } mod test_handle_esc { - use pretty_assertions::assert_eq; - use super::*; + use pretty_assertions::assert_eq; + use rstest::rstest; const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; @@ -405,9 +529,10 @@ mod tests { assert!(!app.should_ignore_quit_key); } - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); @@ -425,13 +550,16 @@ mod tests { mod test_handle_key_char { use pretty_assertions::{assert_eq, assert_str_eq}; - use crate::assert_refresh_key; - use super::*; #[test] fn test_root_folder_add() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); RootFoldersHandler::with( &DEFAULT_KEYBINDINGS.add.key, @@ -449,14 +577,92 @@ mod tests { assert!(app.data.radarr_data.edit_root_folder.is_some()); } + #[test] + fn test_root_folder_add_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.add.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::RootFolders.into() + ); + assert!(!app.should_ignore_quit_key); + assert!(app.data.radarr_data.edit_root_folder.is_none()); + } + #[test] fn test_refresh_root_folders_key() { - assert_refresh_key!(RootFoldersHandler, ActiveRadarrBlock::RootFolders); + let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::RootFolders.into() + ); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_root_folders_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + app.push_navigation_stack(ActiveRadarrBlock::RootFolders.into()); + + RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::RootFolders.into() + ); + assert!(!app.should_refresh); } #[test] fn test_add_root_folder_prompt_backspace_key() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.data.radarr_data.edit_root_folder = Some("/nfs/test".into()); RootFoldersHandler::with( @@ -476,6 +682,11 @@ mod tests { #[test] fn test_add_root_folder_prompt_char_key() { let mut app = App::default(); + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); app.data.radarr_data.edit_root_folder = Some(HorizontallyScrollableText::default()); RootFoldersHandler::with( @@ -503,4 +714,54 @@ mod tests { } }) } + + #[test] + fn test_root_folders_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_root_folders_handler_not_ready_when_root_folders_is_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_root_folders_handler_ready_when_not_loading_and_root_folders_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + + app + .data + .radarr_data + .root_folders + .set_items(vec![RootFolder::default()]); + let handler = RootFoldersHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::RootFolders, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/system/mod.rs b/src/handlers/radarr_handlers/system/mod.rs index d940317..e015915 100644 --- a/src/handlers/radarr_handlers/system/mod.rs +++ b/src/handlers/radarr_handlers/system/mod.rs @@ -53,6 +53,13 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemHandler<'a, 'b self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading + && !self.app.data.radarr_data.logs.is_empty() + && !self.app.data.radarr_data.queued_events.is_empty() + && !self.app.data.radarr_data.tasks.is_empty() + } + fn handle_scroll_up(&mut self) {} fn handle_scroll_down(&mut self) {} diff --git a/src/handlers/radarr_handlers/system/system_details_handler.rs b/src/handlers/radarr_handlers/system/system_details_handler.rs index 0205b43..6420792 100644 --- a/src/handlers/radarr_handlers/system/system_details_handler.rs +++ b/src/handlers/radarr_handlers/system/system_details_handler.rs @@ -41,6 +41,12 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for SystemDetailsHandler self.key } + fn is_ready(&self) -> bool { + !self.app.is_loading + && (!self.app.data.radarr_data.log_details.is_empty() + || !self.app.data.radarr_data.updates.is_empty()) + } + fn handle_scroll_up(&mut self) { match self.active_radarr_block { ActiveRadarrBlock::SystemLogs => self.app.data.radarr_data.log_details.scroll_up(), diff --git a/src/handlers/radarr_handlers/system/system_details_handler_tests.rs b/src/handlers/radarr_handlers/system/system_details_handler_tests.rs index bdf3b5a..87d3eca 100644 --- a/src/handlers/radarr_handlers/system/system_details_handler_tests.rs +++ b/src/handlers/radarr_handlers/system/system_details_handler_tests.rs @@ -12,6 +12,7 @@ mod tests { use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS, }; + use crate::models::{HorizontallyScrollableText, ScrollableText}; mod test_handle_scroll_up_and_down { use rstest::rstest; @@ -31,25 +32,170 @@ mod tests { text ); - test_iterable_scroll!( - test_tasks_scroll, - SystemDetailsHandler, - tasks, - simple_stateful_iterable_vec!(Task, String, name), - ActiveRadarrBlock::SystemTasks, - None, - name - ); + #[rstest] + fn test_log_details_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .log_details + .set_items(simple_stateful_iterable_vec!( + HorizontallyScrollableText, + String, + text + )); - test_iterable_scroll!( - test_queued_events_scroll, - SystemDetailsHandler, - queued_events, - simple_stateful_iterable_vec!(QueueEvent, String, name), - ActiveRadarrBlock::SystemQueuedEvents, - None, - name - ); + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.log_details.current_selection().text, + "Test 1" + ); + + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemLogs, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.log_details.current_selection().text, + "Test 1" + ); + } + + #[rstest] + fn test_tasks_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + app + .data + .radarr_data + .tasks + .set_items(simple_stateful_iterable_vec!(Task, String, name)); + + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 2" + ); + + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + } + + #[rstest] + fn test_tasks_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + app + .data + .radarr_data + .tasks + .set_items(simple_stateful_iterable_vec!(Task, String, name)); + + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + + SystemDetailsHandler::with(&key, &mut app, &ActiveRadarrBlock::SystemTasks, &None).handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + } + + #[rstest] + fn test_queued_events_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + app + .data + .radarr_data + .queued_events + .set_items(simple_stateful_iterable_vec!(QueueEvent, String, name)); + + SystemDetailsHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 2" + ); + + SystemDetailsHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + } + + #[rstest] + fn test_queued_events_scroll_no_op_when_not_ready( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + app + .data + .radarr_data + .queued_events + .set_items(simple_stateful_iterable_vec!(QueueEvent, String, name)); + + SystemDetailsHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + + SystemDetailsHandler::with( + &key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + } #[test] fn test_system_updates_scroll() { @@ -76,6 +222,33 @@ mod tests { assert_eq!(app.data.radarr_data.updates.offset, 1); } + + #[test] + fn test_system_updates_scroll_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.up.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.down.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + } } mod test_handle_home_end { @@ -94,25 +267,200 @@ mod tests { text ); - test_iterable_home_and_end!( - test_tasks_home_end, - SystemDetailsHandler, - tasks, - extended_stateful_iterable_vec!(Task, String, name), - ActiveRadarrBlock::SystemTasks, - None, - name - ); + #[test] + fn test_log_details_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app + .data + .radarr_data + .log_details + .set_items(extended_stateful_iterable_vec!( + HorizontallyScrollableText, + String, + text + )); - test_iterable_home_and_end!( - test_queued_events_home_end, - SystemDetailsHandler, - queued_events, - extended_stateful_iterable_vec!(QueueEvent, String, name), - ActiveRadarrBlock::SystemQueuedEvents, - None, - name - ); + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemLogs, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.log_details.current_selection().text, + "Test 1" + ); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemLogs, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.log_details.current_selection().text, + "Test 1" + ); + } + + #[test] + fn test_tasks_home_end() { + let mut app = App::default(); + app.data.radarr_data.updates = + ScrollableText::with_string("Test 1\nTest 2\nTest 3".to_owned()); + app + .data + .radarr_data + .tasks + .set_items(extended_stateful_iterable_vec!(Task, String, name)); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemTasks, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 3" + ); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemTasks, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + } + + #[test] + fn test_tasks_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = + ScrollableText::with_string("Test 1\nTest 2\nTest 3".to_owned()); + app + .data + .radarr_data + .tasks + .set_items(extended_stateful_iterable_vec!(Task, String, name)); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemTasks, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemTasks, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.tasks.current_selection().name, + "Test 1" + ); + } + + #[test] + fn test_queued_events_home_end() { + let mut app = App::default(); + app.data.radarr_data.updates = + ScrollableText::with_string("Test 1\nTest 2\nTest 3".to_owned()); + app + .data + .radarr_data + .queued_events + .set_items(extended_stateful_iterable_vec!(QueueEvent, String, name)); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 3" + ); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + } + + #[test] + fn test_queued_events_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = + ScrollableText::with_string("Test 1\nTest 2\nTest 3".to_owned()); + app + .data + .radarr_data + .queued_events + .set_items(extended_stateful_iterable_vec!(QueueEvent, String, name)); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemQueuedEvents, + &None, + ) + .handle(); + + assert_str_eq!( + app.data.radarr_data.queued_events.current_selection().name, + "Test 1" + ); + } #[test] fn test_system_updates_home_end() { @@ -139,6 +487,33 @@ mod tests { assert_eq!(app.data.radarr_data.updates.offset, 0); } + + #[test] + fn test_system_updates_home_end_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = ScrollableText::with_string("Test 1\nTest 2".to_owned()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.end.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.home.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ) + .handle(); + + assert_eq!(app.data.radarr_data.updates.offset, 0); + } } mod test_handle_left_right_action { @@ -286,6 +661,7 @@ mod tests { #[test] fn test_system_tasks_submit() { let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); SystemDetailsHandler::with( &SUBMIT_KEY, @@ -301,9 +677,31 @@ mod tests { ); } + #[test] + fn test_system_tasks_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); + + SystemDetailsHandler::with( + &SUBMIT_KEY, + &mut app, + &ActiveRadarrBlock::SystemTasks, + &None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + &ActiveRadarrBlock::SystemTasks.into() + ); + } + #[test] fn test_system_tasks_start_task_prompt_confirm_submit() { let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.data.radarr_data.prompt_confirm = true; app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); @@ -330,6 +728,7 @@ mod tests { #[test] fn test_system_tasks_start_task_prompt_decline_submit() { let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTaskStartConfirmPrompt.into()); @@ -351,17 +750,18 @@ mod tests { } mod test_handle_esc { - use pretty_assertions::assert_eq; - use crate::models::HorizontallyScrollableText; + use pretty_assertions::assert_eq; + use rstest::rstest; use super::*; const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_esc_system_logs() { + #[rstest] + fn test_esc_system_logs(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app .data .radarr_data @@ -382,9 +782,10 @@ mod tests { assert!(app.data.radarr_data.log_details.items.is_empty()); } - #[test] - fn test_esc_system_tasks() { + #[rstest] + fn test_esc_system_tasks(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemTasks.into()); app.data.radarr_data.tasks.set_items(vec![Task::default()]); @@ -395,9 +796,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); } - #[test] - fn test_esc_system_queued_events() { + #[rstest] + fn test_esc_system_queued_events(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemQueuedEvents.into()); app @@ -417,9 +819,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); } - #[test] - fn test_esc_system_updates() { + #[rstest] + fn test_esc_system_updates(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::SystemUpdates.into()); @@ -468,6 +871,7 @@ mod tests { active_radarr_block: ActiveRadarrBlock, ) { let mut app = App::default(); + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); app.push_navigation_stack(active_radarr_block.into()); SystemDetailsHandler::with( @@ -481,6 +885,33 @@ mod tests { assert_eq!(app.get_current_route(), &active_radarr_block.into()); assert!(app.should_refresh); } + + #[rstest] + fn test_refresh_key_no_op_when_not_ready( + #[values( + ActiveRadarrBlock::SystemLogs, + ActiveRadarrBlock::SystemTasks, + ActiveRadarrBlock::SystemQueuedEvents, + ActiveRadarrBlock::SystemUpdates + )] + active_radarr_block: ActiveRadarrBlock, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); + app.push_navigation_stack(active_radarr_block.into()); + + SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &active_radarr_block, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &active_radarr_block.into()); + assert!(!app.should_refresh); + } } #[test] @@ -493,4 +924,70 @@ mod tests { } }) } + + #[test] + fn test_system_details_handler_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let handler = SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_system_details_handler_not_ready_when_both_log_details_and_updates_are_empty() { + let mut app = App::default(); + app.is_loading = false; + + let handler = SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_system_details_handler_ready_when_not_loading_and_log_details_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app + .data + .radarr_data + .log_details + .set_items(vec![HorizontallyScrollableText::default()]); + + let handler = SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ); + + assert!(handler.is_ready()); + } + + #[test] + fn test_system_details_handler_ready_when_not_loading_and_updates_is_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.updates = ScrollableText::with_string("Test".to_owned()); + + let handler = SystemDetailsHandler::with( + &DEFAULT_KEYBINDINGS.esc.key, + &mut app, + &ActiveRadarrBlock::SystemUpdates, + &None, + ); + + assert!(handler.is_ready()); + } } diff --git a/src/handlers/radarr_handlers/system/system_handler_tests.rs b/src/handlers/radarr_handlers/system/system_handler_tests.rs index b7b759e..5f2234d 100644 --- a/src/handlers/radarr_handlers/system/system_handler_tests.rs +++ b/src/handlers/radarr_handlers/system/system_handler_tests.rs @@ -9,6 +9,7 @@ mod tests { use crate::event::Key; use crate::handlers::radarr_handlers::system::SystemHandler; use crate::handlers::KeyEventHandler; + use crate::models::radarr_models::{QueueEvent, Task}; use crate::models::servarr_data::radarr::radarr_data::{ ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS, }; @@ -19,9 +20,10 @@ mod tests { use super::*; - #[test] - fn test_system_tab_left() { + #[rstest] + fn test_system_tab_left(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(6); SystemHandler::with( @@ -39,9 +41,10 @@ mod tests { assert_eq!(app.get_current_route(), &ActiveRadarrBlock::Indexers.into()); } - #[test] - fn test_system_tab_right() { + #[rstest] + fn test_system_tab_right(#[values(true, false)] is_ready: bool) { let mut app = App::default(); + app.is_loading = is_ready; app.data.radarr_data.main_tabs.set_index(6); SystemHandler::with( @@ -67,9 +70,10 @@ mod tests { const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; - #[test] - fn test_default_esc() { + #[rstest] + fn test_default_esc(#[values(true, false)] is_loading: bool) { let mut app = App::default(); + app.is_loading = is_loading; app.error = "test error".to_owned().into(); app.push_navigation_stack(ActiveRadarrBlock::System.into()); app.push_navigation_stack(ActiveRadarrBlock::System.into()); @@ -84,7 +88,6 @@ mod tests { mod test_handle_key_char { use pretty_assertions::{assert_eq, assert_str_eq}; - use crate::assert_refresh_key; use crate::models::HorizontallyScrollableText; use super::*; @@ -92,6 +95,16 @@ mod tests { #[test] fn test_update_system_key() { let mut app = App::default(); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); SystemHandler::with( &DEFAULT_KEYBINDINGS.update.key, @@ -107,9 +120,46 @@ mod tests { ); } + #[test] + fn test_update_system_key_no_op_if_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + } + #[test] fn test_queued_events_key() { let mut app = App::default(); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); SystemHandler::with( &DEFAULT_KEYBINDINGS.events.key, @@ -125,9 +175,87 @@ mod tests { ); } + #[test] + fn test_queued_events_key_no_op_if_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.events.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + } + #[test] fn test_refresh_system_key() { - assert_refresh_key!(SystemHandler, ActiveRadarrBlock::System); + let mut app = App::default(); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + assert!(app.should_refresh); + } + + #[test] + fn test_refresh_system_key_no_op_if_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.refresh.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + assert!(!app.should_refresh); } #[test] @@ -137,6 +265,12 @@ mod tests { HorizontallyScrollableText::from("test 1"), HorizontallyScrollableText::from("test 2"), ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); SystemHandler::with( &DEFAULT_KEYBINDINGS.logs.key, @@ -160,9 +294,47 @@ mod tests { ); } + #[test] + fn test_logs_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.logs.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + assert!(app.data.radarr_data.log_details.is_empty()); + } + #[test] fn test_tasks_key() { let mut app = App::default(); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); SystemHandler::with( &DEFAULT_KEYBINDINGS.tasks.key, @@ -177,6 +349,33 @@ mod tests { &ActiveRadarrBlock::SystemTasks.into() ); } + + #[test] + fn test_tasks_key_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.push_navigation_stack(ActiveRadarrBlock::System.into()); + app.data.radarr_data.logs.set_items(vec![ + HorizontallyScrollableText::from("test 1"), + HorizontallyScrollableText::from("test 2"), + ]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + + SystemHandler::with( + &DEFAULT_KEYBINDINGS.tasks.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ) + .handle(); + + assert_eq!(app.get_current_route(), &ActiveRadarrBlock::System.into()); + } } #[rstest] @@ -210,4 +409,100 @@ mod tests { } }) } + + #[test] + fn test_system_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.is_loading = true; + + let system_handler = SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(!system_handler.is_ready()); + } + + #[test] + fn test_system_handler_is_not_ready_when_logs_is_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + + let system_handler = SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(!system_handler.is_ready()); + } + + #[test] + fn test_system_handler_is_not_ready_when_tasks_is_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.logs.set_items(vec!["test".into()]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + + let system_handler = SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(!system_handler.is_ready()); + } + + #[test] + fn test_system_handler_is_not_ready_when_queued_events_is_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.logs.set_items(vec!["test".into()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + + let system_handler = SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(!system_handler.is_ready()); + } + + #[test] + fn test_system_handler_is_ready_when_all_required_tables_are_not_empty() { + let mut app = App::default(); + app.is_loading = false; + app.data.radarr_data.logs.set_items(vec!["test".into()]); + app.data.radarr_data.tasks.set_items(vec![Task::default()]); + app + .data + .radarr_data + .queued_events + .set_items(vec![QueueEvent::default()]); + + let system_handler = SystemHandler::with( + &DEFAULT_KEYBINDINGS.update.key, + &mut app, + &ActiveRadarrBlock::System, + &None, + ); + + assert!(system_handler.is_ready()); + } } diff --git a/src/models/mod.rs b/src/models/mod.rs index 6598a53..d6e2c1e 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -52,6 +52,10 @@ impl ScrollableText { pub fn get_text(&self) -> String { self.items.join("\n") } + + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } } impl Scrollable for ScrollableText { diff --git a/src/models/model_tests.rs b/src/models/model_tests.rs index 1d9fcf3..4b5f8a3 100644 --- a/src/models/model_tests.rs +++ b/src/models/model_tests.rs @@ -41,6 +41,18 @@ mod tests { assert_str_eq!(scrollable_text.get_text(), test_text); } + #[test] + fn test_scrollable_text_is_empty() { + let scrollable_text = ScrollableText::default(); + + assert!(scrollable_text.is_empty()); + + let test_text = "Test \nString"; + let scrollable_text = ScrollableText::with_string(test_text.to_owned()); + + assert!(!scrollable_text.is_empty()); + } + #[test] fn test_scrollable_text_scroll() { let mut scrollable_text = ScrollableText::with_string("Test \nString".to_owned()); diff --git a/src/models/stateful_list.rs b/src/models/stateful_list.rs index be3db74..5ae712c 100644 --- a/src/models/stateful_list.rs +++ b/src/models/stateful_list.rs @@ -92,4 +92,8 @@ where pub fn current_selection(&self) -> &T { &self.items[self.state.selected().unwrap_or(0)] } + + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } } diff --git a/src/models/stateful_list_tests.rs b/src/models/stateful_list_tests.rs index 9f38c39..3101cfe 100644 --- a/src/models/stateful_list_tests.rs +++ b/src/models/stateful_list_tests.rs @@ -102,6 +102,17 @@ mod tests { assert_eq!(stateful_list.state.selected(), Some(0)); } + #[test] + fn test_stateful_list_is_empty() { + let mut stateful_list = create_test_stateful_list(); + + assert!(!stateful_list.is_empty()); + + stateful_list = StatefulList::default(); + + assert!(stateful_list.is_empty()); + } + fn create_test_stateful_list() -> StatefulList<&'static str> { let mut stateful_list = StatefulList::default(); stateful_list.set_items(vec!["Test 1", "Test 2"]); diff --git a/src/models/stateful_table.rs b/src/models/stateful_table.rs index a130cad..8afce93 100644 --- a/src/models/stateful_table.rs +++ b/src/models/stateful_table.rs @@ -302,4 +302,8 @@ where pub fn reset_search(&mut self) { self.search = None; } + + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } } diff --git a/src/models/stateful_table_tests.rs b/src/models/stateful_table_tests.rs index 42a3483..f87e417 100644 --- a/src/models/stateful_table_tests.rs +++ b/src/models/stateful_table_tests.rs @@ -638,6 +638,17 @@ mod tests { assert_eq!(stateful_table.search, None); } + #[test] + fn test_stateful_table_is_empty() { + let mut stateful_table = create_test_stateful_table(); + + assert!(!stateful_table.is_empty()); + + stateful_table = StatefulTable::default(); + + assert!(stateful_table.is_empty()); + } + fn create_test_stateful_table() -> StatefulTable<&'static str> { let mut stateful_table = StatefulTable::default(); stateful_table.set_items(vec!["Test 1", "Test 2"]); diff --git a/src/ui/radarr_ui/library/mod.rs b/src/ui/radarr_ui/library/mod.rs index d193113..6e966e9 100644 --- a/src/ui/radarr_ui/library/mod.rs +++ b/src/ui/radarr_ui/library/mod.rs @@ -133,17 +133,21 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .get_by_left(&movie.quality_profile_id) .unwrap() .to_owned(); - let tags = movie - .tags - .iter() - .map(|tag_id| { - tags_map - .get_by_left(&tag_id.as_i64().unwrap()) - .unwrap() - .clone() - }) - .collect::>() - .join(", "); + let tags = if !movie.tags.is_empty() { + movie + .tags + .iter() + .map(|tag_id| { + tags_map + .get_by_left(&tag_id.as_i64().unwrap()) + .unwrap() + .clone() + }) + .collect::>() + .join(", ") + } else { + String::new() + }; decorate_with_row_style( downloads_vec, diff --git a/src/ui/radarr_ui/library/movie_details_ui.rs b/src/ui/radarr_ui/library/movie_details_ui.rs index 909d55e..6a05e25 100644 --- a/src/ui/radarr_ui/library/movie_details_ui.rs +++ b/src/ui/radarr_ui/library/movie_details_ui.rs @@ -8,6 +8,7 @@ use ratatui::Frame; use crate::app::App; use crate::models::radarr_models::{Credit, MovieHistoryItem, Release}; +use crate::models::servarr_data::radarr::modals::MovieDetailsModal; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS}; use crate::models::Route; use crate::ui::radarr_ui::library::draw_library; @@ -165,11 +166,17 @@ fn draw_file_info(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, area: Rect) { let block = layout_block_top_border(); - let is_monitored = app.data.radarr_data.movies.current_selection().monitored; - let status = app.data.radarr_data.movies.current_selection().status.clone(); match app.data.radarr_data.movie_details_modal.as_ref() { Some(movie_details_modal) if !app.is_loading => { + let is_monitored = app.data.radarr_data.movies.current_selection().monitored; + let status = app + .data + .radarr_data + .movies + .current_selection() + .status + .clone(); let movie_details = &movie_details_modal.movie_details; let download_status = movie_details .items @@ -368,6 +375,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { _ => (Release::default(), true), }; let current_route = *app.get_current_route(); + let mut default_movie_details_modal = MovieDetailsModal::default(); let help_footer = app .data .radarr_data @@ -379,7 +387,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { .radarr_data .movie_details_modal .as_mut() - .unwrap() + .unwrap_or(&mut default_movie_details_modal) .movie_releases, ); let releases_row_mapping = |release: &Release| {