diff --git a/src/app/app_tests.rs b/src/app/app_tests.rs index 5b85e37..6d9b1f8 100644 --- a/src/app/app_tests.rs +++ b/src/app/app_tests.rs @@ -35,6 +35,7 @@ mod tests { theme: None, radarr: Some(vec![radarr_config_1.clone(), radarr_config_2.clone()]), sonarr: Some(vec![sonarr_config_1.clone(), sonarr_config_2.clone()]), + lidarr: None, }; let expected_tab_routes = vec![ TabRoute { diff --git a/src/app/mod.rs b/src/app/mod.rs index 2b8acb6..68edf1f 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -285,6 +285,12 @@ impl App<'_> { contextual_help: None, config: Some(ServarrConfig::default()), }, + TabRoute { + title: "Lidarr".to_owned(), + route: ActiveLidarrBlock::Artists.into(), + contextual_help: None, + config: Some(ServarrConfig::default()), + }, ]), ..App::default() } @@ -309,6 +315,12 @@ impl App<'_> { contextual_help: None, config: Some(ServarrConfig::default()), }, + TabRoute { + title: "Lidarr".to_owned(), + route: ActiveLidarrBlock::Artists.into(), + contextual_help: None, + config: Some(ServarrConfig::default()), + }, ]), ..App::default() } diff --git a/src/cli/lidarr/lidarr_command_tests.rs b/src/cli/lidarr/lidarr_command_tests.rs new file mode 100644 index 0000000..bf0ffaa --- /dev/null +++ b/src/cli/lidarr/lidarr_command_tests.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +mod tests { + use crate::cli::{ + lidarr::{list_command_handler::LidarrListCommand, LidarrCommand}, + Command, + }; + use crate::Cli; + use clap::CommandFactory; + use pretty_assertions::assert_eq; + + #[test] + fn test_lidarr_command_from() { + let command = LidarrCommand::List(LidarrListCommand::Artists); + + let result = Command::from(command.clone()); + + assert_eq!(result, Command::Lidarr(command)); + } + + mod cli { + use super::*; + + #[test] + fn test_list_artists_has_no_arg_requirements() { + let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "list", "artists"]); + + assert_ok!(&result); + } + + #[test] + fn test_lidarr_list_subcommand_requires_subcommand() { + let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "list"]); + + assert_err!(&result); + } + } +} diff --git a/src/cli/lidarr/list_command_handler.rs b/src/cli/lidarr/list_command_handler.rs index 445e153..cf56ccb 100644 --- a/src/cli/lidarr/list_command_handler.rs +++ b/src/cli/lidarr/list_command_handler.rs @@ -12,6 +12,10 @@ use crate::{ use super::LidarrCommand; +#[cfg(test)] +#[path = "list_command_handler_tests.rs"] +mod list_command_handler_tests; + #[derive(Debug, Clone, PartialEq, Eq, Subcommand)] pub enum LidarrListCommand { #[command(about = "List all artists in your Lidarr library")] diff --git a/src/cli/lidarr/list_command_handler_tests.rs b/src/cli/lidarr/list_command_handler_tests.rs new file mode 100644 index 0000000..6dfbae9 --- /dev/null +++ b/src/cli/lidarr/list_command_handler_tests.rs @@ -0,0 +1,70 @@ +#[cfg(test)] +mod tests { + use crate::Cli; + use crate::cli::{ + Command, + lidarr::{LidarrCommand, list_command_handler::LidarrListCommand}, + }; + use clap::CommandFactory; + use pretty_assertions::assert_eq; + + #[test] + fn test_lidarr_list_command_from() { + let command = LidarrListCommand::Artists; + + let result = Command::from(command.clone()); + + assert_eq!(result, Command::Lidarr(LidarrCommand::List(command))); + } + + mod cli { + use super::*; + + #[test] + fn test_list_artists_has_no_arg_requirements() { + let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "list", "artists"]); + + assert_ok!(&result); + } + } + + mod handler { + use std::sync::Arc; + + use mockall::predicate::eq; + use serde_json::json; + use tokio::sync::Mutex; + + use crate::cli::CliCommandHandler; + use crate::cli::lidarr::list_command_handler::{LidarrListCommand, LidarrListCommandHandler}; + use crate::models::Serdeable; + use crate::models::lidarr_models::LidarrSerdeable; + use crate::network::lidarr_network::LidarrEvent; + use crate::{ + app::App, + network::{MockNetworkTrait, NetworkEvent}, + }; + + #[tokio::test] + async fn test_handle_list_artists_command() { + let mut mock_network = MockNetworkTrait::new(); + mock_network + .expect_handle_network_event() + .with(eq::(LidarrEvent::ListArtists.into())) + .times(1) + .returning(|_| { + Ok(Serdeable::Lidarr(LidarrSerdeable::Value( + json!({"testResponse": "response"}), + ))) + }); + let app_arc = Arc::new(Mutex::new(App::test_default())); + + let result = + LidarrListCommandHandler::with(&app_arc, LidarrListCommand::Artists, &mut mock_network) + .handle() + .await; + + assert_ok!(&result); + } + } +} diff --git a/src/cli/lidarr/mod.rs b/src/cli/lidarr/mod.rs index f1913f4..99dab8b 100644 --- a/src/cli/lidarr/mod.rs +++ b/src/cli/lidarr/mod.rs @@ -14,6 +14,10 @@ use super::{CliCommandHandler, Command}; mod list_command_handler; +#[cfg(test)] +#[path = "lidarr_command_tests.rs"] +mod lidarr_command_tests; + #[derive(Debug, Clone, PartialEq, Eq, Subcommand)] pub enum LidarrCommand { #[command( diff --git a/src/handlers/handler_proptest.rs b/src/handlers/handler_proptest.rs index 83fe8d6..1e3df75 100644 --- a/src/handlers/handler_proptest.rs +++ b/src/handlers/handler_proptest.rs @@ -4,13 +4,12 @@ mod property_tests { use crate::app::App; use crate::handlers::handler_test_utils::test_utils::proptest_helpers::*; + use crate::models::radarr_models::Movie; use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock; use crate::models::stateful_table::StatefulTable; - use crate::models::radarr_models::Movie; - use crate::models::{Scrollable, Paginated}; + use crate::models::{Paginated, Scrollable}; proptest! { - /// Property test: Table never panics on index selection #[test] fn test_table_index_selection_safety( list_size in list_size(), @@ -25,19 +24,15 @@ mod property_tests { table.set_items(movies); - // Try to select an arbitrary index if index < list_size { table.select_index(Some(index)); let selected = table.current_selection(); prop_assert_eq!(selected.id, index as i64); } else { - // Out of bounds selection should be safe table.select_index(Some(index)); - // Should not panic, selection stays valid } } - /// Property test: Table state remains consistent after scroll operations #[test] fn test_table_scroll_consistency( list_size in list_size(), @@ -53,42 +48,34 @@ mod property_tests { table.set_items(movies); let initial_id = table.current_selection().id; - // Scroll down multiple times for _ in 0..scroll_amount { table.scroll_down(); } let after_down_id = table.current_selection().id; - // Position should increase (up to max) prop_assert!(after_down_id >= initial_id); prop_assert!(after_down_id < list_size as i64); - // Scroll back up for _ in 0..scroll_amount { table.scroll_up(); } - // Should return to initial position (or 0 if we hit the top) prop_assert!(table.current_selection().id <= initial_id); } - /// Property test: Empty tables handle operations gracefully #[test] fn test_empty_table_safety(_scroll_ops in 0usize..50) { let table = StatefulTable::::default(); - // Empty table operations should be safe prop_assert!(table.is_empty()); prop_assert!(table.items.is_empty()); } - /// Property test: Navigation operations maintain consistency #[test] fn test_navigation_consistency(pushes in 1usize..20) { let mut app = App::test_default(); let initial_route = app.get_current_route(); - // Push multiple routes let routes = vec![ ActiveRadarrBlock::Movies, ActiveRadarrBlock::Collections, @@ -101,34 +88,27 @@ mod property_tests { app.push_navigation_stack(route.into()); } - // Current route should be the last pushed let last_pushed = routes[(pushes - 1) % routes.len()]; prop_assert_eq!(app.get_current_route(), last_pushed.into()); - // Pop all routes for _ in 0..pushes { app.pop_navigation_stack(); } - // Should return to initial route prop_assert_eq!(app.get_current_route(), initial_route); } - /// Property test: String input handling is safe #[test] fn test_string_input_safety(input in text_input_string()) { - // String operations should never panic let _lowercase = input.to_lowercase(); let _uppercase = input.to_uppercase(); let _trimmed = input.trim(); let _len = input.len(); let _chars: Vec = input.chars().collect(); - // All operations completed without panic prop_assert!(true); } - /// Property test: Table maintains data integrity after operations #[test] fn test_table_data_integrity( list_size in 1usize..100 @@ -144,16 +124,13 @@ mod property_tests { table.set_items(movies.clone()); let original_count = table.items.len(); - // Count should remain the same after various operations prop_assert_eq!(table.items.len(), original_count); - // All original items should still be present for movie in &movies { prop_assert!(table.items.iter().any(|m| m.id == movie.id)); } } - /// Property test: Page up/down maintains bounds #[test] fn test_page_navigation_bounds( list_size in list_size(), @@ -168,7 +145,6 @@ mod property_tests { table.set_items(movies); - // Perform page operations for i in 0..page_ops { if i % 2 == 0 { table.page_down(); @@ -176,14 +152,12 @@ mod property_tests { table.page_up(); } - // Should never exceed bounds let current = table.current_selection(); prop_assert!(current.id >= 0); prop_assert!(current.id < list_size as i64); } } - /// Property test: Table filtering reduces or maintains size #[test] fn test_table_filter_size_invariant( list_size in list_size(), @@ -200,7 +174,6 @@ mod property_tests { table.set_items(movies.clone()); let original_size = table.items.len(); - // Apply filter if !filter_term.is_empty() { let filtered: Vec = movies.into_iter() .filter(|m| m.title.text.to_lowercase().contains(&filter_term.to_lowercase())) @@ -208,10 +181,8 @@ mod property_tests { table.set_items(filtered); } - // Filtered size should be <= original prop_assert!(table.items.len() <= original_size); - // Selection should still be valid if table not empty if !table.items.is_empty() { let current = table.current_selection(); prop_assert!(current.id >= 0); diff --git a/src/handlers/handlers_tests.rs b/src/handlers/handlers_tests.rs index 650511c..48cc18d 100644 --- a/src/handlers/handlers_tests.rs +++ b/src/handlers/handlers_tests.rs @@ -9,22 +9,23 @@ mod tests { use rstest::rstest; use tokio_util::sync::CancellationToken; - use crate::app::App; use crate::app::context_clues::SERVARR_CONTEXT_CLUES; - use crate::app::key_binding::{DEFAULT_KEYBINDINGS, KeyBinding}; + use crate::app::key_binding::{KeyBinding, DEFAULT_KEYBINDINGS}; use crate::app::radarr::radarr_context_clues::{ LIBRARY_CONTEXT_CLUES, MOVIE_DETAILS_CONTEXT_CLUES, }; + use crate::app::App; use crate::event::Key; use crate::handlers::{handle_clear_errors, handle_prompt_toggle}; use crate::handlers::{handle_events, populate_keymapping_table}; - use crate::models::HorizontallyScrollableText; - use crate::models::Route; - use crate::models::servarr_data::ActiveKeybindingBlock; + use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock; use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData}; use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock; + use crate::models::servarr_data::ActiveKeybindingBlock; use crate::models::servarr_models::KeybindingItem; use crate::models::stateful_table::StatefulTable; + use crate::models::HorizontallyScrollableText; + use crate::models::Route; #[test] fn test_handle_clear_errors() { @@ -60,11 +61,16 @@ mod tests { } #[rstest] - #[case(0, ActiveSonarrBlock::Series, ActiveSonarrBlock::Series)] - #[case(1, ActiveRadarrBlock::Movies, ActiveRadarrBlock::Movies)] - fn test_handle_change_tabs(#[case] index: usize, #[case] left_block: T, #[case] right_block: T) - where + #[case(0, ActiveLidarrBlock::Artists, ActiveSonarrBlock::Series)] + #[case(1, ActiveRadarrBlock::Movies, ActiveLidarrBlock::Artists)] + #[case(2, ActiveSonarrBlock::Series, ActiveRadarrBlock::Movies)] + fn test_handle_change_tabs( + #[case] index: usize, + #[case] left_block: T, + #[case] right_block: U, + ) where T: Into + Copy, + U: Into + Copy, { let mut app = App::test_default(); app.error = "Test".into(); @@ -122,8 +128,8 @@ mod tests { } #[test] - fn test_handle_empties_keybindings_table_on_help_button_press_when_keybindings_table_is_already_populated() - { + fn test_handle_empties_keybindings_table_on_help_button_press_when_keybindings_table_is_already_populated( + ) { let mut app = App::test_default(); let keybinding_items = Vec::from(SERVARR_CONTEXT_CLUES) .iter() @@ -254,8 +260,8 @@ mod tests { } #[test] - fn test_populate_keymapping_table_populates_delegated_servarr_context_provider_options_before_global_options() - { + fn test_populate_keymapping_table_populates_delegated_servarr_context_provider_options_before_global_options( + ) { let mut expected_keybinding_items = MOVIE_DETAILS_CONTEXT_CLUES .iter() .map(|(key, desc)| context_clue_to_keybinding_item(key, desc)) diff --git a/src/models/lidarr_models.rs b/src/models/lidarr_models.rs index dcc19df..f17d382 100644 --- a/src/models/lidarr_models.rs +++ b/src/models/lidarr_models.rs @@ -6,6 +6,10 @@ use serde_json::{Number, Value}; use super::{HorizontallyScrollableText, Serdeable}; use crate::serde_enum_from; +#[cfg(test)] +#[path = "lidarr_models_tests.rs"] +mod lidarr_models_tests; + #[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Artist { diff --git a/src/models/lidarr_models_tests.rs b/src/models/lidarr_models_tests.rs new file mode 100644 index 0000000..212ad87 --- /dev/null +++ b/src/models/lidarr_models_tests.rs @@ -0,0 +1,201 @@ +#[cfg(test)] +mod tests { + use pretty_assertions::{assert_eq, assert_str_eq}; + use serde_json::json; + + use crate::models::{ + lidarr_models::{Artist, ArtistStatistics, ArtistStatus, LidarrSerdeable, Ratings}, + Serdeable, + }; + + #[test] + fn test_artist_status_default() { + assert_eq!(ArtistStatus::default(), ArtistStatus::Continuing); + } + + #[test] + fn test_lidarr_serdeable_from() { + let lidarr_serdeable = LidarrSerdeable::Value(json!({})); + + let serdeable: Serdeable = Serdeable::from(lidarr_serdeable.clone()); + + assert_eq!(serdeable, Serdeable::Lidarr(lidarr_serdeable)); + } + + #[test] + fn test_lidarr_serdeable_from_unit() { + let lidarr_serdeable = LidarrSerdeable::from(()); + + assert_eq!(lidarr_serdeable, LidarrSerdeable::Value(json!({}))); + } + + #[test] + fn test_lidarr_serdeable_from_value() { + let value = json!({"test": "test"}); + + let lidarr_serdeable: LidarrSerdeable = value.clone().into(); + + assert_eq!(lidarr_serdeable, LidarrSerdeable::Value(value)); + } + + #[test] + fn test_lidarr_serdeable_from_artists() { + let artists = vec![Artist { + id: 1, + ..Artist::default() + }]; + + let lidarr_serdeable: LidarrSerdeable = artists.clone().into(); + + assert_eq!(lidarr_serdeable, LidarrSerdeable::Artists(artists)); + } + + #[test] + fn test_artist_deserialization() { + let artist_json = json!({ + "id": 1, + "mbId": "test-mb-id", + "artistName": "Test Artist", + "foreignArtistId": "test-foreign-id", + "status": "continuing", + "overview": "Test overview", + "artistType": "Group", + "disambiguation": "UK Band", + "path": "/music/test-artist", + "qualityProfileId": 1, + "metadataProfileId": 1, + "monitored": true, + "genres": ["Rock", "Alternative"], + "tags": [1, 2], + "added": "2023-01-01T00:00:00Z", + "ratings": { + "votes": 100, + "value": 4.5 + }, + "statistics": { + "albumCount": 5, + "trackFileCount": 50, + "trackCount": 60, + "totalTrackCount": 70, + "sizeOnDisk": 1000000000, + "percentOfTracks": 83.33 + } + }); + + let artist: Artist = serde_json::from_value(artist_json).unwrap(); + + assert_eq!(artist.id, 1); + assert_str_eq!(artist.mb_id, "test-mb-id"); + assert_str_eq!(artist.artist_name.text, "Test Artist"); + assert_str_eq!(artist.foreign_artist_id, "test-foreign-id"); + assert_eq!(artist.status, ArtistStatus::Continuing); + assert_some_eq_x!(&artist.overview, "Test overview"); + assert_some_eq_x!(&artist.artist_type, "Group"); + assert_some_eq_x!(&artist.disambiguation, "UK Band"); + assert_str_eq!(artist.path, "/music/test-artist"); + assert_eq!(artist.quality_profile_id, 1); + assert_eq!(artist.metadata_profile_id, 1); + assert!(artist.monitored); + assert_eq!(artist.genres, vec!["Rock", "Alternative"]); + assert_eq!(artist.tags.len(), 2); + assert_some!(&artist.ratings); + assert_some!(&artist.statistics); + + let ratings = artist.ratings.unwrap(); + assert_eq!(ratings.votes, 100); + assert_eq!(ratings.value, 4.5); + + let stats = artist.statistics.unwrap(); + assert_eq!(stats.album_count, 5); + assert_eq!(stats.track_file_count, 50); + assert_eq!(stats.track_count, 60); + assert_eq!(stats.total_track_count, 70); + assert_eq!(stats.size_on_disk, 1000000000); + assert_eq!(stats.percent_of_tracks, 83.33); + } + + #[test] + fn test_artist_status_deserialization() { + assert_eq!( + serde_json::from_str::("\"continuing\"").unwrap(), + ArtistStatus::Continuing + ); + assert_eq!( + serde_json::from_str::("\"ended\"").unwrap(), + ArtistStatus::Ended + ); + assert_eq!( + serde_json::from_str::("\"deleted\"").unwrap(), + ArtistStatus::Deleted + ); + } + + #[test] + fn test_ratings_equality() { + let ratings1 = Ratings { + votes: 100, + value: 4.5, + }; + let ratings2 = Ratings { + votes: 100, + value: 4.5, + }; + let ratings3 = Ratings { + votes: 50, + value: 3.0, + }; + + assert_eq!(ratings1, ratings2); + assert_ne!(ratings1, ratings3); + } + + #[test] + fn test_artist_statistics_equality() { + let stats1 = ArtistStatistics { + album_count: 5, + track_file_count: 50, + track_count: 60, + total_track_count: 70, + size_on_disk: 1000000000, + percent_of_tracks: 83.33, + }; + let stats2 = ArtistStatistics { + album_count: 5, + track_file_count: 50, + track_count: 60, + total_track_count: 70, + size_on_disk: 1000000000, + percent_of_tracks: 83.33, + }; + let stats3 = ArtistStatistics::default(); + + assert_eq!(stats1, stats2); + assert_ne!(stats1, stats3); + } + + #[test] + fn test_artist_with_optional_fields_none() { + let artist_json = json!({ + "id": 1, + "mbId": "", + "artistName": "Test Artist", + "foreignArtistId": "", + "status": "continuing", + "path": "", + "qualityProfileId": 1, + "metadataProfileId": 1, + "monitored": false, + "genres": [], + "tags": [], + "added": "2023-01-01T00:00:00Z" + }); + + let artist: Artist = serde_json::from_value(artist_json).unwrap(); + + assert_none!(&artist.overview); + assert_none!(&artist.artist_type); + assert_none!(&artist.disambiguation); + assert_none!(&artist.ratings); + assert_none!(&artist.statistics); + } +} diff --git a/src/network/lidarr_network/lidarr_network_tests.rs b/src/network/lidarr_network/lidarr_network_tests.rs new file mode 100644 index 0000000..c56fac5 --- /dev/null +++ b/src/network/lidarr_network/lidarr_network_tests.rs @@ -0,0 +1,72 @@ +#[cfg(test)] +mod tests { + use crate::models::lidarr_models::{Artist, LidarrSerdeable}; + use crate::network::network_tests::test_utils::{MockServarrApi, test_network}; + use crate::network::{NetworkEvent, NetworkResource, lidarr_network::LidarrEvent}; + use pretty_assertions::{assert_eq, assert_str_eq}; + use rstest::rstest; + use serde_json::json; + + #[rstest] + #[case(LidarrEvent::HealthCheck, "/health")] + #[case(LidarrEvent::ListArtists, "/artist")] + fn test_resource(#[case] event: LidarrEvent, #[case] expected_uri: &str) { + assert_str_eq!(event.resource(), expected_uri); + } + + #[test] + fn test_from_lidarr_event() { + assert_eq!( + NetworkEvent::Lidarr(LidarrEvent::HealthCheck), + NetworkEvent::from(LidarrEvent::HealthCheck) + ); + } + + #[tokio::test] + async fn test_handle_get_lidarr_healthcheck_event() { + let (mock, app, _server) = MockServarrApi::get() + .build_for(LidarrEvent::HealthCheck) + .await; + app.lock().await.server_tabs.set_index(2); + let mut network = test_network(&app); + + let _ = network.handle_lidarr_event(LidarrEvent::HealthCheck).await; + + mock.assert_async().await; + } + + #[tokio::test] + async fn test_handle_list_artists_event() { + let artists_json = json!([{ + "id": 1, + "mbId": "test-mb-id", + "artistName": "Test Artist", + "foreignArtistId": "test-foreign-id", + "status": "continuing", + "path": "/music/test-artist", + "qualityProfileId": 1, + "metadataProfileId": 1, + "monitored": true, + "genres": [], + "tags": [], + "added": "2023-01-01T00:00:00Z" + }]); + let response: Vec = serde_json::from_value(artists_json.clone()).unwrap(); + let (mock, app, _server) = MockServarrApi::get() + .returns(artists_json) + .build_for(LidarrEvent::ListArtists) + .await; + app.lock().await.server_tabs.set_index(2); + let mut network = test_network(&app); + + let result = network.handle_lidarr_event(LidarrEvent::ListArtists).await; + + mock.assert_async().await; + + let LidarrSerdeable::Artists(artists) = result.unwrap() else { + panic!("Expected Artists"); + }; + + assert_eq!(artists, response); + } +} diff --git a/src/network/lidarr_network/mod.rs b/src/network/lidarr_network/mod.rs index 36119b0..74c32a1 100644 --- a/src/network/lidarr_network/mod.rs +++ b/src/network/lidarr_network/mod.rs @@ -7,6 +7,10 @@ use crate::{ network::RequestMethod, }; +#[cfg(test)] +#[path = "lidarr_network_tests.rs"] +mod lidarr_network_tests; + #[derive(Debug, Eq, PartialEq, Clone)] pub enum LidarrEvent { HealthCheck, diff --git a/src/network/network_tests.rs b/src/network/network_tests.rs index 7b83b9c..7e7abc2 100644 --- a/src/network/network_tests.rs +++ b/src/network/network_tests.rs @@ -810,11 +810,16 @@ pub(in crate::network) mod test_utils { network_event: E, ) -> (Mock, Arc>>, ServerGuard) where - E: Into + NetworkResource, + E: Into + NetworkResource + Clone, { let resource = network_event.resource(); + let network_event_clone: NetworkEvent = network_event.clone().into(); + let api_version = match &network_event_clone { + NetworkEvent::Lidarr(_) => "v1", + _ => "v3", + }; let mut server = Server::new_async().await; - let mut uri = format!("/api/v3{resource}"); + let mut uri = format!("/api/{api_version}{resource}"); if let Some(path) = &self.path { uri = format!("{uri}{path}"); @@ -853,9 +858,10 @@ pub(in crate::network) mod test_utils { ..ServarrConfig::default() }; - match network_event.into() { + match network_event_clone { NetworkEvent::Radarr(_) => app.server_tabs.tabs[0].config = Some(servarr_config), NetworkEvent::Sonarr(_) => app.server_tabs.tabs[1].config = Some(servarr_config), + NetworkEvent::Lidarr(_) => app.server_tabs.tabs[2].config = Some(servarr_config), } let app_arc = Arc::new(Mutex::new(app)); diff --git a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab.snap b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab.snap index 8f537b7..22a7c1a 100644 --- a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab.snap +++ b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab.snap @@ -3,7 +3,7 @@ source: src/ui/ui_tests.rs expression: output --- ╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Radarr │ Sonarr to open help│ +│ Radarr │ Sonarr │ Lidarr to open help│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮ │Radarr Version: 1.2.3.4 ││Test Download Title ││ ⠀⣠⣶⢶⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀ │ diff --git a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_error_popup.snap b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_error_popup.snap index 72a40f2..15eca48 100644 --- a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_error_popup.snap +++ b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_error_popup.snap @@ -3,7 +3,7 @@ source: src/ui/ui_tests.rs expression: output --- ╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Radarr │ Sonarr to open help│ +│ Radarr │ Sonarr │ Lidarr to open help│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮ │Radarr Version: 1.2.3.4 ╭ Keybindings ──────────────────────────────────────────────────────────────────────────╮ ││ ⠀⣠⣶⢶⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀ │ diff --git a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_with_error.snap b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_with_error.snap index 17602d3..dad6259 100644 --- a/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_with_error.snap +++ b/src/ui/snapshots/managarr__ui__ui_tests__snapshot_tests__radarr_ui_renders_library_tab_with_error.snap @@ -3,7 +3,7 @@ source: src/ui/ui_tests.rs expression: output --- ╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ Radarr │ Sonarr to open help│ +│ Radarr │ Sonarr │ Lidarr to open help│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ Error | to close ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │Some error │