feat: Support for updating all Lidarr artists in both the CLI and TUI
This commit is contained in:
@@ -7,7 +7,7 @@ use crate::models::Route;
|
|||||||
#[path = "lidarr_context_clues_tests.rs"]
|
#[path = "lidarr_context_clues_tests.rs"]
|
||||||
mod lidarr_context_clues_tests;
|
mod lidarr_context_clues_tests;
|
||||||
|
|
||||||
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 7] = [
|
pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 8] = [
|
||||||
(
|
(
|
||||||
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
DEFAULT_KEYBINDINGS.toggle_monitoring,
|
||||||
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
|
DEFAULT_KEYBINDINGS.toggle_monitoring.desc,
|
||||||
@@ -20,6 +20,7 @@ pub static ARTISTS_CONTEXT_CLUES: [ContextClue; 7] = [
|
|||||||
DEFAULT_KEYBINDINGS.refresh,
|
DEFAULT_KEYBINDINGS.refresh,
|
||||||
DEFAULT_KEYBINDINGS.refresh.desc,
|
DEFAULT_KEYBINDINGS.refresh.desc,
|
||||||
),
|
),
|
||||||
|
(DEFAULT_KEYBINDINGS.update, "update all"),
|
||||||
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
(DEFAULT_KEYBINDINGS.esc, "cancel filter"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ mod tests {
|
|||||||
DEFAULT_KEYBINDINGS.refresh.desc
|
DEFAULT_KEYBINDINGS.refresh.desc
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
artists_context_clues_iter.next(),
|
||||||
|
&(DEFAULT_KEYBINDINGS.update, "update all")
|
||||||
|
);
|
||||||
assert_some_eq_x!(
|
assert_some_eq_x!(
|
||||||
artists_context_clues_iter.next(),
|
artists_context_clues_iter.next(),
|
||||||
&(DEFAULT_KEYBINDINGS.esc, "cancel filter")
|
&(DEFAULT_KEYBINDINGS.esc, "cancel filter")
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ mod tests {
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
use crate::cli::lidarr::get_command_handler::LidarrGetCommand;
|
||||||
|
use crate::cli::lidarr::refresh_command_handler::LidarrRefreshCommand;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
cli::{
|
cli::{
|
||||||
@@ -170,6 +171,28 @@ mod tests {
|
|||||||
assert_ok!(&result);
|
assert_ok!(&result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_lidarr_cli_handler_delegates_refresh_commands_to_the_refresh_command_handler() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(LidarrEvent::UpdateAllArtists.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let refresh_series_command = LidarrCommand::Refresh(LidarrRefreshCommand::AllArtists);
|
||||||
|
|
||||||
|
let result = LidarrCliHandler::with(&app_arc, refresh_series_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_toggle_artist_monitoring_command() {
|
async fn test_toggle_artist_monitoring_command() {
|
||||||
let mut mock_network = MockNetworkTrait::new();
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use clap::{Subcommand, arg};
|
|||||||
use delete_command_handler::{LidarrDeleteCommand, LidarrDeleteCommandHandler};
|
use delete_command_handler::{LidarrDeleteCommand, LidarrDeleteCommandHandler};
|
||||||
use get_command_handler::{LidarrGetCommand, LidarrGetCommandHandler};
|
use get_command_handler::{LidarrGetCommand, LidarrGetCommandHandler};
|
||||||
use list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
use list_command_handler::{LidarrListCommand, LidarrListCommandHandler};
|
||||||
|
use refresh_command_handler::{LidarrRefreshCommand, LidarrRefreshCommandHandler};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
@@ -15,6 +16,7 @@ use super::{CliCommandHandler, Command};
|
|||||||
mod delete_command_handler;
|
mod delete_command_handler;
|
||||||
mod get_command_handler;
|
mod get_command_handler;
|
||||||
mod list_command_handler;
|
mod list_command_handler;
|
||||||
|
mod refresh_command_handler;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "lidarr_command_tests.rs"]
|
#[path = "lidarr_command_tests.rs"]
|
||||||
@@ -37,6 +39,11 @@ pub enum LidarrCommand {
|
|||||||
about = "Commands to list attributes from your Lidarr instance"
|
about = "Commands to list attributes from your Lidarr instance"
|
||||||
)]
|
)]
|
||||||
List(LidarrListCommand),
|
List(LidarrListCommand),
|
||||||
|
#[command(
|
||||||
|
subcommand,
|
||||||
|
about = "Commands to refresh the data in your Lidarr instance"
|
||||||
|
)]
|
||||||
|
Refresh(LidarrRefreshCommand),
|
||||||
#[command(
|
#[command(
|
||||||
about = "Toggle monitoring for the specified artist corresponding to the given artist ID"
|
about = "Toggle monitoring for the specified artist corresponding to the given artist ID"
|
||||||
)]
|
)]
|
||||||
@@ -92,6 +99,11 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrCommand> for LidarrCliHandler<'a, '
|
|||||||
.handle()
|
.handle()
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
|
LidarrCommand::Refresh(refresh_command) => {
|
||||||
|
LidarrRefreshCommandHandler::with(self.app, refresh_command, self.network)
|
||||||
|
.handle()
|
||||||
|
.await?
|
||||||
|
}
|
||||||
LidarrCommand::ToggleArtistMonitoring { artist_id } => {
|
LidarrCommand::ToggleArtistMonitoring { artist_id } => {
|
||||||
let resp = self
|
let resp = self
|
||||||
.network
|
.network
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::App,
|
||||||
|
cli::{CliCommandHandler, Command},
|
||||||
|
network::{NetworkTrait, lidarr_network::LidarrEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::LidarrCommand;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "refresh_command_handler_tests.rs"]
|
||||||
|
mod refresh_command_handler_tests;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Subcommand)]
|
||||||
|
pub enum LidarrRefreshCommand {
|
||||||
|
#[command(about = "Refresh all artist data for all artists in your Lidarr library")]
|
||||||
|
AllArtists,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LidarrRefreshCommand> for Command {
|
||||||
|
fn from(value: LidarrRefreshCommand) -> Self {
|
||||||
|
Command::Lidarr(LidarrCommand::Refresh(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct LidarrRefreshCommandHandler<'a, 'b> {
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: LidarrRefreshCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrRefreshCommand>
|
||||||
|
for LidarrRefreshCommandHandler<'a, 'b>
|
||||||
|
{
|
||||||
|
fn with(
|
||||||
|
_app: &'a Arc<Mutex<App<'b>>>,
|
||||||
|
command: LidarrRefreshCommand,
|
||||||
|
network: &'a mut dyn NetworkTrait,
|
||||||
|
) -> Self {
|
||||||
|
LidarrRefreshCommandHandler {
|
||||||
|
_app,
|
||||||
|
command,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle(self) -> anyhow::Result<String> {
|
||||||
|
let result = match self.command {
|
||||||
|
LidarrRefreshCommand::AllArtists => {
|
||||||
|
let resp = self
|
||||||
|
.network
|
||||||
|
.handle_network_event(LidarrEvent::UpdateAllArtists.into())
|
||||||
|
.await?;
|
||||||
|
serde_json::to_string_pretty(&resp)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use crate::Cli;
|
||||||
|
use crate::cli::{
|
||||||
|
Command,
|
||||||
|
lidarr::{LidarrCommand, refresh_command_handler::LidarrRefreshCommand},
|
||||||
|
};
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_refresh_command_from() {
|
||||||
|
let command = LidarrRefreshCommand::AllArtists;
|
||||||
|
|
||||||
|
let result = Command::from(command.clone());
|
||||||
|
|
||||||
|
assert_eq!(result, Command::Lidarr(LidarrCommand::Refresh(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mod cli {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_refresh_all_artists_has_no_arg_requirements() {
|
||||||
|
let result =
|
||||||
|
Cli::command().try_get_matches_from(["managarr", "lidarr", "refresh", "all-artists"]);
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod handler {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mockall::predicate::eq;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::{app::App, cli::lidarr::refresh_command_handler::LidarrRefreshCommandHandler};
|
||||||
|
use crate::{
|
||||||
|
cli::{CliCommandHandler, lidarr::refresh_command_handler::LidarrRefreshCommand},
|
||||||
|
network::lidarr_network::LidarrEvent,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
models::{Serdeable, lidarr_models::LidarrSerdeable},
|
||||||
|
network::{MockNetworkTrait, NetworkEvent},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_refresh_all_artists_command() {
|
||||||
|
let mut mock_network = MockNetworkTrait::new();
|
||||||
|
mock_network
|
||||||
|
.expect_handle_network_event()
|
||||||
|
.with(eq::<NetworkEvent>(LidarrEvent::UpdateAllArtists.into()))
|
||||||
|
.times(1)
|
||||||
|
.returning(|_| {
|
||||||
|
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||||
|
json!({"testResponse": "response"}),
|
||||||
|
)))
|
||||||
|
});
|
||||||
|
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||||
|
let refresh_command = LidarrRefreshCommand::AllArtists;
|
||||||
|
|
||||||
|
let result = LidarrRefreshCommandHandler::with(&app_arc, refresh_command, &mut mock_network)
|
||||||
|
.handle()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_ok!(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,18 +2,18 @@
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use pretty_assertions::assert_str_eq;
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
use serde_json::Number;
|
use serde_json::Number;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||||
use crate::assert_modal_absent;
|
|
||||||
use crate::handlers::KeyEventHandler;
|
use crate::handlers::KeyEventHandler;
|
||||||
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
|
use crate::handlers::lidarr_handlers::library::{LibraryHandler, artists_sorting_options};
|
||||||
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
|
use crate::models::lidarr_models::{Artist, ArtistStatistics, ArtistStatus};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
|
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS};
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
|
use crate::{assert_modal_absent, assert_navigation_popped, assert_navigation_pushed};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_library_handler_accepts() {
|
fn test_library_handler_accepts() {
|
||||||
@@ -267,6 +267,173 @@ mod tests {
|
|||||||
assert!(!app.is_routing);
|
assert!(!app.is_routing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::Artists,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_pushed!(app, ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_key_no_op_when_not_ready() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.is_loading = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.update.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::Artists,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_prompt_confirm_submit() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.submit.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert!(app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&app.data.lidarr_data.prompt_confirm_action,
|
||||||
|
&LidarrEvent::UpdateAllArtists
|
||||||
|
);
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::Artists.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_prompt_decline_submit() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.submit.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_none!(app.data.lidarr_data.prompt_confirm_action);
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::Artists.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_prompt_esc() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.esc.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::Artists.into());
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_prompt_left_right() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.left.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert!(app.data.lidarr_data.prompt_confirm);
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.right.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_update_all_artists_prompt_confirm_key() {
|
||||||
|
let mut app = App::test_default();
|
||||||
|
app
|
||||||
|
.data
|
||||||
|
.lidarr_data
|
||||||
|
.artists
|
||||||
|
.set_items(vec![Artist::default()]);
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
|
||||||
|
LibraryHandler::new(
|
||||||
|
DEFAULT_KEYBINDINGS.confirm.key,
|
||||||
|
&mut app,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.handle();
|
||||||
|
|
||||||
|
assert!(app.data.lidarr_data.prompt_confirm);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&app.data.lidarr_data.prompt_confirm_action,
|
||||||
|
&LidarrEvent::UpdateAllArtists
|
||||||
|
);
|
||||||
|
assert_navigation_popped!(app, ActiveLidarrBlock::Artists.into());
|
||||||
|
}
|
||||||
|
|
||||||
fn artists_vec() -> Vec<Artist> {
|
fn artists_vec() -> Vec<Artist> {
|
||||||
vec![
|
vec![
|
||||||
Artist {
|
Artist {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
event::Key,
|
event::Key,
|
||||||
handlers::{KeyEventHandler, handle_clear_errors},
|
handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle},
|
||||||
matches_key,
|
matches_key,
|
||||||
models::{
|
models::{
|
||||||
BlockSelectionState,
|
BlockSelectionState,
|
||||||
@@ -108,21 +108,39 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_left_right_action(&mut self) {
|
fn handle_left_right_action(&mut self) {
|
||||||
if self.active_lidarr_block == ActiveLidarrBlock::Artists {
|
match self.active_lidarr_block {
|
||||||
handle_change_tab_left_right_keys(self.app, self.key);
|
ActiveLidarrBlock::Artists => handle_change_tab_left_right_keys(self.app, self.key),
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt => handle_prompt_toggle(self.app, self.key),
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_submit(&mut self) {}
|
fn handle_submit(&mut self) {
|
||||||
|
if self.active_lidarr_block == ActiveLidarrBlock::UpdateAllArtistsPrompt {
|
||||||
|
if self.app.data.lidarr_data.prompt_confirm {
|
||||||
|
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::UpdateAllArtists);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_esc(&mut self) {
|
fn handle_esc(&mut self) {
|
||||||
handle_clear_errors(self.app);
|
match self.active_lidarr_block {
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt => {
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
self.app.data.lidarr_data.prompt_confirm = false;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
handle_clear_errors(self.app);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_char_key_event(&mut self) {
|
fn handle_char_key_event(&mut self) {
|
||||||
let key = self.key;
|
let key = self.key;
|
||||||
if self.active_lidarr_block == ActiveLidarrBlock::Artists {
|
match self.active_lidarr_block {
|
||||||
match key {
|
ActiveLidarrBlock::Artists => match key {
|
||||||
_ if matches_key!(toggle_monitoring, key) => {
|
_ if matches_key!(toggle_monitoring, key) => {
|
||||||
self.app.data.lidarr_data.prompt_confirm = true;
|
self.app.data.lidarr_data.prompt_confirm = true;
|
||||||
self.app.data.lidarr_data.prompt_confirm_action = Some(
|
self.app.data.lidarr_data.prompt_confirm_action = Some(
|
||||||
@@ -133,11 +151,25 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LibraryHandler<'a, '
|
|||||||
.app
|
.app
|
||||||
.pop_and_push_navigation_stack(self.active_lidarr_block.into());
|
.pop_and_push_navigation_stack(self.active_lidarr_block.into());
|
||||||
}
|
}
|
||||||
|
_ if matches_key!(update, key) => {
|
||||||
|
self
|
||||||
|
.app
|
||||||
|
.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
}
|
||||||
_ if matches_key!(refresh, key) => {
|
_ if matches_key!(refresh, key) => {
|
||||||
self.app.should_refresh = true;
|
self.app.should_refresh = true;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
},
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt => {
|
||||||
|
if matches_key!(confirm, key) {
|
||||||
|
self.app.data.lidarr_data.prompt_confirm = true;
|
||||||
|
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::UpdateAllArtists);
|
||||||
|
|
||||||
|
self.app.pop_navigation_stack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ impl<'a> Default for LidarrData<'a> {
|
|||||||
quality_profile_map: BiMap::new(),
|
quality_profile_map: BiMap::new(),
|
||||||
root_folders: StatefulTable::default(),
|
root_folders: StatefulTable::default(),
|
||||||
selected_block: BlockSelectionState::default(),
|
selected_block: BlockSelectionState::default(),
|
||||||
start_time: Utc::now(),
|
start_time: DateTime::default(),
|
||||||
tags_map: BiMap::new(),
|
tags_map: BiMap::new(),
|
||||||
version: String::new(),
|
version: String::new(),
|
||||||
main_tabs: TabState::new(vec![TabRoute {
|
main_tabs: TabState::new(vec![TabRoute {
|
||||||
@@ -112,19 +112,21 @@ pub enum ActiveLidarrBlock {
|
|||||||
DeleteArtistConfirmPrompt,
|
DeleteArtistConfirmPrompt,
|
||||||
DeleteArtistToggleDeleteFile,
|
DeleteArtistToggleDeleteFile,
|
||||||
DeleteArtistToggleAddListExclusion,
|
DeleteArtistToggleAddListExclusion,
|
||||||
SearchArtists,
|
|
||||||
SearchArtistsError,
|
|
||||||
FilterArtists,
|
FilterArtists,
|
||||||
FilterArtistsError,
|
FilterArtistsError,
|
||||||
|
SearchArtists,
|
||||||
|
SearchArtistsError,
|
||||||
|
UpdateAllArtistsPrompt,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static LIBRARY_BLOCKS: [ActiveLidarrBlock; 6] = [
|
pub static LIBRARY_BLOCKS: [ActiveLidarrBlock; 7] = [
|
||||||
ActiveLidarrBlock::Artists,
|
ActiveLidarrBlock::Artists,
|
||||||
ActiveLidarrBlock::ArtistsSortPrompt,
|
ActiveLidarrBlock::ArtistsSortPrompt,
|
||||||
ActiveLidarrBlock::SearchArtists,
|
|
||||||
ActiveLidarrBlock::SearchArtistsError,
|
|
||||||
ActiveLidarrBlock::FilterArtists,
|
ActiveLidarrBlock::FilterArtists,
|
||||||
ActiveLidarrBlock::FilterArtistsError,
|
ActiveLidarrBlock::FilterArtistsError,
|
||||||
|
ActiveLidarrBlock::SearchArtists,
|
||||||
|
ActiveLidarrBlock::SearchArtistsError,
|
||||||
|
ActiveLidarrBlock::UpdateAllArtistsPrompt,
|
||||||
];
|
];
|
||||||
|
|
||||||
pub static DELETE_ARTIST_BLOCKS: [ActiveLidarrBlock; 4] = [
|
pub static DELETE_ARTIST_BLOCKS: [ActiveLidarrBlock; 4] = [
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::assert_eq;
|
use crate::app::lidarr::lidarr_context_clues::ARTISTS_CONTEXT_CLUES;
|
||||||
|
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||||
use crate::models::{
|
DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS,
|
||||||
Route,
|
|
||||||
servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LidarrData},
|
|
||||||
};
|
};
|
||||||
|
use crate::models::{
|
||||||
|
BlockSelectionState, Route,
|
||||||
|
servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, LIBRARY_BLOCKS, LidarrData},
|
||||||
|
};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_active_lidarr_block_to_route() {
|
fn test_from_active_lidarr_block_to_route() {
|
||||||
@@ -36,4 +40,77 @@ mod tests {
|
|||||||
assert!(!lidarr_data.delete_artist_files);
|
assert!(!lidarr_data.delete_artist_files);
|
||||||
assert!(!lidarr_data.add_import_list_exclusion);
|
assert!(!lidarr_data.add_import_list_exclusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lidarr_data_default() {
|
||||||
|
let lidarr_data = LidarrData::default();
|
||||||
|
|
||||||
|
assert!(!lidarr_data.add_import_list_exclusion);
|
||||||
|
assert_is_empty!(lidarr_data.artists);
|
||||||
|
assert!(!lidarr_data.delete_artist_files);
|
||||||
|
assert_is_empty!(lidarr_data.disk_space_vec);
|
||||||
|
assert_is_empty!(lidarr_data.downloads);
|
||||||
|
assert_is_empty!(lidarr_data.metadata_profile_map);
|
||||||
|
assert!(!lidarr_data.prompt_confirm);
|
||||||
|
assert_none!(lidarr_data.prompt_confirm_action);
|
||||||
|
assert_is_empty!(lidarr_data.quality_profile_map);
|
||||||
|
assert_is_empty!(lidarr_data.root_folders);
|
||||||
|
assert_eq!(lidarr_data.selected_block, BlockSelectionState::default());
|
||||||
|
assert_eq!(lidarr_data.start_time, <DateTime<Utc>>::default());
|
||||||
|
assert_is_empty!(lidarr_data.tags_map);
|
||||||
|
assert_is_empty!(lidarr_data.version);
|
||||||
|
|
||||||
|
assert_eq!(lidarr_data.main_tabs.tabs.len(), 1);
|
||||||
|
|
||||||
|
assert_str_eq!(lidarr_data.main_tabs.tabs[0].title, "Library");
|
||||||
|
assert_eq!(
|
||||||
|
lidarr_data.main_tabs.tabs[0].route,
|
||||||
|
ActiveLidarrBlock::Artists.into()
|
||||||
|
);
|
||||||
|
assert_some_eq_x!(
|
||||||
|
&lidarr_data.main_tabs.tabs[0].contextual_help,
|
||||||
|
&ARTISTS_CONTEXT_CLUES
|
||||||
|
);
|
||||||
|
assert_none!(lidarr_data.main_tabs.tabs[0].config);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_blocks_contains_expected_blocks() {
|
||||||
|
assert_eq!(LIBRARY_BLOCKS.len(), 7);
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::Artists));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::ArtistsSortPrompt));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::SearchArtists));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::SearchArtistsError));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::FilterArtists));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::FilterArtistsError));
|
||||||
|
assert!(LIBRARY_BLOCKS.contains(&ActiveLidarrBlock::UpdateAllArtistsPrompt));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_artist_blocks_contents() {
|
||||||
|
assert_eq!(DELETE_ARTIST_BLOCKS.len(), 4);
|
||||||
|
assert!(DELETE_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::DeleteArtistPrompt));
|
||||||
|
assert!(DELETE_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::DeleteArtistConfirmPrompt));
|
||||||
|
assert!(DELETE_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::DeleteArtistToggleDeleteFile));
|
||||||
|
assert!(DELETE_ARTIST_BLOCKS.contains(&ActiveLidarrBlock::DeleteArtistToggleAddListExclusion));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_delete_artist_selection_blocks_ordering() {
|
||||||
|
let mut delete_artist_block_iter = DELETE_ARTIST_SELECTION_BLOCKS.iter();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
delete_artist_block_iter.next().unwrap(),
|
||||||
|
&[ActiveLidarrBlock::DeleteArtistToggleDeleteFile]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
delete_artist_block_iter.next().unwrap(),
|
||||||
|
&[ActiveLidarrBlock::DeleteArtistToggleAddListExclusion]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
delete_artist_block_iter.next().unwrap(),
|
||||||
|
&[ActiveLidarrBlock::DeleteArtistConfirmPrompt]
|
||||||
|
);
|
||||||
|
assert_none!(delete_artist_block_iter.next());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::{
|
|||||||
mod modals_tests;
|
mod modals_tests;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
pub struct AddSeriesModal {
|
pub struct AddSeriesModal {
|
||||||
pub root_folder_list: StatefulList<RootFolder>,
|
pub root_folder_list: StatefulList<RootFolder>,
|
||||||
pub monitor_list: StatefulList<SeriesMonitor>,
|
pub monitor_list: StatefulList<SeriesMonitor>,
|
||||||
@@ -130,6 +131,7 @@ impl From<&SonarrData<'_>> for EditIndexerModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
pub struct EditSeriesModal {
|
pub struct EditSeriesModal {
|
||||||
pub series_type_list: StatefulList<SeriesType>,
|
pub series_type_list: StatefulList<SeriesType>,
|
||||||
pub quality_profile_list: StatefulList<String>,
|
pub quality_profile_list: StatefulList<String>,
|
||||||
@@ -260,6 +262,7 @@ impl Default for EpisodeDetailsModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
pub struct SeasonDetailsModal {
|
pub struct SeasonDetailsModal {
|
||||||
pub episodes: StatefulTable<Episode>,
|
pub episodes: StatefulTable<Episode>,
|
||||||
pub episode_files: StatefulTable<EpisodeFile>,
|
pub episode_files: StatefulTable<EpisodeFile>,
|
||||||
|
|||||||
@@ -82,39 +82,39 @@ mod tests {
|
|||||||
let sonarr_data = SonarrData::default();
|
let sonarr_data = SonarrData::default();
|
||||||
|
|
||||||
assert!(!sonarr_data.add_list_exclusion);
|
assert!(!sonarr_data.add_list_exclusion);
|
||||||
assert!(sonarr_data.add_searched_series.is_none());
|
assert_none!(sonarr_data.add_searched_series);
|
||||||
assert!(sonarr_data.add_series_search.is_none());
|
assert_none!(sonarr_data.add_series_search);
|
||||||
assert!(sonarr_data.add_series_modal.is_none());
|
assert_none!(sonarr_data.add_series_modal);
|
||||||
assert!(sonarr_data.blocklist.is_empty());
|
assert_is_empty!(sonarr_data.blocklist);
|
||||||
assert!(!sonarr_data.delete_series_files);
|
assert!(!sonarr_data.delete_series_files);
|
||||||
assert!(sonarr_data.downloads.is_empty());
|
assert_is_empty!(sonarr_data.downloads);
|
||||||
assert!(sonarr_data.disk_space_vec.is_empty());
|
assert_is_empty!(sonarr_data.disk_space_vec);
|
||||||
assert!(sonarr_data.edit_indexer_modal.is_none());
|
assert_none!(sonarr_data.edit_indexer_modal);
|
||||||
assert!(sonarr_data.edit_root_folder.is_none());
|
assert_none!(sonarr_data.edit_root_folder);
|
||||||
assert!(sonarr_data.edit_series_modal.is_none());
|
assert_none!(sonarr_data.edit_series_modal);
|
||||||
assert!(sonarr_data.history.is_empty());
|
assert_is_empty!(sonarr_data.history);
|
||||||
assert!(sonarr_data.indexers.is_empty());
|
assert_is_empty!(sonarr_data.indexers);
|
||||||
assert!(sonarr_data.indexer_settings.is_none());
|
assert_none!(sonarr_data.indexer_settings);
|
||||||
assert!(sonarr_data.indexer_test_errors.is_none());
|
assert_none!(sonarr_data.indexer_test_errors);
|
||||||
assert!(sonarr_data.indexer_test_all_results.is_none());
|
assert_none!(sonarr_data.indexer_test_all_results);
|
||||||
assert!(sonarr_data.language_profiles_map.is_empty());
|
assert_is_empty!(sonarr_data.language_profiles_map);
|
||||||
assert!(sonarr_data.logs.is_empty());
|
assert_is_empty!(sonarr_data.logs);
|
||||||
assert!(sonarr_data.log_details.is_empty());
|
assert_is_empty!(sonarr_data.log_details);
|
||||||
assert!(!sonarr_data.prompt_confirm);
|
assert!(!sonarr_data.prompt_confirm);
|
||||||
assert!(sonarr_data.prompt_confirm_action.is_none());
|
assert_none!(sonarr_data.prompt_confirm_action);
|
||||||
assert!(sonarr_data.quality_profile_map.is_empty());
|
assert_is_empty!(sonarr_data.quality_profile_map);
|
||||||
assert!(sonarr_data.queued_events.is_empty());
|
assert_is_empty!(sonarr_data.queued_events);
|
||||||
assert!(sonarr_data.root_folders.is_empty());
|
assert_is_empty!(sonarr_data.root_folders);
|
||||||
assert!(sonarr_data.seasons.is_empty());
|
assert_is_empty!(sonarr_data.seasons);
|
||||||
assert!(sonarr_data.season_details_modal.is_none());
|
assert_none!(sonarr_data.season_details_modal);
|
||||||
assert_eq!(sonarr_data.selected_block, BlockSelectionState::default());
|
assert_eq!(sonarr_data.selected_block, BlockSelectionState::default());
|
||||||
assert!(sonarr_data.series.is_empty());
|
assert_is_empty!(sonarr_data.series);
|
||||||
assert!(sonarr_data.series_history.is_none());
|
assert_none!(sonarr_data.series_history);
|
||||||
assert_eq!(sonarr_data.start_time, <DateTime<Utc>>::default());
|
assert_eq!(sonarr_data.start_time, <DateTime<Utc>>::default());
|
||||||
assert!(sonarr_data.tags_map.is_empty());
|
assert_is_empty!(sonarr_data.tags_map);
|
||||||
assert!(sonarr_data.tasks.is_empty());
|
assert_is_empty!(sonarr_data.tasks);
|
||||||
assert!(sonarr_data.updates.is_empty());
|
assert_is_empty!(sonarr_data.updates);
|
||||||
assert!(sonarr_data.version.is_empty());
|
assert_is_empty!(sonarr_data.version);
|
||||||
|
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs.len(), 7);
|
assert_eq!(sonarr_data.main_tabs.tabs.len(), 7);
|
||||||
|
|
||||||
@@ -123,84 +123,77 @@ mod tests {
|
|||||||
sonarr_data.main_tabs.tabs[0].route,
|
sonarr_data.main_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::Series.into()
|
ActiveSonarrBlock::Series.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[0].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[0].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[0].contextual_help.unwrap(),
|
|
||||||
&SERIES_CONTEXT_CLUES
|
&SERIES_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[0].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[0].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[1].title, "Downloads");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[1].title, "Downloads");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[1].route,
|
sonarr_data.main_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::Downloads.into()
|
ActiveSonarrBlock::Downloads.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[1].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[1].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[1].contextual_help.unwrap(),
|
|
||||||
&DOWNLOADS_CONTEXT_CLUES
|
&DOWNLOADS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[1].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[1].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[2].title, "Blocklist");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[2].title, "Blocklist");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[2].route,
|
sonarr_data.main_tabs.tabs[2].route,
|
||||||
ActiveSonarrBlock::Blocklist.into()
|
ActiveSonarrBlock::Blocklist.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[2].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[2].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[2].contextual_help.unwrap(),
|
|
||||||
&BLOCKLIST_CONTEXT_CLUES
|
&BLOCKLIST_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[2].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[2].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[3].title, "History");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[3].title, "History");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[3].route,
|
sonarr_data.main_tabs.tabs[3].route,
|
||||||
ActiveSonarrBlock::History.into()
|
ActiveSonarrBlock::History.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[3].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[3].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[3].contextual_help.unwrap(),
|
|
||||||
&HISTORY_CONTEXT_CLUES
|
&HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[3].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[3].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[4].title, "Root Folders");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[4].title, "Root Folders");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[4].route,
|
sonarr_data.main_tabs.tabs[4].route,
|
||||||
ActiveSonarrBlock::RootFolders.into()
|
ActiveSonarrBlock::RootFolders.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[4].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[4].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[4].contextual_help.unwrap(),
|
|
||||||
&ROOT_FOLDERS_CONTEXT_CLUES
|
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[4].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[4].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[5].title, "Indexers");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[5].title, "Indexers");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[5].route,
|
sonarr_data.main_tabs.tabs[5].route,
|
||||||
ActiveSonarrBlock::Indexers.into()
|
ActiveSonarrBlock::Indexers.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[5].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[5].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[5].contextual_help.unwrap(),
|
|
||||||
&INDEXERS_CONTEXT_CLUES
|
&INDEXERS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[5].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[5].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.main_tabs.tabs[6].title, "System");
|
assert_str_eq!(sonarr_data.main_tabs.tabs[6].title, "System");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.main_tabs.tabs[6].route,
|
sonarr_data.main_tabs.tabs[6].route,
|
||||||
ActiveSonarrBlock::System.into()
|
ActiveSonarrBlock::System.into()
|
||||||
);
|
);
|
||||||
assert!(sonarr_data.main_tabs.tabs[6].contextual_help.is_some());
|
assert_some_eq_x!(
|
||||||
assert_eq!(
|
&sonarr_data.main_tabs.tabs[6].contextual_help,
|
||||||
sonarr_data.main_tabs.tabs[6].contextual_help.unwrap(),
|
|
||||||
&SYSTEM_CONTEXT_CLUES
|
&SYSTEM_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.main_tabs.tabs[6].config, None);
|
assert_none!(sonarr_data.main_tabs.tabs[6].config);
|
||||||
|
|
||||||
assert_eq!(sonarr_data.series_info_tabs.tabs.len(), 2);
|
assert_eq!(sonarr_data.series_info_tabs.tabs.len(), 2);
|
||||||
|
|
||||||
@@ -209,36 +202,22 @@ mod tests {
|
|||||||
sonarr_data.series_info_tabs.tabs[0].route,
|
sonarr_data.series_info_tabs.tabs[0].route,
|
||||||
ActiveSonarrBlock::SeriesDetails.into()
|
ActiveSonarrBlock::SeriesDetails.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert_some_eq_x!(
|
||||||
sonarr_data.series_info_tabs.tabs[0]
|
&sonarr_data.series_info_tabs.tabs[0].contextual_help,
|
||||||
.contextual_help
|
|
||||||
.is_some()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
sonarr_data.series_info_tabs.tabs[0]
|
|
||||||
.contextual_help
|
|
||||||
.unwrap(),
|
|
||||||
&SERIES_DETAILS_CONTEXT_CLUES
|
&SERIES_DETAILS_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.series_info_tabs.tabs[0].config, None);
|
assert_none!(sonarr_data.series_info_tabs.tabs[0].config);
|
||||||
|
|
||||||
assert_str_eq!(sonarr_data.series_info_tabs.tabs[1].title, "History");
|
assert_str_eq!(sonarr_data.series_info_tabs.tabs[1].title, "History");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sonarr_data.series_info_tabs.tabs[1].route,
|
sonarr_data.series_info_tabs.tabs[1].route,
|
||||||
ActiveSonarrBlock::SeriesHistory.into()
|
ActiveSonarrBlock::SeriesHistory.into()
|
||||||
);
|
);
|
||||||
assert!(
|
assert_some_eq_x!(
|
||||||
sonarr_data.series_info_tabs.tabs[1]
|
&sonarr_data.series_info_tabs.tabs[1].contextual_help,
|
||||||
.contextual_help
|
|
||||||
.is_some()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
sonarr_data.series_info_tabs.tabs[1]
|
|
||||||
.contextual_help
|
|
||||||
.unwrap(),
|
|
||||||
&SERIES_HISTORY_CONTEXT_CLUES
|
&SERIES_HISTORY_CONTEXT_CLUES
|
||||||
);
|
);
|
||||||
assert_eq!(sonarr_data.series_info_tabs.tabs[1].config, None);
|
assert_none!(sonarr_data.series_info_tabs.tabs[1].config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -162,4 +162,26 @@ mod tests {
|
|||||||
get_mock.assert_async().await;
|
get_mock.assert_async().await;
|
||||||
put_mock.assert_async().await;
|
put_mock.assert_async().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_handle_update_all_artists_event() {
|
||||||
|
let (mock, app, _server) = MockServarrApi::post()
|
||||||
|
.with_request_body(json!({
|
||||||
|
"name": "RefreshArtist"
|
||||||
|
}))
|
||||||
|
.returns(json!({}))
|
||||||
|
.build_for(LidarrEvent::UpdateAllArtists)
|
||||||
|
.await;
|
||||||
|
app.lock().await.server_tabs.set_index(2);
|
||||||
|
let mut network = test_network(&app);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
network
|
||||||
|
.handle_lidarr_event(LidarrEvent::UpdateAllArtists)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
|
mock.assert_async().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use serde_json::{Value, json};
|
|||||||
use crate::models::Route;
|
use crate::models::Route;
|
||||||
use crate::models::lidarr_models::{Artist, DeleteArtistParams};
|
use crate::models::lidarr_models::{Artist, DeleteArtistParams};
|
||||||
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||||
|
use crate::models::servarr_models::CommandBody;
|
||||||
use crate::network::lidarr_network::LidarrEvent;
|
use crate::network::lidarr_network::LidarrEvent;
|
||||||
use crate::network::{Network, RequestMethod};
|
use crate::network::{Network, RequestMethod};
|
||||||
|
|
||||||
@@ -151,4 +152,20 @@ impl Network<'_, '_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(in crate::network::lidarr_network) async fn update_all_artists(&mut self) -> Result<Value> {
|
||||||
|
info!("Updating all artists");
|
||||||
|
let event = LidarrEvent::UpdateAllArtists;
|
||||||
|
let body = CommandBody {
|
||||||
|
name: "RefreshArtist".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let request_props = self
|
||||||
|
.request_props_from(event, RequestMethod::Post, Some(body), None, None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self
|
||||||
|
.handle_request::<CommandBody, Value>(request_props, |_, _| ())
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ mod tests {
|
|||||||
assert_str_eq!(event.resource(), "/config/host");
|
assert_str_eq!(event.resource(), "/config/host");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn test_resource_command(#[values(LidarrEvent::UpdateAllArtists)] event: LidarrEvent) {
|
||||||
|
assert_str_eq!(event.resource(), "/command");
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
||||||
#[case(LidarrEvent::GetDownloads(500), "/queue")]
|
#[case(LidarrEvent::GetDownloads(500), "/queue")]
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub enum LidarrEvent {
|
|||||||
HealthCheck,
|
HealthCheck,
|
||||||
ListArtists,
|
ListArtists,
|
||||||
ToggleArtistMonitoring(i64),
|
ToggleArtistMonitoring(i64),
|
||||||
|
UpdateAllArtists,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NetworkResource for LidarrEvent {
|
impl NetworkResource for LidarrEvent {
|
||||||
@@ -43,6 +44,7 @@ impl NetworkResource for LidarrEvent {
|
|||||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||||
LidarrEvent::GetDownloads(_) => "/queue",
|
LidarrEvent::GetDownloads(_) => "/queue",
|
||||||
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
|
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
|
||||||
|
LidarrEvent::UpdateAllArtists => "/command",
|
||||||
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
||||||
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||||
LidarrEvent::GetRootFolders => "/rootfolder",
|
LidarrEvent::GetRootFolders => "/rootfolder",
|
||||||
@@ -108,6 +110,7 @@ impl Network<'_, '_> {
|
|||||||
.toggle_artist_monitoring(artist_id)
|
.toggle_artist_monitoring(artist_id)
|
||||||
.await
|
.await
|
||||||
.map(LidarrSerdeable::from),
|
.map(LidarrSerdeable::from),
|
||||||
|
LidarrEvent::UpdateAllArtists => self.update_all_artists().await.map(LidarrSerdeable::from),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,5 +247,18 @@ mod tests {
|
|||||||
|
|
||||||
insta::assert_snapshot!(output);
|
insta::assert_snapshot!(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_ui_renders_update_all_artists_prompt() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||||
|
app.push_navigation_stack(ActiveLidarrBlock::UpdateAllArtistsPrompt.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
LibraryUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||||
|
use crate::ui::widgets::{
|
||||||
|
confirmation_prompt::ConfirmationPrompt,
|
||||||
|
popup::{Popup, Size},
|
||||||
|
};
|
||||||
use crate::utils::convert_to_gb;
|
use crate::utils::convert_to_gb;
|
||||||
use crate::{
|
use crate::{
|
||||||
app::App,
|
app::App,
|
||||||
@@ -42,8 +46,20 @@ impl DrawUi for LibraryUi {
|
|||||||
let route = app.get_current_route();
|
let route = app.get_current_route();
|
||||||
draw_library(f, app, area);
|
draw_library(f, app, area);
|
||||||
|
|
||||||
if DeleteArtistUi::accepts(route) {
|
match route {
|
||||||
DeleteArtistUi::draw(f, app, area);
|
_ if DeleteArtistUi::accepts(route) => DeleteArtistUi::draw(f, app, area),
|
||||||
|
Route::Lidarr(ActiveLidarrBlock::UpdateAllArtistsPrompt, _) => {
|
||||||
|
let confirmation_prompt = ConfirmationPrompt::new()
|
||||||
|
.title("Update All Artists")
|
||||||
|
.prompt("Do you want to update info and scan your disks for all of your artists?")
|
||||||
|
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
||||||
|
|
||||||
|
f.render_widget(
|
||||||
|
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||||
|
f.area(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/lidarr_ui/library/library_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags
|
||||||
|
=> Continuing 0 0.00 GB
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭────────────────── Update All Artists ───────────────────╮
|
||||||
|
│ Do you want to update info and scan your disks for all of │
|
||||||
|
│ your artists? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│╭────────────────────────────╮╭───────────────────────────╮│
|
||||||
|
││ Yes ││ No ││
|
||||||
|
│╰────────────────────────────╯╰───────────────────────────╯│
|
||||||
|
╰───────────────────────────────────────────────────────────╯
|
||||||
@@ -382,5 +382,18 @@ mod tests {
|
|||||||
|
|
||||||
insta::assert_snapshot!(output);
|
insta::assert_snapshot!(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_library_ui_renders_update_all_series_prompt() {
|
||||||
|
let mut app = App::test_default_fully_populated();
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::Series.into());
|
||||||
|
app.push_navigation_stack(ActiveSonarrBlock::UpdateAllSeriesPrompt.into());
|
||||||
|
|
||||||
|
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||||
|
LibraryUi::draw(f, app, f.area());
|
||||||
|
});
|
||||||
|
|
||||||
|
insta::assert_snapshot!(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
source: src/ui/sonarr_ui/library/library_ui_tests.rs
|
||||||
|
expression: output
|
||||||
|
---
|
||||||
|
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
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 🏷
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
╭─────────────────── Update All Series ───────────────────╮
|
||||||
|
│ Do you want to update info and scan your disks for all of │
|
||||||
|
│ your series? │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│ │
|
||||||
|
│╭────────────────────────────╮╭───────────────────────────╮│
|
||||||
|
││ Yes ││ No ││
|
||||||
|
│╰────────────────────────────╯╰───────────────────────────╯│
|
||||||
|
╰───────────────────────────────────────────────────────────╯
|
||||||
Reference in New Issue
Block a user