diff --git a/src/app/app_tests.rs b/src/app/app_tests.rs index a3902f2..59cfb8e 100644 --- a/src/app/app_tests.rs +++ b/src/app/app_tests.rs @@ -1,22 +1,81 @@ #[cfg(test)] mod tests { + use crate::models::Route; use anyhow::anyhow; use pretty_assertions::assert_eq; + use rstest::rstest; use tokio::sync::mpsc; use crate::app::context_clues::{build_context_clue_string, SERVARR_CONTEXT_CLUES}; - use crate::app::{App, AppConfig, Data, ServarrConfig, DEFAULT_ROUTE}; + use crate::app::{App, AppConfig, Data, ServarrConfig}; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData}; use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData}; use crate::models::{HorizontallyScrollableText, TabRoute}; use crate::network::radarr_network::RadarrEvent; use crate::network::NetworkEvent; + use tokio_util::sync::CancellationToken; + + #[rstest] + fn test_app_new( + #[values(ActiveRadarrBlock::default(), ActiveSonarrBlock::default())] servarr: impl Into + + Copy, + ) { + let (title, config) = match servarr.into() { + Route::Radarr(_, _) => ( + "Radarr", + AppConfig { + radarr: Some(ServarrConfig::default()), + ..AppConfig::default() + }, + ), + Route::Sonarr(_, _) => ( + "Sonarr", + AppConfig { + sonarr: Some(ServarrConfig::default()), + ..AppConfig::default() + }, + ), + _ => unreachable!(), + }; + let tab_route = |title: &'static str| TabRoute { + title, + route: servarr.into(), + help: format!( + "<↑↓> scroll | ←→ change tab | {} ", + build_context_clue_string(&SERVARR_CONTEXT_CLUES) + ), + contextual_help: None, + }; + + let app = App::new( + mpsc::channel::(500).0, + config, + CancellationToken::new(), + ); + + assert!(app.navigation_stack.is_empty()); + assert_eq!(app.get_current_route(), servarr.into()); + assert!(app.network_tx.is_some()); + assert!(!app.cancellation_token.is_cancelled()); + assert!(app.is_first_render); + assert_eq!(app.error, HorizontallyScrollableText::default()); + assert_eq!(app.server_tabs.index, 0); + assert_eq!(app.server_tabs.tabs, vec![tab_route(title)]); + assert_eq!(app.tick_until_poll, 400); + assert_eq!(app.ticks_until_scroll, 4); + assert_eq!(app.tick_count, 0); + assert!(!app.is_loading); + assert!(!app.is_routing); + assert!(!app.should_refresh); + assert!(!app.should_ignore_quit_key); + assert!(!app.cli_mode); + } #[test] fn test_app_default() { let app = App::default(); - assert_eq!(app.navigation_stack, vec![DEFAULT_ROUTE]); + assert!(app.navigation_stack.is_empty()); assert!(app.network_tx.is_none()); assert!(!app.cancellation_token.is_cancelled()); assert!(app.is_first_render); @@ -37,7 +96,10 @@ mod tests { TabRoute { title: "Sonarr", route: ActiveSonarrBlock::Series.into(), - help: format!("{} ", build_context_clue_string(&SERVARR_CONTEXT_CLUES)), + help: format!( + "<↑↓> scroll | ←→ change tab | {} ", + build_context_clue_string(&SERVARR_CONTEXT_CLUES) + ), contextual_help: None, }, ] @@ -55,8 +117,9 @@ mod tests { #[test] fn test_navigation_stack_methods() { let mut app = App::default(); + let default_route = app.server_tabs.tabs.first().unwrap().route; - assert_eq!(app.get_current_route(), DEFAULT_ROUTE); + assert_eq!(app.get_current_route(), default_route); app.push_navigation_stack(ActiveRadarrBlock::Downloads.into()); @@ -75,13 +138,13 @@ mod tests { app.is_routing = false; app.pop_navigation_stack(); - assert_eq!(app.get_current_route(), DEFAULT_ROUTE); + assert_eq!(app.get_current_route(), default_route); assert!(app.is_routing); app.is_routing = false; app.pop_navigation_stack(); - assert_eq!(app.get_current_route(), DEFAULT_ROUTE); + assert_eq!(app.get_current_route(), default_route); assert!(app.is_routing); } diff --git a/src/app/mod.rs b/src/app/mod.rs index 3579ad8..e39e2b2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -23,8 +23,6 @@ mod key_binding_tests; pub mod radarr; pub mod sonarr; -const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies, None); - pub struct App<'a> { navigation_stack: Vec, network_tx: Option>, @@ -50,10 +48,37 @@ impl<'a> App<'a> { config: AppConfig, cancellation_token: CancellationToken, ) -> Self { + let mut server_tabs = Vec::new(); + + if config.radarr.is_some() { + server_tabs.push(TabRoute { + title: "Radarr", + route: ActiveRadarrBlock::Movies.into(), + help: format!( + "<↑↓> scroll | ←→ change tab | {} ", + build_context_clue_string(&SERVARR_CONTEXT_CLUES) + ), + contextual_help: None, + }); + } + + if config.sonarr.is_some() { + server_tabs.push(TabRoute { + title: "Sonarr", + route: ActiveSonarrBlock::Series.into(), + help: format!( + "<↑↓> scroll | ←→ change tab | {} ", + build_context_clue_string(&SERVARR_CONTEXT_CLUES) + ), + contextual_help: None, + }); + } + App { network_tx: Some(network_tx), config, cancellation_token, + server_tabs: TabState::new(server_tabs), ..App::default() } } @@ -114,7 +139,7 @@ impl<'a> App<'a> { pub fn pop_navigation_stack(&mut self) { self.is_routing = true; - if self.navigation_stack.len() > 1 { + if !self.navigation_stack.is_empty() { self.navigation_stack.pop(); } } @@ -133,14 +158,17 @@ impl<'a> App<'a> { } pub fn get_current_route(&self) -> Route { - *self.navigation_stack.last().unwrap_or(&DEFAULT_ROUTE) + *self + .navigation_stack + .last() + .unwrap_or(&self.server_tabs.tabs.first().unwrap().route) } } impl<'a> Default for App<'a> { fn default() -> Self { App { - navigation_stack: vec![DEFAULT_ROUTE], + navigation_stack: Vec::new(), network_tx: None, cancellation_token: CancellationToken::new(), error: HorizontallyScrollableText::default(), @@ -158,7 +186,10 @@ impl<'a> Default for App<'a> { TabRoute { title: "Sonarr", route: ActiveSonarrBlock::Series.into(), - help: format!("{} ", build_context_clue_string(&SERVARR_CONTEXT_CLUES)), + help: format!( + "<↑↓> scroll | ←→ change tab | {} ", + build_context_clue_string(&SERVARR_CONTEXT_CLUES) + ), contextual_help: None, }, ]), @@ -190,6 +221,13 @@ pub struct AppConfig { impl AppConfig { pub fn validate(&self) { + if self.radarr.is_none() && self.sonarr.is_none() { + log_and_print_error( + "No Servarr configuration provided in the specified configuration file".to_owned(), + ); + process::exit(1); + } + if let Some(radarr_config) = &self.radarr { radarr_config.validate(); }