From adb1f07fd077a2f40a0ce73c15ba4c4c5b17b1f2 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 2 Dec 2024 14:58:51 -0700 Subject: [PATCH] feat(handler): Edit series support --- .../library/edit_movie_handler_tests.rs | 15 +- .../library/edit_series_handler.rs | 408 +++++ .../library/edit_series_handler_tests.rs | 1322 +++++++++++++++++ .../library/library_handler_tests.rs | 38 +- src/handlers/sonarr_handlers/library/mod.rs | 7 + 5 files changed, 1762 insertions(+), 28 deletions(-) create mode 100644 src/handlers/sonarr_handlers/library/edit_series_handler.rs create mode 100644 src/handlers/sonarr_handlers/library/edit_series_handler_tests.rs 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 c63c2a7..8835d13 100644 --- a/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs +++ b/src/handlers/radarr_handlers/library/edit_movie_handler_tests.rs @@ -535,9 +535,7 @@ mod tests { use rstest::rstest; use crate::models::servarr_data::radarr::modals::EditMovieModal; - use crate::models::servarr_data::radarr::radarr_data::{ - EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS, - }; + use crate::models::servarr_data::radarr::radarr_data::EDIT_MOVIE_SELECTION_BLOCKS; use crate::models::{BlockSelectionState, Route}; use crate::network::radarr_network::RadarrEvent; @@ -626,7 +624,7 @@ mod tests { .data .radarr_data .selected_block - .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1); EditMovieHandler::with( SUBMIT_KEY, @@ -652,7 +650,7 @@ mod tests { .data .radarr_data .selected_block - .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1); EditMovieHandler::with( SUBMIT_KEY, @@ -936,10 +934,7 @@ mod tests { use super::*; use crate::{ models::{ - servarr_data::radarr::{ - modals::EditMovieModal, - radarr_data::{EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS}, - }, + servarr_data::radarr::{modals::EditMovieModal, radarr_data::EDIT_MOVIE_SELECTION_BLOCKS}, BlockSelectionState, }, network::radarr_network::RadarrEvent, @@ -1066,7 +1061,7 @@ mod tests { .data .radarr_data .selected_block - .set_index(0, EDIT_COLLECTION_SELECTION_BLOCKS.len() - 1); + .set_index(0, EDIT_MOVIE_SELECTION_BLOCKS.len() - 1); EditMovieHandler::with( DEFAULT_KEYBINDINGS.confirm.key, diff --git a/src/handlers/sonarr_handlers/library/edit_series_handler.rs b/src/handlers/sonarr_handlers/library/edit_series_handler.rs new file mode 100644 index 0000000..0a13078 --- /dev/null +++ b/src/handlers/sonarr_handlers/library/edit_series_handler.rs @@ -0,0 +1,408 @@ +use crate::app::key_binding::DEFAULT_KEYBINDINGS; +use crate::app::App; +use crate::event::Key; +use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; +use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS}; +use crate::models::Scrollable; +use crate::network::sonarr_network::SonarrEvent; +use crate::{handle_text_box_keys, handle_text_box_left_right_keys}; + +#[cfg(test)] +#[path = "edit_series_handler_tests.rs"] +mod edit_series_handler_tests; + +pub(super) struct EditSeriesHandler<'a, 'b> { + key: Key, + app: &'a mut App<'b>, + active_sonarr_block: ActiveSonarrBlock, + _context: Option, +} + +impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for EditSeriesHandler<'a, 'b> { + fn accepts(active_block: ActiveSonarrBlock) -> bool { + EDIT_SERIES_BLOCKS.contains(&active_block) + } + + fn with( + key: Key, + app: &'a mut App<'b>, + active_block: ActiveSonarrBlock, + _context: Option, + ) -> EditSeriesHandler<'a, 'b> { + EditSeriesHandler { + key, + app, + active_sonarr_block: active_block, + _context, + } + } + + fn get_key(&self) -> Key { + self.key + } + + fn is_ready(&self) -> bool { + !self.app.is_loading && self.app.data.sonarr_data.edit_series_modal.is_some() + } + + fn handle_scroll_up(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesSelectSeriesType => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .scroll_up(), + ActiveSonarrBlock::EditSeriesSelectQualityProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .scroll_up(), + ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .scroll_up(), + ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.up(), + _ => (), + } + } + + fn handle_scroll_down(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesSelectSeriesType => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .scroll_down(), + ActiveSonarrBlock::EditSeriesSelectQualityProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .scroll_down(), + ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .scroll_down(), + ActiveSonarrBlock::EditSeriesPrompt => self.app.data.sonarr_data.selected_block.down(), + _ => (), + } + } + + fn handle_home(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesSelectSeriesType => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .scroll_to_top(), + ActiveSonarrBlock::EditSeriesSelectQualityProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .scroll_to_top(), + ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .scroll_to_top(), + ActiveSonarrBlock::EditSeriesPathInput => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .path + .scroll_home(), + ActiveSonarrBlock::EditSeriesTagsInput => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .tags + .scroll_home(), + _ => (), + } + } + + fn handle_end(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesSelectSeriesType => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .scroll_to_bottom(), + ActiveSonarrBlock::EditSeriesSelectQualityProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .scroll_to_bottom(), + ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .scroll_to_bottom(), + ActiveSonarrBlock::EditSeriesPathInput => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .path + .reset_offset(), + ActiveSonarrBlock::EditSeriesTagsInput => self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .tags + .reset_offset(), + _ => (), + } + } + + fn handle_delete(&mut self) {} + + fn handle_left_right_action(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesPrompt => handle_prompt_toggle(self.app, self.key), + ActiveSonarrBlock::EditSeriesPathInput => { + handle_text_box_left_right_keys!( + self, + self.key, + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .path + ) + } + ActiveSonarrBlock::EditSeriesTagsInput => { + handle_text_box_left_right_keys!( + self, + self.key, + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .tags + ) + } + _ => (), + } + } + + fn handle_submit(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesPrompt => { + match self.app.data.sonarr_data.selected_block.get_active_block() { + ActiveSonarrBlock::EditSeriesConfirmPrompt => { + if self.app.data.sonarr_data.prompt_confirm { + self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None)); + self.app.should_refresh = true; + } + + self.app.pop_navigation_stack(); + } + ActiveSonarrBlock::EditSeriesSelectSeriesType + | ActiveSonarrBlock::EditSeriesSelectQualityProfile + | ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.push_navigation_stack( + self + .app + .data + .sonarr_data + .selected_block + .get_active_block() + .into(), + ), + ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => { + self.app.push_navigation_stack( + self + .app + .data + .sonarr_data + .selected_block + .get_active_block() + .into(), + ); + self.app.should_ignore_quit_key = true; + } + ActiveSonarrBlock::EditSeriesToggleMonitored => { + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .monitored = Some( + !self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .monitored + .unwrap_or_default(), + ) + } + ActiveSonarrBlock::EditSeriesToggleSeasonFolder => { + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .use_season_folders = Some( + !self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .use_season_folders + .unwrap_or_default(), + ) + } + _ => (), + } + } + ActiveSonarrBlock::EditSeriesSelectSeriesType + | ActiveSonarrBlock::EditSeriesSelectQualityProfile + | ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(), + ActiveSonarrBlock::EditSeriesPathInput | ActiveSonarrBlock::EditSeriesTagsInput => { + self.app.pop_navigation_stack(); + self.app.should_ignore_quit_key = false; + } + _ => (), + } + } + + fn handle_esc(&mut self) { + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesTagsInput | ActiveSonarrBlock::EditSeriesPathInput => { + self.app.pop_navigation_stack(); + self.app.should_ignore_quit_key = false; + } + ActiveSonarrBlock::EditSeriesPrompt => { + self.app.pop_navigation_stack(); + self.app.data.sonarr_data.edit_series_modal = None; + self.app.data.sonarr_data.prompt_confirm = false; + } + ActiveSonarrBlock::EditSeriesSelectSeriesType + | ActiveSonarrBlock::EditSeriesSelectQualityProfile + | ActiveSonarrBlock::EditSeriesSelectLanguageProfile => self.app.pop_navigation_stack(), + _ => (), + } + } + + fn handle_char_key_event(&mut self) { + let key = self.key; + match self.active_sonarr_block { + ActiveSonarrBlock::EditSeriesPathInput => { + handle_text_box_keys!( + self, + key, + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .path + ) + } + ActiveSonarrBlock::EditSeriesTagsInput => { + handle_text_box_keys!( + self, + key, + self + .app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .tags + ) + } + ActiveSonarrBlock::EditSeriesPrompt => { + if self.app.data.sonarr_data.selected_block.get_active_block() + == ActiveSonarrBlock::EditSeriesConfirmPrompt + && key == DEFAULT_KEYBINDINGS.confirm.key + { + self.app.data.sonarr_data.prompt_confirm = true; + self.app.data.sonarr_data.prompt_confirm_action = Some(SonarrEvent::EditSeries(None)); + self.app.should_refresh = true; + + self.app.pop_navigation_stack(); + } + } + _ => (), + } + } +} diff --git a/src/handlers/sonarr_handlers/library/edit_series_handler_tests.rs b/src/handlers/sonarr_handlers/library/edit_series_handler_tests.rs new file mode 100644 index 0000000..4a8743a --- /dev/null +++ b/src/handlers/sonarr_handlers/library/edit_series_handler_tests.rs @@ -0,0 +1,1322 @@ +#[cfg(test)] +mod tests { + use pretty_assertions::assert_str_eq; + use strum::IntoEnumIterator; + + use crate::app::key_binding::DEFAULT_KEYBINDINGS; + use crate::app::App; + use crate::event::Key; + use crate::handlers::sonarr_handlers::library::edit_series_handler::EditSeriesHandler; + use crate::handlers::KeyEventHandler; + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, EDIT_SERIES_BLOCKS}; + use crate::models::sonarr_models::SeriesType; + + mod test_handle_scroll_up_and_down { + use pretty_assertions::assert_eq; + use rstest::rstest; + use strum::IntoEnumIterator; + + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + use crate::models::servarr_data::sonarr::sonarr_data::EDIT_SERIES_SELECTION_BLOCKS; + use crate::models::BlockSelectionState; + + use super::*; + + #[rstest] + fn test_edit_series_select_series_type_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let series_type_vec = Vec::from_iter(SeriesType::iter()); + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .set_items(series_type_vec.clone()); + + if key == Key::Up { + for i in (0..series_type_vec.len()).rev() { + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectSeriesType, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .series_type_list + .current_selection(), + &series_type_vec[i] + ); + } + } else { + for i in 0..series_type_vec.len() { + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectSeriesType, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .series_type_list + .current_selection(), + &series_type_vec[(i + 1) % series_type_vec.len()] + ); + } + } + } + + #[rstest] + fn test_edit_series_select_quality_profile_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); + + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .quality_profile_list + .current_selection(), + "Test 2" + ); + + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .quality_profile_list + .current_selection(), + "Test 1" + ); + } + + #[rstest] + fn test_edit_series_select_language_profile_scroll( + #[values(DEFAULT_KEYBINDINGS.up.key, DEFAULT_KEYBINDINGS.down.key)] key: Key, + ) { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .set_items(vec!["Test 1".to_owned(), "Test 2".to_owned()]); + + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .language_profile_list + .current_selection(), + "Test 2" + ); + + EditSeriesHandler::with( + key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .language_profile_list + .current_selection(), + "Test 1" + ); + } + + #[rstest] + fn test_edit_series_prompt_scroll(#[values(Key::Up, Key::Down)] key: Key) { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.data.sonarr_data.selected_block.down(); + + EditSeriesHandler::with(key, &mut app, ActiveSonarrBlock::EditSeriesPrompt, None).handle(); + + if key == Key::Up { + assert_eq!( + app.data.sonarr_data.selected_block.get_active_block(), + ActiveSonarrBlock::EditSeriesToggleMonitored + ); + } else { + assert_eq!( + app.data.sonarr_data.selected_block.get_active_block(), + ActiveSonarrBlock::EditSeriesSelectQualityProfile + ); + } + } + + #[rstest] + fn test_edit_series_prompt_scroll_no_op_when_not_ready(#[values(Key::Up, Key::Down)] key: Key) { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.is_loading = true; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.data.sonarr_data.selected_block.down(); + + EditSeriesHandler::with(key, &mut app, ActiveSonarrBlock::EditSeriesPrompt, None).handle(); + + assert_eq!( + app.data.sonarr_data.selected_block.get_active_block(), + ActiveSonarrBlock::EditSeriesToggleSeasonFolder + ); + } + } + + mod test_handle_home_end { + use std::sync::atomic::Ordering; + + use strum::IntoEnumIterator; + + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + + use super::*; + + #[test] + fn test_edit_series_select_series_type_home_end() { + let series_type_vec = Vec::from_iter(SeriesType::iter()); + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .series_type_list + .set_items(series_type_vec.clone()); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.end.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectSeriesType, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .series_type_list + .current_selection(), + &series_type_vec[series_type_vec.len() - 1] + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.home.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectSeriesType, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .series_type_list + .current_selection(), + &series_type_vec[0] + ); + } + + #[test] + fn test_edit_series_select_quality_profile_scroll() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .quality_profile_list + .set_items(vec![ + "Test 1".to_owned(), + "Test 2".to_owned(), + "Test 3".to_owned(), + ]); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.end.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .quality_profile_list + .current_selection(), + "Test 3" + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.home.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .quality_profile_list + .current_selection(), + "Test 1" + ); + } + + #[test] + fn test_edit_series_select_language_profile_scroll() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .language_profile_list + .set_items(vec![ + "Test 1".to_owned(), + "Test 2".to_owned(), + "Test 3".to_owned(), + ]); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.end.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .language_profile_list + .current_selection(), + "Test 3" + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.home.key, + &mut app, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .language_profile_list + .current_selection(), + "Test 1" + ); + } + + #[test] + fn test_edit_series_path_input_home_end_keys() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + path: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.home.key, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .offset + .load(Ordering::SeqCst), + 4 + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.end.key, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .offset + .load(Ordering::SeqCst), + 0 + ); + } + + #[test] + fn test_edit_series_tags_input_home_end_keys() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + tags: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.home.key, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .offset + .load(Ordering::SeqCst), + 4 + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.end.key, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .offset + .load(Ordering::SeqCst), + 0 + ); + } + } + + mod test_handle_left_right_action { + use std::sync::atomic::Ordering; + + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + use rstest::rstest; + + use super::*; + + #[rstest] + fn test_left_right_prompt_toggle(#[values(Key::Left, Key::Right)] key: Key) { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + + EditSeriesHandler::with(key, &mut app, ActiveSonarrBlock::EditSeriesPrompt, None).handle(); + + assert!(app.data.sonarr_data.prompt_confirm); + + EditSeriesHandler::with(key, &mut app, ActiveSonarrBlock::EditSeriesPrompt, None).handle(); + + assert!(!app.data.sonarr_data.prompt_confirm); + } + + #[test] + fn test_edit_series_path_input_left_right_keys() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + path: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.left.key, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .offset + .load(Ordering::SeqCst), + 1 + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.right.key, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .offset + .load(Ordering::SeqCst), + 0 + ); + } + + #[test] + fn test_edit_series_tags_input_left_right_keys() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + tags: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.left.key, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .offset + .load(Ordering::SeqCst), + 1 + ); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.right.key, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .offset + .load(Ordering::SeqCst), + 0 + ); + } + } + + mod test_handle_submit { + use pretty_assertions::assert_eq; + use rstest::rstest; + + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + use crate::models::servarr_data::sonarr::sonarr_data::EDIT_SERIES_SELECTION_BLOCKS; + use crate::models::BlockSelectionState; + use crate::network::sonarr_network::SonarrEvent; + + use super::*; + + const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key; + + #[test] + fn test_edit_series_path_input_submit() { + let mut app = App::default(); + app.should_ignore_quit_key = true; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + path: "Test Path".into(), + ..EditSeriesModal::default() + }); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPathInput.into()); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert!(!app.should_ignore_quit_key); + assert!(!app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .text + .is_empty()); + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + } + + #[test] + fn test_edit_series_tags_input_submit() { + let mut app = App::default(); + app.should_ignore_quit_key = true; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + tags: "Test Tags".into(), + ..EditSeriesModal::default() + }); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPathInput.into()); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert!(!app.should_ignore_quit_key); + assert!(!app + .data + .sonarr_data + .edit_series_modal + .as_mut() + .unwrap() + .tags + .text + .is_empty()); + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + } + + #[test] + fn test_edit_series_prompt_prompt_decline_submit() { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app + .data + .sonarr_data + .selected_block + .set_index(0, EDIT_SERIES_SELECTION_BLOCKS.len() - 1); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into()); + assert_eq!(app.data.sonarr_data.prompt_confirm_action, None); + } + + #[test] + fn test_edit_series_confirm_prompt_prompt_confirmation_submit() { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.prompt_confirm = true; + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app + .data + .sonarr_data + .selected_block + .set_index(0, EDIT_SERIES_SELECTION_BLOCKS.len() - 1); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into()); + assert_eq!( + app.data.sonarr_data.prompt_confirm_action, + Some(SonarrEvent::EditSeries(None)) + ); + assert!(app.data.sonarr_data.edit_series_modal.is_some()); + assert!(app.should_refresh); + } + + #[test] + fn test_edit_series_confirm_prompt_prompt_confirmation_submit_no_op_when_not_ready() { + let mut app = App::default(); + app.is_loading = true; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.prompt_confirm = true; + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!(app.data.sonarr_data.prompt_confirm_action, None); + assert!(!app.should_refresh); + } + + #[test] + fn test_edit_series_toggle_monitored_submit() { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .monitored, + Some(true) + ); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .monitored, + Some(false) + ); + } + + #[test] + fn test_edit_series_toggle_use_season_folders_submit() { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.data.sonarr_data.selected_block.set_index(0, 1); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .use_season_folders, + Some(true) + ); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .use_season_folders, + Some(false) + ); + } + + #[rstest] + #[case(ActiveSonarrBlock::EditSeriesSelectQualityProfile, 2)] + #[case(ActiveSonarrBlock::EditSeriesSelectLanguageProfile, 3)] + #[case(ActiveSonarrBlock::EditSeriesSelectSeriesType, 4)] + #[case(ActiveSonarrBlock::EditSeriesPathInput, 5)] + #[case(ActiveSonarrBlock::EditSeriesTagsInput, 6)] + fn test_edit_series_prompt_selected_block_submit( + #[case] selected_block: ActiveSonarrBlock, + #[case] y_index: usize, + ) { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.data.sonarr_data.selected_block.set_index(0, y_index); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + Some(ActiveSonarrBlock::Series), + ) + .handle(); + + assert_eq!(app.get_current_route(), selected_block.into()); + assert_eq!(app.data.sonarr_data.prompt_confirm_action, None); + + if selected_block == ActiveSonarrBlock::EditSeriesPathInput + || selected_block == ActiveSonarrBlock::EditSeriesTagsInput + { + assert!(app.should_ignore_quit_key); + } + } + + #[rstest] + fn test_edit_series_prompt_selected_block_submit_no_op_when_not_ready( + #[values(1, 2, 3, 4)] y_index: usize, + ) { + let mut app = App::default(); + app.is_loading = true; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app.data.sonarr_data.selected_block.set_index(0, y_index); + + EditSeriesHandler::with( + SUBMIT_KEY, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + assert_eq!(app.data.sonarr_data.prompt_confirm_action, None); + assert!(!app.should_ignore_quit_key); + } + + #[rstest] + fn test_edit_series_prompt_selecting_preferences_blocks_submit( + #[values( + ActiveSonarrBlock::EditSeriesSelectSeriesType, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + ActiveSonarrBlock::EditSeriesPathInput, + ActiveSonarrBlock::EditSeriesTagsInput + )] + active_sonarr_block: ActiveSonarrBlock, + ) { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.push_navigation_stack(active_sonarr_block.into()); + + EditSeriesHandler::with(SUBMIT_KEY, &mut app, active_sonarr_block, None).handle(); + + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + + if active_sonarr_block == ActiveSonarrBlock::EditSeriesPathInput + || active_sonarr_block == ActiveSonarrBlock::EditSeriesTagsInput + { + assert!(!app.should_ignore_quit_key); + } + } + } + + mod test_handle_esc { + use pretty_assertions::assert_eq; + use rstest::rstest; + + use crate::models::servarr_data::sonarr::modals::EditSeriesModal; + use crate::models::servarr_data::sonarr::sonarr_data::sonarr_test_utils::utils::create_test_sonarr_data; + + use super::*; + + const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key; + + #[rstest] + fn test_edit_series_input_esc( + #[values( + ActiveSonarrBlock::EditSeriesTagsInput, + ActiveSonarrBlock::EditSeriesPathInput + )] + active_sonarr_block: ActiveSonarrBlock, + ) { + let mut app = App::default(); + app.data.sonarr_data = create_test_sonarr_data(); + app.should_ignore_quit_key = true; + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.push_navigation_stack(active_sonarr_block.into()); + + EditSeriesHandler::with(ESC_KEY, &mut app, active_sonarr_block, None).handle(); + + assert!(!app.should_ignore_quit_key); + assert_eq!( + app.get_current_route(), + ActiveSonarrBlock::EditSeriesPrompt.into() + ); + } + + #[test] + fn test_edit_series_prompt_esc() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data = create_test_sonarr_data(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + + EditSeriesHandler::with(ESC_KEY, &mut app, ActiveSonarrBlock::EditSeriesPrompt, None) + .handle(); + + assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into()); + + assert!(app.data.sonarr_data.edit_series_modal.is_none()); + assert!(!app.data.sonarr_data.prompt_confirm); + } + + #[rstest] + fn test_edit_series_esc( + #[values( + ActiveSonarrBlock::EditSeriesSelectSeriesType, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile + )] + active_sonarr_block: ActiveSonarrBlock, + #[values(true, false)] is_ready: bool, + ) { + let mut app = App::default(); + app.is_loading = is_ready; + app.data.sonarr_data = create_test_sonarr_data(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(active_sonarr_block.into()); + + EditSeriesHandler::with(ESC_KEY, &mut app, active_sonarr_block, None).handle(); + + assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into()); + } + } + + mod test_handle_key_char { + use super::*; + use crate::{ + models::{ + servarr_data::sonarr::{ + modals::EditSeriesModal, sonarr_data::EDIT_SERIES_SELECTION_BLOCKS, + }, + BlockSelectionState, + }, + network::sonarr_network::SonarrEvent, + }; + + #[test] + fn test_edit_series_path_input_backspace() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + path: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.backspace.key, + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .text, + "Tes" + ); + } + + #[test] + fn test_edit_series_tags_input_backspace() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal { + tags: "Test".into(), + ..EditSeriesModal::default() + }); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.backspace.key, + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .text, + "Tes" + ); + } + + #[test] + fn test_edit_series_path_input_char_key() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + + EditSeriesHandler::with( + Key::Char('h'), + &mut app, + ActiveSonarrBlock::EditSeriesPathInput, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .path + .text, + "h" + ); + } + + #[test] + fn test_edit_series_tags_input_char_key() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + + EditSeriesHandler::with( + Key::Char('h'), + &mut app, + ActiveSonarrBlock::EditSeriesTagsInput, + None, + ) + .handle(); + + assert_str_eq!( + app + .data + .sonarr_data + .edit_series_modal + .as_ref() + .unwrap() + .tags + .text, + "h" + ); + } + + #[test] + fn test_edit_series_confirm_prompt_prompt_confirm() { + let mut app = App::default(); + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.push_navigation_stack(ActiveSonarrBlock::EditSeriesPrompt.into()); + app.data.sonarr_data.selected_block = BlockSelectionState::new(EDIT_SERIES_SELECTION_BLOCKS); + app + .data + .sonarr_data + .selected_block + .set_index(0, EDIT_SERIES_SELECTION_BLOCKS.len() - 1); + + EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.confirm.key, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ) + .handle(); + + assert_eq!(app.get_current_route(), ActiveSonarrBlock::Series.into()); + assert_eq!( + app.data.sonarr_data.prompt_confirm_action, + Some(SonarrEvent::EditSeries(None)) + ); + assert!(app.data.sonarr_data.edit_series_modal.is_some()); + assert!(app.should_refresh); + } + } + + #[test] + fn test_edit_series_handler_accepts() { + ActiveSonarrBlock::iter().for_each(|active_sonarr_block| { + if EDIT_SERIES_BLOCKS.contains(&active_sonarr_block) { + assert!(EditSeriesHandler::accepts(active_sonarr_block)); + } else { + assert!(!EditSeriesHandler::accepts(active_sonarr_block)); + } + }); + } + + #[test] + fn test_edit_series_handler_is_not_ready_when_loading() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.is_loading = true; + + let handler = EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_series_handler_is_not_ready_when_edit_series_modal_is_none() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.is_loading = false; + + let handler = EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ); + + assert!(!handler.is_ready()); + } + + #[test] + fn test_edit_series_handler_is_ready_when_edit_series_modal_is_some() { + let mut app = App::default(); + app.push_navigation_stack(ActiveSonarrBlock::Series.into()); + app.is_loading = false; + app.data.sonarr_data.edit_series_modal = Some(EditSeriesModal::default()); + + let handler = EditSeriesHandler::with( + DEFAULT_KEYBINDINGS.esc.key, + &mut app, + ActiveSonarrBlock::EditSeriesPrompt, + None, + ); + + assert!(handler.is_ready()); + } +} diff --git a/src/handlers/sonarr_handlers/library/library_handler_tests.rs b/src/handlers/sonarr_handlers/library/library_handler_tests.rs index 207d0cb..d3d1fae 100644 --- a/src/handlers/sonarr_handlers/library/library_handler_tests.rs +++ b/src/handlers/sonarr_handlers/library/library_handler_tests.rs @@ -12,7 +12,7 @@ mod tests { use crate::handlers::sonarr_handlers::library::{series_sorting_options, LibraryHandler}; use crate::handlers::KeyEventHandler; use crate::models::servarr_data::sonarr::sonarr_data::{ - ActiveSonarrBlock, ADD_SERIES_BLOCKS, DELETE_SERIES_BLOCKS, LIBRARY_BLOCKS, + ActiveSonarrBlock, ADD_SERIES_BLOCKS, DELETE_SERIES_BLOCKS, EDIT_SERIES_BLOCKS, LIBRARY_BLOCKS, }; use crate::models::sonarr_models::{Series, SeriesStatus, SeriesType}; use crate::models::stateful_table::SortOption; @@ -1487,23 +1487,24 @@ mod tests { // ); // } - // #[rstest] - // fn test_delegates_edit_series_blocks_to_edit_series_handler( - // #[values( - // ActiveSonarrBlock::EditSeriesPrompt, - // ActiveSonarrBlock::EditSeriesPathInput, - // ActiveSonarrBlock::EditSeriesSelectMinimumAvailability, - // ActiveSonarrBlock::EditSeriesSelectQualityProfile, - // ActiveSonarrBlock::EditSeriesTagsInput - // )] - // active_sonarr_block: ActiveSonarrBlock, - // ) { - // test_handler_delegation!( - // LibraryHandler, - // ActiveSonarrBlock::Series, - // active_sonarr_block - // ); - // } + #[rstest] + fn test_delegates_edit_series_blocks_to_edit_series_handler( + #[values( + ActiveSonarrBlock::EditSeriesPrompt, + ActiveSonarrBlock::EditSeriesPathInput, + ActiveSonarrBlock::EditSeriesSelectSeriesType, + ActiveSonarrBlock::EditSeriesSelectQualityProfile, + ActiveSonarrBlock::EditSeriesSelectLanguageProfile, + ActiveSonarrBlock::EditSeriesTagsInput + )] + active_sonarr_block: ActiveSonarrBlock, + ) { + test_handler_delegation!( + LibraryHandler, + ActiveSonarrBlock::Series, + active_sonarr_block + ); + } #[test] fn test_delegates_delete_series_blocks_to_delete_series_handler() { @@ -1709,6 +1710,7 @@ mod tests { library_handler_blocks.extend(LIBRARY_BLOCKS); library_handler_blocks.extend(ADD_SERIES_BLOCKS); library_handler_blocks.extend(DELETE_SERIES_BLOCKS); + library_handler_blocks.extend(EDIT_SERIES_BLOCKS); ActiveSonarrBlock::iter().for_each(|active_sonarr_block| { if library_handler_blocks.contains(&active_sonarr_block) { diff --git a/src/handlers/sonarr_handlers/library/mod.rs b/src/handlers/sonarr_handlers/library/mod.rs index fee9089..47ff6ea 100644 --- a/src/handlers/sonarr_handlers/library/mod.rs +++ b/src/handlers/sonarr_handlers/library/mod.rs @@ -1,5 +1,7 @@ use add_series_handler::AddSeriesHandler; +mod edit_series_handler; use delete_series_handler::DeleteSeriesHandler; +use edit_series_handler::EditSeriesHandler; use crate::{ app::App, @@ -45,6 +47,10 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, ' DeleteSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context) .handle(); } + _ if EditSeriesHandler::accepts(self.active_sonarr_block) => { + EditSeriesHandler::with(self.key, self.app, self.active_sonarr_block, self.context) + .handle(); + } _ => self.handle_key_event(), } } @@ -52,6 +58,7 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, ' fn accepts(active_block: ActiveSonarrBlock) -> bool { AddSeriesHandler::accepts(active_block) || DeleteSeriesHandler::accepts(active_block) + || EditSeriesHandler::accepts(active_block) || LIBRARY_BLOCKS.contains(&active_block) }