diff --git a/src/ui/sonarr_ui/library/series_details_ui.rs b/src/ui/sonarr_ui/library/series_details_ui.rs index 72849732..94116624 100644 --- a/src/ui/sonarr_ui/library/series_details_ui.rs +++ b/src/ui/sonarr_ui/library/series_details_ui.rs @@ -230,16 +230,12 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let content = Some(&mut app.data.sonarr_data.seasons); let season_row_mapping = |season: &Season| { let Season { - title, - monitored, - statistics, - .. + title, statistics, .. } = season; let SeasonStatistics { episode_file_count, episode_count, size_on_disk, - next_airing, .. } = if let Some(stats) = statistics { stats @@ -249,25 +245,15 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { let season_monitored = if season.monitored { "🏷" } else { "" }; let size = convert_to_gb(*size_on_disk); - let row = Row::new(vec![ - Cell::from(season_monitored.to_owned()), - Cell::from(title.clone().unwrap_or_default()), - Cell::from(format!("{episode_file_count}/{episode_count}")), - Cell::from(format!("{size:.2} GB")), - ]); - if !monitored { - row.unmonitored() - } else if episode_file_count == episode_count { - row.downloaded() - } else if let Some(next_airing_utc) = next_airing.as_ref() { - if next_airing_utc > &Utc::now() { - row.unreleased() - } else { - row.missing() - } - } else { - row.missing() - } + decorate_season_row_with_style( + season, + Row::new(vec![ + Cell::from(season_monitored.to_owned()), + Cell::from(title.clone().unwrap_or_default()), + Cell::from(format!("{episode_file_count}/{episode_count}")), + Cell::from(format!("{size:.2} GB")), + ]), + ) }; let is_searching = active_sonarr_block == ActiveSonarrBlock::SearchSeason; let season_table = ManagarrTable::new(content, season_row_mapping) @@ -291,6 +277,33 @@ fn draw_seasons_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { } } +fn decorate_season_row_with_style<'a>(season: &Season, row: Row<'a>) -> Row<'a> { + let SeasonStatistics { + episode_file_count, + episode_count, + next_airing, + .. + } = if let Some(stats) = &season.statistics { + stats + } else { + &SeasonStatistics::default() + }; + + if !season.monitored { + row.unmonitored() + } else if episode_file_count == episode_count { + row.downloaded() + } else if let Some(next_airing_utc) = next_airing.as_ref() { + if next_airing_utc > &Utc::now() { + row.unreleased() + } else { + row.missing() + } + } else { + row.missing() + } +} + fn draw_series_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) { match app.data.sonarr_data.series_history.as_ref() { Some(series_history) if !app.is_loading => { diff --git a/src/ui/sonarr_ui/library/series_details_ui_tests.rs b/src/ui/sonarr_ui/library/series_details_ui_tests.rs index 88bcc299..f607129a 100644 --- a/src/ui/sonarr_ui/library/series_details_ui_tests.rs +++ b/src/ui/sonarr_ui/library/series_details_ui_tests.rs @@ -1,5 +1,8 @@ #[cfg(test)] mod tests { + use chrono::{Duration, Utc}; + use pretty_assertions::assert_eq; + use ratatui::widgets::{Cell, Row}; use strum::IntoEnumIterator; use crate::app::App; @@ -7,8 +10,12 @@ mod tests { ActiveSonarrBlock, EPISODE_DETAILS_BLOCKS, SEASON_DETAILS_BLOCKS, SERIES_DETAILS_BLOCKS, SERIES_OVERVIEW_BLOCKS, }; + use crate::models::sonarr_models::{Season, SeasonStatistics}; use crate::ui::DrawUi; - use crate::ui::sonarr_ui::library::series_details_ui::SeriesDetailsUi; + use crate::ui::sonarr_ui::library::series_details_ui::{ + SeriesDetailsUi, decorate_season_row_with_style, + }; + use crate::ui::styles::ManagarrStyle; use crate::ui::ui_test_utils::test_utils::render_to_string_with_app; #[test] @@ -27,6 +34,107 @@ mod tests { }); } + #[test] + fn test_decorate_season_row_with_style_unmonitored() { + let season = Season::default(); + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.unmonitored()); + } + + #[test] + fn test_decorate_season_row_with_style_downloaded_when_all_episodes_present() { + let season = Season { + monitored: true, + statistics: Some(SeasonStatistics { + episode_file_count: 3, + episode_count: 3, + ..SeasonStatistics::default() + }), + ..Season::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.downloaded()); + } + + #[test] + fn test_decorate_season_row_with_style_downloaded_when_no_statistics() { + let season = Season { + monitored: true, + statistics: None, + ..Season::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.downloaded()); + } + + #[test] + fn test_decorate_season_row_with_style_unreleased_when_episodes_are_missing_and_next_airing_is_future() + { + let season = Season { + monitored: true, + statistics: Some(SeasonStatistics { + episode_file_count: 0, + episode_count: 3, + next_airing: Some(Utc::now() + Duration::days(1)), + ..SeasonStatistics::default() + }), + ..Season::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.unreleased()); + } + + #[test] + fn test_decorate_season_row_with_style_missing_when_episodes_are_missing_and_next_airing_has_passed() + { + let season = Season { + monitored: true, + statistics: Some(SeasonStatistics { + episode_file_count: 1, + episode_count: 3, + next_airing: Some(Utc::now() - Duration::days(1)), + ..SeasonStatistics::default() + }), + ..Season::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.missing()); + } + + #[test] + fn test_decorate_season_row_with_style_missing_when_episodes_are_missing_and_no_next_airing() { + let season = Season { + monitored: true, + statistics: Some(SeasonStatistics { + episode_file_count: 1, + episode_count: 3, + next_airing: None, + ..SeasonStatistics::default() + }), + ..Season::default() + }; + let row = Row::new(vec![Cell::from("test".to_owned())]); + + let style = decorate_season_row_with_style(&season, row.clone()); + + assert_eq!(style, row.missing()); + } + mod snapshot_tests { use crate::models::stateful_table::StatefulTable; use crate::ui::ui_test_utils::test_utils::TerminalSize;