feat: Added support for a system-wide notification popup mechanism that works across Servarrs

This commit is contained in:
2026-02-03 17:03:12 -07:00
parent 447cf6a2b4
commit af573cac2a
16 changed files with 587 additions and 21 deletions
+3
View File
@@ -13,6 +13,7 @@ use tokio_util::sync::CancellationToken;
use veil::Redact;
use crate::cli::Command;
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LidarrData};
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, RadarrData};
use crate::models::servarr_data::sonarr::sonarr_data::{ActiveSonarrBlock, SonarrData};
@@ -38,6 +39,7 @@ pub struct App<'a> {
pub server_tabs: TabState,
pub keymapping_table: Option<StatefulTable<KeybindingItem>>,
pub error: HorizontallyScrollableText,
pub notification: Option<Notification>,
pub tick_until_poll: u64,
pub ticks_until_scroll: u64,
pub tick_count: u64,
@@ -254,6 +256,7 @@ impl Default for App<'_> {
cancellation_token: CancellationToken::new(),
keymapping_table: None,
error: HorizontallyScrollableText::default(),
notification: None,
is_first_render: true,
server_tabs: TabState::new(Vec::new()),
tick_until_poll: 400,
+34 -1
View File
@@ -20,10 +20,10 @@ mod tests {
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, Notification};
use crate::models::servarr_models::KeybindingItem;
use crate::models::stateful_table::StatefulTable;
@@ -284,6 +284,39 @@ mod tests {
);
}
#[test]
fn test_handle_events_esc_clears_notification() {
let mut app = App::test_default();
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
handle_events(DEFAULT_KEYBINDINGS.esc.key, &mut app);
assert_none!(app.notification);
assert_eq!(app.get_current_route(), ActiveRadarrBlock::Movies.into());
}
#[test]
fn test_handle_events_esc_does_not_clear_notification_when_none() {
let mut app = App::test_default();
app
.data
.radarr_data
.movies
.set_items(vec![Movie::default()]);
app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
app.push_navigation_stack(ActiveRadarrBlock::SearchMovie.into());
handle_events(DEFAULT_KEYBINDINGS.esc.key, &mut app);
assert_none!(app.notification);
assert_navigation_popped!(app, ActiveRadarrBlock::Movies.into());
}
fn context_clue_to_keybinding_item(key: &KeyBinding, desc: &&str) -> KeybindingItem {
let (key, alt_key) = if key.alt.is_some() {
(key.key.to_string(), key.alt.as_ref().unwrap().to_string())
+7 -2
View File
@@ -2,11 +2,11 @@ use lidarr_handlers::LidarrHandler;
use radarr_handlers::RadarrHandler;
use sonarr_handlers::SonarrHandler;
use crate::app::App;
use crate::app::context_clues::{
ContextClueProvider, SERVARR_CONTEXT_CLUES, ServarrContextClueProvider,
ContextClueProvider, ServarrContextClueProvider, SERVARR_CONTEXT_CLUES,
};
use crate::app::key_binding::KeyBinding;
use crate::app::App;
use crate::event::Key;
use crate::handlers::keybinding_handler::KeybindingHandler;
use crate::matches_key;
@@ -116,6 +116,7 @@ pub fn handle_events(key: Key, app: &mut App<'_>) {
} else {
app.keymapping_table = None;
}
} else if matches_key!(esc, key) && handle_clear_notification(app) {
} else {
match app.get_current_route() {
_ if app.keymapping_table.is_some() => {
@@ -183,6 +184,10 @@ fn handle_clear_errors(app: &mut App<'_>) {
}
}
fn handle_clear_notification(app: &mut App<'_>) -> bool {
app.notification.take().is_some()
}
fn handle_prompt_toggle(app: &mut App<'_>, key: Key) {
match key {
_ if matches_key!(left, key) || matches_key!(right, key) => match app.get_current_route() {
+17
View File
@@ -10,6 +10,23 @@ pub(in crate::models::servarr_data) mod data_test_utils;
#[cfg(test)]
mod servarr_data_tests;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Notification {
pub title: String,
pub message: String,
pub success: bool,
}
impl Notification {
pub fn new(title: String, message: String, success: bool) -> Self {
Self {
title,
message,
success,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum ActiveKeybindingBlock {
#[default]
@@ -1,8 +1,10 @@
#[cfg(test)]
mod tests {
use crate::models::lidarr_models::LidarrReleaseDownloadBody;
use crate::models::servarr_data::Notification;
use crate::network::lidarr_network::LidarrEvent;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::network_tests::test_utils::{test_network, MockServarrApi};
use pretty_assertions::assert_eq;
use serde_json::json;
#[tokio::test]
@@ -30,5 +32,51 @@ mod tests {
mock.assert_async().await;
assert_ok!(result);
assert_eq!(
app.lock().await.notification,
Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
))
);
}
#[tokio::test]
async fn test_handle_download_lidarr_release_event_sets_failure_notification_on_error() {
let params = LidarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
};
let (mock, app, _server) = MockServarrApi::post()
.with_request_body(json!({
"guid": "1234",
"indexerId": 2,
}))
.returns(json!({}))
.status(500)
.build_for(LidarrEvent::DownloadRelease(params.clone()))
.await;
app.lock().await.server_tabs.set_index(2);
let mut network = test_network(&app);
let result = network
.handle_lidarr_event(LidarrEvent::DownloadRelease(params))
.await;
mock.assert_async().await;
assert_err!(result);
let app = app.lock().await;
assert_is_empty!(app.error.text);
assert_some_eq_x!(
&app.notification,
&Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
)
);
}
}
+22 -3
View File
@@ -1,4 +1,5 @@
use crate::models::lidarr_models::LidarrReleaseDownloadBody;
use crate::models::servarr_data::Notification;
use crate::network::lidarr_network::LidarrEvent;
use crate::network::{Network, RequestMethod};
use anyhow::Result;
@@ -31,8 +32,26 @@ impl Network<'_, '_> {
)
.await;
self
.handle_request::<LidarrReleaseDownloadBody, Value>(request_props, |_, _| ())
.await
let result = self
.handle_request::<LidarrReleaseDownloadBody, Value>(request_props, |_, mut app| {
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
})
.await;
if result.is_err() {
let mut app = self.app.lock().await;
std::mem::take(&mut app.error.text);
app.notification = Some(Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
));
}
result
}
}
+22 -3
View File
@@ -3,6 +3,7 @@ use crate::models::radarr_models::{
EditMovieParams, Movie, MovieCommandBody, MovieHistoryItem, RadarrRelease,
RadarrReleaseDownloadBody,
};
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::stateful_table::StatefulTable;
@@ -85,9 +86,27 @@ impl Network<'_, '_> {
.request_props_from(event, RequestMethod::Post, Some(params), None, None)
.await;
self
.handle_request::<RadarrReleaseDownloadBody, Value>(request_props, |_, _| ())
.await
let result = self
.handle_request::<RadarrReleaseDownloadBody, Value>(request_props, |_, mut app| {
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
})
.await;
if result.is_err() {
let mut app = self.app.lock().await;
std::mem::take(&mut app.error.text);
app.notification = Some(Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
));
}
result
}
pub(in crate::network::radarr_network) async fn edit_movie(
@@ -4,6 +4,7 @@ mod tests {
AddMovieBody, AddMovieOptions, Credit, DeleteMovieParams, DownloadRecord, EditMovieParams,
MinimumAvailability, Movie, MovieHistoryItem, MovieMonitor, RadarrReleaseDownloadBody,
};
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::radarr::modals::MovieDetailsModal;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::stateful_table::SortOption;
@@ -164,14 +165,58 @@ mod tests {
.await;
let mut network = test_network(&app);
assert!(
network
.handle_radarr_event(RadarrEvent::DownloadRelease(expected_body))
.await
.is_ok()
);
let result = network
.handle_radarr_event(RadarrEvent::DownloadRelease(expected_body))
.await;
mock.assert_async().await;
assert_ok!(result);
assert_some_eq_x!(
&app.lock().await.notification,
&Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
)
);
}
#[tokio::test]
async fn test_handle_download_radarr_release_event_sets_failure_notification_on_error() {
let expected_body = RadarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
movie_id: 1,
};
let body = json!({
"guid": "1234",
"indexerId": 2,
"movieId": 1
});
let (mock, app, _server) = MockServarrApi::post()
.with_request_body(body)
.returns(json!({}))
.status(500)
.build_for(RadarrEvent::DownloadRelease(expected_body.clone()))
.await;
let mut network = test_network(&app);
let result = network
.handle_radarr_event(RadarrEvent::DownloadRelease(expected_body))
.await;
mock.assert_async().await;
assert_err!(result);
let app = app.lock().await;
assert_is_empty!(app.error.text);
assert_some_eq_x!(
&app.notification,
&Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
)
);
}
#[tokio::test]
+22 -3
View File
@@ -1,3 +1,4 @@
use crate::models::servarr_data::Notification;
use crate::models::sonarr_models::SonarrReleaseDownloadBody;
use crate::network::sonarr_network::SonarrEvent;
use crate::network::{Network, RequestMethod};
@@ -31,8 +32,26 @@ impl Network<'_, '_> {
)
.await;
self
.handle_request::<SonarrReleaseDownloadBody, Value>(request_props, |_, _| ())
.await
let result = self
.handle_request::<SonarrReleaseDownloadBody, Value>(request_props, |_, mut app| {
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
})
.await;
if result.is_err() {
let mut app = self.app.lock().await;
std::mem::take(&mut app.error.text);
app.notification = Some(Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
));
}
result
}
}
@@ -1,8 +1,10 @@
#[cfg(test)]
mod tests {
use crate::models::servarr_data::Notification;
use crate::models::sonarr_models::SonarrReleaseDownloadBody;
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
use crate::network::network_tests::test_utils::{test_network, MockServarrApi};
use crate::network::sonarr_network::SonarrEvent;
use pretty_assertions::assert_eq;
use serde_json::json;
#[tokio::test]
@@ -33,5 +35,54 @@ mod tests {
mock.assert_async().await;
assert_ok!(result);
assert_eq!(
app.lock().await.notification,
Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
))
);
}
#[tokio::test]
async fn test_handle_download_sonarr_release_event_sets_failure_notification_on_error() {
let params = SonarrReleaseDownloadBody {
guid: "1234".to_owned(),
indexer_id: 2,
series_id: Some(1),
..SonarrReleaseDownloadBody::default()
};
let (mock, app, _server) = MockServarrApi::post()
.with_request_body(json!({
"guid": "1234",
"indexerId": 2,
"seriesId": 1,
}))
.returns(json!({}))
.status(500)
.build_for(SonarrEvent::DownloadRelease(params.clone()))
.await;
app.lock().await.server_tabs.next();
let mut network = test_network(&app);
let result = network
.handle_sonarr_event(SonarrEvent::DownloadRelease(params))
.await;
mock.assert_async().await;
assert_err!(result);
let app = app.lock().await;
assert_is_empty!(app.error.text);
assert_some_eq_x!(
&app.notification,
&Notification::new(
"Download Failed".to_owned(),
"Download request failed. Check the logs for more details.".to_owned(),
false,
)
);
}
}
+23 -1
View File
@@ -14,6 +14,7 @@ use sonarr_ui::SonarrUi;
use utils::layout_block;
use crate::app::App;
use crate::models::servarr_data::Notification;
use crate::models::servarr_models::KeybindingItem;
use crate::models::{HorizontallyScrollableText, Route, TabState};
use crate::ui::radarr_ui::RadarrUi;
@@ -25,7 +26,8 @@ use crate::ui::utils::{
};
use crate::ui::widgets::input_box::InputBox;
use crate::ui::widgets::managarr_table::ManagarrTable;
use crate::ui::widgets::popup::Size;
use crate::ui::widgets::message::Message;
use crate::ui::widgets::popup::{Popup, Size};
mod builtin_themes;
mod lidarr_ui;
@@ -95,6 +97,10 @@ pub fn ui(f: &mut Frame<'_>, app: &mut App<'_>) {
_ => (),
}
if let Some(notification) = &app.notification {
draw_notification_popup(f, notification);
}
if app.keymapping_table.is_some() {
draw_help_popup(f, app);
}
@@ -183,6 +189,22 @@ pub fn draw_help_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
f.render_widget(keymapping_table, table_area);
}
fn draw_notification_popup(f: &mut Frame<'_>, notification: &Notification) {
let style = if notification.success {
styles::success_style().bold()
} else {
styles::failure_style().bold()
};
let popup = Popup::new(
Message::new(notification.message.as_str())
.title(notification.title.as_str())
.style(style),
)
.size(Size::Message);
f.render_widget(popup, f.area());
}
fn draw_tabs(f: &mut Frame<'_>, area: Rect, title: &str, tab_state: &TabState) -> Rect {
if title.is_empty() {
f.render_widget(layout_block().default_color(), area);
@@ -0,0 +1,54 @@
---
source: src/ui/ui_tests.rs
expression: output
---
╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Radarr │ Sonarr │ Lidarr <?> to open help│
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮
│Lidarr Version: 1.2.3.4 ││Test download title ││ ⠀⠀⠀⣠⣴⣶⡿⠻⣿⣶⣦⣄⠀⠀⠀ │
│Uptime: 0d 00:00:44 ││50% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ⠀⢠⣾⠟⠋⠀⠀⢀⣀⠀⠙⠻⣷⡄⠀ │
│Storage: ││ ││ ⢠⣿⠋⠀⣴⠃⠀⢸⣿⣿⣦⡀⠙⣿⡄ │
│/path: 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ││ ⣾⡟⠀⢸⠃⠀⠀⠀⠈⠉⠉⠁⠀⢹⣷ │
│Root Folders: ││ ││ ⢿⣧⠀⠈⠀⠀⠀⠀⠀⠀⢀⡟⠀⣸⡿ │
│/nfs: 204800.00 GB free ││ ││ ⠘⣿⣄⠀⠻⣿⣿⡇⠀⢀⠞⠀⣠⣿⠃ │
│ ││ ││ ⠀⠘⢿⣦⣄⠀⠉⠁⠀⠀⣠⣴⡿⠃⠀ │
│ ││ ││ ⠀⠀⠀⠉⠻⠿⢿⡆⡾⠿⠟⠉⠀⠀⠀ │
╰───────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────╯╰──────────────────╯
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Library │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags │
│=> Alex Person Continuing Lossless Standard 1 15/15 0.00 GB 🏷 alex │
│ │
│ │
│ │
│ │
│ │
│ ╭────────── Download Result ──────────╮ │
│ │ Download request sent successfully │ │
│ │ │ │
│ ╰───────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,54 @@
---
source: src/ui/ui_tests.rs
expression: output
---
╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Radarr │ Sonarr │ Lidarr <?> to open help│
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮
│Radarr Version: 1.2.3.4 ││Test Download Title ││ ⠀⣠⣶⢶⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀ │
│Uptime: 0d 00:00:44 ││50% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ⠀⣿⡇⠀⠈⠙⠻⢿⣶⣤⡀⠀⠀⠀⠀ │
│Storage: ││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⠈⠙⠻⢷⣦⡄⠀ │
│/path: 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢉⠻⠀ │
│Root Folders: ││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⢀⣠⣴⣾⠿⠀⠀ │
│/nfs: 204800.00 GB free ││ ││ ⠀⢿⡇⠀⠀⣀⣤⣶⡿⠛⠉⠀⠀⠀⠀ │
│ ││ ││ ⠀⠀⠰⠶⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀ │
│ ││ ││ │
╰───────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────╯╰──────────────────╯
╭ Movies ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Library │ Collections │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ Title ▼ Year Studio Runtime Rating Language Size Quality Profile Monitored Tags │
│=> Test 2023 21st Century Alex 2h 0m R English 3.30 GB HD - 1080p 🏷 alex │
│ │
│ │
│ │
│ │
│ │
│ ╭────────── Download Failed ──────────╮ │
│ │ Request failed. Received 500 response │ │
│ │ code │ │
│ ╰───────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,54 @@
---
source: src/ui/ui_tests.rs
expression: output
---
╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Radarr │ Sonarr │ Lidarr <?> to open help│
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮
│Radarr Version: 1.2.3.4 ││Test Download Title ││ ⠀⣠⣶⢶⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀ │
│Uptime: 0d 00:00:44 ││50% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ⠀⣿⡇⠀⠈⠙⠻⢿⣶⣤⡀⠀⠀⠀⠀ │
│Storage: ││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⠈⠙⠻⢷⣦⡄⠀ │
│/path: 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢉⠻⠀ │
│Root Folders: ││ ││ ⠀⣿⡇⠀⠀⠀⠀⠀⢀⣠⣴⣾⠿⠀⠀ │
│/nfs: 204800.00 GB free ││ ││ ⠀⢿⡇⠀⠀⣀⣤⣶⡿⠛⠉⠀⠀⠀⠀ │
│ ││ ││ ⠀⠀⠰⠶⡿⠟⠋⠁⠀⠀⠀⠀⠀⠀⠀ │
│ ││ ││ │
╰───────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────╯╰──────────────────╯
╭ Movies ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Library │ Collections │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ Title ▼ Year Studio Runtime Rating Language Size Quality Profile Monitored Tags │
│=> Test 2023 21st Century Alex 2h 0m R English 3.30 GB HD - 1080p 🏷 alex │
│ │
│ │
│ │
│ │
│ │
│ ╭────────── Download Result ──────────╮ │
│ │ Download request sent successfully │ │
│ │ │ │
│ ╰───────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
@@ -0,0 +1,54 @@
---
source: src/ui/ui_tests.rs
expression: output
---
╭ Managarr - A Servarr management TUI ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Radarr │ Sonarr │ Lidarr <?> to open help│
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭ Stats ──────────────────────────────────────────────────────────────╮╭ Downloads ─────────────────────────────────────────────────────────╮╭──────────────────╮
│Sonarr Version: 1.2.3.4 ││Test Download Title ││ ⠀⠀⠀⣠⣴⣶⣿⣿⣿⣿⣶⣦⣄⠀⠀⠀ │
│Uptime: 0d 00:00:44 ││50% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ⠀⣠⡀⠈⠻⣿⣿⣿⣿⣿⣿⠟⠁⢀⣀⠀ │
│Storage: ││ ││ ⢰⣿⣿⣦⠐⠄⠉⠉⠉⠉⠠⠂⣰⣿⣿⡆ │
│/path: 100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━││ ││ ⣿⣿⣿⣿⡆⠀⣴⣿⣿⣦⠀⢰⣿⣿⣿⣿ │
│Root Folders: ││ ││ ⣿⣿⣿⣿⡇⠀⠻⣿⣿⠟⠀⠸⣿⣿⣿⣿ │
│/nfs: 204800.00 GB free ││ ││ ⠸⣿⣿⠟⠠⠂⠀⢀⡀⠀⠐⠄⠻⣿⣿⠇ │
│ ││ ││ ⠀⠙⠁⢀⣴⣾⣿⣿⣿⣿⣷⣦⡀⠈⠋⠀ │
│ ││ ││ ⠀⠀⠀⠘⠻⠿⣿⣿⣿⣿⠿⠟⠋⠀⠀⠀ │
╰───────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────╯╰──────────────────╯
╭ Series ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Library │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
│ Title ▼ Year Network Status Rating Type Quality Profile Language Size Monitored Tags │
│=> Test 2022 HBO Continuin TV-MA Standard Bluray-1080p English 59.51 GB 🏷 │
│ │
│ │
│ │
│ │
│ │
│ ╭────────── Download Result ──────────╮ │
│ │ Download request sent successfully │ │
│ │ │ │
│ ╰───────────────────────────────────────╯ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+69
View File
@@ -2,6 +2,7 @@
mod snapshot_tests {
use crate::app::App;
use crate::handlers::populate_keymapping_table;
use crate::models::servarr_data::Notification;
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
use crate::models::servarr_data::sonarr::sonarr_data::ActiveSonarrBlock;
@@ -46,6 +47,40 @@ mod snapshot_tests {
insta::assert_snapshot!(output);
}
#[test]
fn test_radarr_ui_renders_notification_success_popup() {
let mut app = App::test_default_fully_populated();
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
app.push_navigation_stack(ActiveRadarrBlock::default().into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
ui(f, app);
});
insta::assert_snapshot!(output);
}
#[test]
fn test_radarr_ui_renders_notification_failure_popup() {
let mut app = App::test_default_fully_populated();
app.notification = Some(Notification::new(
"Download Failed".to_owned(),
"Request failed. Received 500 response code".to_owned(),
false,
));
app.push_navigation_stack(ActiveRadarrBlock::default().into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
ui(f, app);
});
insta::assert_snapshot!(output);
}
#[test]
fn test_sonarr_ui_renders_library_tab() {
let mut app = App::test_default_fully_populated();
@@ -84,6 +119,23 @@ mod snapshot_tests {
insta::assert_snapshot!(output);
}
#[test]
fn test_sonarr_ui_renders_notification_success_popup() {
let mut app = App::test_default_fully_populated();
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
app.push_navigation_stack(ActiveSonarrBlock::default().into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
ui(f, app);
});
insta::assert_snapshot!(output);
}
#[test]
fn test_lidarr_ui_renders_library_tab() {
let mut app = App::test_default_fully_populated();
@@ -109,6 +161,23 @@ mod snapshot_tests {
insta::assert_snapshot!(output);
}
#[test]
fn test_lidarr_ui_renders_notification_success_popup() {
let mut app = App::test_default_fully_populated();
app.notification = Some(Notification::new(
"Download Result".to_owned(),
"Download request sent successfully".to_owned(),
true,
));
app.push_navigation_stack(ActiveLidarrBlock::default().into());
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
ui(f, app);
});
insta::assert_snapshot!(output);
}
#[test]
fn test_lidarr_ui_renders_library_tab_error_popup() {
let mut app = App::test_default_fully_populated();