From 110da1c0ee7fc139552b534279b0455978f8919f Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Tue, 8 Aug 2023 10:50:05 -0600 Subject: [PATCH] Implemented unit tests for ui utils --- src/event/input_event.rs | 2 +- src/event/key.rs | 5 +- src/ui/radarr_ui/add_movie_ui.rs | 8 +- src/ui/radarr_ui/mod.rs | 13 +- src/ui/radarr_ui/movie_details_ui.rs | 4 +- src/ui/utils.rs | 420 ++++++++++++++++++++++++++- 6 files changed, 429 insertions(+), 23 deletions(-) diff --git a/src/event/input_event.rs b/src/event/input_event.rs index 5f53aef..6e406f4 100644 --- a/src/event/input_event.rs +++ b/src/event/input_event.rs @@ -1,5 +1,5 @@ use std::sync::mpsc; -use std::sync::mpsc::{Receiver, Sender}; +use std::sync::mpsc::Receiver; use std::thread; use std::time::{Duration, Instant}; diff --git a/src/event/key.rs b/src/event/key.rs index 678fb5c..a063386 100644 --- a/src/event/key.rs +++ b/src/event/key.rs @@ -80,17 +80,18 @@ impl From for Key { #[cfg(test)] mod tests { use crossterm::event::{KeyCode, KeyEvent}; + use pretty_assertions::{assert_eq, assert_str_eq}; use crate::event::key::Key; #[test] fn test_key_formatter() { - assert_eq!(format!("{}", Key::Esc), ""); + assert_str_eq!(format!("{}", Key::Esc), ""); } #[test] fn test_key_formatter_char() { - assert_eq!(format!("{}", Key::Char('q')), ""); + assert_str_eq!(format!("{}", Key::Char('q')), ""); } #[test] diff --git a/src/ui/radarr_ui/add_movie_ui.rs b/src/ui/radarr_ui/add_movie_ui.rs index c94642d..1240ff7 100644 --- a/src/ui/radarr_ui/add_movie_ui.rs +++ b/src/ui/radarr_ui/add_movie_ui.rs @@ -8,9 +8,9 @@ use crate::app::radarr::ActiveRadarrBlock; use crate::models::radarr_models::AddMovieSearchResult; use crate::models::Route; use crate::ui::utils::{ - borderless_block, get_width, horizontal_chunks, layout_block, layout_paragraph_borderless, - show_cursor, style_default, style_help, style_primary, title_block_centered, - vertical_chunks_with_margin, + borderless_block, get_width_with_margin, horizontal_chunks, layout_block, + layout_paragraph_borderless, show_cursor, style_default, style_help, style_primary, + title_block_centered, vertical_chunks_with_margin, }; use crate::ui::{ draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup, @@ -146,7 +146,7 @@ fn draw_add_movie_search(f: &mut Frame<'_, B>, app: &mut App, area: movie .title - .scroll_or_reset(get_width(area), *movie == current_selection); + .scroll_or_reset(get_width_with_margin(area), *movie == current_selection); Row::new(vec![ Cell::from(movie.title.to_string()), diff --git a/src/ui/radarr_ui/mod.rs b/src/ui/radarr_ui/mod.rs index 1e755d1..d7bad5d 100644 --- a/src/ui/radarr_ui/mod.rs +++ b/src/ui/radarr_ui/mod.rs @@ -18,10 +18,10 @@ use crate::ui::radarr_ui::add_movie_ui::draw_add_movie_search_popup; use crate::ui::radarr_ui::collection_details_ui::draw_collection_details_popup; use crate::ui::radarr_ui::movie_details_ui::draw_movie_info_popup; use crate::ui::utils::{ - borderless_block, get_width, horizontal_chunks, layout_block, layout_block_top_border, - line_gauge_with_label, line_gauge_with_title, show_cursor, style_bold, style_default, - style_failure, style_primary, style_success, style_warning, title_block, title_block_centered, - vertical_chunks_with_margin, + borderless_block, get_width_with_margin, horizontal_chunks, layout_block, + layout_block_top_border, line_gauge_with_label, line_gauge_with_title, show_cursor, style_bold, + style_default, style_failure, style_primary, style_success, style_warning, title_block, + title_block_centered, vertical_chunks_with_margin, }; use crate::ui::{ draw_large_popup_over, draw_popup_over, draw_prompt_box, draw_prompt_popup_over, draw_table, @@ -385,7 +385,10 @@ fn draw_downloads(f: &mut Frame<'_, B>, app: &mut App, area: Rect) { } = download_record; let path = output_path.clone().unwrap_or_default(); - path.scroll_or_reset(get_width(area), current_selection == *download_record); + path.scroll_or_reset( + get_width_with_margin(area), + current_selection == *download_record, + ); let percent = 1f64 - (sizeleft.as_f64().unwrap() / size.as_f64().unwrap()); let file_size: f64 = convert_to_gb(size.as_u64().unwrap()); diff --git a/src/ui/radarr_ui/movie_details_ui.rs b/src/ui/radarr_ui/movie_details_ui.rs index 2ff0022..c099739 100644 --- a/src/ui/radarr_ui/movie_details_ui.rs +++ b/src/ui/radarr_ui/movie_details_ui.rs @@ -12,7 +12,7 @@ use crate::app::App; use crate::models::radarr_models::{Credit, MovieHistoryItem, Release}; use crate::models::Route; use crate::ui::utils::{ - borderless_block, get_width, layout_block_bottom_border, layout_block_top_border, + borderless_block, get_width_with_margin, layout_block_bottom_border, layout_block_top_border, spans_info_default, style_bold, style_default, style_failure, style_primary, style_success, style_warning, vertical_chunks, }; @@ -393,7 +393,7 @@ fn draw_movie_releases(f: &mut Frame<'_, B>, app: &mut App, content_ } = release; let age = format!("{} days", age.as_u64().unwrap_or(0)); title.scroll_or_reset( - get_width(content_area), + get_width_with_margin(content_area), current_selection == *release && current_route != ActiveRadarrBlock::ManualSearchConfirmPrompt.into(), ); diff --git a/src/ui/utils.rs b/src/ui/utils.rs index 450be6a..ac1c33a 100644 --- a/src/ui/utils.rs +++ b/src/ui/utils.rs @@ -5,38 +5,38 @@ use tui::text::{Span, Spans, Text}; use tui::widgets::{Block, BorderType, Borders, LineGauge, Paragraph, Wrap}; use tui::{symbols, Frame}; -pub fn horizontal_chunks(constraints: Vec, size: Rect) -> Vec { +pub fn horizontal_chunks(constraints: Vec, area: Rect) -> Vec { layout_with_constraints(constraints) .direction(Direction::Horizontal) - .split(size) + .split(area) } pub fn horizontal_chunks_with_margin( constraints: Vec, - size: Rect, + area: Rect, margin: u16, ) -> Vec { layout_with_constraints(constraints) .direction(Direction::Horizontal) .margin(margin) - .split(size) + .split(area) } -pub fn vertical_chunks(constraints: Vec, size: Rect) -> Vec { +pub fn vertical_chunks(constraints: Vec, area: Rect) -> Vec { layout_with_constraints(constraints) .direction(Direction::Vertical) - .split(size) + .split(area) } pub fn vertical_chunks_with_margin( constraints: Vec, - size: Rect, + area: Rect, margin: u16, ) -> Vec { layout_with_constraints(constraints) .direction(Direction::Vertical) .margin(margin) - .split(size) + .split(area) } fn layout_with_constraints(constraints: Vec) -> Layout { @@ -241,6 +241,408 @@ pub fn show_cursor(f: &mut Frame<'_, B>, area: Rect, string: &str) { f.set_cursor(area.x + string.len() as u16 + 1, area.y + 1); } -pub fn get_width(area: Rect) -> usize { +pub fn get_width_with_margin(area: Rect) -> usize { (area.width as f32 * 0.30) as usize } + +#[cfg(test)] +mod test { + use pretty_assertions::assert_eq; + use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; + use tui::style::{Color, Modifier, Style}; + use tui::text::{Span, Spans}; + use tui::widgets::{Block, BorderType, Borders}; + + use crate::ui::utils::{ + borderless_block, centered_rect, get_width_with_margin, horizontal_chunks, + horizontal_chunks_with_margin, layout_block, layout_block_bottom_border, + layout_block_top_border, layout_block_top_border_with_title, layout_block_with_title, + layout_with_constraints, logo_block, spans_info_default, spans_info_primary, + spans_info_with_style, style_bold, style_button_highlight, style_default, style_default_bold, + style_failure, style_help, style_highlight, style_primary, style_secondary, style_success, + style_system_function, style_warning, title_block, title_block_centered, title_style, + vertical_chunks, vertical_chunks_with_margin, + }; + + #[test] + fn test_horizontal_chunks() { + let constraints = [ + Constraint::Percentage(10), + Constraint::Max(20), + Constraint::Min(10), + Constraint::Length(30), + Constraint::Ratio(3, 4), + ]; + let area = rect(); + let expected_layout = Layout::default() + .constraints(constraints) + .direction(Direction::Horizontal) + .split(area); + + assert_eq!(horizontal_chunks(constraints.into(), area), expected_layout); + } + + #[test] + fn test_horizontal_chunks_with_margin() { + let constraints = [ + Constraint::Percentage(10), + Constraint::Max(20), + Constraint::Min(10), + Constraint::Length(30), + Constraint::Ratio(3, 4), + ]; + let area = rect(); + let expected_layout = Layout::default() + .constraints(constraints) + .direction(Direction::Horizontal) + .margin(1) + .split(area); + + assert_eq!( + horizontal_chunks_with_margin(constraints.into(), area, 1), + expected_layout + ); + } + + #[test] + fn test_vertical_chunks() { + let constraints = [ + Constraint::Percentage(10), + Constraint::Max(20), + Constraint::Min(10), + Constraint::Length(30), + Constraint::Ratio(3, 4), + ]; + let area = rect(); + let expected_layout = Layout::default() + .constraints(constraints) + .direction(Direction::Vertical) + .split(area); + + assert_eq!(vertical_chunks(constraints.into(), area), expected_layout); + } + + #[test] + fn test_vertical_chunks_with_margin() { + let constraints = [ + Constraint::Percentage(10), + Constraint::Max(20), + Constraint::Min(10), + Constraint::Length(30), + Constraint::Ratio(3, 4), + ]; + let area = rect(); + let expected_layout = Layout::default() + .constraints(constraints) + .direction(Direction::Vertical) + .margin(1) + .split(area); + + assert_eq!( + vertical_chunks_with_margin(constraints.into(), area, 1), + expected_layout + ); + } + + #[test] + fn test_layout_with_constraints() { + let constraints = [ + Constraint::Percentage(10), + Constraint::Max(20), + Constraint::Min(10), + Constraint::Length(30), + Constraint::Ratio(3, 4), + ]; + let expected_layout = Layout::default().constraints(constraints); + + assert_eq!(layout_with_constraints(constraints.into()), expected_layout); + } + + #[test] + fn test_layout_block() { + assert_eq!( + layout_block(), + Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + ); + } + + #[test] + fn test_layout_block_with_title() { + let title_span = Span::styled( + "title", + Style::default() + .fg(Color::DarkGray) + .add_modifier(Modifier::BOLD), + ); + let expected_block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title(title_span.clone()); + + assert_eq!(layout_block_with_title(title_span), expected_block); + } + + #[test] + fn test_layout_block_top_border_with_title() { + let title_span = Span::styled( + "title", + Style::default() + .fg(Color::DarkGray) + .add_modifier(Modifier::BOLD), + ); + let expected_block = Block::default() + .borders(Borders::TOP) + .title(title_span.clone()); + + assert_eq!( + layout_block_top_border_with_title(title_span), + expected_block + ); + } + + #[test] + fn test_layout_block_top_border() { + assert_eq!( + layout_block_top_border(), + Block::default().borders(Borders::TOP) + ); + } + + #[test] + fn test_layout_block_bottom_border() { + assert_eq!( + layout_block_bottom_border(), + Block::default().borders(Borders::BOTTOM) + ); + } + + #[test] + fn test_borderless_block() { + assert_eq!(borderless_block(), Block::default()); + } + + #[test] + fn test_spans_info_with_style() { + let first_style = Style::default() + .fg(Color::DarkGray) + .add_modifier(Modifier::BOLD); + let second_style = Style::default() + .fg(Color::LightYellow) + .add_modifier(Modifier::ITALIC); + let expected_spans = Spans::from(vec![ + Span::styled("title".to_owned(), first_style), + Span::styled("content".to_owned(), second_style), + ]); + + assert_eq!( + spans_info_with_style( + "title".to_owned(), + "content".to_owned(), + first_style, + second_style + ), + expected_spans + ); + } + + #[test] + fn test_spans_info_default() { + let expected_spans = Spans::from(vec![ + Span::styled( + "title".to_owned(), + Style::default().add_modifier(Modifier::BOLD), + ), + Span::styled("content".to_owned(), Style::default().fg(Color::White)), + ]); + + assert_eq!( + spans_info_default("title".to_owned(), "content".to_owned()), + expected_spans + ); + } + + #[test] + fn test_spans_info_primary() { + let expected_spans = Spans::from(vec![ + Span::styled( + "title".to_owned(), + Style::default() + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD), + ), + Span::styled("content".to_owned(), Style::default().fg(Color::White)), + ]); + + assert_eq!( + spans_info_primary("title".to_owned(), "content".to_owned()), + expected_spans + ); + } + + #[test] + fn test_style_bold() { + assert_eq!(style_bold(), Style::default().add_modifier(Modifier::BOLD)); + } + + #[test] + fn test_style_highlight() { + assert_eq!( + style_highlight(), + Style::default().add_modifier(Modifier::REVERSED) + ); + } + + #[test] + fn test_style_default() { + assert_eq!(style_default(), Style::default().fg(Color::White)); + } + + #[test] + fn test_style_default_bold() { + assert_eq!( + style_default_bold(), + Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD) + ); + } + + #[test] + fn test_style_primary() { + assert_eq!(style_primary(), Style::default().fg(Color::Cyan)); + } + + #[test] + fn test_style_secondary() { + assert_eq!(style_secondary(), Style::default().fg(Color::Yellow)); + } + + #[test] + fn test_style_system_function() { + assert_eq!(style_system_function(), Style::default().fg(Color::Yellow)); + } + + #[test] + fn test_style_success() { + assert_eq!(style_success(), Style::default().fg(Color::Green)); + } + + #[test] + fn test_style_warning() { + assert_eq!(style_warning(), Style::default().fg(Color::Magenta)); + } + + #[test] + fn test_style_failure() { + assert_eq!(style_failure(), Style::default().fg(Color::Red)); + } + + #[test] + fn test_style_help() { + assert_eq!(style_help(), Style::default().fg(Color::LightBlue)); + } + + #[test] + fn test_style_button_highlight_selected() { + let expected_style = Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD); + + assert_eq!(style_button_highlight(true), expected_style); + } + + #[test] + fn test_style_button_highlight_unselected() { + let expected_style = Style::default() + .fg(Color::White) + .add_modifier(Modifier::BOLD); + + assert_eq!(style_button_highlight(false), expected_style); + } + + #[test] + fn test_title_style() { + let expected_span = Span::styled(" test ", Style::default().add_modifier(Modifier::BOLD)); + + assert_eq!(title_style("test"), expected_span); + } + + #[test] + fn test_title_block() { + let expected_block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title(Span::styled( + " test ", + Style::default().add_modifier(Modifier::BOLD), + )); + + assert_eq!(title_block("test"), expected_block); + } + + #[test] + fn test_title_block_centered() { + let expected_block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title(Span::styled( + " test ", + Style::default().add_modifier(Modifier::BOLD), + )) + .title_alignment(Alignment::Center); + + assert_eq!(title_block_centered("test"), expected_block); + } + + #[test] + fn test_logo_block() { + let expected_block = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title(Span::styled( + " Managarr - A Servarr management TUI ", + Style::default() + .fg(Color::Magenta) + .add_modifier(Modifier::BOLD) + .add_modifier(Modifier::ITALIC), + )); + + assert_eq!(logo_block(), expected_block); + } + + #[test] + fn test_centered_rect() { + let expected_rect = Rect { + x: 30, + y: 45, + width: 60, + height: 90, + }; + + assert_eq!(centered_rect(50, 50, rect()), expected_rect); + } + + #[test] + fn test_get_width_with_margin() { + assert_eq!( + get_width_with_margin(Rect { + x: 0, + y: 0, + width: 100, + height: 10 + }), + 30 + ); + } + + fn rect() -> Rect { + Rect { + x: 0, + y: 0, + width: 120, + height: 180, + } + } +}