Compare commits
5 Commits
0172253d20
...
8abcf44866
| Author | SHA1 | Date | |
|---|---|---|---|
| 8abcf44866 | |||
| d2217509f2 | |||
| c68cd75015 | |||
| e1a25bfaf2 | |||
| ad9e2b3671 |
@@ -5,7 +5,8 @@ use crate::app::context_clues::{
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::models::Route;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ARTIST_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock, EDIT_ARTIST_BLOCKS,
|
||||
ADD_ARTIST_BLOCKS, ADD_ROOT_FOLDER_BLOCKS, ARTIST_DETAILS_BLOCKS, ActiveLidarrBlock,
|
||||
EDIT_ARTIST_BLOCKS,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -73,7 +74,9 @@ impl ContextClueProvider for LidarrContextClueProvider {
|
||||
ActiveLidarrBlock::AddArtistSearchInput | ActiveLidarrBlock::AddArtistEmptySearchResults => {
|
||||
Some(&BARE_POPUP_CONTEXT_CLUES)
|
||||
}
|
||||
_ if EDIT_ARTIST_BLOCKS.contains(&active_lidarr_block) => {
|
||||
_ if EDIT_ARTIST_BLOCKS.contains(&active_lidarr_block)
|
||||
|| ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block) =>
|
||||
{
|
||||
Some(&CONFIRMATION_PROMPT_CONTEXT_CLUES)
|
||||
}
|
||||
ActiveLidarrBlock::AddArtistPrompt
|
||||
|
||||
@@ -10,7 +10,7 @@ mod tests {
|
||||
LidarrContextClueProvider,
|
||||
};
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, LidarrData,
|
||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, EDIT_ARTIST_BLOCKS, LidarrData,
|
||||
};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use rstest::rstest;
|
||||
@@ -218,7 +218,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_lidarr_context_clue_provider_confirmation_prompt_popup_clues_edit_indexer_blocks() {
|
||||
for active_lidarr_block in EDIT_ARTIST_BLOCKS {
|
||||
let mut blocks = EDIT_ARTIST_BLOCKS.to_vec();
|
||||
blocks.extend(ADD_ROOT_FOLDER_BLOCKS);
|
||||
|
||||
for active_lidarr_block in blocks {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ mod tests {
|
||||
async fn test_dispatch_by_lidarr_block_artists() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.network_tx = Some(tx);
|
||||
|
||||
app
|
||||
@@ -37,6 +38,7 @@ mod tests {
|
||||
async fn test_dispatch_by_lidarr_block_artist_details() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.data.lidarr_data.artists.set_items(vec![artist()]);
|
||||
app.network_tx = Some(tx);
|
||||
|
||||
@@ -50,10 +52,31 @@ mod tests {
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_downloads_block() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.network_tx = Some(tx);
|
||||
|
||||
app
|
||||
.dispatch_by_lidarr_block(&ActiveLidarrBlock::Downloads)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
rx.recv().await.unwrap(),
|
||||
LidarrEvent::GetDownloads(500).into()
|
||||
);
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_lidarr_block_add_artist_search_results() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.network_tx = Some(tx);
|
||||
app.data.lidarr_data.add_artist_search = Some("test artist".into());
|
||||
|
||||
@@ -74,6 +97,7 @@ mod tests {
|
||||
async fn test_dispatch_by_history_block() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.network_tx = Some(tx);
|
||||
|
||||
app
|
||||
@@ -89,6 +113,23 @@ mod tests {
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_root_folders_block() {
|
||||
let (tx, mut rx) = mpsc::channel::<NetworkEvent>(500);
|
||||
let mut app = App::test_default();
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.network_tx = Some(tx);
|
||||
|
||||
app
|
||||
.dispatch_by_lidarr_block(&ActiveLidarrBlock::RootFolders)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(rx.recv().await.unwrap(), LidarrEvent::GetRootFolders.into());
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_extract_add_new_artist_search_query() {
|
||||
let app = App::test_default_fully_populated();
|
||||
|
||||
@@ -28,6 +28,11 @@ impl App<'_> {
|
||||
.dispatch_network_event(LidarrEvent::ListArtists.into())
|
||||
.await;
|
||||
}
|
||||
ActiveLidarrBlock::Downloads => {
|
||||
self
|
||||
.dispatch_network_event(LidarrEvent::GetDownloads(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveLidarrBlock::ArtistDetails => {
|
||||
self
|
||||
.dispatch_network_event(LidarrEvent::GetAlbums(self.extract_artist_id().await).into())
|
||||
@@ -45,6 +50,11 @@ impl App<'_> {
|
||||
.dispatch_network_event(LidarrEvent::GetHistory(500).into())
|
||||
.await
|
||||
}
|
||||
ActiveLidarrBlock::RootFolders => {
|
||||
self
|
||||
.dispatch_network_event(LidarrEvent::GetRootFolders.into())
|
||||
.await;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ impl App<'_> {
|
||||
.dispatch_network_event(RadarrEvent::GetDownloads(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::History => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetHistory(500).into())
|
||||
.await;
|
||||
}
|
||||
ActiveRadarrBlock::Indexers => {
|
||||
self
|
||||
.dispatch_network_event(RadarrEvent::GetTags.into())
|
||||
|
||||
@@ -3,8 +3,8 @@ mod tests {
|
||||
use crate::app::App;
|
||||
use crate::app::context_clues::{
|
||||
BARE_POPUP_CONTEXT_CLUES, BLOCKLIST_CONTEXT_CLUES, CONFIRMATION_PROMPT_CONTEXT_CLUES,
|
||||
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
ContextClue, ContextClueProvider, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::app::radarr::radarr_context_clues::{
|
||||
@@ -459,9 +459,10 @@ mod tests {
|
||||
#[case(1, ActiveRadarrBlock::Collections, &COLLECTIONS_CONTEXT_CLUES)]
|
||||
#[case(2, ActiveRadarrBlock::Downloads, &DOWNLOADS_CONTEXT_CLUES)]
|
||||
#[case(3, ActiveRadarrBlock::Blocklist, &BLOCKLIST_CONTEXT_CLUES)]
|
||||
#[case(4, ActiveRadarrBlock::RootFolders, &ROOT_FOLDERS_CONTEXT_CLUES)]
|
||||
#[case(5, ActiveRadarrBlock::Indexers, &INDEXERS_CONTEXT_CLUES)]
|
||||
#[case(6, ActiveRadarrBlock::System, &SYSTEM_CONTEXT_CLUES)]
|
||||
#[case(4, ActiveRadarrBlock::History, &HISTORY_CONTEXT_CLUES)]
|
||||
#[case(5, ActiveRadarrBlock::RootFolders, &ROOT_FOLDERS_CONTEXT_CLUES)]
|
||||
#[case(6, ActiveRadarrBlock::Indexers, &INDEXERS_CONTEXT_CLUES)]
|
||||
#[case(7, ActiveRadarrBlock::System, &SYSTEM_CONTEXT_CLUES)]
|
||||
fn test_radarr_context_clue_provider_radarr_blocks_context_clues(
|
||||
#[case] index: usize,
|
||||
#[case] active_radarr_block: ActiveRadarrBlock,
|
||||
|
||||
@@ -147,6 +147,23 @@ mod tests {
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_history_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
app
|
||||
.dispatch_by_radarr_block(&ActiveRadarrBlock::History)
|
||||
.await;
|
||||
|
||||
assert!(app.is_loading);
|
||||
assert_eq!(
|
||||
sync_network_rx.recv().await.unwrap(),
|
||||
RadarrEvent::GetHistory(500).into()
|
||||
);
|
||||
assert!(!app.data.radarr_data.prompt_confirm);
|
||||
assert_eq!(app.tick_count, 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dispatch_by_root_folders_block() {
|
||||
let (mut app, mut sync_network_rx) = construct_app_unit();
|
||||
|
||||
@@ -8,7 +8,9 @@ use super::LidarrCommand;
|
||||
use crate::{
|
||||
app::App,
|
||||
cli::{CliCommandHandler, Command},
|
||||
models::lidarr_models::{AddArtistBody, AddArtistOptions, MonitorType, NewItemMonitorType},
|
||||
models::lidarr_models::{
|
||||
AddArtistBody, AddArtistOptions, AddLidarrRootFolderBody, MonitorType, NewItemMonitorType,
|
||||
},
|
||||
network::{NetworkTrait, lidarr_network::LidarrEvent},
|
||||
};
|
||||
|
||||
@@ -75,6 +77,46 @@ pub enum LidarrAddCommand {
|
||||
)]
|
||||
no_search_for_missing_albums: bool,
|
||||
},
|
||||
#[command(about = "Add a new root folder")]
|
||||
RootFolder {
|
||||
#[arg(long, help = "The name of the root folder", required = true)]
|
||||
name: String,
|
||||
#[arg(long, help = "The path of the new root folder", required = true)]
|
||||
root_folder_path: String,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The ID of the default quality profile for artists in this root folder",
|
||||
required = true
|
||||
)]
|
||||
quality_profile_id: i64,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The ID of the default metadata profile for artists in this root folder",
|
||||
required = true
|
||||
)]
|
||||
metadata_profile_id: i64,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The default monitor option for artists in this root folder",
|
||||
value_enum,
|
||||
default_value_t = MonitorType::default()
|
||||
)]
|
||||
monitor: MonitorType,
|
||||
#[arg(
|
||||
long,
|
||||
help = "The default monitor new items option for artists in this root folder",
|
||||
value_enum,
|
||||
default_value_t = NewItemMonitorType::default()
|
||||
)]
|
||||
monitor_new_items: NewItemMonitorType,
|
||||
#[arg(
|
||||
long,
|
||||
help = "Default tag IDs for artists in this root folder",
|
||||
value_parser,
|
||||
action = ArgAction::Append
|
||||
)]
|
||||
tag: Vec<i64>,
|
||||
},
|
||||
#[command(about = "Add new tag")]
|
||||
Tag {
|
||||
#[arg(long, help = "The name of the tag to be added", required = true)]
|
||||
@@ -142,6 +184,31 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrAddCommand> for LidarrAddCommandHan
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrAddCommand::RootFolder {
|
||||
name,
|
||||
root_folder_path,
|
||||
quality_profile_id,
|
||||
metadata_profile_id,
|
||||
monitor,
|
||||
monitor_new_items,
|
||||
tag: tags,
|
||||
} => {
|
||||
let add_root_folder_body = AddLidarrRootFolderBody {
|
||||
name,
|
||||
path: root_folder_path,
|
||||
default_quality_profile_id: quality_profile_id,
|
||||
default_metadata_profile_id: metadata_profile_id,
|
||||
default_monitor_option: monitor,
|
||||
default_new_item_monitor_option: monitor_new_items,
|
||||
default_tags: tags,
|
||||
tag_input_string: None,
|
||||
};
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::AddRootFolder(add_root_folder_body).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrAddCommand::Tag { name } => {
|
||||
let resp = self
|
||||
.network
|
||||
|
||||
@@ -27,6 +27,53 @@ mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_requires_arguments() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "add", "root-folder"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_success() {
|
||||
let expected_args = LidarrAddCommand::RootFolder {
|
||||
name: "Music".to_owned(),
|
||||
root_folder_path: "/nfs/test".to_owned(),
|
||||
quality_profile_id: 1,
|
||||
metadata_profile_id: 1,
|
||||
monitor: MonitorType::All,
|
||||
monitor_new_items: NewItemMonitorType::All,
|
||||
tag: vec![],
|
||||
};
|
||||
|
||||
let result = Cli::try_parse_from([
|
||||
"managarr",
|
||||
"lidarr",
|
||||
"add",
|
||||
"root-folder",
|
||||
"--name",
|
||||
"Music",
|
||||
"--root-folder-path",
|
||||
"/nfs/test",
|
||||
"--quality-profile-id",
|
||||
"1",
|
||||
"--metadata-profile-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert_ok!(&result);
|
||||
|
||||
let Some(Command::Lidarr(LidarrCommand::Add(add_command))) = result.unwrap().command else {
|
||||
panic!("Unexpected command type");
|
||||
};
|
||||
assert_eq!(add_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tag_requires_arguments() {
|
||||
let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "add", "tag"]);
|
||||
@@ -381,7 +428,8 @@ mod tests {
|
||||
use crate::cli::lidarr::add_command_handler::{LidarrAddCommand, LidarrAddCommandHandler};
|
||||
use crate::models::Serdeable;
|
||||
use crate::models::lidarr_models::{
|
||||
AddArtistBody, AddArtistOptions, LidarrSerdeable, MonitorType, NewItemMonitorType,
|
||||
AddArtistBody, AddArtistOptions, AddLidarrRootFolderBody, LidarrSerdeable, MonitorType,
|
||||
NewItemMonitorType,
|
||||
};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::{
|
||||
@@ -389,6 +437,50 @@ mod tests {
|
||||
network::{MockNetworkTrait, NetworkEvent},
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_root_folder_command() {
|
||||
let expected_root_folder_path = "/nfs/test".to_owned();
|
||||
let expected_add_root_folder_body = AddLidarrRootFolderBody {
|
||||
name: "Music".to_owned(),
|
||||
path: expected_root_folder_path.clone(),
|
||||
default_quality_profile_id: 1,
|
||||
default_metadata_profile_id: 1,
|
||||
default_monitor_option: MonitorType::All,
|
||||
default_new_item_monitor_option: NewItemMonitorType::All,
|
||||
default_tags: vec![1, 2],
|
||||
tag_input_string: None,
|
||||
};
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
LidarrEvent::AddRootFolder(expected_add_root_folder_body.clone()).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let add_root_folder_command = LidarrAddCommand::RootFolder {
|
||||
name: "Music".to_owned(),
|
||||
root_folder_path: expected_root_folder_path,
|
||||
quality_profile_id: 1,
|
||||
metadata_profile_id: 1,
|
||||
monitor: MonitorType::All,
|
||||
monitor_new_items: NewItemMonitorType::All,
|
||||
tag: vec![1, 2],
|
||||
};
|
||||
|
||||
let result =
|
||||
LidarrAddCommandHandler::with(&app_arc, add_root_folder_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_tag_command() {
|
||||
let expected_tag_name = "test".to_owned();
|
||||
|
||||
@@ -37,6 +37,16 @@ pub enum LidarrDeleteCommand {
|
||||
#[arg(long, help = "Add a list exclusion for this artist")]
|
||||
add_list_exclusion: bool,
|
||||
},
|
||||
#[command(about = "Delete the specified download")]
|
||||
Download {
|
||||
#[arg(long, help = "The ID of the download to delete", required = true)]
|
||||
download_id: i64,
|
||||
},
|
||||
#[command(about = "Delete the root folder with the given ID")]
|
||||
RootFolder {
|
||||
#[arg(long, help = "The ID of the root folder to delete", required = true)]
|
||||
root_folder_id: i64,
|
||||
},
|
||||
#[command(about = "Delete the tag with the specified ID")]
|
||||
Tag {
|
||||
#[arg(long, help = "The ID of the tag to delete", required = true)]
|
||||
@@ -103,6 +113,20 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrDeleteCommand> for LidarrDeleteComm
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrDeleteCommand::Download { download_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::DeleteDownload(download_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrDeleteCommand::RootFolder { root_folder_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::DeleteRootFolder(root_folder_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrDeleteCommand::Tag { tag_id } => {
|
||||
let resp = self
|
||||
.network
|
||||
|
||||
@@ -145,6 +145,74 @@ mod tests {
|
||||
assert_eq!(delete_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_download_requires_arguments() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "delete", "download"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_download_success() {
|
||||
let expected_args = LidarrDeleteCommand::Download { download_id: 1 };
|
||||
|
||||
let result = Cli::try_parse_from([
|
||||
"managarr",
|
||||
"lidarr",
|
||||
"delete",
|
||||
"download",
|
||||
"--download-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert_ok!(&result);
|
||||
|
||||
let Some(Command::Lidarr(LidarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||
else {
|
||||
panic!("Unexpected command type");
|
||||
};
|
||||
assert_eq!(delete_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_requires_arguments() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "delete", "root-folder"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_success() {
|
||||
let expected_args = LidarrDeleteCommand::RootFolder { root_folder_id: 1 };
|
||||
|
||||
let result = Cli::try_parse_from([
|
||||
"managarr",
|
||||
"lidarr",
|
||||
"delete",
|
||||
"root-folder",
|
||||
"--root-folder-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert_ok!(&result);
|
||||
|
||||
let Some(Command::Lidarr(LidarrCommand::Delete(delete_command))) = result.unwrap().command
|
||||
else {
|
||||
panic!("Unexpected command type");
|
||||
};
|
||||
assert_eq!(delete_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_tag_requires_arguments() {
|
||||
let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "delete", "tag"]);
|
||||
@@ -260,6 +328,58 @@ mod tests {
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_download_command() {
|
||||
let expected_download_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
LidarrEvent::DeleteDownload(expected_download_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let delete_download_command = LidarrDeleteCommand::Download { download_id: 1 };
|
||||
|
||||
let result =
|
||||
LidarrDeleteCommandHandler::with(&app_arc, delete_download_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_root_folder_command() {
|
||||
let expected_root_folder_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
LidarrEvent::DeleteRootFolder(expected_root_folder_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let delete_root_folder_command = LidarrDeleteCommand::RootFolder { root_folder_id: 1 };
|
||||
|
||||
let result =
|
||||
LidarrDeleteCommandHandler::with(&app_arc, delete_root_folder_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_tag_command() {
|
||||
let expected_tag_id = 1;
|
||||
|
||||
@@ -29,6 +29,11 @@ pub enum LidarrListCommand {
|
||||
},
|
||||
#[command(about = "List all artists in your Lidarr library")]
|
||||
Artists,
|
||||
#[command(about = "List all active downloads in Lidarr")]
|
||||
Downloads {
|
||||
#[arg(long, help = "How many downloads to fetch", default_value_t = 500)]
|
||||
count: u64,
|
||||
},
|
||||
#[command(about = "Fetch all Lidarr history events")]
|
||||
History {
|
||||
#[arg(long, help = "How many history events to fetch", default_value_t = 500)]
|
||||
@@ -38,6 +43,8 @@ pub enum LidarrListCommand {
|
||||
MetadataProfiles,
|
||||
#[command(about = "List all Lidarr quality profiles")]
|
||||
QualityProfiles,
|
||||
#[command(about = "List all root folders in Lidarr")]
|
||||
RootFolders,
|
||||
#[command(about = "List all Lidarr tags")]
|
||||
Tags,
|
||||
}
|
||||
@@ -83,6 +90,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrListCommand::Downloads { count } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::GetDownloads(count).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrListCommand::History { events: items } => {
|
||||
let resp = self
|
||||
.network
|
||||
@@ -104,6 +118,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrListCommand> for LidarrListCommandH
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrListCommand::RootFolders => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::GetRootFolders.into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrListCommand::Tags => {
|
||||
let resp = self
|
||||
.network
|
||||
|
||||
@@ -25,7 +25,14 @@ mod tests {
|
||||
|
||||
#[rstest]
|
||||
fn test_list_commands_have_no_arg_requirements(
|
||||
#[values("artists", "metadata-profiles", "quality-profiles", "tags")] subcommand: &str,
|
||||
#[values(
|
||||
"artists",
|
||||
"metadata-profiles",
|
||||
"quality-profiles",
|
||||
"tags",
|
||||
"root-folders"
|
||||
)]
|
||||
subcommand: &str,
|
||||
) {
|
||||
let result = Cli::command().try_get_matches_from(["managarr", "lidarr", "list", subcommand]);
|
||||
|
||||
@@ -58,6 +65,29 @@ mod tests {
|
||||
assert_eq!(album_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_downloads_count_flag_requires_arguments() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "list", "downloads", "--count"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_downloads_default_values() {
|
||||
let expected_args = LidarrListCommand::Downloads { count: 500 };
|
||||
let result = Cli::try_parse_from(["managarr", "lidarr", "list", "downloads"]);
|
||||
|
||||
assert_ok!(&result);
|
||||
|
||||
let Some(Command::Lidarr(LidarrCommand::List(downloads_command))) = result.unwrap().command
|
||||
else {
|
||||
panic!("Unexpected command type");
|
||||
};
|
||||
assert_eq!(downloads_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_history_events_flag_requires_arguments() {
|
||||
let result =
|
||||
@@ -104,6 +134,7 @@ mod tests {
|
||||
#[case(LidarrListCommand::Artists, LidarrEvent::ListArtists)]
|
||||
#[case(LidarrListCommand::MetadataProfiles, LidarrEvent::GetMetadataProfiles)]
|
||||
#[case(LidarrListCommand::QualityProfiles, LidarrEvent::GetQualityProfiles)]
|
||||
#[case(LidarrListCommand::RootFolders, LidarrEvent::GetRootFolders)]
|
||||
#[case(LidarrListCommand::Tags, LidarrEvent::GetTags)]
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_command(
|
||||
@@ -151,6 +182,32 @@ mod tests {
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_downloads_command() {
|
||||
let expected_count = 1000;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
LidarrEvent::GetDownloads(expected_count).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let list_downloads_command = LidarrListCommand::Downloads { count: 1000 };
|
||||
|
||||
let result =
|
||||
LidarrListCommandHandler::with(&app_arc, list_downloads_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_history_command() {
|
||||
let expected_events = 1000;
|
||||
|
||||
@@ -28,6 +28,8 @@ pub enum LidarrRefreshCommand {
|
||||
)]
|
||||
artist_id: i64,
|
||||
},
|
||||
#[command(about = "Refresh all downloads in Lidarr")]
|
||||
Downloads,
|
||||
}
|
||||
|
||||
impl From<LidarrRefreshCommand> for Command {
|
||||
@@ -73,6 +75,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, LidarrRefreshCommand>
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
LidarrRefreshCommand::Downloads => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(LidarrEvent::UpdateDownloads.into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
|
||||
@@ -22,11 +22,14 @@ mod tests {
|
||||
use super::*;
|
||||
use clap::{Parser, error::ErrorKind};
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
#[test]
|
||||
fn test_refresh_all_artists_has_no_arg_requirements() {
|
||||
#[rstest]
|
||||
fn test_refresh_commands_have_no_arg_requirements(
|
||||
#[values("all-artists", "downloads")] subcommand: &str,
|
||||
) {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "refresh", "all-artists"]);
|
||||
Cli::command().try_get_matches_from(["managarr", "lidarr", "refresh", subcommand]);
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
@@ -67,6 +70,7 @@ mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use mockall::predicate::eq;
|
||||
use rstest::rstest;
|
||||
use serde_json::json;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@@ -80,12 +84,18 @@ mod tests {
|
||||
network::{MockNetworkTrait, NetworkEvent},
|
||||
};
|
||||
|
||||
#[rstest]
|
||||
#[case(LidarrRefreshCommand::AllArtists, LidarrEvent::UpdateAllArtists)]
|
||||
#[case(LidarrRefreshCommand::Downloads, LidarrEvent::UpdateDownloads)]
|
||||
#[tokio::test]
|
||||
async fn test_handle_refresh_all_artists_command() {
|
||||
async fn test_handle_refresh_command(
|
||||
#[case] refresh_command: LidarrRefreshCommand,
|
||||
#[case] expected_sonarr_event: LidarrEvent,
|
||||
) {
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(LidarrEvent::UpdateAllArtists.into()))
|
||||
.with(eq::<NetworkEvent>(expected_sonarr_event.into()))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Lidarr(LidarrSerdeable::Value(
|
||||
@@ -93,7 +103,6 @@ mod tests {
|
||||
)))
|
||||
});
|
||||
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()
|
||||
|
||||
@@ -29,6 +29,11 @@ pub enum RadarrListCommand {
|
||||
},
|
||||
#[command(about = "List disk space details for all provisioned root folders in Radarr")]
|
||||
DiskSpace,
|
||||
#[command(about = "Fetch all Radarr history events")]
|
||||
History {
|
||||
#[arg(long, help = "How many history events to fetch", default_value_t = 500)]
|
||||
events: u64,
|
||||
},
|
||||
#[command(about = "List all Radarr indexers")]
|
||||
Indexers,
|
||||
#[command(about = "Fetch Radarr logs")]
|
||||
@@ -121,6 +126,13 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrListCommand> for RadarrListCommandH
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrListCommand::History { events: items } => {
|
||||
let resp = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::GetHistory(items).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrListCommand::Indexers => {
|
||||
let resp = self
|
||||
.network
|
||||
|
||||
@@ -111,6 +111,29 @@ mod tests {
|
||||
assert_eq!(refresh_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_history_events_flag_requires_arguments() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "radarr", "list", "history", "--events"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_history_default_values() {
|
||||
let expected_args = RadarrListCommand::History { events: 500 };
|
||||
let result = Cli::try_parse_from(["managarr", "radarr", "list", "history"]);
|
||||
|
||||
assert_ok!(&result);
|
||||
|
||||
let Some(Command::Radarr(RadarrCommand::List(history_command))) = result.unwrap().command
|
||||
else {
|
||||
panic!("Unexpected command type");
|
||||
};
|
||||
assert_eq!(history_command, expected_args);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_logs_default_values() {
|
||||
let expected_args = RadarrListCommand::Logs {
|
||||
@@ -233,6 +256,32 @@ mod tests {
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_history_command() {
|
||||
let expected_events = 1000;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::GetHistory(expected_events).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let list_history_command = RadarrListCommand::History { events: 1000 };
|
||||
|
||||
let result =
|
||||
RadarrListCommandHandler::with(&app_arc, list_history_command, &mut mock_network)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_list_logs_command() {
|
||||
let expected_events = 1000;
|
||||
|
||||
@@ -64,6 +64,15 @@ pub enum RadarrCommand {
|
||||
Refresh(RadarrRefreshCommand),
|
||||
#[command(about = "Clear the blocklist")]
|
||||
ClearBlocklist,
|
||||
#[command(about = "Mark the Radarr history item with the given ID as 'failed'")]
|
||||
MarkHistoryItemAsFailed {
|
||||
#[arg(
|
||||
long,
|
||||
help = "The Radarr ID of the history item you wish to mark as 'failed'",
|
||||
required = true
|
||||
)]
|
||||
history_item_id: i64,
|
||||
},
|
||||
#[command(about = "Manually download the given release for the specified movie ID")]
|
||||
DownloadRelease {
|
||||
#[arg(long, help = "The GUID of the release to download", required = true)]
|
||||
@@ -208,6 +217,15 @@ impl<'a, 'b> CliCommandHandler<'a, 'b, RadarrCommand> for RadarrCliHandler<'a, '
|
||||
.await?;
|
||||
serde_json::to_string_pretty(&resp)?
|
||||
}
|
||||
RadarrCommand::MarkHistoryItemAsFailed { history_item_id } => {
|
||||
let _ = self
|
||||
.network
|
||||
.handle_network_event(RadarrEvent::MarkHistoryItemAsFailed(history_item_id).into())
|
||||
.await?;
|
||||
serde_json::to_string_pretty(
|
||||
&serde_json::json!({"message": "Radarr history item marked as 'failed'"}),
|
||||
)?
|
||||
}
|
||||
RadarrCommand::DownloadRelease {
|
||||
guid,
|
||||
indexer_id,
|
||||
|
||||
@@ -31,6 +31,31 @@ mod tests {
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_history_item_as_failed_requires_history_item_id() {
|
||||
let result =
|
||||
Cli::command().try_get_matches_from(["managarr", "radarr", "mark-history-item-as-failed"]);
|
||||
|
||||
assert_err!(&result);
|
||||
assert_eq!(
|
||||
result.unwrap_err().kind(),
|
||||
ErrorKind::MissingRequiredArgument
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mark_history_item_as_failed_requirements_satisfied() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
"managarr",
|
||||
"radarr",
|
||||
"mark-history-item-as-failed",
|
||||
"--history-item-id",
|
||||
"1",
|
||||
]);
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_download_release_requires_movie_id() {
|
||||
let result = Cli::command().try_get_matches_from([
|
||||
@@ -327,6 +352,36 @@ mod tests {
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_mark_history_item_as_failed_command() {
|
||||
let expected_history_item_id = 1;
|
||||
let mut mock_network = MockNetworkTrait::new();
|
||||
mock_network
|
||||
.expect_handle_network_event()
|
||||
.with(eq::<NetworkEvent>(
|
||||
RadarrEvent::MarkHistoryItemAsFailed(expected_history_item_id).into(),
|
||||
))
|
||||
.times(1)
|
||||
.returning(|_| {
|
||||
Ok(Serdeable::Radarr(RadarrSerdeable::Value(
|
||||
json!({"testResponse": "response"}),
|
||||
)))
|
||||
});
|
||||
let app_arc = Arc::new(Mutex::new(App::test_default()));
|
||||
let mark_history_item_as_failed_command =
|
||||
RadarrCommand::MarkHistoryItemAsFailed { history_item_id: 1 };
|
||||
|
||||
let result = RadarrCliHandler::with(
|
||||
&app_arc,
|
||||
mark_history_item_as_failed_command,
|
||||
&mut mock_network,
|
||||
)
|
||||
.handle()
|
||||
.await;
|
||||
|
||||
assert_ok!(&result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_download_release_command() {
|
||||
let expected_release_download_body = RadarrReleaseDownloadBody {
|
||||
|
||||
@@ -330,90 +330,7 @@ mod test_utils {
|
||||
#[macro_export]
|
||||
macro_rules! test_handler_delegation {
|
||||
($handler:ident, $base:expr, $active_block:expr) => {
|
||||
let mut app = App::test_default();
|
||||
app.data.sonarr_data.history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.root_folders
|
||||
.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.indexers
|
||||
.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.blocklist
|
||||
.set_items(vec![$crate::models::sonarr_models::BlocklistItem::default()]);
|
||||
app.data.sonarr_data.add_searched_series =
|
||||
Some($crate::models::stateful_table::StatefulTable::default());
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.movies
|
||||
.set_items(vec![$crate::models::radarr_models::Movie::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.collections
|
||||
.set_items(vec![$crate::models::radarr_models::Collection::default()]);
|
||||
app.data.radarr_data.collection_movies.set_items(vec![
|
||||
$crate::models::radarr_models::CollectionMovie::default(),
|
||||
]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.indexers
|
||||
.set_items(vec![$crate::models::servarr_models::Indexer::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.root_folders
|
||||
.set_items(vec![$crate::models::servarr_models::RootFolder::default()]);
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.blocklist
|
||||
.set_items(vec![$crate::models::radarr_models::BlocklistItem::default()]);
|
||||
app.data.radarr_data.add_searched_movies =
|
||||
Some($crate::models::stateful_table::StatefulTable::default());
|
||||
let mut movie_details_modal =
|
||||
$crate::models::servarr_data::radarr::modals::MovieDetailsModal::default();
|
||||
movie_details_modal.movie_history.set_items(vec![
|
||||
$crate::models::radarr_models::MovieHistoryItem::default(),
|
||||
]);
|
||||
movie_details_modal
|
||||
.movie_cast
|
||||
.set_items(vec![$crate::models::radarr_models::Credit::default()]);
|
||||
movie_details_modal
|
||||
.movie_crew
|
||||
.set_items(vec![$crate::models::radarr_models::Credit::default()]);
|
||||
movie_details_modal
|
||||
.movie_releases
|
||||
.set_items(vec![$crate::models::radarr_models::RadarrRelease::default()]);
|
||||
app.data.radarr_data.movie_details_modal = Some(movie_details_modal);
|
||||
let mut season_details_modal =
|
||||
$crate::models::servarr_data::sonarr::modals::SeasonDetailsModal::default();
|
||||
season_details_modal.season_history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
season_details_modal.episode_details_modal =
|
||||
Some($crate::models::servarr_data::sonarr::modals::EpisodeDetailsModal::default());
|
||||
app.data.sonarr_data.season_details_modal = Some(season_details_modal);
|
||||
let mut series_history = $crate::models::stateful_table::StatefulTable::default();
|
||||
series_history.set_items(vec![
|
||||
$crate::models::sonarr_models::SonarrHistoryItem::default(),
|
||||
]);
|
||||
app.data.sonarr_data.series_history = Some(series_history);
|
||||
app
|
||||
.data
|
||||
.sonarr_data
|
||||
.series
|
||||
.set_items(vec![$crate::models::sonarr_models::Series::default()]);
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack($base.into());
|
||||
app.push_navigation_stack($active_block.into());
|
||||
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::assert_navigation_pushed;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::handlers::lidarr_handlers::downloads::DownloadsHandler;
|
||||
use crate::models::lidarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::download_record;
|
||||
|
||||
mod test_handle_delete {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
|
||||
|
||||
#[test]
|
||||
fn test_delete_download_prompt() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::new(DELETE_KEY, &mut app, ActiveLidarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::DeleteDownloadPrompt.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_download_prompt_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::new(DELETE_KEY, &mut app, ActiveLidarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Downloads.into());
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_left_right_action {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_pushed;
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(1);
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::Artists.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Artists.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(1);
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::History.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::History.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_left_right_prompt_toggle(
|
||||
#[values(
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
DownloadsHandler::new(key, &mut app, active_lidarr_block, None).handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
|
||||
DownloadsHandler::new(key, &mut app, active_lidarr_block, None).handle();
|
||||
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::download_record;
|
||||
|
||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
LidarrEvent::DeleteDownload(1)
|
||||
)]
|
||||
#[case(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt,
|
||||
LidarrEvent::UpdateDownloads
|
||||
)]
|
||||
fn test_downloads_prompt_confirm_submit(
|
||||
#[case] base_route: ActiveLidarrBlock,
|
||||
#[case] prompt_block: ActiveLidarrBlock,
|
||||
#[case] expected_action: LidarrEvent,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![download_record()]);
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::new(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
assert_some_eq_x!(
|
||||
&app.data.lidarr_data.prompt_confirm_action,
|
||||
&expected_action
|
||||
);
|
||||
assert_navigation_popped!(app, base_route.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveLidarrBlock::Downloads, ActiveLidarrBlock::DeleteDownloadPrompt)]
|
||||
#[case(ActiveLidarrBlock::Downloads, ActiveLidarrBlock::UpdateDownloadsPrompt)]
|
||||
fn test_downloads_prompt_decline_submit(
|
||||
#[case] base_route: ActiveLidarrBlock,
|
||||
#[case] prompt_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::new(SUBMIT_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
assert_none!(app.data.lidarr_data.prompt_confirm_action);
|
||||
assert_navigation_popped!(app, base_route.into());
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
|
||||
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveLidarrBlock::Downloads, ActiveLidarrBlock::DeleteDownloadPrompt)]
|
||||
#[case(ActiveLidarrBlock::Downloads, ActiveLidarrBlock::UpdateDownloadsPrompt)]
|
||||
fn test_downloads_prompt_blocks_esc(
|
||||
#[case] base_block: ActiveLidarrBlock,
|
||||
#[case] prompt_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(base_block.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
|
||||
DownloadsHandler::new(ESC_KEY, &mut app, prompt_block, None).handle();
|
||||
|
||||
assert_navigation_popped!(app, base_block.into());
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_default_esc(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.error = "test error".to_owned().into();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
DownloadsHandler::new(ESC_KEY, &mut app, ActiveLidarrBlock::Downloads, None).handle();
|
||||
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::Downloads.into());
|
||||
assert_is_empty!(app.error.text);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_key_char {
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::download_record;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
#[test]
|
||||
fn test_update_downloads_key() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.update.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::UpdateDownloadsPrompt.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_downloads_key_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.update.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Downloads.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_downloads_key() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Downloads.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_downloads_key_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Downloads.into());
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
LidarrEvent::DeleteDownload(1)
|
||||
)]
|
||||
#[case(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt,
|
||||
LidarrEvent::UpdateDownloads
|
||||
)]
|
||||
fn test_downloads_prompt_confirm_submit(
|
||||
#[case] base_route: ActiveLidarrBlock,
|
||||
#[case] prompt_block: ActiveLidarrBlock,
|
||||
#[case] expected_action: LidarrEvent,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![download_record()]);
|
||||
app.push_navigation_stack(base_route.into());
|
||||
app.push_navigation_stack(prompt_block.into());
|
||||
|
||||
DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
prompt_block,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
assert_some_eq_x!(
|
||||
&app.data.lidarr_data.prompt_confirm_action,
|
||||
&expected_action
|
||||
);
|
||||
assert_navigation_popped!(app, base_route.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_handler_accepts() {
|
||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||
if DOWNLOADS_BLOCKS.contains(&active_lidarr_block) {
|
||||
assert!(DownloadsHandler::accepts(active_lidarr_block));
|
||||
} else {
|
||||
assert!(!DownloadsHandler::accepts(active_lidarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_handler_ignore_special_keys(
|
||||
#[values(true, false)] ignore_special_keys_for_textbox_input: bool,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.ignore_special_keys_for_textbox_input = ignore_special_keys_for_textbox_input;
|
||||
let handler = DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
handler.ignore_special_keys(),
|
||||
ignore_special_keys_for_textbox_input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_download_id() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![download_record()]);
|
||||
|
||||
let download_id = DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
)
|
||||
.extract_download_id();
|
||||
|
||||
assert_eq!(download_id, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_handler_not_ready_when_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_handler_not_ready_when_downloads_is_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_handler_ready_when_not_loading_and_downloads_is_not_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
app.is_loading = false;
|
||||
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![DownloadRecord::default()]);
|
||||
let handler = DownloadsHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::lidarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
|
||||
use crate::matches_key;
|
||||
use crate::models::Route;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "downloads_handler_tests.rs"]
|
||||
mod downloads_handler_tests;
|
||||
|
||||
pub(super) struct DownloadsHandler<'a, 'b> {
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
_context: Option<ActiveLidarrBlock>,
|
||||
}
|
||||
|
||||
impl DownloadsHandler<'_, '_> {
|
||||
fn extract_download_id(&self) -> i64 {
|
||||
self.app.data.lidarr_data.downloads.current_selection().id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for DownloadsHandler<'a, 'b> {
|
||||
fn handle(&mut self) {
|
||||
let download_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
if !handle_table(
|
||||
self,
|
||||
|app| &mut app.data.lidarr_data.downloads,
|
||||
download_table_handling_config,
|
||||
) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||
DOWNLOADS_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn ignore_special_keys(&self) -> bool {
|
||||
self.app.ignore_special_keys_for_textbox_input
|
||||
}
|
||||
|
||||
fn new(
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: ActiveLidarrBlock,
|
||||
_context: Option<ActiveLidarrBlock>,
|
||||
) -> DownloadsHandler<'a, 'b> {
|
||||
DownloadsHandler {
|
||||
key,
|
||||
app,
|
||||
active_lidarr_block: active_block,
|
||||
_context,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn is_ready(&self) -> bool {
|
||||
!self.app.is_loading && !self.app.data.lidarr_data.downloads.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {
|
||||
if self.active_lidarr_block == ActiveLidarrBlock::Downloads {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveLidarrBlock::DeleteDownloadPrompt.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_left_right_action(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::Downloads => handle_change_tab_left_right_keys(self.app, self.key),
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt | ActiveLidarrBlock::UpdateDownloadsPrompt => {
|
||||
handle_prompt_toggle(self.app, self.key)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt => {
|
||||
if self.app.data.lidarr_data.prompt_confirm {
|
||||
self.app.data.lidarr_data.prompt_confirm_action =
|
||||
Some(LidarrEvent::DeleteDownload(self.extract_download_id()));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt => {
|
||||
if self.app.data.lidarr_data.prompt_confirm {
|
||||
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::UpdateDownloads);
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt | ActiveLidarrBlock::UpdateDownloadsPrompt => {
|
||||
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) {
|
||||
let key = self.key;
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::Downloads => match self.key {
|
||||
_ if matches_key!(update, key) => {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveLidarrBlock::UpdateDownloadsPrompt.into());
|
||||
}
|
||||
_ if matches_key!(refresh, key) => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt => {
|
||||
if matches_key!(confirm, key) {
|
||||
self.app.data.lidarr_data.prompt_confirm = true;
|
||||
self.app.data.lidarr_data.prompt_confirm_action =
|
||||
Some(LidarrEvent::DeleteDownload(self.extract_download_id()));
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt => {
|
||||
if matches_key!(confirm, key) {
|
||||
self.app.data.lidarr_data.prompt_confirm = true;
|
||||
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::UpdateDownloads);
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn app_mut(&mut self) -> &mut App<'b> {
|
||||
self.app
|
||||
}
|
||||
|
||||
fn current_route(&self) -> Route {
|
||||
self.app.get_current_route()
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ mod tests {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::History.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(1);
|
||||
app.data.lidarr_data.main_tabs.set_index(2);
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
@@ -41,9 +41,9 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::Artists.into()
|
||||
ActiveLidarrBlock::Downloads.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Artists.into());
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Downloads.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -51,7 +51,7 @@ mod tests {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::History.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(1);
|
||||
app.data.lidarr_data.main_tabs.set_index(2);
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
@@ -63,9 +63,12 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::Artists.into()
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
mod tests {
|
||||
use crate::app::App;
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::assert_navigation_pushed;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::handlers::lidarr_handlers::{LidarrHandler, handle_change_tab_left_right_keys};
|
||||
use crate::models::lidarr_models::Artist;
|
||||
use crate::models::lidarr_models::LidarrHistoryItem;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock;
|
||||
use crate::models::servarr_data::lidarr::modals::EditArtistModal;
|
||||
use crate::{assert_navigation_pushed, test_handler_delegation};
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use strum::IntoEnumIterator;
|
||||
@@ -55,8 +52,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(0, ActiveLidarrBlock::History, ActiveLidarrBlock::History)]
|
||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::Artists)]
|
||||
#[case(0, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::Downloads)]
|
||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
||||
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
||||
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Artists)]
|
||||
fn test_lidarr_handler_change_tab_left_right_keys(
|
||||
#[case] index: usize,
|
||||
#[case] left_block: ActiveLidarrBlock,
|
||||
@@ -85,8 +84,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(0, ActiveLidarrBlock::History, ActiveLidarrBlock::History)]
|
||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::Artists)]
|
||||
#[case(0, ActiveLidarrBlock::RootFolders, ActiveLidarrBlock::Downloads)]
|
||||
#[case(1, ActiveLidarrBlock::Artists, ActiveLidarrBlock::History)]
|
||||
#[case(2, ActiveLidarrBlock::Downloads, ActiveLidarrBlock::RootFolders)]
|
||||
#[case(3, ActiveLidarrBlock::History, ActiveLidarrBlock::Artists)]
|
||||
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation(
|
||||
#[case] index: usize,
|
||||
#[case] left_block: ActiveLidarrBlock,
|
||||
@@ -116,7 +117,9 @@ mod tests {
|
||||
|
||||
#[rstest]
|
||||
#[case(0, ActiveLidarrBlock::Artists)]
|
||||
#[case(1, ActiveLidarrBlock::History)]
|
||||
#[case(1, ActiveLidarrBlock::Downloads)]
|
||||
#[case(2, ActiveLidarrBlock::History)]
|
||||
#[case(3, ActiveLidarrBlock::RootFolders)]
|
||||
fn test_lidarr_handler_change_tab_left_right_keys_alt_navigation_no_op_when_ignoring_quit_key(
|
||||
#[case] index: usize,
|
||||
#[case] block: ActiveLidarrBlock,
|
||||
@@ -165,25 +168,27 @@ mod tests {
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.artists
|
||||
.set_items(vec![Artist::default()]);
|
||||
app.data.lidarr_data.edit_artist_modal = Some(EditArtistModal::default());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Artists.into());
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
test_handler_delegation!(
|
||||
LidarrHandler,
|
||||
ActiveLidarrBlock::Artists,
|
||||
active_lidarr_block
|
||||
);
|
||||
}
|
||||
|
||||
LidarrHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
active_lidarr_block,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::Artists.into());
|
||||
#[rstest]
|
||||
fn test_delegates_downloads_blocks_to_downloads_handler(
|
||||
#[values(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
test_handler_delegation!(
|
||||
LidarrHandler,
|
||||
ActiveLidarrBlock::Downloads,
|
||||
active_lidarr_block
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@@ -199,23 +204,26 @@ mod tests {
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.history
|
||||
.set_items(vec![LidarrHistoryItem::default()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::History.into());
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
test_handler_delegation!(
|
||||
LidarrHandler,
|
||||
ActiveLidarrBlock::History,
|
||||
active_lidarr_block
|
||||
);
|
||||
}
|
||||
|
||||
LidarrHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
active_lidarr_block,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveLidarrBlock::History.into());
|
||||
#[rstest]
|
||||
fn test_delegates_root_folders_blocks_to_root_folders_handler(
|
||||
#[values(
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
ActiveLidarrBlock::AddRootFolderPrompt,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
test_handler_delegation!(
|
||||
LidarrHandler,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
active_lidarr_block
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ use history::HistoryHandler;
|
||||
use library::LibraryHandler;
|
||||
|
||||
use super::KeyEventHandler;
|
||||
use crate::handlers::lidarr_handlers::downloads::DownloadsHandler;
|
||||
use crate::handlers::lidarr_handlers::root_folders::RootFoldersHandler;
|
||||
use crate::models::Route;
|
||||
use crate::{
|
||||
app::App, event::Key, matches_key, models::servarr_data::lidarr::lidarr_data::ActiveLidarrBlock,
|
||||
@@ -10,9 +12,11 @@ use crate::{
|
||||
mod history;
|
||||
mod library;
|
||||
|
||||
mod downloads;
|
||||
#[cfg(test)]
|
||||
#[path = "lidarr_handler_tests.rs"]
|
||||
mod lidarr_handler_tests;
|
||||
mod root_folders;
|
||||
|
||||
pub(super) struct LidarrHandler<'a, 'b> {
|
||||
key: Key,
|
||||
@@ -27,9 +31,16 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for LidarrHandler<'a, 'b
|
||||
_ if LibraryHandler::accepts(self.active_lidarr_block) => {
|
||||
LibraryHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
||||
}
|
||||
_ if DownloadsHandler::accepts(self.active_lidarr_block) => {
|
||||
DownloadsHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
||||
}
|
||||
_ if HistoryHandler::accepts(self.active_lidarr_block) => {
|
||||
HistoryHandler::new(self.key, self.app, self.active_lidarr_block, self.context).handle();
|
||||
}
|
||||
_ if RootFoldersHandler::accepts(self.active_lidarr_block) => {
|
||||
RootFoldersHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||
.handle();
|
||||
}
|
||||
_ => self.handle_key_event(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::{KeyEventHandler, handle_prompt_toggle};
|
||||
use crate::models::lidarr_models::AddLidarrRootFolderBody;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock};
|
||||
use crate::models::servarr_data::lidarr::modals::AddRootFolderModal;
|
||||
use crate::models::{Route, Scrollable};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::{handle_text_box_keys, handle_text_box_left_right_keys, matches_key};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "add_root_folder_handler_tests.rs"]
|
||||
mod add_root_folder_handler_tests;
|
||||
|
||||
pub(super) struct AddRootFolderHandler<'a, 'b> {
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
context: Option<ActiveLidarrBlock>,
|
||||
}
|
||||
|
||||
impl AddRootFolderHandler<'_, '_> {
|
||||
fn build_add_root_folder_body(&mut self) -> AddLidarrRootFolderBody {
|
||||
let add_root_folder_modal = self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.take()
|
||||
.expect("AddRootFolderModal is None");
|
||||
|
||||
let tags = add_root_folder_modal.tags.text.clone();
|
||||
|
||||
let AddRootFolderModal {
|
||||
name,
|
||||
path,
|
||||
monitor_list,
|
||||
monitor_new_items_list,
|
||||
quality_profile_list,
|
||||
metadata_profile_list,
|
||||
..
|
||||
} = add_root_folder_modal;
|
||||
|
||||
let quality_profile = quality_profile_list.current_selection();
|
||||
let quality_profile_id = *self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.quality_profile_map
|
||||
.iter()
|
||||
.filter(|(_, value)| *value == quality_profile)
|
||||
.map(|(key, _)| key)
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let metadata_profile = metadata_profile_list.current_selection();
|
||||
let metadata_profile_id = *self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.metadata_profile_map
|
||||
.iter()
|
||||
.filter(|(_, value)| *value == metadata_profile)
|
||||
.map(|(key, _)| key)
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
AddLidarrRootFolderBody {
|
||||
name: name.text,
|
||||
path: path.text,
|
||||
default_quality_profile_id: quality_profile_id,
|
||||
default_metadata_profile_id: metadata_profile_id,
|
||||
default_monitor_option: *monitor_list.current_selection(),
|
||||
default_new_item_monitor_option: *monitor_new_items_list.current_selection(),
|
||||
default_tags: Vec::new(),
|
||||
tag_input_string: Some(tags),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for AddRootFolderHandler<'a, 'b> {
|
||||
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||
ADD_ROOT_FOLDER_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn ignore_special_keys(&self) -> bool {
|
||||
self.app.ignore_special_keys_for_textbox_input
|
||||
}
|
||||
|
||||
fn new(
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: ActiveLidarrBlock,
|
||||
context: Option<ActiveLidarrBlock>,
|
||||
) -> AddRootFolderHandler<'a, 'b> {
|
||||
AddRootFolderHandler {
|
||||
key,
|
||||
app,
|
||||
active_lidarr_block: active_block,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn is_ready(&self) -> bool {
|
||||
!self.app.is_loading && self.app.data.lidarr_data.add_root_folder_modal.is_some()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_list
|
||||
.scroll_up(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_new_items_list
|
||||
.scroll_up(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_up(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.metadata_profile_list
|
||||
.scroll_up(),
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => self.app.data.lidarr_data.selected_block.up(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scroll_down(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_list
|
||||
.scroll_down(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_new_items_list
|
||||
.scroll_down(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_down(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.metadata_profile_list
|
||||
.scroll_down(),
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => self.app.data.lidarr_data.selected_block.down(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_home(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_list
|
||||
.scroll_to_top(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_new_items_list
|
||||
.scroll_to_top(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_to_top(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.metadata_profile_list
|
||||
.scroll_to_top(),
|
||||
ActiveLidarrBlock::AddRootFolderNameInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.name
|
||||
.scroll_home(),
|
||||
ActiveLidarrBlock::AddRootFolderPathInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.path
|
||||
.scroll_home(),
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.tags
|
||||
.scroll_home(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_end(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_list
|
||||
.scroll_to_bottom(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.monitor_new_items_list
|
||||
.scroll_to_bottom(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.quality_profile_list
|
||||
.scroll_to_bottom(),
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.metadata_profile_list
|
||||
.scroll_to_bottom(),
|
||||
ActiveLidarrBlock::AddRootFolderNameInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.name
|
||||
.reset_offset(),
|
||||
ActiveLidarrBlock::AddRootFolderPathInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.path
|
||||
.reset_offset(),
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput => self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.tags
|
||||
.reset_offset(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
fn handle_left_right_action(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => handle_prompt_toggle(self.app, self.key),
|
||||
ActiveLidarrBlock::AddRootFolderNameInput => {
|
||||
handle_text_box_left_right_keys!(
|
||||
self,
|
||||
self.key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.name
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderPathInput => {
|
||||
handle_text_box_left_right_keys!(
|
||||
self,
|
||||
self.key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.path
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput => {
|
||||
handle_text_box_left_right_keys!(
|
||||
self,
|
||||
self.key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.tags
|
||||
)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => {
|
||||
match self.app.data.lidarr_data.selected_block.get_active_block() {
|
||||
ActiveLidarrBlock::AddRootFolderConfirmPrompt => {
|
||||
if self.app.data.lidarr_data.prompt_confirm {
|
||||
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::AddRootFolder(
|
||||
self.build_add_root_folder_body(),
|
||||
));
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems
|
||||
| ActiveLidarrBlock::AddRootFolderSelectQualityProfile
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
self.app.data.lidarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderNameInput
|
||||
| ActiveLidarrBlock::AddRootFolderPathInput
|
||||
| ActiveLidarrBlock::AddRootFolderTagsInput => {
|
||||
self.app.push_navigation_stack(
|
||||
(
|
||||
self.app.data.lidarr_data.selected_block.get_active_block(),
|
||||
self.context,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
self.app.ignore_special_keys_for_textbox_input = true;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems
|
||||
| ActiveLidarrBlock::AddRootFolderSelectQualityProfile
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self.app.pop_navigation_stack(),
|
||||
ActiveLidarrBlock::AddRootFolderNameInput
|
||||
| ActiveLidarrBlock::AddRootFolderPathInput
|
||||
| ActiveLidarrBlock::AddRootFolderTagsInput => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.ignore_special_keys_for_textbox_input = false;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderNameInput
|
||||
| ActiveLidarrBlock::AddRootFolderPathInput
|
||||
| ActiveLidarrBlock::AddRootFolderTagsInput => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.ignore_special_keys_for_textbox_input = false;
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => {
|
||||
self.app.pop_navigation_stack();
|
||||
self.app.data.lidarr_data.add_root_folder_modal = None;
|
||||
self.app.data.lidarr_data.prompt_confirm = false;
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems
|
||||
| ActiveLidarrBlock::AddRootFolderSelectQualityProfile
|
||||
| ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => self.app.pop_navigation_stack(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_char_key_event(&mut self) {
|
||||
let key = self.key;
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderNameInput => {
|
||||
handle_text_box_keys!(
|
||||
self,
|
||||
key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.name
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderPathInput => {
|
||||
handle_text_box_keys!(
|
||||
self,
|
||||
key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.path
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput => {
|
||||
handle_text_box_keys!(
|
||||
self,
|
||||
key,
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.tags
|
||||
)
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderPrompt => {
|
||||
if self.app.data.lidarr_data.selected_block.get_active_block()
|
||||
== ActiveLidarrBlock::AddRootFolderConfirmPrompt
|
||||
&& matches_key!(confirm, key)
|
||||
{
|
||||
self.app.data.lidarr_data.prompt_confirm = true;
|
||||
self.app.data.lidarr_data.prompt_confirm_action = Some(LidarrEvent::AddRootFolder(
|
||||
self.build_add_root_folder_body(),
|
||||
));
|
||||
self.app.should_refresh = true;
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn app_mut(&mut self) -> &mut App<'b> {
|
||||
self.app
|
||||
}
|
||||
|
||||
fn current_route(&self) -> Route {
|
||||
self.app.get_current_route()
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,173 @@
|
||||
use add_root_folder_handler::AddRootFolderHandler;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::lidarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||
use crate::handlers::{KeyEventHandler, handle_clear_errors, handle_prompt_toggle};
|
||||
use crate::matches_key;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ADD_ROOT_FOLDER_SELECTION_BLOCKS, ActiveLidarrBlock, ROOT_FOLDERS_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_data::lidarr::modals::AddRootFolderModal;
|
||||
use crate::models::{BlockSelectionState, Route};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
|
||||
mod add_root_folder_handler;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "root_folders_handler_tests.rs"]
|
||||
mod root_folders_handler_tests;
|
||||
|
||||
pub(super) struct RootFoldersHandler<'a, 'b> {
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
context: Option<ActiveLidarrBlock>,
|
||||
}
|
||||
|
||||
impl RootFoldersHandler<'_, '_> {
|
||||
fn extract_root_folder_id(&self) -> i64 {
|
||||
self
|
||||
.app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.current_selection()
|
||||
.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveLidarrBlock> for RootFoldersHandler<'a, 'b> {
|
||||
fn handle(&mut self) {
|
||||
let root_folders_table_handling_config =
|
||||
TableHandlingConfig::new(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
if AddRootFolderHandler::accepts(self.active_lidarr_block) {
|
||||
return AddRootFolderHandler::new(self.key, self.app, self.active_lidarr_block, self.context)
|
||||
.handle();
|
||||
}
|
||||
|
||||
if !handle_table(
|
||||
self,
|
||||
|app| &mut app.data.lidarr_data.root_folders,
|
||||
root_folders_table_handling_config,
|
||||
) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveLidarrBlock) -> bool {
|
||||
ROOT_FOLDERS_BLOCKS.contains(&active_block) || ADD_ROOT_FOLDER_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn new(
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: ActiveLidarrBlock,
|
||||
context: Option<ActiveLidarrBlock>,
|
||||
) -> RootFoldersHandler<'a, 'b> {
|
||||
RootFoldersHandler {
|
||||
key,
|
||||
app,
|
||||
active_lidarr_block: active_block,
|
||||
context,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn ignore_special_keys(&self) -> bool {
|
||||
self.app.ignore_special_keys_for_textbox_input
|
||||
}
|
||||
|
||||
fn is_ready(&self) -> bool {
|
||||
!self.app.is_loading && !self.app.data.lidarr_data.root_folders.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {
|
||||
if self.active_lidarr_block == ActiveLidarrBlock::RootFolders {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveLidarrBlock::DeleteRootFolderPrompt.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_left_right_action(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::RootFolders => handle_change_tab_left_right_keys(self.app, self.key),
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt => handle_prompt_toggle(self.app, self.key),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
if self.active_lidarr_block == ActiveLidarrBlock::DeleteRootFolderPrompt {
|
||||
if self.app.data.lidarr_data.prompt_confirm {
|
||||
self.app.data.lidarr_data.prompt_confirm_action =
|
||||
Some(LidarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
|
||||
}
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt => {
|
||||
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) {
|
||||
let key = self.key;
|
||||
match self.active_lidarr_block {
|
||||
ActiveLidarrBlock::RootFolders => match self.key {
|
||||
_ if matches_key!(refresh, key) => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ if matches_key!(add, key) => {
|
||||
self.app.data.lidarr_data.add_root_folder_modal =
|
||||
Some(AddRootFolderModal::from(&self.app.data.lidarr_data));
|
||||
self.app.data.lidarr_data.selected_block =
|
||||
BlockSelectionState::new(ADD_ROOT_FOLDER_SELECTION_BLOCKS);
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveLidarrBlock::AddRootFolderPrompt.into());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt => {
|
||||
if matches_key!(confirm, key) {
|
||||
self.app.data.lidarr_data.prompt_confirm = true;
|
||||
self.app.data.lidarr_data.prompt_confirm_action =
|
||||
Some(LidarrEvent::DeleteRootFolder(self.extract_root_folder_id()));
|
||||
|
||||
self.app.pop_navigation_stack();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn app_mut(&mut self) -> &mut App<'b> {
|
||||
self.app
|
||||
}
|
||||
|
||||
fn current_route(&self) -> Route {
|
||||
self.app.get_current_route()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::assert_modal_present;
|
||||
use crate::assert_navigation_pushed;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::handlers::lidarr_handlers::root_folders::RootFoldersHandler;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, ROOT_FOLDERS_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::RootFolder;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::root_folder;
|
||||
|
||||
mod test_handle_delete {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
const DELETE_KEY: Key = DEFAULT_KEYBINDINGS.delete.key;
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
|
||||
RootFoldersHandler::new(DELETE_KEY, &mut app, ActiveLidarrBlock::RootFolders, None).handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::DeleteRootFolderPrompt.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
|
||||
RootFoldersHandler::new(DELETE_KEY, &mut app, ActiveLidarrBlock::RootFolders, None).handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_left_right_action {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_pushed;
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(3);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::History.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::History.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.lidarr_data.main_tabs.set_index(3);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.lidarr_data.main_tabs.get_active_route(),
|
||||
ActiveLidarrBlock::Artists.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::Artists.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_left_right_delete_root_folder_prompt_toggle(
|
||||
#[values(DEFAULT_KEYBINDINGS.left.key, DEFAULT_KEYBINDINGS.right.key)] key: Key,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::root_folder;
|
||||
|
||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt_confirm_submit() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![root_folder()]);
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::DeleteRootFolderPrompt.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
assert_some_eq_x!(
|
||||
&app.data.lidarr_data.prompt_confirm_action,
|
||||
&LidarrEvent::DeleteRootFolder(1)
|
||||
);
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt_decline_submit() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::DeleteRootFolderPrompt.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
SUBMIT_KEY,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
assert_none!(app.data.lidarr_data.prompt_confirm_action);
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
use rstest::rstest;
|
||||
|
||||
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt_block_esc() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::DeleteRootFolderPrompt.into());
|
||||
app.data.lidarr_data.prompt_confirm = true;
|
||||
|
||||
RootFoldersHandler::new(
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
assert!(!app.data.lidarr_data.prompt_confirm);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_default_esc(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.error = "test error".to_owned().into();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
RootFoldersHandler::new(ESC_KEY, &mut app, ActiveLidarrBlock::RootFolders, None).handle();
|
||||
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
assert_is_empty!(app.error.text);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_key_char {
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::root_folder;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_root_folder_add() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.add.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::AddRootFolderPrompt.into());
|
||||
assert_modal_present!(app.data.lidarr_data.add_root_folder_modal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folder_add_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.add.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_none!(app.data.lidarr_data.add_root_folder_modal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_root_folders_key() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_root_folders_key_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_root_folder_prompt_confirm() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![root_folder()]);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.push_navigation_stack(ActiveLidarrBlock::DeleteRootFolderPrompt.into());
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.confirm.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert!(app.data.lidarr_data.prompt_confirm);
|
||||
assert_some_eq_x!(
|
||||
&app.data.lidarr_data.prompt_confirm_action,
|
||||
&LidarrEvent::DeleteRootFolder(1)
|
||||
);
|
||||
assert_navigation_popped!(app, ActiveLidarrBlock::RootFolders.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_handler_accepts() {
|
||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||
if ROOT_FOLDERS_BLOCKS.contains(&active_lidarr_block)
|
||||
|| ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block)
|
||||
{
|
||||
assert!(RootFoldersHandler::accepts(active_lidarr_block));
|
||||
} else {
|
||||
assert!(!RootFoldersHandler::accepts(active_lidarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_handler_ignore_special_keys(
|
||||
#[values(true, false)] ignore_special_keys_for_textbox_input: bool,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.ignore_special_keys_for_textbox_input = ignore_special_keys_for_textbox_input;
|
||||
let handler = RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
handler.ignore_special_keys(),
|
||||
ignore_special_keys_for_textbox_input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_root_folder_id() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![root_folder()]);
|
||||
|
||||
let root_folder_id = RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
None,
|
||||
)
|
||||
.extract_root_folder_id();
|
||||
|
||||
assert_eq!(root_folder_id, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_handler_not_ready_when_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_handler_not_ready_when_root_folders_is_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_handler_ready_when_not_loading_and_root_folders_is_not_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
app.is_loading = false;
|
||||
|
||||
app
|
||||
.data
|
||||
.lidarr_data
|
||||
.root_folders
|
||||
.set_items(vec![RootFolder::default()]);
|
||||
let handler = RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
}
|
||||
}
|
||||
@@ -91,9 +91,9 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::History.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::RootFolders.into());
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::History.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use chrono::DateTime;
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use rstest::rstest;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::app::key_binding::DEFAULT_KEYBINDINGS;
|
||||
use crate::assert_navigation_pushed;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::KeyEventHandler;
|
||||
use crate::handlers::radarr_handlers::history::{HistoryHandler, history_sorting_options};
|
||||
use crate::models::radarr_models::{RadarrHistoryEventType, RadarrHistoryItem};
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, HISTORY_BLOCKS};
|
||||
use crate::models::servarr_models::{Language, Quality, QualityWrapper};
|
||||
|
||||
mod test_handle_left_right_action {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_pushed;
|
||||
|
||||
#[rstest]
|
||||
fn test_history_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(4);
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
ActiveRadarrBlock::Blocklist.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::Blocklist.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_history_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(4);
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_eq!(
|
||||
app.get_current_route(),
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_submit {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
const SUBMIT_KEY: Key = DEFAULT_KEYBINDINGS.submit.key;
|
||||
|
||||
#[test]
|
||||
fn test_history_submit() {
|
||||
let mut app = App::test_default();
|
||||
app.data.radarr_data.history.set_items(history_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
|
||||
HistoryHandler::new(SUBMIT_KEY, &mut app, ActiveRadarrBlock::History, None).handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::HistoryItemDetails.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_submit_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.history.set_items(history_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
|
||||
HistoryHandler::new(SUBMIT_KEY, &mut app, ActiveRadarrBlock::History, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::History.into());
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_esc {
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
|
||||
use crate::models::servarr_data::radarr::radarr_data::radarr_test_utils::utils::create_test_radarr_data;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_popped;
|
||||
|
||||
const ESC_KEY: Key = DEFAULT_KEYBINDINGS.esc.key;
|
||||
|
||||
#[test]
|
||||
fn test_esc_history_item_details() {
|
||||
let mut app = App::test_default();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.set_items(vec![RadarrHistoryItem::default()]);
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::HistoryItemDetails.into());
|
||||
|
||||
HistoryHandler::new(
|
||||
ESC_KEY,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::HistoryItemDetails,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_popped!(app, ActiveRadarrBlock::History.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_default_esc(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.error = "test error".to_owned().into();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.data.radarr_data = create_test_radarr_data();
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.set_items(vec![RadarrHistoryItem::default()]);
|
||||
|
||||
HistoryHandler::new(ESC_KEY, &mut app, ActiveRadarrBlock::History, None).handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::History.into());
|
||||
assert_is_empty!(app.error.text);
|
||||
}
|
||||
}
|
||||
|
||||
mod test_handle_key_char {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_navigation_pushed;
|
||||
|
||||
#[test]
|
||||
fn test_refresh_history_key() {
|
||||
let mut app = App::test_default();
|
||||
app.data.radarr_data.history.set_items(history_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::History.into());
|
||||
assert!(app.should_refresh);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_refresh_history_key_no_op_when_not_ready() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.data.radarr_data.history.set_items(history_vec());
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
|
||||
HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.refresh.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
)
|
||||
.handle();
|
||||
|
||||
assert_eq!(app.get_current_route(), ActiveRadarrBlock::History.into());
|
||||
assert!(!app.should_refresh);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_sorting_options_source_title() {
|
||||
let expected_cmp_fn: fn(&RadarrHistoryItem, &RadarrHistoryItem) -> Ordering = |a, b| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
};
|
||||
let mut expected_history_vec = history_vec();
|
||||
expected_history_vec.sort_by(expected_cmp_fn);
|
||||
|
||||
let sort_option = history_sorting_options()[0].clone();
|
||||
let mut sorted_history_vec = history_vec();
|
||||
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
|
||||
|
||||
assert_eq!(sorted_history_vec, expected_history_vec);
|
||||
assert_str_eq!(sort_option.name, "Source Title");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_sorting_options_event_type() {
|
||||
let expected_cmp_fn: fn(&RadarrHistoryItem, &RadarrHistoryItem) -> Ordering = |a, b| {
|
||||
a.event_type
|
||||
.to_string()
|
||||
.to_lowercase()
|
||||
.cmp(&b.event_type.to_string().to_lowercase())
|
||||
};
|
||||
let mut expected_history_vec = history_vec();
|
||||
expected_history_vec.sort_by(expected_cmp_fn);
|
||||
|
||||
let sort_option = history_sorting_options()[1].clone();
|
||||
let mut sorted_history_vec = history_vec();
|
||||
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
|
||||
|
||||
assert_eq!(sorted_history_vec, expected_history_vec);
|
||||
assert_str_eq!(sort_option.name, "Event Type");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_sorting_options_language() {
|
||||
let expected_cmp_fn: fn(&RadarrHistoryItem, &RadarrHistoryItem) -> Ordering = |a, b| {
|
||||
let default_language = Language {
|
||||
id: 1,
|
||||
name: "_".to_owned(),
|
||||
};
|
||||
let language_a = a.languages.first().unwrap_or(&default_language);
|
||||
let language_b = b.languages.first().unwrap_or(&default_language);
|
||||
|
||||
language_a.cmp(language_b)
|
||||
};
|
||||
let mut expected_history_vec = history_vec();
|
||||
expected_history_vec.sort_by(expected_cmp_fn);
|
||||
|
||||
let sort_option = history_sorting_options()[2].clone();
|
||||
let mut sorted_history_vec = history_vec();
|
||||
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
|
||||
|
||||
assert_eq!(sorted_history_vec, expected_history_vec);
|
||||
assert_str_eq!(sort_option.name, "Language");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_sorting_options_quality() {
|
||||
let expected_cmp_fn: fn(&RadarrHistoryItem, &RadarrHistoryItem) -> Ordering = |a, b| {
|
||||
a.quality
|
||||
.quality
|
||||
.name
|
||||
.to_lowercase()
|
||||
.cmp(&b.quality.quality.name.to_lowercase())
|
||||
};
|
||||
let mut expected_history_vec = history_vec();
|
||||
expected_history_vec.sort_by(expected_cmp_fn);
|
||||
|
||||
let sort_option = history_sorting_options()[3].clone();
|
||||
let mut sorted_history_vec = history_vec();
|
||||
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
|
||||
|
||||
assert_eq!(sorted_history_vec, expected_history_vec);
|
||||
assert_str_eq!(sort_option.name, "Quality");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_sorting_options_date() {
|
||||
let expected_cmp_fn: fn(&RadarrHistoryItem, &RadarrHistoryItem) -> Ordering =
|
||||
|a, b| a.date.cmp(&b.date);
|
||||
let mut expected_history_vec = history_vec();
|
||||
expected_history_vec.sort_by(expected_cmp_fn);
|
||||
|
||||
let sort_option = history_sorting_options()[4].clone();
|
||||
let mut sorted_history_vec = history_vec();
|
||||
sorted_history_vec.sort_by(sort_option.cmp_fn.unwrap());
|
||||
|
||||
assert_eq!(sorted_history_vec, expected_history_vec);
|
||||
assert_str_eq!(sort_option.name, "Date");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if HISTORY_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(HistoryHandler::accepts(active_radarr_block));
|
||||
} else {
|
||||
assert!(!HistoryHandler::accepts(active_radarr_block));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_history_handler_ignore_special_keys(
|
||||
#[values(true, false)] ignore_special_keys_for_textbox_input: bool,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.ignore_special_keys_for_textbox_input = ignore_special_keys_for_textbox_input;
|
||||
let handler = HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
handler.ignore_special_keys(),
|
||||
ignore_special_keys_for_textbox_input
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_handler_not_ready_when_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.is_loading = true;
|
||||
|
||||
let handler = HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_handler_not_ready_when_history_is_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.is_loading = false;
|
||||
|
||||
let handler = HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(!handler.is_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_handler_ready_when_not_loading_and_history_is_not_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
app.is_loading = false;
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.set_items(vec![RadarrHistoryItem::default()]);
|
||||
|
||||
let handler = HistoryHandler::new(
|
||||
DEFAULT_KEYBINDINGS.esc.key,
|
||||
&mut app,
|
||||
ActiveRadarrBlock::History,
|
||||
None,
|
||||
);
|
||||
|
||||
assert!(handler.is_ready());
|
||||
}
|
||||
|
||||
fn history_vec() -> Vec<RadarrHistoryItem> {
|
||||
vec![
|
||||
RadarrHistoryItem {
|
||||
id: 3,
|
||||
source_title: "test 1".into(),
|
||||
movie_id: 1,
|
||||
event_type: RadarrHistoryEventType::Grabbed,
|
||||
languages: vec![Language {
|
||||
id: 1,
|
||||
name: "telgu".to_owned(),
|
||||
}],
|
||||
quality: QualityWrapper {
|
||||
quality: Quality {
|
||||
name: "HD - 1080p".to_owned(),
|
||||
},
|
||||
},
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2024-01-10T07:28:45Z").unwrap()),
|
||||
..RadarrHistoryItem::default()
|
||||
},
|
||||
RadarrHistoryItem {
|
||||
id: 2,
|
||||
source_title: "test 2".into(),
|
||||
movie_id: 2,
|
||||
event_type: RadarrHistoryEventType::DownloadFolderImported,
|
||||
languages: vec![Language {
|
||||
id: 3,
|
||||
name: "chinese".to_owned(),
|
||||
}],
|
||||
quality: QualityWrapper {
|
||||
quality: Quality {
|
||||
name: "SD - 720p".to_owned(),
|
||||
},
|
||||
},
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2024-02-10T07:28:45Z").unwrap()),
|
||||
..RadarrHistoryItem::default()
|
||||
},
|
||||
RadarrHistoryItem {
|
||||
id: 1,
|
||||
source_title: "test 3".into(),
|
||||
movie_id: 3,
|
||||
event_type: RadarrHistoryEventType::MovieFileDeleted,
|
||||
languages: vec![Language {
|
||||
id: 1,
|
||||
name: "english".to_owned(),
|
||||
}],
|
||||
quality: QualityWrapper {
|
||||
quality: Quality {
|
||||
name: "HD - 1080p".to_owned(),
|
||||
},
|
||||
},
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2024-03-10T07:28:45Z").unwrap()),
|
||||
..RadarrHistoryItem::default()
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
use crate::app::App;
|
||||
use crate::event::Key;
|
||||
use crate::handlers::radarr_handlers::handle_change_tab_left_right_keys;
|
||||
use crate::handlers::table_handler::{TableHandlingConfig, handle_table};
|
||||
use crate::handlers::{KeyEventHandler, handle_clear_errors};
|
||||
use crate::matches_key;
|
||||
use crate::models::Route;
|
||||
use crate::models::radarr_models::RadarrHistoryItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, HISTORY_BLOCKS};
|
||||
use crate::models::servarr_models::Language;
|
||||
use crate::models::stateful_table::SortOption;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "history_handler_tests.rs"]
|
||||
mod history_handler_tests;
|
||||
|
||||
pub(super) struct HistoryHandler<'a, 'b> {
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
_context: Option<ActiveRadarrBlock>,
|
||||
}
|
||||
|
||||
impl HistoryHandler<'_, '_> {}
|
||||
|
||||
impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for HistoryHandler<'a, 'b> {
|
||||
fn handle(&mut self) {
|
||||
let history_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::History.into())
|
||||
.sorting_block(ActiveRadarrBlock::HistorySortPrompt.into())
|
||||
.sort_options(history_sorting_options())
|
||||
.searching_block(ActiveRadarrBlock::SearchHistory.into())
|
||||
.search_error_block(ActiveRadarrBlock::SearchHistoryError.into())
|
||||
.search_field_fn(|history| &history.source_title.text)
|
||||
.filtering_block(ActiveRadarrBlock::FilterHistory.into())
|
||||
.filter_error_block(ActiveRadarrBlock::FilterHistoryError.into())
|
||||
.filter_field_fn(|history| &history.source_title.text);
|
||||
|
||||
if !handle_table(
|
||||
self,
|
||||
|app| &mut app.data.radarr_data.history,
|
||||
history_table_handling_config,
|
||||
) {
|
||||
self.handle_key_event();
|
||||
}
|
||||
}
|
||||
|
||||
fn accepts(active_block: ActiveRadarrBlock) -> bool {
|
||||
HISTORY_BLOCKS.contains(&active_block)
|
||||
}
|
||||
|
||||
fn new(
|
||||
key: Key,
|
||||
app: &'a mut App<'b>,
|
||||
active_block: ActiveRadarrBlock,
|
||||
context: Option<ActiveRadarrBlock>,
|
||||
) -> Self {
|
||||
HistoryHandler {
|
||||
key,
|
||||
app,
|
||||
active_radarr_block: active_block,
|
||||
_context: context,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
|
||||
fn ignore_special_keys(&self) -> bool {
|
||||
self.app.ignore_special_keys_for_textbox_input
|
||||
}
|
||||
|
||||
fn is_ready(&self) -> bool {
|
||||
!self.app.is_loading && !self.app.data.radarr_data.history.is_empty()
|
||||
}
|
||||
|
||||
fn handle_scroll_up(&mut self) {}
|
||||
|
||||
fn handle_scroll_down(&mut self) {}
|
||||
|
||||
fn handle_home(&mut self) {}
|
||||
|
||||
fn handle_end(&mut self) {}
|
||||
|
||||
fn handle_delete(&mut self) {}
|
||||
|
||||
fn handle_left_right_action(&mut self) {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::History {
|
||||
handle_change_tab_left_right_keys(self.app, self.key)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_submit(&mut self) {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::History {
|
||||
self
|
||||
.app
|
||||
.push_navigation_stack(ActiveRadarrBlock::HistoryItemDetails.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_esc(&mut self) {
|
||||
if self.active_radarr_block == ActiveRadarrBlock::HistoryItemDetails {
|
||||
self.app.pop_navigation_stack();
|
||||
} else {
|
||||
handle_clear_errors(self.app);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_char_key_event(&mut self) {
|
||||
let key = self.key;
|
||||
if self.active_radarr_block == ActiveRadarrBlock::History {
|
||||
match self.key {
|
||||
_ if matches_key!(refresh, key) => {
|
||||
self.app.should_refresh = true;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn app_mut(&mut self) -> &mut App<'b> {
|
||||
self.app
|
||||
}
|
||||
|
||||
fn current_route(&self) -> Route {
|
||||
self.app.get_current_route()
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::handlers::radarr_handlers) fn history_sorting_options()
|
||||
-> Vec<SortOption<RadarrHistoryItem>> {
|
||||
vec![
|
||||
SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(|a, b| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
}),
|
||||
},
|
||||
SortOption {
|
||||
name: "Event Type",
|
||||
cmp_fn: Some(|a, b| {
|
||||
a.event_type
|
||||
.to_string()
|
||||
.to_lowercase()
|
||||
.cmp(&b.event_type.to_string().to_lowercase())
|
||||
}),
|
||||
},
|
||||
SortOption {
|
||||
name: "Language",
|
||||
cmp_fn: Some(|a, b| {
|
||||
let default_language = Language {
|
||||
id: 1,
|
||||
name: "_".to_owned(),
|
||||
};
|
||||
let language_a = a.languages.first().unwrap_or(&default_language);
|
||||
let language_b = b.languages.first().unwrap_or(&default_language);
|
||||
|
||||
language_a.cmp(language_b)
|
||||
}),
|
||||
},
|
||||
SortOption {
|
||||
name: "Quality",
|
||||
cmp_fn: Some(|a, b| {
|
||||
a.quality
|
||||
.quality
|
||||
.name
|
||||
.to_lowercase()
|
||||
.cmp(&b.quality.quality.name.to_lowercase())
|
||||
}),
|
||||
},
|
||||
SortOption {
|
||||
name: "Date",
|
||||
cmp_fn: Some(|a, b| a.date.cmp(&b.date)),
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -65,7 +65,7 @@ mod tests {
|
||||
fn test_indexers_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
app.data.radarr_data.main_tabs.set_index(6);
|
||||
|
||||
IndexersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
@@ -86,7 +86,7 @@ mod tests {
|
||||
fn test_indexers_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
app.data.radarr_data.main_tabs.set_index(6);
|
||||
|
||||
IndexersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::handlers::KeyEventHandler;
|
||||
use crate::handlers::radarr_handlers::blocklist::BlocklistHandler;
|
||||
use crate::handlers::radarr_handlers::collections::CollectionsHandler;
|
||||
use crate::handlers::radarr_handlers::downloads::DownloadsHandler;
|
||||
use crate::handlers::radarr_handlers::history::HistoryHandler;
|
||||
use crate::handlers::radarr_handlers::indexers::IndexersHandler;
|
||||
use crate::handlers::radarr_handlers::library::LibraryHandler;
|
||||
use crate::handlers::radarr_handlers::root_folders::RootFoldersHandler;
|
||||
@@ -13,6 +14,7 @@ use crate::{App, Key, matches_key};
|
||||
mod blocklist;
|
||||
mod collections;
|
||||
mod downloads;
|
||||
mod history;
|
||||
mod indexers;
|
||||
mod library;
|
||||
mod root_folders;
|
||||
@@ -51,6 +53,9 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for RadarrHandler<'a, 'b
|
||||
_ if DownloadsHandler::accepts(self.active_radarr_block) => {
|
||||
DownloadsHandler::new(self.key, self.app, self.active_radarr_block, self.context).handle()
|
||||
}
|
||||
_ if HistoryHandler::accepts(self.active_radarr_block) => {
|
||||
HistoryHandler::new(self.key, self.app, self.active_radarr_block, self.context).handle()
|
||||
}
|
||||
_ if RootFoldersHandler::accepts(self.active_radarr_block) => {
|
||||
RootFoldersHandler::new(self.key, self.app, self.active_radarr_block, self.context).handle()
|
||||
}
|
||||
|
||||
@@ -16,10 +16,11 @@ mod tests {
|
||||
#[case(0, ActiveRadarrBlock::System, ActiveRadarrBlock::Collections)]
|
||||
#[case(1, ActiveRadarrBlock::Movies, ActiveRadarrBlock::Downloads)]
|
||||
#[case(2, ActiveRadarrBlock::Collections, ActiveRadarrBlock::Blocklist)]
|
||||
#[case(3, ActiveRadarrBlock::Downloads, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(4, ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Indexers)]
|
||||
#[case(5, ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::System)]
|
||||
#[case(6, ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Movies)]
|
||||
#[case(3, ActiveRadarrBlock::Downloads, ActiveRadarrBlock::History)]
|
||||
#[case(4, ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(5, ActiveRadarrBlock::History, ActiveRadarrBlock::Indexers)]
|
||||
#[case(6, ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::System)]
|
||||
#[case(7, ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Movies)]
|
||||
fn test_radarr_handler_change_tab_left_right_keys(
|
||||
#[case] index: usize,
|
||||
#[case] left_block: ActiveRadarrBlock,
|
||||
@@ -51,10 +52,11 @@ mod tests {
|
||||
#[case(0, ActiveRadarrBlock::System, ActiveRadarrBlock::Collections)]
|
||||
#[case(1, ActiveRadarrBlock::Movies, ActiveRadarrBlock::Downloads)]
|
||||
#[case(2, ActiveRadarrBlock::Collections, ActiveRadarrBlock::Blocklist)]
|
||||
#[case(3, ActiveRadarrBlock::Downloads, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(4, ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::Indexers)]
|
||||
#[case(5, ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::System)]
|
||||
#[case(6, ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Movies)]
|
||||
#[case(3, ActiveRadarrBlock::Downloads, ActiveRadarrBlock::History)]
|
||||
#[case(4, ActiveRadarrBlock::Blocklist, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(5, ActiveRadarrBlock::History, ActiveRadarrBlock::Indexers)]
|
||||
#[case(6, ActiveRadarrBlock::RootFolders, ActiveRadarrBlock::System)]
|
||||
#[case(7, ActiveRadarrBlock::Indexers, ActiveRadarrBlock::Movies)]
|
||||
fn test_radarr_handler_change_tab_left_right_keys_alt_navigation(
|
||||
#[case] index: usize,
|
||||
#[case] left_block: ActiveRadarrBlock,
|
||||
@@ -88,9 +90,10 @@ mod tests {
|
||||
#[case(1, ActiveRadarrBlock::Collections)]
|
||||
#[case(2, ActiveRadarrBlock::Downloads)]
|
||||
#[case(3, ActiveRadarrBlock::Blocklist)]
|
||||
#[case(4, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(5, ActiveRadarrBlock::Indexers)]
|
||||
#[case(6, ActiveRadarrBlock::System)]
|
||||
#[case(4, ActiveRadarrBlock::History)]
|
||||
#[case(5, ActiveRadarrBlock::RootFolders)]
|
||||
#[case(6, ActiveRadarrBlock::Indexers)]
|
||||
#[case(7, ActiveRadarrBlock::System)]
|
||||
fn test_radarr_handler_change_tab_left_right_keys_alt_navigation_no_op_when_ignoring_quit_key(
|
||||
#[case] index: usize,
|
||||
#[case] block: ActiveRadarrBlock,
|
||||
@@ -281,6 +284,26 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_delegates_history_blocks_to_history_handler(
|
||||
#[values(
|
||||
ActiveRadarrBlock::History,
|
||||
ActiveRadarrBlock::HistoryItemDetails,
|
||||
ActiveRadarrBlock::HistorySortPrompt,
|
||||
ActiveRadarrBlock::FilterHistory,
|
||||
ActiveRadarrBlock::FilterHistoryError,
|
||||
ActiveRadarrBlock::SearchHistory,
|
||||
ActiveRadarrBlock::SearchHistoryError
|
||||
)]
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
) {
|
||||
test_handler_delegation!(
|
||||
RadarrHandler,
|
||||
ActiveRadarrBlock::History,
|
||||
active_radarr_block
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radarr_handler_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
|
||||
@@ -130,7 +130,7 @@ mod tests {
|
||||
fn test_root_folders_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(4);
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
@@ -142,16 +142,16 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
app.data.radarr_data.main_tabs.get_active_route(),
|
||||
ActiveRadarrBlock::Blocklist.into()
|
||||
ActiveRadarrBlock::History.into()
|
||||
);
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::Blocklist.into());
|
||||
assert_navigation_pushed!(app, ActiveRadarrBlock::History.into());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(4);
|
||||
app.data.radarr_data.main_tabs.set_index(5);
|
||||
|
||||
RootFoldersHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
|
||||
@@ -26,7 +26,7 @@ mod tests {
|
||||
fn test_system_tab_left(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(6);
|
||||
app.data.radarr_data.main_tabs.set_index(7);
|
||||
|
||||
SystemHandler::new(
|
||||
DEFAULT_KEYBINDINGS.left.key,
|
||||
@@ -47,7 +47,7 @@ mod tests {
|
||||
fn test_system_tab_right(#[values(true, false)] is_ready: bool) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = is_ready;
|
||||
app.data.radarr_data.main_tabs.set_index(6);
|
||||
app.data.radarr_data.main_tabs.set_index(7);
|
||||
|
||||
SystemHandler::new(
|
||||
DEFAULT_KEYBINDINGS.right.key,
|
||||
|
||||
@@ -301,6 +301,20 @@ pub struct EditArtistParams {
|
||||
pub clear_tags: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Serialize, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AddLidarrRootFolderBody {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub default_quality_profile_id: i64,
|
||||
pub default_metadata_profile_id: i64,
|
||||
pub default_monitor_option: MonitorType,
|
||||
pub default_new_item_monitor_option: NewItemMonitorType,
|
||||
pub default_tags: Vec<i64>,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub tag_input_string: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Derivative, Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Album {
|
||||
|
||||
@@ -408,6 +408,69 @@ pub struct SystemStatus {
|
||||
pub start_time: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RadarrHistoryWrapper {
|
||||
pub records: Vec<RadarrHistoryItem>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RadarrHistoryData {
|
||||
pub indexer: Option<String>,
|
||||
pub release_group: Option<String>,
|
||||
pub nzb_info_url: Option<String>,
|
||||
pub download_client: Option<String>,
|
||||
pub download_client_name: Option<String>,
|
||||
pub age: Option<String>,
|
||||
pub published_date: Option<DateTime<Utc>>,
|
||||
pub message: Option<String>,
|
||||
pub reason: Option<String>,
|
||||
pub dropped_path: Option<String>,
|
||||
pub imported_path: Option<String>,
|
||||
pub source_path: Option<String>,
|
||||
pub path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Display, EnumDisplayStyle,
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[strum(serialize_all = "camelCase")]
|
||||
pub enum RadarrHistoryEventType {
|
||||
#[default]
|
||||
Unknown,
|
||||
Grabbed,
|
||||
#[display_style(name = "Download Folder Imported")]
|
||||
DownloadFolderImported,
|
||||
#[display_style(name = "Download Failed")]
|
||||
DownloadFailed,
|
||||
#[display_style(name = "Movie File Deleted")]
|
||||
MovieFileDeleted,
|
||||
#[display_style(name = "Movie Folder Imported")]
|
||||
MovieFolderImported,
|
||||
#[display_style(name = "Movie File Renamed")]
|
||||
MovieFileRenamed,
|
||||
#[display_style(name = "Download Ignored")]
|
||||
DownloadIgnored,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RadarrHistoryItem {
|
||||
#[serde(deserialize_with = "super::from_i64")]
|
||||
pub id: i64,
|
||||
pub source_title: HorizontallyScrollableText,
|
||||
#[serde(deserialize_with = "super::from_i64")]
|
||||
pub movie_id: i64,
|
||||
pub quality: QualityWrapper,
|
||||
pub languages: Vec<Language>,
|
||||
pub date: DateTime<Utc>,
|
||||
pub event_type: RadarrHistoryEventType,
|
||||
#[serde(default)]
|
||||
pub data: RadarrHistoryData,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RadarrTask {
|
||||
@@ -461,6 +524,7 @@ serde_enum_from!(
|
||||
Credits(Vec<Credit>),
|
||||
DiskSpaces(Vec<DiskSpace>),
|
||||
DownloadsResponse(DownloadsResponse),
|
||||
HistoryWrapper(RadarrHistoryWrapper),
|
||||
HostConfig(HostConfig),
|
||||
Indexers(Vec<Indexer>),
|
||||
IndexerSettings(IndexerSettings),
|
||||
|
||||
@@ -3,6 +3,9 @@ mod tests {
|
||||
use pretty_assertions::{assert_eq, assert_str_eq};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::models::radarr_models::{
|
||||
RadarrHistoryEventType, RadarrHistoryItem, RadarrHistoryWrapper,
|
||||
};
|
||||
use crate::models::{
|
||||
Serdeable,
|
||||
radarr_models::{
|
||||
@@ -61,6 +64,66 @@ mod tests {
|
||||
assert_str_eq!(MovieMonitor::None.to_display_str(), "None");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radarr_history_event_type_display() {
|
||||
assert_str_eq!(RadarrHistoryEventType::Unknown.to_string(), "unknown");
|
||||
assert_str_eq!(RadarrHistoryEventType::Grabbed.to_string(), "grabbed");
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadFolderImported.to_string(),
|
||||
"downloadFolderImported"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadFailed.to_string(),
|
||||
"downloadFailed"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFileDeleted.to_string(),
|
||||
"movieFileDeleted"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFolderImported.to_string(),
|
||||
"movieFolderImported"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFileRenamed.to_string(),
|
||||
"movieFileRenamed"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadIgnored.to_string(),
|
||||
"downloadIgnored"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radarr_history_event_type_to_display_str() {
|
||||
assert_str_eq!(RadarrHistoryEventType::Unknown.to_display_str(), "Unknown");
|
||||
assert_str_eq!(RadarrHistoryEventType::Grabbed.to_display_str(), "Grabbed");
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadFolderImported.to_display_str(),
|
||||
"Download Folder Imported"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadFailed.to_display_str(),
|
||||
"Download Failed"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFileDeleted.to_display_str(),
|
||||
"Movie File Deleted"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFolderImported.to_display_str(),
|
||||
"Movie Folder Imported"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::MovieFileRenamed.to_display_str(),
|
||||
"Movie File Renamed"
|
||||
);
|
||||
assert_str_eq!(
|
||||
RadarrHistoryEventType::DownloadIgnored.to_display_str(),
|
||||
"Download Ignored"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_download_record_default_indexer_value() {
|
||||
let json = r#"{
|
||||
@@ -235,6 +298,23 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radarr_serdeable_from_history_wrapper() {
|
||||
let history_wrapper = RadarrHistoryWrapper {
|
||||
records: vec![RadarrHistoryItem {
|
||||
id: 1,
|
||||
..RadarrHistoryItem::default()
|
||||
}],
|
||||
};
|
||||
|
||||
let radarr_serdeable: RadarrSerdeable = history_wrapper.clone().into();
|
||||
|
||||
assert_eq!(
|
||||
radarr_serdeable,
|
||||
RadarrSerdeable::HistoryWrapper(history_wrapper)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_radarr_serdeable_from_log_response() {
|
||||
let log_response = LogResponse {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use serde_json::Number;
|
||||
|
||||
use super::modals::{AddArtistModal, EditArtistModal};
|
||||
use crate::app::context_clues::HISTORY_CONTEXT_CLUES;
|
||||
use super::modals::{AddArtistModal, AddRootFolderModal, EditArtistModal};
|
||||
use crate::app::context_clues::{
|
||||
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::lidarr::lidarr_context_clues::{
|
||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||
};
|
||||
@@ -37,6 +39,7 @@ pub struct LidarrData<'a> {
|
||||
pub add_artist_modal: Option<AddArtistModal>,
|
||||
pub add_artist_search: Option<HorizontallyScrollableText>,
|
||||
pub add_import_list_exclusion: bool,
|
||||
pub add_root_folder_modal: Option<AddRootFolderModal>,
|
||||
pub add_searched_artists: Option<StatefulTable<AddArtistSearchResult>>,
|
||||
pub albums: StatefulTable<Album>,
|
||||
pub artist_info_tabs: TabState,
|
||||
@@ -107,6 +110,7 @@ impl<'a> Default for LidarrData<'a> {
|
||||
add_artist_modal: None,
|
||||
add_artist_search: None,
|
||||
add_import_list_exclusion: false,
|
||||
add_root_folder_modal: None,
|
||||
add_searched_artists: None,
|
||||
albums: StatefulTable::default(),
|
||||
artists: StatefulTable::default(),
|
||||
@@ -131,12 +135,24 @@ impl<'a> Default for LidarrData<'a> {
|
||||
contextual_help: Some(&ARTISTS_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "Downloads".to_string(),
|
||||
route: ActiveLidarrBlock::Downloads.into(),
|
||||
contextual_help: Some(&DOWNLOADS_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "History".to_string(),
|
||||
route: ActiveLidarrBlock::History.into(),
|
||||
contextual_help: Some(&HISTORY_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "Root Folders".to_string(),
|
||||
route: ActiveLidarrBlock::RootFolders.into(),
|
||||
contextual_help: Some(&ROOT_FOLDERS_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
]),
|
||||
artist_info_tabs: TabState::new(vec![TabRoute {
|
||||
title: "Albums".to_string(),
|
||||
@@ -186,12 +202,32 @@ impl LidarrData<'_> {
|
||||
.metadata_profile_list
|
||||
.set_items(vec![metadata_profile().name]);
|
||||
|
||||
let mut add_root_folder_modal = AddRootFolderModal {
|
||||
name: "Test Root Folder".into(),
|
||||
path: "/nfs/music".into(),
|
||||
tags: "test".into(),
|
||||
..AddRootFolderModal::default()
|
||||
};
|
||||
add_root_folder_modal
|
||||
.monitor_list
|
||||
.set_items(Vec::from_iter(MonitorType::iter()));
|
||||
add_root_folder_modal
|
||||
.monitor_new_items_list
|
||||
.set_items(Vec::from_iter(NewItemMonitorType::iter()));
|
||||
add_root_folder_modal
|
||||
.quality_profile_list
|
||||
.set_items(vec![quality_profile().name]);
|
||||
add_root_folder_modal
|
||||
.metadata_profile_list
|
||||
.set_items(vec![metadata_profile().name]);
|
||||
|
||||
let mut lidarr_data = LidarrData {
|
||||
delete_files: true,
|
||||
disk_space_vec: vec![diskspace()],
|
||||
quality_profile_map: quality_profile_map(),
|
||||
metadata_profile_map: metadata_profile_map(),
|
||||
edit_artist_modal: Some(edit_artist_modal),
|
||||
add_root_folder_modal: Some(add_root_folder_modal),
|
||||
add_artist_modal: Some(add_artist_modal),
|
||||
tags_map: tags_map(),
|
||||
..LidarrData::default()
|
||||
@@ -243,6 +279,15 @@ pub enum ActiveLidarrBlock {
|
||||
AddArtistSelectQualityProfile,
|
||||
AddArtistSelectRootFolder,
|
||||
AddArtistTagsInput,
|
||||
AddRootFolderPrompt,
|
||||
AddRootFolderConfirmPrompt,
|
||||
AddRootFolderNameInput,
|
||||
AddRootFolderPathInput,
|
||||
AddRootFolderSelectMonitor,
|
||||
AddRootFolderSelectMonitorNewItems,
|
||||
AddRootFolderSelectQualityProfile,
|
||||
AddRootFolderSelectMetadataProfile,
|
||||
AddRootFolderTagsInput,
|
||||
AutomaticallySearchArtistPrompt,
|
||||
DeleteAlbumPrompt,
|
||||
DeleteAlbumConfirmPrompt,
|
||||
@@ -252,6 +297,9 @@ pub enum ActiveLidarrBlock {
|
||||
DeleteArtistConfirmPrompt,
|
||||
DeleteArtistToggleDeleteFile,
|
||||
DeleteArtistToggleAddListExclusion,
|
||||
DeleteDownloadPrompt,
|
||||
DeleteRootFolderPrompt,
|
||||
Downloads,
|
||||
EditArtistPrompt,
|
||||
EditArtistConfirmPrompt,
|
||||
EditArtistPathInput,
|
||||
@@ -267,6 +315,7 @@ pub enum ActiveLidarrBlock {
|
||||
History,
|
||||
HistoryItemDetails,
|
||||
HistorySortPrompt,
|
||||
RootFolders,
|
||||
SearchAlbums,
|
||||
SearchAlbumsError,
|
||||
SearchArtists,
|
||||
@@ -275,6 +324,7 @@ pub enum ActiveLidarrBlock {
|
||||
SearchHistoryError,
|
||||
UpdateAllArtistsPrompt,
|
||||
UpdateAndScanArtistPrompt,
|
||||
UpdateDownloadsPrompt,
|
||||
}
|
||||
|
||||
pub static LIBRARY_BLOCKS: [ActiveLidarrBlock; 7] = [
|
||||
@@ -295,6 +345,12 @@ pub static ARTIST_DETAILS_BLOCKS: [ActiveLidarrBlock; 5] = [
|
||||
ActiveLidarrBlock::UpdateAndScanArtistPrompt,
|
||||
];
|
||||
|
||||
pub static DOWNLOADS_BLOCKS: [ActiveLidarrBlock; 3] = [
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt,
|
||||
];
|
||||
|
||||
pub static HISTORY_BLOCKS: [ActiveLidarrBlock; 7] = [
|
||||
ActiveLidarrBlock::History,
|
||||
ActiveLidarrBlock::HistoryItemDetails,
|
||||
@@ -377,6 +433,34 @@ pub const EDIT_ARTIST_SELECTION_BLOCKS: &[&[ActiveLidarrBlock]] = &[
|
||||
&[ActiveLidarrBlock::EditArtistConfirmPrompt],
|
||||
];
|
||||
|
||||
pub const ROOT_FOLDERS_BLOCKS: [ActiveLidarrBlock; 2] = [
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt,
|
||||
];
|
||||
|
||||
pub static ADD_ROOT_FOLDER_BLOCKS: [ActiveLidarrBlock; 9] = [
|
||||
ActiveLidarrBlock::AddRootFolderPrompt,
|
||||
ActiveLidarrBlock::AddRootFolderConfirmPrompt,
|
||||
ActiveLidarrBlock::AddRootFolderNameInput,
|
||||
ActiveLidarrBlock::AddRootFolderPathInput,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems,
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile,
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput,
|
||||
];
|
||||
|
||||
pub const ADD_ROOT_FOLDER_SELECTION_BLOCKS: &[&[ActiveLidarrBlock]] = &[
|
||||
&[ActiveLidarrBlock::AddRootFolderNameInput],
|
||||
&[ActiveLidarrBlock::AddRootFolderPathInput],
|
||||
&[ActiveLidarrBlock::AddRootFolderSelectMonitor],
|
||||
&[ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems],
|
||||
&[ActiveLidarrBlock::AddRootFolderSelectQualityProfile],
|
||||
&[ActiveLidarrBlock::AddRootFolderSelectMetadataProfile],
|
||||
&[ActiveLidarrBlock::AddRootFolderTagsInput],
|
||||
&[ActiveLidarrBlock::AddRootFolderConfirmPrompt],
|
||||
];
|
||||
|
||||
impl From<ActiveLidarrBlock> for Route {
|
||||
fn from(active_lidarr_block: ActiveLidarrBlock) -> Route {
|
||||
Route::Lidarr(active_lidarr_block, None)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::context_clues::HISTORY_CONTEXT_CLUES;
|
||||
use crate::app::context_clues::{
|
||||
DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::lidarr::lidarr_context_clues::{
|
||||
ARTIST_DETAILS_CONTEXT_CLUES, ARTISTS_CONTEXT_CLUES,
|
||||
};
|
||||
@@ -8,7 +10,8 @@ mod tests {
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ARTIST_BLOCKS, ADD_ARTIST_SELECTION_BLOCKS, ARTIST_DETAILS_BLOCKS, DELETE_ALBUM_BLOCKS,
|
||||
DELETE_ALBUM_SELECTION_BLOCKS, DELETE_ARTIST_BLOCKS, DELETE_ARTIST_SELECTION_BLOCKS,
|
||||
EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS, HISTORY_BLOCKS,
|
||||
DOWNLOADS_BLOCKS, EDIT_ARTIST_BLOCKS, EDIT_ARTIST_SELECTION_BLOCKS, HISTORY_BLOCKS,
|
||||
ROOT_FOLDERS_BLOCKS,
|
||||
};
|
||||
use crate::models::{
|
||||
BlockSelectionState, Route,
|
||||
@@ -135,6 +138,7 @@ mod tests {
|
||||
assert_is_empty!(lidarr_data.disk_space_vec);
|
||||
assert_is_empty!(lidarr_data.downloads);
|
||||
assert_none!(lidarr_data.edit_artist_modal);
|
||||
assert_none!(lidarr_data.add_root_folder_modal);
|
||||
assert_is_empty!(lidarr_data.history);
|
||||
assert_is_empty!(lidarr_data.metadata_profile_map);
|
||||
assert!(!lidarr_data.prompt_confirm);
|
||||
@@ -146,7 +150,7 @@ mod tests {
|
||||
assert_is_empty!(lidarr_data.tags_map);
|
||||
assert_is_empty!(lidarr_data.version);
|
||||
|
||||
assert_eq!(lidarr_data.main_tabs.tabs.len(), 2);
|
||||
assert_eq!(lidarr_data.main_tabs.tabs.len(), 4);
|
||||
|
||||
assert_str_eq!(lidarr_data.main_tabs.tabs[0].title, "Library");
|
||||
assert_eq!(
|
||||
@@ -159,17 +163,39 @@ mod tests {
|
||||
);
|
||||
assert_none!(lidarr_data.main_tabs.tabs[0].config);
|
||||
|
||||
assert_str_eq!(lidarr_data.main_tabs.tabs[1].title, "History");
|
||||
assert_str_eq!(lidarr_data.main_tabs.tabs[1].title, "Downloads");
|
||||
assert_eq!(
|
||||
lidarr_data.main_tabs.tabs[1].route,
|
||||
ActiveLidarrBlock::History.into()
|
||||
ActiveLidarrBlock::Downloads.into()
|
||||
);
|
||||
assert_some_eq_x!(
|
||||
&lidarr_data.main_tabs.tabs[1].contextual_help,
|
||||
&HISTORY_CONTEXT_CLUES
|
||||
&DOWNLOADS_CONTEXT_CLUES
|
||||
);
|
||||
assert_none!(lidarr_data.main_tabs.tabs[1].config);
|
||||
|
||||
assert_str_eq!(lidarr_data.main_tabs.tabs[2].title, "History");
|
||||
assert_eq!(
|
||||
lidarr_data.main_tabs.tabs[2].route,
|
||||
ActiveLidarrBlock::History.into()
|
||||
);
|
||||
assert_some_eq_x!(
|
||||
&lidarr_data.main_tabs.tabs[2].contextual_help,
|
||||
&HISTORY_CONTEXT_CLUES
|
||||
);
|
||||
assert_none!(lidarr_data.main_tabs.tabs[2].config);
|
||||
|
||||
assert_str_eq!(lidarr_data.main_tabs.tabs[3].title, "Root Folders");
|
||||
assert_eq!(
|
||||
lidarr_data.main_tabs.tabs[3].route,
|
||||
ActiveLidarrBlock::RootFolders.into()
|
||||
);
|
||||
assert_some_eq_x!(
|
||||
&lidarr_data.main_tabs.tabs[3].contextual_help,
|
||||
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||
);
|
||||
assert_none!(lidarr_data.main_tabs.tabs[3].config);
|
||||
|
||||
assert_eq!(lidarr_data.artist_info_tabs.tabs.len(), 1);
|
||||
assert_str_eq!(lidarr_data.artist_info_tabs.tabs[0].title, "Albums");
|
||||
assert_eq!(
|
||||
@@ -205,6 +231,14 @@ mod tests {
|
||||
assert!(ARTIST_DETAILS_BLOCKS.contains(&ActiveLidarrBlock::UpdateAndScanArtistPrompt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_blocks_contains_expected_blocks() {
|
||||
assert_eq!(DOWNLOADS_BLOCKS.len(), 3);
|
||||
assert!(DOWNLOADS_BLOCKS.contains(&ActiveLidarrBlock::Downloads));
|
||||
assert!(DOWNLOADS_BLOCKS.contains(&ActiveLidarrBlock::DeleteDownloadPrompt));
|
||||
assert!(DOWNLOADS_BLOCKS.contains(&ActiveLidarrBlock::UpdateDownloadsPrompt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_blocks_contains_expected_blocks() {
|
||||
assert_eq!(HISTORY_BLOCKS.len(), 7);
|
||||
@@ -372,4 +406,30 @@ mod tests {
|
||||
);
|
||||
assert_none!(edit_artist_block_iter.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_blocks_contents() {
|
||||
assert_eq!(ROOT_FOLDERS_BLOCKS.len(), 2);
|
||||
assert!(ROOT_FOLDERS_BLOCKS.contains(&ActiveLidarrBlock::RootFolders));
|
||||
assert!(ROOT_FOLDERS_BLOCKS.contains(&ActiveLidarrBlock::DeleteRootFolderPrompt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_blocks_contents() {
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::ADD_ROOT_FOLDER_BLOCKS;
|
||||
assert_eq!(ADD_ROOT_FOLDER_BLOCKS.len(), 9);
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderPrompt));
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderConfirmPrompt));
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderNameInput));
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderPathInput));
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderSelectMonitor));
|
||||
assert!(
|
||||
ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems)
|
||||
);
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderSelectQualityProfile));
|
||||
assert!(
|
||||
ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderSelectMetadataProfile)
|
||||
);
|
||||
assert!(ADD_ROOT_FOLDER_BLOCKS.contains(&ActiveLidarrBlock::AddRootFolderTagsInput));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,3 +113,35 @@ impl From<&LidarrData<'_>> for EditArtistModal {
|
||||
edit_artist_modal
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct AddRootFolderModal {
|
||||
pub name: HorizontallyScrollableText,
|
||||
pub path: HorizontallyScrollableText,
|
||||
pub monitor_list: StatefulList<MonitorType>,
|
||||
pub monitor_new_items_list: StatefulList<NewItemMonitorType>,
|
||||
pub quality_profile_list: StatefulList<String>,
|
||||
pub metadata_profile_list: StatefulList<String>,
|
||||
pub tags: HorizontallyScrollableText,
|
||||
}
|
||||
|
||||
impl From<&LidarrData<'_>> for AddRootFolderModal {
|
||||
fn from(lidarr_data: &LidarrData<'_>) -> AddRootFolderModal {
|
||||
let mut add_root_folder_modal = AddRootFolderModal::default();
|
||||
add_root_folder_modal
|
||||
.monitor_list
|
||||
.set_items(Vec::from_iter(MonitorType::iter()));
|
||||
add_root_folder_modal
|
||||
.monitor_new_items_list
|
||||
.set_items(Vec::from_iter(NewItemMonitorType::iter()));
|
||||
add_root_folder_modal
|
||||
.quality_profile_list
|
||||
.set_items(lidarr_data.sorted_quality_profile_names());
|
||||
add_root_folder_modal
|
||||
.metadata_profile_list
|
||||
.set_items(lidarr_data.sorted_metadata_profile_names());
|
||||
|
||||
add_root_folder_modal
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::models::{HorizontallyScrollableText, ScrollableText};
|
||||
mod modals_tests;
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct MovieDetailsModal {
|
||||
pub movie_details: ScrollableText,
|
||||
pub file_details: String,
|
||||
@@ -97,6 +98,7 @@ impl From<&RadarrData<'_>> for EditIndexerModal {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct EditMovieModal {
|
||||
pub minimum_availability_list: StatefulList<MinimumAvailability>,
|
||||
pub quality_profile_list: StatefulList<String>,
|
||||
@@ -157,6 +159,7 @@ impl From<&RadarrData<'_>> for EditMovieModal {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct AddMovieModal {
|
||||
pub root_folder_list: StatefulList<RootFolder>,
|
||||
pub monitor_list: StatefulList<MovieMonitor>,
|
||||
@@ -186,6 +189,7 @@ impl From<&RadarrData<'_>> for AddMovieModal {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub struct EditCollectionModal {
|
||||
pub monitored: Option<bool>,
|
||||
pub minimum_availability_list: StatefulList<MinimumAvailability>,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::app::context_clues::{
|
||||
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::radarr::radarr_context_clues::{
|
||||
@@ -8,7 +8,7 @@ use crate::app::radarr::radarr_context_clues::{
|
||||
};
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieSearchResult, BlocklistItem, Collection, CollectionMovie, DownloadRecord,
|
||||
IndexerSettings, Movie, RadarrTask,
|
||||
IndexerSettings, Movie, RadarrHistoryItem, RadarrTask,
|
||||
};
|
||||
use crate::models::servarr_data::modals::{EditIndexerModal, IndexerTestResultModalItem};
|
||||
use crate::models::servarr_data::radarr::modals::{
|
||||
@@ -34,7 +34,8 @@ use {
|
||||
crate::network::radarr_network::radarr_network_test_utils::test_utils::{
|
||||
add_movie_search_result, blocklist_item, cast_credit, collection, collection_movie,
|
||||
crew_credit, download_record, indexer, log_line, movie, movie_history_item,
|
||||
quality_profile_map, tags_map, task, torrent_release, updates, usenet_release,
|
||||
quality_profile_map, radarr_history_item, tags_map, task, torrent_release, updates,
|
||||
usenet_release,
|
||||
},
|
||||
crate::network::servarr_test_utils::diskspace,
|
||||
crate::network::servarr_test_utils::indexer_test_result,
|
||||
@@ -62,6 +63,7 @@ pub struct RadarrData<'a> {
|
||||
pub downloads: StatefulTable<DownloadRecord>,
|
||||
pub indexers: StatefulTable<Indexer>,
|
||||
pub blocklist: StatefulTable<BlocklistItem>,
|
||||
pub history: StatefulTable<RadarrHistoryItem>,
|
||||
pub quality_profile_map: BiMap<i64, String>,
|
||||
pub tags_map: BiMap<i64, String>,
|
||||
pub collections: StatefulTable<Collection>,
|
||||
@@ -135,6 +137,7 @@ impl<'a> Default for RadarrData<'a> {
|
||||
downloads: StatefulTable::default(),
|
||||
indexers: StatefulTable::default(),
|
||||
blocklist: StatefulTable::default(),
|
||||
history: StatefulTable::default(),
|
||||
quality_profile_map: BiMap::default(),
|
||||
tags_map: BiMap::default(),
|
||||
collections: StatefulTable::default(),
|
||||
@@ -184,6 +187,12 @@ impl<'a> Default for RadarrData<'a> {
|
||||
contextual_help: Some(&BLOCKLIST_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "History".to_string(),
|
||||
route: ActiveRadarrBlock::History.into(),
|
||||
contextual_help: Some(&HISTORY_CONTEXT_CLUES),
|
||||
config: None,
|
||||
},
|
||||
TabRoute {
|
||||
title: "Root Folders".to_string(),
|
||||
route: ActiveRadarrBlock::RootFolders.into(),
|
||||
@@ -387,6 +396,10 @@ impl RadarrData<'_> {
|
||||
radarr_data.downloads.set_items(vec![download_record()]);
|
||||
radarr_data.blocklist.set_items(vec![blocklist_item()]);
|
||||
radarr_data.blocklist.sorting(vec![sort_option!(id)]);
|
||||
radarr_data.history.set_items(vec![radarr_history_item()]);
|
||||
radarr_data.history.sorting(vec![sort_option!(id)]);
|
||||
radarr_data.history.search = Some("Something".into());
|
||||
radarr_data.history.filter = Some("Something".into());
|
||||
radarr_data.indexers.set_items(vec![indexer()]);
|
||||
radarr_data.indexers.sorting(vec![sort_option!(id)]);
|
||||
radarr_data.indexers.search = Some("Something".into());
|
||||
@@ -420,6 +433,13 @@ pub enum ActiveRadarrBlock {
|
||||
BlocklistClearAllItemsPrompt,
|
||||
BlocklistItemDetails,
|
||||
BlocklistSortPrompt,
|
||||
History,
|
||||
HistoryItemDetails,
|
||||
HistorySortPrompt,
|
||||
FilterHistory,
|
||||
FilterHistoryError,
|
||||
SearchHistory,
|
||||
SearchHistoryError,
|
||||
Collections,
|
||||
CollectionsSortPrompt,
|
||||
CollectionDetails,
|
||||
@@ -538,6 +558,15 @@ pub static BLOCKLIST_BLOCKS: [ActiveRadarrBlock; 5] = [
|
||||
ActiveRadarrBlock::BlocklistClearAllItemsPrompt,
|
||||
ActiveRadarrBlock::BlocklistSortPrompt,
|
||||
];
|
||||
pub static HISTORY_BLOCKS: [ActiveRadarrBlock; 7] = [
|
||||
ActiveRadarrBlock::History,
|
||||
ActiveRadarrBlock::HistoryItemDetails,
|
||||
ActiveRadarrBlock::HistorySortPrompt,
|
||||
ActiveRadarrBlock::FilterHistory,
|
||||
ActiveRadarrBlock::FilterHistoryError,
|
||||
ActiveRadarrBlock::SearchHistory,
|
||||
ActiveRadarrBlock::SearchHistoryError,
|
||||
];
|
||||
pub static ADD_MOVIE_BLOCKS: [ActiveRadarrBlock; 10] = [
|
||||
ActiveRadarrBlock::AddMovieSearchInput,
|
||||
ActiveRadarrBlock::AddMovieSearchResults,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
mod tests {
|
||||
mod radarr_data_tests {
|
||||
use crate::app::context_clues::{
|
||||
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, INDEXERS_CONTEXT_CLUES,
|
||||
ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
BLOCKLIST_CONTEXT_CLUES, DOWNLOADS_CONTEXT_CLUES, HISTORY_CONTEXT_CLUES,
|
||||
INDEXERS_CONTEXT_CLUES, ROOT_FOLDERS_CONTEXT_CLUES, SYSTEM_CONTEXT_CLUES,
|
||||
};
|
||||
use crate::app::radarr::radarr_context_clues::{
|
||||
COLLECTIONS_CONTEXT_CLUES, LIBRARY_CONTEXT_CLUES, MANUAL_MOVIE_SEARCH_CONTEXT_CLUES,
|
||||
@@ -105,41 +105,42 @@ mod tests {
|
||||
fn test_radarr_data_defaults() {
|
||||
let radarr_data = RadarrData::default();
|
||||
|
||||
assert!(radarr_data.root_folders.items.is_empty());
|
||||
assert_is_empty!(radarr_data.root_folders.items);
|
||||
assert_eq!(radarr_data.disk_space_vec, Vec::new());
|
||||
assert!(radarr_data.version.is_empty());
|
||||
assert_is_empty!(radarr_data.version);
|
||||
assert_eq!(radarr_data.start_time, <DateTime<Utc>>::default());
|
||||
assert!(radarr_data.movies.is_empty());
|
||||
assert_is_empty!(radarr_data.movies);
|
||||
assert_eq!(radarr_data.selected_block, BlockSelectionState::default());
|
||||
assert!(radarr_data.downloads.items.is_empty());
|
||||
assert!(radarr_data.indexers.items.is_empty());
|
||||
assert!(radarr_data.blocklist.items.is_empty());
|
||||
assert!(radarr_data.quality_profile_map.is_empty());
|
||||
assert!(radarr_data.tags_map.is_empty());
|
||||
assert!(radarr_data.collections.items.is_empty());
|
||||
assert!(radarr_data.collection_movies.items.is_empty());
|
||||
assert!(radarr_data.logs.items.is_empty());
|
||||
assert!(radarr_data.log_details.items.is_empty());
|
||||
assert!(radarr_data.tasks.items.is_empty());
|
||||
assert!(radarr_data.queued_events.items.is_empty());
|
||||
assert!(radarr_data.updates.get_text().is_empty());
|
||||
assert!(radarr_data.add_movie_search.is_none());
|
||||
assert!(radarr_data.add_movie_modal.is_none());
|
||||
assert!(radarr_data.add_searched_movies.is_none());
|
||||
assert!(radarr_data.edit_movie_modal.is_none());
|
||||
assert!(radarr_data.edit_collection_modal.is_none());
|
||||
assert!(radarr_data.edit_root_folder.is_none());
|
||||
assert!(radarr_data.edit_indexer_modal.is_none());
|
||||
assert!(radarr_data.indexer_settings.is_none());
|
||||
assert!(radarr_data.indexer_test_errors.is_none());
|
||||
assert!(radarr_data.indexer_test_all_results.is_none());
|
||||
assert!(radarr_data.movie_details_modal.is_none());
|
||||
assert!(radarr_data.prompt_confirm_action.is_none());
|
||||
assert_is_empty!(radarr_data.downloads.items);
|
||||
assert_is_empty!(radarr_data.indexers.items);
|
||||
assert_is_empty!(radarr_data.blocklist.items);
|
||||
assert_is_empty!(radarr_data.history.items);
|
||||
assert_is_empty!(radarr_data.quality_profile_map);
|
||||
assert_is_empty!(radarr_data.tags_map);
|
||||
assert_is_empty!(radarr_data.collections.items);
|
||||
assert_is_empty!(radarr_data.collection_movies.items);
|
||||
assert_is_empty!(radarr_data.logs.items);
|
||||
assert_is_empty!(radarr_data.log_details.items);
|
||||
assert_is_empty!(radarr_data.tasks.items);
|
||||
assert_is_empty!(radarr_data.queued_events.items);
|
||||
assert_is_empty!(radarr_data.updates.get_text());
|
||||
assert_none!(&radarr_data.add_movie_search);
|
||||
assert_none!(&radarr_data.add_movie_modal);
|
||||
assert_none!(&radarr_data.add_searched_movies);
|
||||
assert_none!(&radarr_data.edit_movie_modal);
|
||||
assert_none!(&radarr_data.edit_collection_modal);
|
||||
assert_none!(&radarr_data.edit_root_folder);
|
||||
assert_none!(&radarr_data.edit_indexer_modal);
|
||||
assert_none!(&radarr_data.indexer_settings);
|
||||
assert_none!(&radarr_data.indexer_test_errors);
|
||||
assert_none!(&radarr_data.indexer_test_all_results);
|
||||
assert_none!(&radarr_data.movie_details_modal);
|
||||
assert_none!(&radarr_data.prompt_confirm_action);
|
||||
assert!(!radarr_data.prompt_confirm);
|
||||
assert!(!radarr_data.delete_movie_files);
|
||||
assert!(!radarr_data.add_list_exclusion);
|
||||
|
||||
assert_eq!(radarr_data.main_tabs.tabs.len(), 7);
|
||||
assert_eq!(radarr_data.main_tabs.tabs.len(), 8);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[0].title, "Library");
|
||||
assert_eq!(
|
||||
@@ -189,42 +190,54 @@ mod tests {
|
||||
);
|
||||
assert_eq!(radarr_data.main_tabs.tabs[3].config, None);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[4].title, "Root Folders");
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[4].title, "History");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[4].route,
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
ActiveRadarrBlock::History.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[4].contextual_help.is_some());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[4].contextual_help.unwrap(),
|
||||
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||
&HISTORY_CONTEXT_CLUES
|
||||
);
|
||||
assert_eq!(radarr_data.main_tabs.tabs[4].config, None);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[5].title, "Indexers");
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[5].title, "Root Folders");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[5].route,
|
||||
ActiveRadarrBlock::Indexers.into()
|
||||
ActiveRadarrBlock::RootFolders.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[5].contextual_help.is_some());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[5].contextual_help.unwrap(),
|
||||
&INDEXERS_CONTEXT_CLUES
|
||||
&ROOT_FOLDERS_CONTEXT_CLUES
|
||||
);
|
||||
assert_eq!(radarr_data.main_tabs.tabs[5].config, None);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[6].title, "System");
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[6].title, "Indexers");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[6].route,
|
||||
ActiveRadarrBlock::System.into()
|
||||
ActiveRadarrBlock::Indexers.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[6].contextual_help.is_some());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[6].contextual_help.unwrap(),
|
||||
&SYSTEM_CONTEXT_CLUES
|
||||
&INDEXERS_CONTEXT_CLUES
|
||||
);
|
||||
assert_eq!(radarr_data.main_tabs.tabs[6].config, None);
|
||||
|
||||
assert_str_eq!(radarr_data.main_tabs.tabs[7].title, "System");
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[7].route,
|
||||
ActiveRadarrBlock::System.into()
|
||||
);
|
||||
assert!(radarr_data.main_tabs.tabs[7].contextual_help.is_some());
|
||||
assert_eq!(
|
||||
radarr_data.main_tabs.tabs[7].contextual_help.unwrap(),
|
||||
&SYSTEM_CONTEXT_CLUES
|
||||
);
|
||||
assert_eq!(radarr_data.main_tabs.tabs[7].config, None);
|
||||
|
||||
assert_eq!(radarr_data.movie_info_tabs.tabs.len(), 6);
|
||||
|
||||
assert_str_eq!(radarr_data.movie_info_tabs.tabs[0].title, "Details");
|
||||
@@ -334,8 +347,8 @@ mod tests {
|
||||
DELETE_MOVIE_SELECTION_BLOCKS, DOWNLOADS_BLOCKS, EDIT_COLLECTION_BLOCKS,
|
||||
EDIT_COLLECTION_SELECTION_BLOCKS, EDIT_INDEXER_BLOCKS, EDIT_INDEXER_NZB_SELECTION_BLOCKS,
|
||||
EDIT_INDEXER_TORRENT_SELECTION_BLOCKS, EDIT_MOVIE_BLOCKS, EDIT_MOVIE_SELECTION_BLOCKS,
|
||||
INDEXER_SETTINGS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS, LIBRARY_BLOCKS,
|
||||
MOVIE_DETAILS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
||||
HISTORY_BLOCKS, INDEXER_SETTINGS_BLOCKS, INDEXER_SETTINGS_SELECTION_BLOCKS, INDEXERS_BLOCKS,
|
||||
LIBRARY_BLOCKS, MOVIE_DETAILS_BLOCKS, ROOT_FOLDERS_BLOCKS, SYSTEM_DETAILS_BLOCKS,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -388,6 +401,18 @@ mod tests {
|
||||
assert!(BLOCKLIST_BLOCKS.contains(&ActiveRadarrBlock::BlocklistSortPrompt));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_history_blocks_contents() {
|
||||
assert_eq!(HISTORY_BLOCKS.len(), 7);
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::History));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::HistoryItemDetails));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::HistorySortPrompt));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::FilterHistory));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::FilterHistoryError));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::SearchHistory));
|
||||
assert!(HISTORY_BLOCKS.contains(&ActiveRadarrBlock::SearchHistoryError));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_movie_blocks_contents() {
|
||||
assert_eq!(ADD_MOVIE_BLOCKS.len(), 10);
|
||||
|
||||
@@ -2,10 +2,37 @@
|
||||
mod tests {
|
||||
use crate::models::lidarr_models::{DownloadsResponse, LidarrSerdeable};
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::download_record;
|
||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_lidarr_download_event() {
|
||||
let (mock, app, _server) = MockServarrApi::delete()
|
||||
.path("/1")
|
||||
.build_for(LidarrEvent::DeleteDownload(1))
|
||||
.await;
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.lidarr_data
|
||||
.downloads
|
||||
.set_items(vec![download_record()]);
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
assert!(
|
||||
network
|
||||
.handle_lidarr_event(LidarrEvent::DeleteDownload(1))
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_downloads_event() {
|
||||
let downloads_json = json!({
|
||||
@@ -40,4 +67,26 @@ mod tests {
|
||||
assert_eq!(downloads_response, response);
|
||||
assert!(!app.lock().await.data.lidarr_data.downloads.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_update_lidarr_downloads_event() {
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.with_request_body(json!({
|
||||
"name": "RefreshMonitoredDownloads"
|
||||
}))
|
||||
.returns(json!({}))
|
||||
.build_for(LidarrEvent::UpdateDownloads)
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
assert!(
|
||||
network
|
||||
.handle_lidarr_event(LidarrEvent::UpdateDownloads)
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
|
||||
use crate::models::lidarr_models::DownloadsResponse;
|
||||
use crate::models::servarr_models::CommandBody;
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::{Network, RequestMethod};
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "lidarr_downloads_network_tests.rs"]
|
||||
mod lidarr_downloads_network_tests;
|
||||
|
||||
impl Network<'_, '_> {
|
||||
pub(in crate::network::lidarr_network) async fn delete_lidarr_download(
|
||||
&mut self,
|
||||
download_id: i64,
|
||||
) -> Result<()> {
|
||||
let event = LidarrEvent::DeleteDownload(download_id);
|
||||
info!("Deleting Lidarr download for download with id: {download_id}");
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Delete,
|
||||
None::<()>,
|
||||
Some(format!("/{download_id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::lidarr_network) async fn get_lidarr_downloads(
|
||||
&mut self,
|
||||
count: u64,
|
||||
@@ -37,4 +60,22 @@ impl Network<'_, '_> {
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::lidarr_network) async fn update_lidarr_downloads(
|
||||
&mut self,
|
||||
) -> Result<Value> {
|
||||
info!("Updating Lidarr downloads");
|
||||
let event = LidarrEvent::UpdateDownloads;
|
||||
let body = CommandBody {
|
||||
name: "RefreshMonitoredDownloads".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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ mod tests {
|
||||
panic!("Expected LidarrHistoryWrapper")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert!(app.lock().await.data.lidarr_data.history.is_empty());
|
||||
assert_is_empty!(app.lock().await.data.lidarr_data.history);
|
||||
assert!(app.lock().await.data.lidarr_data.history.sort_asc);
|
||||
assert_eq!(history, response);
|
||||
}
|
||||
@@ -194,6 +194,6 @@ mod tests {
|
||||
.await;
|
||||
|
||||
mock.assert_async().await;
|
||||
assert!(result.is_ok());
|
||||
assert_ok!(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ mod tests {
|
||||
assert_str_eq!(event.resource(), "/artist");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_resource_downloads(
|
||||
#[values(LidarrEvent::GetDownloads(0), LidarrEvent::DeleteDownload(0))] event: LidarrEvent,
|
||||
) {
|
||||
assert_str_eq!(event.resource(), "/queue");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_resource_history(#[values(LidarrEvent::GetHistory(0))] event: LidarrEvent) {
|
||||
assert_str_eq!(event.resource(), "/history");
|
||||
@@ -59,7 +66,8 @@ mod tests {
|
||||
#[values(
|
||||
LidarrEvent::UpdateAllArtists,
|
||||
LidarrEvent::TriggerAutomaticArtistSearch(0),
|
||||
LidarrEvent::UpdateAndScanArtist(0)
|
||||
LidarrEvent::UpdateAndScanArtist(0),
|
||||
LidarrEvent::UpdateDownloads
|
||||
)]
|
||||
event: LidarrEvent,
|
||||
) {
|
||||
@@ -79,12 +87,22 @@ mod tests {
|
||||
assert_str_eq!(event.resource(), "/album");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_resource_root_folder(
|
||||
#[values(
|
||||
LidarrEvent::GetRootFolders,
|
||||
LidarrEvent::DeleteRootFolder(0),
|
||||
LidarrEvent::AddRootFolder(Default::default())
|
||||
)]
|
||||
event: LidarrEvent,
|
||||
) {
|
||||
assert_str_eq!(event.resource(), "/rootfolder");
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(LidarrEvent::GetDiskSpace, "/diskspace")]
|
||||
#[case(LidarrEvent::GetDownloads(500), "/queue")]
|
||||
#[case(LidarrEvent::GetMetadataProfiles, "/metadataprofile")]
|
||||
#[case(LidarrEvent::GetQualityProfiles, "/qualityprofile")]
|
||||
#[case(LidarrEvent::GetRootFolders, "/rootfolder")]
|
||||
#[case(LidarrEvent::GetStatus, "/system/status")]
|
||||
#[case(LidarrEvent::GetTags, "/tag")]
|
||||
#[case(LidarrEvent::HealthCheck, "/health")]
|
||||
|
||||
@@ -3,7 +3,8 @@ use log::info;
|
||||
|
||||
use super::{NetworkEvent, NetworkResource};
|
||||
use crate::models::lidarr_models::{
|
||||
AddArtistBody, DeleteParams, EditArtistParams, LidarrSerdeable, MetadataProfile,
|
||||
AddArtistBody, AddLidarrRootFolderBody, DeleteParams, EditArtistParams, LidarrSerdeable,
|
||||
MetadataProfile,
|
||||
};
|
||||
use crate::models::servarr_models::{QualityProfile, Tag};
|
||||
use crate::network::{Network, RequestMethod};
|
||||
@@ -25,9 +26,12 @@ pub mod lidarr_network_test_utils;
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub enum LidarrEvent {
|
||||
AddArtist(AddArtistBody),
|
||||
AddRootFolder(AddLidarrRootFolderBody),
|
||||
AddTag(String),
|
||||
DeleteAlbum(DeleteParams),
|
||||
DeleteArtist(DeleteParams),
|
||||
DeleteDownload(i64),
|
||||
DeleteRootFolder(i64),
|
||||
DeleteTag(i64),
|
||||
EditArtist(EditArtistParams),
|
||||
GetAlbums(i64),
|
||||
@@ -52,6 +56,7 @@ pub enum LidarrEvent {
|
||||
TriggerAutomaticArtistSearch(i64),
|
||||
UpdateAllArtists,
|
||||
UpdateAndScanArtist(i64),
|
||||
UpdateDownloads,
|
||||
}
|
||||
|
||||
impl NetworkResource for LidarrEvent {
|
||||
@@ -69,16 +74,19 @@ impl NetworkResource for LidarrEvent {
|
||||
| LidarrEvent::GetAlbumDetails(_)
|
||||
| LidarrEvent::DeleteAlbum(_) => "/album",
|
||||
LidarrEvent::GetDiskSpace => "/diskspace",
|
||||
LidarrEvent::GetDownloads(_) => "/queue",
|
||||
LidarrEvent::GetDownloads(_) | LidarrEvent::DeleteDownload(_) => "/queue",
|
||||
LidarrEvent::GetHistory(_) => "/history",
|
||||
LidarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
||||
LidarrEvent::GetHostConfig | LidarrEvent::GetSecurityConfig => "/config/host",
|
||||
LidarrEvent::TriggerAutomaticArtistSearch(_)
|
||||
| LidarrEvent::UpdateAllArtists
|
||||
| LidarrEvent::UpdateAndScanArtist(_) => "/command",
|
||||
| LidarrEvent::UpdateAndScanArtist(_)
|
||||
| LidarrEvent::UpdateDownloads => "/command",
|
||||
LidarrEvent::GetMetadataProfiles => "/metadataprofile",
|
||||
LidarrEvent::GetQualityProfiles => "/qualityprofile",
|
||||
LidarrEvent::GetRootFolders => "/rootfolder",
|
||||
LidarrEvent::GetRootFolders
|
||||
| LidarrEvent::AddRootFolder(_)
|
||||
| LidarrEvent::DeleteRootFolder(_) => "/rootfolder",
|
||||
LidarrEvent::GetStatus => "/system/status",
|
||||
LidarrEvent::HealthCheck => "/health",
|
||||
LidarrEvent::SearchNewArtist(_) => "/artist/lookup",
|
||||
@@ -99,12 +107,24 @@ impl Network<'_, '_> {
|
||||
) -> Result<LidarrSerdeable> {
|
||||
match lidarr_event {
|
||||
LidarrEvent::AddTag(tag) => self.add_lidarr_tag(tag).await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::AddRootFolder(path) => self
|
||||
.add_lidarr_root_folder(path)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::DeleteAlbum(params) => {
|
||||
self.delete_album(params).await.map(LidarrSerdeable::from)
|
||||
}
|
||||
LidarrEvent::DeleteArtist(params) => {
|
||||
self.delete_artist(params).await.map(LidarrSerdeable::from)
|
||||
}
|
||||
LidarrEvent::DeleteDownload(download_id) => self
|
||||
.delete_lidarr_download(download_id)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::DeleteRootFolder(root_folder_id) => self
|
||||
.delete_lidarr_root_folder(root_folder_id)
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::DeleteTag(tag_id) => self
|
||||
.delete_lidarr_tag(tag_id)
|
||||
.await
|
||||
@@ -182,6 +202,10 @@ impl Network<'_, '_> {
|
||||
.map(LidarrSerdeable::from),
|
||||
LidarrEvent::EditArtist(params) => self.edit_artist(params).await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::AddArtist(body) => self.add_artist(body).await.map(LidarrSerdeable::from),
|
||||
LidarrEvent::UpdateDownloads => self
|
||||
.update_lidarr_downloads()
|
||||
.await
|
||||
.map(LidarrSerdeable::from),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,125 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::lidarr_models::LidarrSerdeable;
|
||||
use crate::models::lidarr_models::{
|
||||
AddLidarrRootFolderBody, LidarrSerdeable, MonitorType, NewItemMonitorType,
|
||||
};
|
||||
use crate::models::servarr_models::RootFolder;
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::lidarr_network::lidarr_network_test_utils::test_utils::root_folder;
|
||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||
use bimap::BiMap;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_lidarr_root_folder_event() {
|
||||
let expected_add_root_folder_body = AddLidarrRootFolderBody {
|
||||
name: "Music".to_owned(),
|
||||
path: "/nfs/test".to_owned(),
|
||||
default_quality_profile_id: 1,
|
||||
default_metadata_profile_id: 1,
|
||||
default_monitor_option: MonitorType::All,
|
||||
default_new_item_monitor_option: NewItemMonitorType::All,
|
||||
default_tags: vec![],
|
||||
tag_input_string: Some("usenet, testing".to_owned()),
|
||||
};
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.with_request_body(json!({
|
||||
"name": "Music",
|
||||
"path": "/nfs/test",
|
||||
"defaultQualityProfileId": 1,
|
||||
"defaultMetadataProfileId": 1,
|
||||
"defaultMonitorOption": "all",
|
||||
"defaultNewItemMonitorOption": "all",
|
||||
"defaultTags": [1, 2]
|
||||
}))
|
||||
.returns(json!({}))
|
||||
.build_for(LidarrEvent::AddRootFolder(
|
||||
expected_add_root_folder_body.clone(),
|
||||
))
|
||||
.await;
|
||||
app.lock().await.data.lidarr_data.tags_map =
|
||||
BiMap::from_iter([(1, "usenet".to_owned()), (2, "testing".to_owned())]);
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
assert_ok!(
|
||||
network
|
||||
.handle_lidarr_event(LidarrEvent::AddRootFolder(expected_add_root_folder_body))
|
||||
.await
|
||||
);
|
||||
|
||||
mock.assert_async().await;
|
||||
assert_none!(app.lock().await.data.lidarr_data.add_root_folder_modal);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_add_lidarr_root_folder_event_does_not_overwrite_default_tags_vec_when_tag_input_string_is_none()
|
||||
{
|
||||
let expected_add_root_folder_body = AddLidarrRootFolderBody {
|
||||
name: "Music".to_owned(),
|
||||
path: "/nfs/test".to_owned(),
|
||||
default_quality_profile_id: 1,
|
||||
default_metadata_profile_id: 1,
|
||||
default_monitor_option: MonitorType::All,
|
||||
default_new_item_monitor_option: NewItemMonitorType::All,
|
||||
default_tags: vec![1, 2],
|
||||
tag_input_string: None,
|
||||
};
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.with_request_body(json!({
|
||||
"name": "Music",
|
||||
"path": "/nfs/test",
|
||||
"defaultQualityProfileId": 1,
|
||||
"defaultMetadataProfileId": 1,
|
||||
"defaultMonitorOption": "all",
|
||||
"defaultNewItemMonitorOption": "all",
|
||||
"defaultTags": [1, 2]
|
||||
}))
|
||||
.returns(json!({}))
|
||||
.build_for(LidarrEvent::AddRootFolder(
|
||||
expected_add_root_folder_body.clone(),
|
||||
))
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
assert_ok!(
|
||||
network
|
||||
.handle_lidarr_event(LidarrEvent::AddRootFolder(expected_add_root_folder_body))
|
||||
.await
|
||||
);
|
||||
|
||||
mock.assert_async().await;
|
||||
assert_none!(app.lock().await.data.lidarr_data.add_root_folder_modal);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_delete_lidarr_root_folder_event() {
|
||||
let (mock, app, _server) = MockServarrApi::delete()
|
||||
.path("/1")
|
||||
.build_for(LidarrEvent::DeleteRootFolder(1))
|
||||
.await;
|
||||
app.lock().await.server_tabs.set_index(2);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
assert!(
|
||||
network
|
||||
.handle_lidarr_event(LidarrEvent::DeleteRootFolder(1))
|
||||
.await
|
||||
.is_ok()
|
||||
);
|
||||
|
||||
mock.assert_async().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_root_folders_event() {
|
||||
let root_folders_json = json!([{
|
||||
"id": 1,
|
||||
"path": "/music",
|
||||
"path": "/nfs",
|
||||
"accessible": true,
|
||||
"freeSpace": 50000000000i64
|
||||
"freeSpace": 219902325555200i64
|
||||
}]);
|
||||
let response: Vec<RootFolder> = serde_json::from_value(root_folders_json.clone()).unwrap();
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
@@ -34,6 +140,9 @@ mod tests {
|
||||
};
|
||||
|
||||
assert_eq!(root_folders, response);
|
||||
assert!(!app.lock().await.data.lidarr_data.root_folders.is_empty());
|
||||
assert_eq!(
|
||||
app.lock().await.data.lidarr_data.root_folders.items,
|
||||
vec![root_folder()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,66 @@
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
|
||||
use crate::models::lidarr_models::AddLidarrRootFolderBody;
|
||||
use crate::models::servarr_models::RootFolder;
|
||||
use crate::network::lidarr_network::LidarrEvent;
|
||||
use crate::network::{Network, RequestMethod};
|
||||
use anyhow::Result;
|
||||
use log::{debug, info};
|
||||
use serde_json::Value;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "lidarr_root_folders_network_tests.rs"]
|
||||
mod lidarr_root_folders_network_tests;
|
||||
|
||||
impl Network<'_, '_> {
|
||||
pub(in crate::network::lidarr_network) async fn add_lidarr_root_folder(
|
||||
&mut self,
|
||||
mut add_root_folder_body: AddLidarrRootFolderBody,
|
||||
) -> Result<Value> {
|
||||
info!("Adding new root folder to Lidarr");
|
||||
if let Some(tag_input_str) = add_root_folder_body.tag_input_string.as_ref() {
|
||||
let tag_ids_vec = self.extract_and_add_lidarr_tag_ids_vec(tag_input_str).await;
|
||||
add_root_folder_body.default_tags = tag_ids_vec;
|
||||
}
|
||||
let event = LidarrEvent::AddRootFolder(AddLidarrRootFolderBody::default());
|
||||
|
||||
debug!("Add root folder body: {add_root_folder_body:?}");
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Post,
|
||||
Some(add_root_folder_body),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<AddLidarrRootFolderBody, Value>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::lidarr_network) async fn delete_lidarr_root_folder(
|
||||
&mut self,
|
||||
root_folder_id: i64,
|
||||
) -> Result<()> {
|
||||
let event = LidarrEvent::DeleteRootFolder(root_folder_id);
|
||||
info!("Deleting Lidarr root folder for folder with id: {root_folder_id}");
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Delete,
|
||||
None::<()>,
|
||||
Some(format!("/{root_folder_id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), ()>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::lidarr_network) async fn get_lidarr_root_folders(
|
||||
&mut self,
|
||||
) -> Result<Vec<RootFolder>> {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::models::Route;
|
||||
use crate::models::radarr_models::RadarrHistoryWrapper;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::{Network, RequestMethod};
|
||||
use anyhow::Result;
|
||||
use log::info;
|
||||
use serde_json::Value;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "radarr_history_network_tests.rs"]
|
||||
mod radarr_history_network_tests;
|
||||
|
||||
impl Network<'_, '_> {
|
||||
pub(in crate::network::radarr_network) async fn get_radarr_history(
|
||||
&mut self,
|
||||
events: u64,
|
||||
) -> Result<RadarrHistoryWrapper> {
|
||||
info!("Fetching all Radarr history events");
|
||||
let event = RadarrEvent::GetHistory(events);
|
||||
|
||||
let params = format!("pageSize={events}&sortDirection=descending&sortKey=date");
|
||||
let request_props = self
|
||||
.request_props_from(event, RequestMethod::Get, None::<()>, None, Some(params))
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), RadarrHistoryWrapper>(request_props, |history_response, mut app| {
|
||||
if !matches!(
|
||||
app.get_current_route(),
|
||||
Route::Radarr(ActiveRadarrBlock::HistorySortPrompt, _)
|
||||
) {
|
||||
let mut history_vec = history_response.records;
|
||||
history_vec.sort_by(|a, b| a.id.cmp(&b.id));
|
||||
app.data.radarr_data.history.set_items(history_vec);
|
||||
app.data.radarr_data.history.apply_sorting_toggle(false);
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub(in crate::network::radarr_network) async fn mark_radarr_history_item_as_failed(
|
||||
&mut self,
|
||||
history_item_id: i64,
|
||||
) -> Result<Value> {
|
||||
info!("Marking the Radarr history item with ID: {history_item_id} as 'failed'");
|
||||
let event = RadarrEvent::MarkHistoryItemAsFailed(history_item_id);
|
||||
|
||||
let request_props = self
|
||||
.request_props_from(
|
||||
event,
|
||||
RequestMethod::Post,
|
||||
None,
|
||||
Some(format!("/{history_item_id}")),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
self
|
||||
.handle_request::<(), Value>(request_props, |_, _| ())
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::models::radarr_models::{RadarrHistoryItem, RadarrHistoryWrapper, RadarrSerdeable};
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::stateful_table::SortOption;
|
||||
use crate::network::network_tests::test_utils::{MockServarrApi, test_network};
|
||||
use crate::network::radarr_network::RadarrEvent;
|
||||
use crate::network::radarr_network::radarr_network_test_utils::test_utils::radarr_history_item;
|
||||
use pretty_assertions::assert_eq;
|
||||
use rstest::rstest;
|
||||
use serde_json::json;
|
||||
|
||||
#[rstest]
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_radarr_history_event(#[values(true, false)] use_custom_sorting: bool) {
|
||||
let history_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"sourceTitle": "z movie",
|
||||
"movieId": 1007,
|
||||
"quality": { "quality": { "name": "HD - 1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2022-12-30T07:37:56Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS",
|
||||
"downloadClient": "transmission",
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 456,
|
||||
"sourceTitle": "A Movie",
|
||||
"movieId": 2001,
|
||||
"quality": { "quality": { "name": "HD - 1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2022-12-30T07:37:56Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS",
|
||||
"downloadClient": "transmission",
|
||||
}
|
||||
}]});
|
||||
let response: RadarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap();
|
||||
let mut expected_history_items = vec![
|
||||
RadarrHistoryItem {
|
||||
id: 123,
|
||||
movie_id: 1007,
|
||||
source_title: "z movie".into(),
|
||||
..radarr_history_item()
|
||||
},
|
||||
RadarrHistoryItem {
|
||||
id: 456,
|
||||
movie_id: 2001,
|
||||
source_title: "A Movie".into(),
|
||||
..radarr_history_item()
|
||||
},
|
||||
];
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(history_json)
|
||||
.query("pageSize=500&sortDirection=descending&sortKey=date")
|
||||
.build_for(RadarrEvent::GetHistory(500))
|
||||
.await;
|
||||
app.lock().await.data.radarr_data.history.sort_asc = true;
|
||||
if use_custom_sorting {
|
||||
let cmp_fn = |a: &RadarrHistoryItem, b: &RadarrHistoryItem| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
};
|
||||
expected_history_items.sort_by(cmp_fn);
|
||||
|
||||
let history_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.sorting(vec![history_sort_option]);
|
||||
}
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let RadarrSerdeable::HistoryWrapper(history) = network
|
||||
.handle_radarr_event(RadarrEvent::GetHistory(500))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected HistoryWrapper")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_eq!(
|
||||
app.lock().await.data.radarr_data.history.items,
|
||||
expected_history_items
|
||||
);
|
||||
assert!(app.lock().await.data.radarr_data.history.sort_asc);
|
||||
assert_eq!(history, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_get_radarr_history_event_no_op_when_user_is_selecting_sort_options() {
|
||||
let history_json = json!({"records": [{
|
||||
"id": 123,
|
||||
"sourceTitle": "z movie",
|
||||
"movieId": 1007,
|
||||
"quality": { "quality": { "name": "Bluray-1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 456,
|
||||
"sourceTitle": "A Movie",
|
||||
"movieId": 2001,
|
||||
"quality": { "quality": { "name": "Bluray-1080p" } },
|
||||
"languages": [{ "id": 1, "name": "English" }],
|
||||
"date": "2024-02-10T07:28:45Z",
|
||||
"eventType": "grabbed",
|
||||
"data": {
|
||||
"indexer": "DrunkenSlug (Prowlarr)",
|
||||
"releaseGroup": "SPARKS"
|
||||
}
|
||||
}]});
|
||||
let response: RadarrHistoryWrapper = serde_json::from_value(history_json.clone()).unwrap();
|
||||
let (mock, app, _server) = MockServarrApi::get()
|
||||
.returns(history_json)
|
||||
.query("pageSize=500&sortDirection=descending&sortKey=date")
|
||||
.build_for(RadarrEvent::GetHistory(500))
|
||||
.await;
|
||||
app.lock().await.data.radarr_data.history.sort_asc = true;
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.push_navigation_stack(ActiveRadarrBlock::HistorySortPrompt.into());
|
||||
let cmp_fn = |a: &RadarrHistoryItem, b: &RadarrHistoryItem| {
|
||||
a.source_title
|
||||
.text
|
||||
.to_lowercase()
|
||||
.cmp(&b.source_title.text.to_lowercase())
|
||||
};
|
||||
let history_sort_option = SortOption {
|
||||
name: "Source Title",
|
||||
cmp_fn: Some(cmp_fn),
|
||||
};
|
||||
app
|
||||
.lock()
|
||||
.await
|
||||
.data
|
||||
.radarr_data
|
||||
.history
|
||||
.sorting(vec![history_sort_option]);
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let RadarrSerdeable::HistoryWrapper(history) = network
|
||||
.handle_radarr_event(RadarrEvent::GetHistory(500))
|
||||
.await
|
||||
.unwrap()
|
||||
else {
|
||||
panic!("Expected HistoryWrapper")
|
||||
};
|
||||
mock.assert_async().await;
|
||||
assert_is_empty!(app.lock().await.data.radarr_data.history);
|
||||
assert!(app.lock().await.data.radarr_data.history.sort_asc);
|
||||
assert_eq!(history, response);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_handle_mark_radarr_history_item_as_failed_event() {
|
||||
let expected_history_item_id = 1;
|
||||
let (mock, app, _server) = MockServarrApi::post()
|
||||
.returns(json!({}))
|
||||
.path("/1")
|
||||
.build_for(RadarrEvent::MarkHistoryItemAsFailed(
|
||||
expected_history_item_id,
|
||||
))
|
||||
.await;
|
||||
let mut network = test_network(&app);
|
||||
|
||||
let result = network
|
||||
.handle_radarr_event(RadarrEvent::MarkHistoryItemAsFailed(
|
||||
expected_history_item_id,
|
||||
))
|
||||
.await;
|
||||
|
||||
mock.assert_async().await;
|
||||
assert_ok!(result);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use super::NetworkResource;
|
||||
mod blocklist;
|
||||
mod collections;
|
||||
mod downloads;
|
||||
mod history;
|
||||
mod indexers;
|
||||
mod library;
|
||||
mod root_folders;
|
||||
@@ -47,10 +48,12 @@ pub enum RadarrEvent {
|
||||
GetBlocklist,
|
||||
GetCollections,
|
||||
GetDownloads(u64),
|
||||
GetHistory(u64),
|
||||
GetHostConfig,
|
||||
GetIndexers,
|
||||
GetAllIndexerSettings,
|
||||
GetLogs(u64),
|
||||
MarkHistoryItemAsFailed(i64),
|
||||
GetMovieCredits(i64),
|
||||
GetMovieDetails(i64),
|
||||
GetMovieHistory(i64),
|
||||
@@ -86,7 +89,9 @@ impl NetworkResource for RadarrEvent {
|
||||
RadarrEvent::GetBlocklist => "/blocklist?page=1&pageSize=10000",
|
||||
RadarrEvent::GetCollections | RadarrEvent::EditCollection(_) => "/collection",
|
||||
RadarrEvent::GetDownloads(_) | RadarrEvent::DeleteDownload(_) => "/queue",
|
||||
RadarrEvent::GetHistory(_) => "/history",
|
||||
RadarrEvent::GetHostConfig | RadarrEvent::GetSecurityConfig => "/config/host",
|
||||
RadarrEvent::MarkHistoryItemAsFailed(_) => "/history/failed",
|
||||
RadarrEvent::GetIndexers | RadarrEvent::EditIndexer(_) | RadarrEvent::DeleteIndexer(_) => {
|
||||
"/indexer"
|
||||
}
|
||||
@@ -199,6 +204,10 @@ impl Network<'_, '_> {
|
||||
.get_radarr_downloads(count)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetHistory(events) => self
|
||||
.get_radarr_history(events)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetHostConfig => self
|
||||
.get_radarr_host_config()
|
||||
.await
|
||||
@@ -208,6 +217,10 @@ impl Network<'_, '_> {
|
||||
.get_radarr_logs(events)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::MarkHistoryItemAsFailed(history_item_id) => self
|
||||
.mark_radarr_history_item_as_failed(history_item_id)
|
||||
.await
|
||||
.map(RadarrSerdeable::from),
|
||||
RadarrEvent::GetMovieCredits(movie_id) => {
|
||||
self.get_credits(movie_id).await.map(RadarrSerdeable::from)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ pub mod test_utils {
|
||||
use crate::models::radarr_models::{
|
||||
AddMovieSearchResult, BlocklistItem, BlocklistItemMovie, Collection, CollectionMovie, Credit,
|
||||
CreditType, DownloadRecord, DownloadsResponse, IndexerSettings, MediaInfo, MinimumAvailability,
|
||||
Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName,
|
||||
Rating, RatingsList,
|
||||
Movie, MovieCollection, MovieFile, MovieHistoryItem, RadarrHistoryData, RadarrHistoryEventType,
|
||||
RadarrHistoryItem, RadarrRelease, RadarrTask, RadarrTaskName, Rating, RatingsList,
|
||||
};
|
||||
use crate::models::servarr_models::{
|
||||
Indexer, IndexerField, Language, Quality, QualityWrapper, RootFolder,
|
||||
@@ -313,6 +313,24 @@ pub mod test_utils {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn radarr_history_item() -> RadarrHistoryItem {
|
||||
RadarrHistoryItem {
|
||||
id: 1,
|
||||
source_title: HorizontallyScrollableText::from("Test"),
|
||||
movie_id: 1,
|
||||
quality: quality_wrapper(),
|
||||
languages: vec![language()],
|
||||
date: DateTime::from(DateTime::parse_from_rfc3339("2022-12-30T07:37:56Z").unwrap()),
|
||||
event_type: RadarrHistoryEventType::Grabbed,
|
||||
data: RadarrHistoryData {
|
||||
indexer: Some("DrunkenSlug (Prowlarr)".to_owned()),
|
||||
release_group: Some("SPARKS".to_owned()),
|
||||
download_client: Some("transmission".to_owned()),
|
||||
..RadarrHistoryData::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn download_record() -> DownloadRecord {
|
||||
DownloadRecord {
|
||||
title: "Test Download Title".to_owned(),
|
||||
|
||||
@@ -136,7 +136,9 @@ mod test {
|
||||
#[case(RadarrEvent::ClearBlocklist, "/blocklist/bulk")]
|
||||
#[case(RadarrEvent::DeleteBlocklistItem(1), "/blocklist")]
|
||||
#[case(RadarrEvent::GetBlocklist, "/blocklist?page=1&pageSize=10000")]
|
||||
#[case(RadarrEvent::GetHistory(500), "/history")]
|
||||
#[case(RadarrEvent::GetLogs(500), "/log")]
|
||||
#[case(RadarrEvent::MarkHistoryItemAsFailed(1), "/history/failed")]
|
||||
#[case(RadarrEvent::SearchNewMovie(String::new()), "/movie/lookup")]
|
||||
#[case(RadarrEvent::GetMovieCredits(0), "/credit")]
|
||||
#[case(RadarrEvent::GetMovieHistory(0), "/history/movie")]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::lidarr_ui::downloads::DownloadsUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_downloads_ui_accepts() {
|
||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||
if DOWNLOADS_BLOCKS.contains(&active_lidarr_block) {
|
||||
assert!(DownloadsUi::accepts(active_lidarr_block.into()));
|
||||
} else {
|
||||
assert!(!DownloadsUi::accepts(active_lidarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_downloads_ui_renders_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
DownloadsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_downloads_ui_renders_empty_downloads() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::Downloads.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
DownloadsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_downloads_ui_renders(
|
||||
#[values(
|
||||
ActiveLidarrBlock::Downloads,
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt,
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
DownloadsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("downloads_ui_{active_lidarr_block}"), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::lidarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ActiveLidarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::widgets::popup::{Popup, Size};
|
||||
use crate::utils::convert_f64_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "downloads_ui_tests.rs"]
|
||||
mod downloads_ui_tests;
|
||||
|
||||
pub(super) struct DownloadsUi;
|
||||
|
||||
impl DrawUi for DownloadsUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
if let Route::Lidarr(active_lidarr_block, _) = route {
|
||||
return DOWNLOADS_BLOCKS.contains(&active_lidarr_block);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
||||
draw_downloads(f, app, area);
|
||||
|
||||
match active_lidarr_block {
|
||||
ActiveLidarrBlock::DeleteDownloadPrompt => {
|
||||
let prompt = format!(
|
||||
"Do you really want to delete this download: \n{}?",
|
||||
app.data.lidarr_data.downloads.current_selection().title
|
||||
);
|
||||
let confirmation_prompt = ConfirmationPrompt::new()
|
||||
.title("Cancel Download")
|
||||
.prompt(&prompt)
|
||||
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
||||
|
||||
f.render_widget(
|
||||
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||
f.area(),
|
||||
);
|
||||
}
|
||||
ActiveLidarrBlock::UpdateDownloadsPrompt => {
|
||||
let confirmation_prompt = ConfirmationPrompt::new()
|
||||
.title("Update Downloads")
|
||||
.prompt("Do you want to update your downloads?")
|
||||
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
||||
|
||||
f.render_widget(
|
||||
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||
f.area(),
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let current_selection = if app.data.lidarr_data.downloads.items.is_empty() {
|
||||
DownloadRecord::default()
|
||||
} else {
|
||||
app.data.lidarr_data.downloads.current_selection().clone()
|
||||
};
|
||||
|
||||
let downloads_row_mapping = |download_record: &DownloadRecord| {
|
||||
let DownloadRecord {
|
||||
title,
|
||||
size,
|
||||
sizeleft,
|
||||
download_client,
|
||||
indexer,
|
||||
output_path,
|
||||
..
|
||||
} = download_record;
|
||||
|
||||
if output_path.is_some() {
|
||||
output_path.as_ref().unwrap().scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 18),
|
||||
current_selection == *download_record,
|
||||
app.ui_scroll_tick_count == 0,
|
||||
);
|
||||
}
|
||||
|
||||
let percent = if *size == 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
1f64 - (*sizeleft / *size)
|
||||
};
|
||||
let file_size: f64 = convert_f64_to_gb(*size);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(title.to_owned()),
|
||||
Cell::from(format!("{:.0}%", percent * 100.0)),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(
|
||||
output_path
|
||||
.as_ref()
|
||||
.unwrap_or(&HorizontallyScrollableText::default())
|
||||
.to_string(),
|
||||
),
|
||||
Cell::from(indexer.to_owned()),
|
||||
Cell::from(
|
||||
download_client
|
||||
.as_ref()
|
||||
.unwrap_or(&String::new())
|
||||
.to_owned(),
|
||||
),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let downloads_table = ManagarrTable::new(
|
||||
Some(&mut app.data.lidarr_data.downloads),
|
||||
downloads_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.headers([
|
||||
"Title",
|
||||
"Percent Complete",
|
||||
"Size",
|
||||
"Output Path",
|
||||
"Indexer",
|
||||
"Download Client",
|
||||
])
|
||||
.constraints([
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(11),
|
||||
Constraint::Percentage(18),
|
||||
Constraint::Percentage(17),
|
||||
Constraint::Percentage(13),
|
||||
]);
|
||||
|
||||
f.render_widget(downloads_table, area);
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/downloads/downloads_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Title Percent Complete Size Output Path Indexer Download Client
|
||||
=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭──────────────────── Cancel Download ────────────────────╮
|
||||
│ Do you really want to delete this download: │
|
||||
│ Test download title? │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭────────────────────────────╮╭───────────────────────────╮│
|
||||
││ Yes ││ No ││
|
||||
│╰────────────────────────────╯╰───────────────────────────╯│
|
||||
╰───────────────────────────────────────────────────────────╯
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/downloads/downloads_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Title Percent Complete Size Output Path Indexer Download Client
|
||||
=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/downloads/downloads_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Title Percent Complete Size Output Path Indexer Download Client
|
||||
=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────── Update Downloads ────────────────────╮
|
||||
│ Do you want to update your downloads? │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭────────────────────────────╮╭───────────────────────────╮│
|
||||
││ Yes ││ No ││
|
||||
│╰────────────────────────────╯╰───────────────────────────╯│
|
||||
╰───────────────────────────────────────────────────────────╯
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/downloads/downloads_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/downloads/downloads_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Loading ...
|
||||
@@ -13,4 +13,31 @@ mod tests {
|
||||
assert!(LidarrUi::accepts(Route::Lidarr(lidarr_block, None)));
|
||||
}
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use super::*;
|
||||
use crate::app::App;
|
||||
use crate::ui::ui_test_utils::test_utils::{TerminalSize, render_to_string_with_app};
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveLidarrBlock::Artists, 0)]
|
||||
#[case(ActiveLidarrBlock::Downloads, 1)]
|
||||
#[case(ActiveLidarrBlock::History, 2)]
|
||||
#[case(ActiveLidarrBlock::RootFolders, 3)]
|
||||
fn test_lidarr_ui_renders_lidarr_tabs(
|
||||
#[case] active_lidarr_block: ActiveLidarrBlock,
|
||||
#[case] index: usize,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
app.data.lidarr_data.main_tabs.set_index(index);
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
LidarrUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("lidarr_tabs_{active_lidarr_block}"), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ pub(super) fn create_history_event_details(history_item: LidarrHistoryItem) -> V
|
||||
} = data;
|
||||
|
||||
let mut lines = vec![
|
||||
Line::from(format!("Source Title: {}", source_title.text)),
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start())),
|
||||
Line::from(format!("Event Type: {event_type}")),
|
||||
Line::from(format!("Quality: {}", quality.quality.name)),
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start())),
|
||||
Line::from(format!("Date: {date}")),
|
||||
];
|
||||
|
||||
@@ -42,19 +42,19 @@ pub(super) fn create_history_event_details(history_item: LidarrHistoryItem) -> V
|
||||
LidarrHistoryEventType::Grabbed => {
|
||||
lines.push(Line::from(format!(
|
||||
"Indexer: {}",
|
||||
indexer.unwrap_or_default()
|
||||
indexer.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"NZB Info URL: {}",
|
||||
nzb_info_url.unwrap_or_default()
|
||||
nzb_info_url.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Age: {} days",
|
||||
age.unwrap_or("0".to_owned())
|
||||
age.unwrap_or("0".to_owned()).trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Published Date: {}",
|
||||
@@ -62,86 +62,95 @@ pub(super) fn create_history_event_details(history_item: LidarrHistoryItem) -> V
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Download Client: {}",
|
||||
download_client_name.unwrap_or(download_client.unwrap_or_default())
|
||||
download_client_name
|
||||
.unwrap_or(download_client.unwrap_or_default())
|
||||
.trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::DownloadImported => {
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::DownloadFailed => {
|
||||
lines.push(Line::from(format!(
|
||||
"Download Client: {}",
|
||||
download_client_name.unwrap_or(download_client.unwrap_or_default())
|
||||
download_client_name
|
||||
.unwrap_or(download_client.unwrap_or_default())
|
||||
.trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Message: {}",
|
||||
message.unwrap_or_default()
|
||||
message.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Indexer: {}",
|
||||
indexer.unwrap_or_default()
|
||||
indexer.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::TrackFileDeleted => {
|
||||
lines.push(Line::from(format!(
|
||||
"Reason: {}",
|
||||
reason.unwrap_or_default()
|
||||
reason.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::TrackFileImported => {
|
||||
lines.push(Line::from(format!(
|
||||
"Dropped Path: {}",
|
||||
dropped_path.unwrap_or_default()
|
||||
dropped_path.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Imported Path: {}",
|
||||
imported_path.unwrap_or_default()
|
||||
imported_path.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Download Client: {}",
|
||||
download_client_name.unwrap_or(download_client.unwrap_or_default())
|
||||
download_client_name
|
||||
.unwrap_or(download_client.unwrap_or_default())
|
||||
.trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::TrackFileRenamed => {
|
||||
lines.push(Line::from(format!(
|
||||
"Source Path: {}",
|
||||
source_path.unwrap_or_default()
|
||||
source_path.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Path: {}",
|
||||
path.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!("Path: {}", path.unwrap_or_default())));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::TrackFileRetagged => {
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
LidarrHistoryEventType::AlbumImportIncomplete => {
|
||||
lines.push(Line::from(format!(
|
||||
"Status Messages: {}",
|
||||
status_messages.unwrap_or_default()
|
||||
status_messages.unwrap_or_default().trim_start()
|
||||
)));
|
||||
lines.push(Line::from(format!(
|
||||
"Release Group: {}",
|
||||
release_group.unwrap_or_default()
|
||||
release_group.unwrap_or_default().trim_start()
|
||||
)));
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -26,29 +26,35 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Indexer: {}", data.indexer.unwrap()))
|
||||
Line::from(format!("Indexer: {}", data.indexer.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("NZB Info URL: {}", data.nzb_info_url.unwrap()))
|
||||
Line::from(format!(
|
||||
"NZB Info URL: {}",
|
||||
data.nzb_info_url.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[6],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[7],
|
||||
Line::from(format!("Age: {} days", data.age.unwrap()))
|
||||
Line::from(format!("Age: {} days", data.age.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(
|
||||
result[8],
|
||||
@@ -58,7 +64,7 @@ mod tests {
|
||||
result[9],
|
||||
Line::from(format!(
|
||||
"Download Client: {}",
|
||||
data.download_client_name.unwrap()
|
||||
data.download_client_name.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 10);
|
||||
@@ -68,7 +74,7 @@ mod tests {
|
||||
fn test_create_history_event_details_grabbed_uses_download_client_as_fallback() {
|
||||
let mut history_item = lidarr_history_item(LidarrHistoryEventType::Grabbed);
|
||||
history_item.data.download_client_name = None;
|
||||
history_item.data.download_client = Some("Fallback Client".to_owned());
|
||||
history_item.data.download_client = Some("\nFallback Client".to_owned());
|
||||
|
||||
let result = create_history_event_details(history_item);
|
||||
|
||||
@@ -91,17 +97,20 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 5);
|
||||
}
|
||||
@@ -122,32 +131,35 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!(
|
||||
"Download Client: {}",
|
||||
data.download_client_name.unwrap()
|
||||
data.download_client_name.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("Message: {}", data.message.unwrap()))
|
||||
Line::from(format!("Message: {}", data.message.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(
|
||||
result[6],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[7],
|
||||
Line::from(format!("Indexer: {}", data.indexer.unwrap()))
|
||||
Line::from(format!("Indexer: {}", data.indexer.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(result.len(), 8);
|
||||
}
|
||||
@@ -168,21 +180,24 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Reason: {}", data.reason.unwrap()))
|
||||
Line::from(format!("Reason: {}", data.reason.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 6);
|
||||
}
|
||||
@@ -203,32 +218,41 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Dropped Path: {}", data.dropped_path.unwrap()))
|
||||
Line::from(format!(
|
||||
"Dropped Path: {}",
|
||||
data.dropped_path.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("Imported Path: {}", data.imported_path.unwrap()))
|
||||
Line::from(format!(
|
||||
"Imported Path: {}",
|
||||
data.imported_path.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[6],
|
||||
Line::from(format!(
|
||||
"Download Client: {}",
|
||||
data.download_client_name.unwrap()
|
||||
data.download_client_name.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[7],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 8);
|
||||
}
|
||||
@@ -249,25 +273,31 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Source Path: {}", data.source_path.unwrap()))
|
||||
Line::from(format!(
|
||||
"Source Path: {}",
|
||||
data.source_path.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("Path: {}", data.path.unwrap()))
|
||||
Line::from(format!("Path: {}", data.path.unwrap().trim_start()))
|
||||
);
|
||||
assert_eq!(
|
||||
result[6],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 7);
|
||||
}
|
||||
@@ -288,17 +318,20 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 5);
|
||||
}
|
||||
@@ -319,24 +352,27 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(
|
||||
result[4],
|
||||
Line::from(format!(
|
||||
"Status Messages: {}",
|
||||
data.status_messages.unwrap()
|
||||
data.status_messages.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
result[5],
|
||||
Line::from(format!("Release Group: {}", data.release_group.unwrap()))
|
||||
Line::from(format!(
|
||||
"Release Group: {}",
|
||||
data.release_group.unwrap().trim_start()
|
||||
))
|
||||
);
|
||||
assert_eq!(result.len(), 6);
|
||||
}
|
||||
@@ -356,12 +392,12 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
result[0],
|
||||
Line::from(format!("Source Title: {}", source_title.text))
|
||||
Line::from(format!("Source Title: {}", source_title.text.trim_start()))
|
||||
);
|
||||
assert_eq!(result[1], Line::from(format!("Event Type: {event_type}")));
|
||||
assert_eq!(
|
||||
result[2],
|
||||
Line::from(format!("Quality: {}", quality.quality.name))
|
||||
Line::from(format!("Quality: {}", quality.quality.name.trim_start()))
|
||||
);
|
||||
assert_eq!(result[3], Line::from(format!("Date: {date}")));
|
||||
assert_eq!(result[4], Line::from("No additional details available."));
|
||||
@@ -386,13 +422,13 @@ mod tests {
|
||||
fn lidarr_history_item(event_type: LidarrHistoryEventType) -> LidarrHistoryItem {
|
||||
LidarrHistoryItem {
|
||||
id: 1,
|
||||
source_title: "Test Album - Artist Name".into(),
|
||||
source_title: "\nTest Album - Artist Name".into(),
|
||||
album_id: 100,
|
||||
artist_id: 10,
|
||||
event_type,
|
||||
quality: QualityWrapper {
|
||||
quality: Quality {
|
||||
name: "FLAC".to_owned(),
|
||||
name: "\nFLAC".to_owned(),
|
||||
},
|
||||
},
|
||||
date: Utc::now(),
|
||||
@@ -402,20 +438,20 @@ mod tests {
|
||||
|
||||
fn lidarr_history_data() -> LidarrHistoryData {
|
||||
LidarrHistoryData {
|
||||
indexer: Some("Test Indexer".to_owned()),
|
||||
release_group: Some("Test Release Group".to_owned()),
|
||||
nzb_info_url: Some("https://test.url".to_owned()),
|
||||
download_client_name: Some("Test Download Client".to_owned()),
|
||||
download_client: Some("Fallback Download Client".to_owned()),
|
||||
age: Some("7".to_owned()),
|
||||
indexer: Some("\nTest Indexer".to_owned()),
|
||||
release_group: Some("\nTest Release Group".to_owned()),
|
||||
nzb_info_url: Some("\nhttps://test.url".to_owned()),
|
||||
download_client_name: Some("\nTest Download Client".to_owned()),
|
||||
download_client: Some("\nFallback Download Client".to_owned()),
|
||||
age: Some("\n7".to_owned()),
|
||||
published_date: Some(Utc::now()),
|
||||
message: Some("Test failure message".to_owned()),
|
||||
reason: Some("Test deletion reason".to_owned()),
|
||||
dropped_path: Some("/downloads/completed/album".to_owned()),
|
||||
imported_path: Some("/music/artist/album".to_owned()),
|
||||
source_path: Some("/music/artist/old_album_name".to_owned()),
|
||||
path: Some("/music/artist/new_album_name".to_owned()),
|
||||
status_messages: Some("Missing tracks: 1, 2, 3".to_owned()),
|
||||
message: Some("\nTest failure message".to_owned()),
|
||||
reason: Some("\nTest deletion reason".to_owned()),
|
||||
dropped_path: Some("\n/downloads/completed/album".to_owned()),
|
||||
imported_path: Some("\n/music/artist/album".to_owned()),
|
||||
source_path: Some("\n/music/artist/old_album_name".to_owned()),
|
||||
path: Some("\n/music/artist/new_album_name".to_owned()),
|
||||
status_messages: Some("\nMissing tracks: 1, 2, 3".to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-9
@@ -15,6 +15,16 @@ use ratatui::{
|
||||
widgets::Paragraph,
|
||||
};
|
||||
|
||||
use super::{
|
||||
DrawUi, draw_tabs,
|
||||
styles::ManagarrStyle,
|
||||
utils::{
|
||||
borderless_block, layout_block, line_gauge_with_label, line_gauge_with_title, title_block,
|
||||
},
|
||||
widgets::loading_block::LoadingBlock,
|
||||
};
|
||||
use crate::ui::lidarr_ui::downloads::DownloadsUi;
|
||||
use crate::ui::lidarr_ui::root_folders::RootFoldersUi;
|
||||
use crate::{
|
||||
app::App,
|
||||
logos::LIDARR_LOGO,
|
||||
@@ -27,15 +37,7 @@ use crate::{
|
||||
utils::convert_to_gb,
|
||||
};
|
||||
|
||||
use super::{
|
||||
DrawUi, draw_tabs,
|
||||
styles::ManagarrStyle,
|
||||
utils::{
|
||||
borderless_block, layout_block, line_gauge_with_label, line_gauge_with_title, title_block,
|
||||
},
|
||||
widgets::loading_block::LoadingBlock,
|
||||
};
|
||||
|
||||
mod downloads;
|
||||
mod history;
|
||||
mod library;
|
||||
mod lidarr_ui_utils;
|
||||
@@ -43,6 +45,7 @@ mod lidarr_ui_utils;
|
||||
#[cfg(test)]
|
||||
#[path = "lidarr_ui_tests.rs"]
|
||||
mod lidarr_ui_tests;
|
||||
mod root_folders;
|
||||
|
||||
pub(super) struct LidarrUi;
|
||||
|
||||
@@ -57,7 +60,9 @@ impl DrawUi for LidarrUi {
|
||||
|
||||
match route {
|
||||
_ if LibraryUi::accepts(route) => LibraryUi::draw(f, app, content_area),
|
||||
_ if DownloadsUi::accepts(route) => DownloadsUi::draw(f, app, content_area),
|
||||
_ if HistoryUi::accepts(route) => HistoryUi::draw(f, app, content_area),
|
||||
_ if RootFoldersUi::accepts(route) => RootFoldersUi::draw(f, app, content_area),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::prelude::Layout;
|
||||
use ratatui::widgets::ListItem;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock};
|
||||
use crate::models::servarr_data::lidarr::modals::AddRootFolderModal;
|
||||
use crate::render_selectable_input_box;
|
||||
|
||||
use crate::ui::utils::title_block_centered;
|
||||
use crate::ui::widgets::button::Button;
|
||||
use crate::ui::widgets::input_box::InputBox;
|
||||
use crate::ui::widgets::popup::{Popup, Size};
|
||||
use crate::ui::widgets::selectable_list::SelectableList;
|
||||
use crate::ui::{DrawUi, draw_popup};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "add_root_folder_ui_tests.rs"]
|
||||
mod add_root_folder_ui_tests;
|
||||
|
||||
pub(super) struct AddRootFolderUi;
|
||||
|
||||
impl DrawUi for AddRootFolderUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
let Route::Lidarr(active_lidarr_block, _) = route else {
|
||||
return false;
|
||||
};
|
||||
ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block)
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
||||
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
||||
let draw_add_root_folder_prompt =
|
||||
|f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: Rect| {
|
||||
draw_add_root_folder_confirmation_prompt(f, app, prompt_area);
|
||||
|
||||
match active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor => {
|
||||
draw_add_root_folder_select_monitor_popup(f, app);
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems => {
|
||||
draw_add_root_folder_select_monitor_new_items_popup(f, app);
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile => {
|
||||
draw_add_root_folder_select_quality_profile_popup(f, app);
|
||||
}
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile => {
|
||||
draw_add_root_folder_select_metadata_profile_popup(f, app);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
|
||||
draw_popup(f, app, draw_add_root_folder_prompt, Size::Long);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_confirmation_prompt(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let title = "Add Root Folder";
|
||||
f.render_widget(title_block_centered(title), area);
|
||||
|
||||
let yes_no_value = app.data.lidarr_data.prompt_confirm;
|
||||
let selected_block = app.data.lidarr_data.selected_block.get_active_block();
|
||||
let highlight_yes_no = selected_block == ActiveLidarrBlock::AddRootFolderConfirmPrompt;
|
||||
let AddRootFolderModal {
|
||||
name,
|
||||
path,
|
||||
monitor_list,
|
||||
monitor_new_items_list,
|
||||
quality_profile_list,
|
||||
metadata_profile_list,
|
||||
tags,
|
||||
} = app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_ref()
|
||||
.expect("add_root_folder_modal must exist in this context");
|
||||
let selected_monitor = monitor_list.current_selection();
|
||||
let selected_monitor_new_items = monitor_new_items_list.current_selection();
|
||||
let selected_quality_profile = quality_profile_list.current_selection();
|
||||
let selected_metadata_profile = metadata_profile_list.current_selection();
|
||||
|
||||
let [
|
||||
_,
|
||||
name_area,
|
||||
path_area,
|
||||
monitor_area,
|
||||
monitor_new_items_area,
|
||||
quality_profile_area,
|
||||
metadata_profile_area,
|
||||
tags_area,
|
||||
_,
|
||||
buttons_area,
|
||||
] = Layout::vertical([
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Length(3),
|
||||
])
|
||||
.margin(1)
|
||||
.areas(area);
|
||||
let [save_area, cancel_area] =
|
||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.areas(buttons_area);
|
||||
|
||||
let monitor_drop_down_button = Button::default()
|
||||
.title(selected_monitor.to_display_str())
|
||||
.label("Monitor")
|
||||
.icon("▼")
|
||||
.selected(selected_block == ActiveLidarrBlock::AddRootFolderSelectMonitor);
|
||||
let monitor_new_items_drop_down_button = Button::default()
|
||||
.title(selected_monitor_new_items.to_display_str())
|
||||
.label("Monitor New Albums")
|
||||
.icon("▼")
|
||||
.selected(selected_block == ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems);
|
||||
let quality_profile_drop_down_button = Button::default()
|
||||
.title(selected_quality_profile)
|
||||
.label("Quality Profile")
|
||||
.icon("▼")
|
||||
.selected(selected_block == ActiveLidarrBlock::AddRootFolderSelectQualityProfile);
|
||||
let metadata_profile_drop_down_button = Button::default()
|
||||
.title(selected_metadata_profile)
|
||||
.label("Metadata Profile")
|
||||
.icon("▼")
|
||||
.selected(selected_block == ActiveLidarrBlock::AddRootFolderSelectMetadataProfile);
|
||||
|
||||
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
||||
let name_input_box = InputBox::new(&name.text)
|
||||
.offset(name.offset.load(Ordering::SeqCst))
|
||||
.label("Name")
|
||||
.highlighted(selected_block == ActiveLidarrBlock::AddRootFolderNameInput)
|
||||
.selected(active_lidarr_block == ActiveLidarrBlock::AddRootFolderNameInput);
|
||||
let path_input_box = InputBox::new(&path.text)
|
||||
.offset(path.offset.load(Ordering::SeqCst))
|
||||
.label("Path")
|
||||
.highlighted(selected_block == ActiveLidarrBlock::AddRootFolderPathInput)
|
||||
.selected(active_lidarr_block == ActiveLidarrBlock::AddRootFolderPathInput);
|
||||
let tags_input_box = InputBox::new(&tags.text)
|
||||
.offset(tags.offset.load(Ordering::SeqCst))
|
||||
.label("Tags")
|
||||
.highlighted(selected_block == ActiveLidarrBlock::AddRootFolderTagsInput)
|
||||
.selected(active_lidarr_block == ActiveLidarrBlock::AddRootFolderTagsInput);
|
||||
|
||||
match active_lidarr_block {
|
||||
ActiveLidarrBlock::AddRootFolderNameInput => name_input_box.show_cursor(f, name_area),
|
||||
ActiveLidarrBlock::AddRootFolderPathInput => path_input_box.show_cursor(f, path_area),
|
||||
ActiveLidarrBlock::AddRootFolderTagsInput => tags_input_box.show_cursor(f, tags_area),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
render_selectable_input_box!(name_input_box, f, name_area);
|
||||
render_selectable_input_box!(path_input_box, f, path_area);
|
||||
render_selectable_input_box!(tags_input_box, f, tags_area);
|
||||
}
|
||||
|
||||
let save_button = Button::default()
|
||||
.title("Save")
|
||||
.selected(yes_no_value && highlight_yes_no);
|
||||
let cancel_button = Button::default()
|
||||
.title("Cancel")
|
||||
.selected(!yes_no_value && highlight_yes_no);
|
||||
|
||||
f.render_widget(monitor_drop_down_button, monitor_area);
|
||||
f.render_widget(monitor_new_items_drop_down_button, monitor_new_items_area);
|
||||
f.render_widget(quality_profile_drop_down_button, quality_profile_area);
|
||||
f.render_widget(metadata_profile_drop_down_button, metadata_profile_area);
|
||||
f.render_widget(save_button, save_area);
|
||||
f.render_widget(cancel_button, cancel_area);
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_select_monitor_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let monitor_list = SelectableList::new(
|
||||
&mut app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.expect("add_root_folder_modal must exist in this context")
|
||||
.monitor_list,
|
||||
|monitor_type| ListItem::new(monitor_type.to_display_str().to_owned()),
|
||||
);
|
||||
let popup = Popup::new(monitor_list).size(Size::Dropdown);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_select_monitor_new_items_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let monitor_new_items_list = SelectableList::new(
|
||||
&mut app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.expect("add_root_folder_modal must exist in this context")
|
||||
.monitor_new_items_list,
|
||||
|monitor_type| ListItem::new(monitor_type.to_display_str().to_owned()),
|
||||
);
|
||||
let popup = Popup::new(monitor_new_items_list).size(Size::Dropdown);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_select_quality_profile_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let quality_profile_list = SelectableList::new(
|
||||
&mut app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.expect("add_root_folder_modal must exist in this context")
|
||||
.quality_profile_list,
|
||||
|quality_profile| ListItem::new(quality_profile.clone()),
|
||||
);
|
||||
let popup = Popup::new(quality_profile_list).size(Size::Dropdown);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
|
||||
fn draw_add_root_folder_select_metadata_profile_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let metadata_profile_list = SelectableList::new(
|
||||
&mut app
|
||||
.data
|
||||
.lidarr_data
|
||||
.add_root_folder_modal
|
||||
.as_mut()
|
||||
.expect("add_root_folder_modal must exist in this context")
|
||||
.metadata_profile_list,
|
||||
|metadata_profile| ListItem::new(metadata_profile.clone()),
|
||||
);
|
||||
let popup = Popup::new(metadata_profile_list).size(Size::Dropdown);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::BlockSelectionState;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ADD_ROOT_FOLDER_SELECTION_BLOCKS, ActiveLidarrBlock,
|
||||
};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::lidarr_ui::root_folders::add_root_folder_ui::AddRootFolderUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_add_root_folder_ui_accepts() {
|
||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||
if ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block) {
|
||||
assert!(AddRootFolderUi::accepts(active_lidarr_block.into()));
|
||||
} else {
|
||||
assert!(!AddRootFolderUi::accepts(active_lidarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderPrompt)]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderConfirmPrompt)]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderSelectMonitor)]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems)]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderSelectQualityProfile)]
|
||||
#[case(ActiveLidarrBlock::AddRootFolderSelectMetadataProfile)]
|
||||
fn test_add_root_folder_ui_renders(#[case] active_lidarr_block: ActiveLidarrBlock) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
app.data.lidarr_data.selected_block =
|
||||
BlockSelectionState::new(ADD_ROOT_FOLDER_SELECTION_BLOCKS);
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
AddRootFolderUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("add_root_folder_{active_lidarr_block}"), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use add_root_folder_ui::AddRootFolderUi;
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, ROOT_FOLDERS_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::RootFolder;
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::widgets::popup::{Popup, Size};
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
mod add_root_folder_ui;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "root_folders_ui_tests.rs"]
|
||||
mod root_folders_ui_tests;
|
||||
|
||||
pub(super) struct RootFoldersUi;
|
||||
|
||||
impl DrawUi for RootFoldersUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
if let Route::Lidarr(active_lidarr_block, _) = route {
|
||||
return ROOT_FOLDERS_BLOCKS.contains(&active_lidarr_block)
|
||||
|| ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
if let Route::Lidarr(active_lidarr_block, _) = app.get_current_route() {
|
||||
draw_root_folders(f, app, area);
|
||||
|
||||
if ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block) {
|
||||
AddRootFolderUi::draw(f, app, area);
|
||||
} else if active_lidarr_block == ActiveLidarrBlock::DeleteRootFolderPrompt {
|
||||
let prompt = format!(
|
||||
"Do you really want to delete this root folder: \n{}?",
|
||||
app.data.lidarr_data.root_folders.current_selection().path
|
||||
);
|
||||
let confirmation_prompt = ConfirmationPrompt::new()
|
||||
.title("Delete Root Folder")
|
||||
.prompt(&prompt)
|
||||
.yes_no_value(app.data.lidarr_data.prompt_confirm);
|
||||
|
||||
f.render_widget(
|
||||
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||
f.area(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let root_folders_row_mapping = |root_folders: &RootFolder| {
|
||||
let RootFolder {
|
||||
path,
|
||||
free_space,
|
||||
unmapped_folders,
|
||||
..
|
||||
} = root_folders;
|
||||
|
||||
let space: f64 = convert_to_gb(*free_space);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(path.to_owned()),
|
||||
Cell::from(format!("{space:.2} GB")),
|
||||
Cell::from(
|
||||
unmapped_folders
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::new())
|
||||
.len()
|
||||
.to_string(),
|
||||
),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
|
||||
let root_folders_table = ManagarrTable::new(
|
||||
Some(&mut app.data.lidarr_data.root_folders),
|
||||
root_folders_row_mapping,
|
||||
)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.headers(["Path", "Free Space", "Unmapped Folders"])
|
||||
.constraints([
|
||||
Constraint::Ratio(3, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
Constraint::Ratio(1, 5),
|
||||
]);
|
||||
|
||||
f.render_widget(root_folders_table, area);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::{
|
||||
ADD_ROOT_FOLDER_BLOCKS, ActiveLidarrBlock, ROOT_FOLDERS_BLOCKS,
|
||||
};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::lidarr_ui::root_folders::RootFoldersUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_ui_accepts() {
|
||||
ActiveLidarrBlock::iter().for_each(|active_lidarr_block| {
|
||||
if ROOT_FOLDERS_BLOCKS.contains(&active_lidarr_block)
|
||||
|| ADD_ROOT_FOLDER_BLOCKS.contains(&active_lidarr_block)
|
||||
{
|
||||
assert!(RootFoldersUi::accepts(active_lidarr_block.into()));
|
||||
} else {
|
||||
assert!(!RootFoldersUi::accepts(active_lidarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use super::*;
|
||||
use crate::models::BlockSelectionState;
|
||||
use crate::models::servarr_data::lidarr::lidarr_data::ADD_ROOT_FOLDER_SELECTION_BLOCKS;
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_ui_renders_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
RootFoldersUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_folders_ui_renders_empty_root_folders() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveLidarrBlock::RootFolders.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
RootFoldersUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_ui_renders_root_folders_tab(
|
||||
#[values(
|
||||
ActiveLidarrBlock::RootFolders,
|
||||
ActiveLidarrBlock::DeleteRootFolderPrompt
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
RootFoldersUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(active_lidarr_block.to_string(), output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_root_folders_ui_renders_add_root_folders_popup_over_root_folders_table(
|
||||
#[values(
|
||||
ActiveLidarrBlock::AddRootFolderPrompt,
|
||||
ActiveLidarrBlock::AddRootFolderConfirmPrompt,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitor,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMonitorNewItems,
|
||||
ActiveLidarrBlock::AddRootFolderSelectQualityProfile,
|
||||
ActiveLidarrBlock::AddRootFolderSelectMetadataProfile
|
||||
)]
|
||||
active_lidarr_block: ActiveLidarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.data.lidarr_data.selected_block =
|
||||
BlockSelectionState::new(ADD_ROOT_FOLDER_SELECTION_BLOCKS);
|
||||
app.push_navigation_stack(ActiveLidarrBlock::AddRootFolderPrompt.into());
|
||||
app.push_navigation_stack(active_lidarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
RootFoldersUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(active_lidarr_block.to_string(), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor New Albums: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Quality Profile: │Lossless ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Metadata Profile: │Standard ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Tags: │test │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor New Albums: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Quality Profile: │Lossless ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Metadata Profile: │Standard ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Tags: │test │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │Standard │──────────────────────────────╮ │
|
||||
│ │ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │All Albums │──────────────────────────────╮ │
|
||||
│ │Future Albums │ ▼ │ │
|
||||
│ │Missing Albums │──────────────────────────────╯ │
|
||||
│ │Existing Albums │──────────────────────────────╮ │
|
||||
│ Monitor│First Album │ ▼ │ │
|
||||
│ │Latest Album │──────────────────────────────╯ │
|
||||
│ │None │──────────────────────────────╮ │
|
||||
│ Qual│Unknown │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │All Albums │──────────────────────────────╮ │
|
||||
│ │No New Albums │ ▼ │ │
|
||||
│ │New Albums │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/add_root_folder_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │Lossless │──────────────────────────────╮ │
|
||||
│ │ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor New Albums: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Quality Profile: │Lossless ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Metadata Profile: │Standard ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Tags: │test │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Monitor New Albums: │All Albums ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Quality Profile: │Lossless ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Metadata Profile: │Standard ▼ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Tags: │test │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │Standard │──────────────────────────────╮ │
|
||||
│ │ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │All Albums │──────────────────────────────╮ │
|
||||
│ │Future Albums │ ▼ │ │
|
||||
│ │Missing Albums │──────────────────────────────╯ │
|
||||
│ │Existing Albums │──────────────────────────────╮ │
|
||||
│ Monitor│First Album │ ▼ │ │
|
||||
│ │Latest Album │──────────────────────────────╯ │
|
||||
│ │None │──────────────────────────────╮ │
|
||||
│ Qual│Unknown │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │All Albums │──────────────────────────────╮ │
|
||||
│ │No New Albums │ ▼ │ │
|
||||
│ │New Albums │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────────────── Add Root Folder ───────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Name: │Test Root Folder │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ ╭─────────────────────────────────────────────────╮ │
|
||||
│ Path: │/nfs/music │ │
|
||||
│ ╭───────────────────────────────╮──────────────────────────────╯ │
|
||||
│ │Lossless │──────────────────────────────╮ │
|
||||
│ │ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Monitor│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Qual│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ Metad│ │ ▼ │ │
|
||||
│ │ │──────────────────────────────╯ │
|
||||
│ │ │──────────────────────────────╮ │
|
||||
│ ╰───────────────────────────────╯ │ │
|
||||
│ ╰─────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭───────────────────────────────────────────────────╮╭──────────────────────────────────────────────────╮│
|
||||
││ Save ││ Cancel ││
|
||||
│╰───────────────────────────────────────────────────╯╰──────────────────────────────────────────────────╯│
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭────────────────── Delete Root Folder ───────────────────╮
|
||||
│ Do you really want to delete this root folder: │
|
||||
│ /nfs? │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭────────────────────────────╮╭───────────────────────────╮│
|
||||
││ Yes ││ No ││
|
||||
│╰────────────────────────────╯╰───────────────────────────╯│
|
||||
╰───────────────────────────────────────────────────────────╯
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Path Free Space Unmapped Folders
|
||||
=> /nfs 204800.00 GB 0
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/root_folders/root_folders_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Loading ...
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Library │ Downloads │ History │ Root Folders │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ Name ▼ Type Status Quality Profile Metadata Profile Albums Tracks Size Monitored Tags │
|
||||
│=> Alex Person Continuing Lossless Standard 1 15/15 0.00 GB 🏷 alex │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Library │ Downloads │ History │ Root Folders │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ Title Percent Complete Size Output Path Indexer Download Client │
|
||||
│=> Test download title 50% 3.30 GB /nfs/music/alex/album kickass torrents transmission │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Library │ Downloads │ History │ Root Folders │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ Source Title ▼ Event Type Quality Date │
|
||||
│=> Test source title grabbed Lossless 2023-01-01 00:00:00 UTC │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/lidarr_ui/lidarr_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Artists ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Library │ Downloads │ History │ Root Folders │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ Path Free Space Unmapped Folders │
|
||||
│=> /nfs 204800.00 GB 0 │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
@@ -0,0 +1,79 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, HISTORY_BLOCKS};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::radarr_ui::history::HistoryUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_history_ui_accepts() {
|
||||
ActiveRadarrBlock::iter().for_each(|active_radarr_block| {
|
||||
if HISTORY_BLOCKS.contains(&active_radarr_block) {
|
||||
assert!(HistoryUi::accepts(active_radarr_block.into()));
|
||||
} else {
|
||||
assert!(!HistoryUi::accepts(active_radarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_history_ui_renders_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveRadarrBlock::History.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
HistoryUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_history_ui_renders_empty(
|
||||
#[values(ActiveRadarrBlock::History, ActiveRadarrBlock::HistoryItemDetails)]
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
HistoryUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("loading_history_tab_{active_radarr_block}"), output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_history_ui_renders(
|
||||
#[values(
|
||||
ActiveRadarrBlock::History,
|
||||
ActiveRadarrBlock::HistoryItemDetails,
|
||||
ActiveRadarrBlock::HistorySortPrompt,
|
||||
ActiveRadarrBlock::FilterHistory,
|
||||
ActiveRadarrBlock::FilterHistoryError,
|
||||
ActiveRadarrBlock::SearchHistory,
|
||||
ActiveRadarrBlock::SearchHistoryError
|
||||
)]
|
||||
active_radarr_block: ActiveRadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.push_navigation_stack(active_radarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
HistoryUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("history_tab_{active_radarr_block}"), output);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
use crate::models::radarr_models::RadarrHistoryItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, HISTORY_BLOCKS};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::styles::{ManagarrStyle, secondary_style};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::widgets::message::Message;
|
||||
use crate::ui::widgets::popup::{Popup, Size};
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
use super::radarr_ui_utils::create_history_event_details;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "history_ui_tests.rs"]
|
||||
mod history_ui_tests;
|
||||
|
||||
pub(super) struct HistoryUi;
|
||||
|
||||
impl DrawUi for HistoryUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
if let Route::Radarr(active_radarr_block, _) = route {
|
||||
return HISTORY_BLOCKS.contains(&active_radarr_block);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() {
|
||||
draw_history_table(f, app, area);
|
||||
|
||||
if active_radarr_block == ActiveRadarrBlock::HistoryItemDetails {
|
||||
draw_history_item_details_popup(f, app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_history_table(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let current_selection = if app.data.radarr_data.history.items.is_empty() {
|
||||
RadarrHistoryItem::default()
|
||||
} else {
|
||||
app.data.radarr_data.history.current_selection().clone()
|
||||
};
|
||||
if let Route::Radarr(active_radarr_block, _) = app.get_current_route() {
|
||||
let history_row_mapping = |history_item: &RadarrHistoryItem| {
|
||||
let RadarrHistoryItem {
|
||||
source_title,
|
||||
languages,
|
||||
quality,
|
||||
event_type,
|
||||
date,
|
||||
..
|
||||
} = history_item;
|
||||
|
||||
source_title.scroll_left_or_reset(
|
||||
get_width_from_percentage(area, 40),
|
||||
current_selection == *history_item,
|
||||
app.ui_scroll_tick_count == 0,
|
||||
);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(source_title.to_string()),
|
||||
Cell::from(event_type.to_string()),
|
||||
Cell::from(
|
||||
languages
|
||||
.iter()
|
||||
.map(|language| language.name.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(","),
|
||||
),
|
||||
Cell::from(quality.quality.name.to_owned()),
|
||||
Cell::from(date.to_string()),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let history_table =
|
||||
ManagarrTable::new(Some(&mut app.data.radarr_data.history), history_row_mapping)
|
||||
.block(layout_block_top_border())
|
||||
.loading(app.is_loading)
|
||||
.sorting(active_radarr_block == ActiveRadarrBlock::HistorySortPrompt)
|
||||
.searching(active_radarr_block == ActiveRadarrBlock::SearchHistory)
|
||||
.search_produced_empty_results(active_radarr_block == ActiveRadarrBlock::SearchHistoryError)
|
||||
.filtering(active_radarr_block == ActiveRadarrBlock::FilterHistory)
|
||||
.filter_produced_empty_results(active_radarr_block == ActiveRadarrBlock::FilterHistoryError)
|
||||
.headers(["Source Title", "Event Type", "Language", "Quality", "Date"])
|
||||
.constraints([
|
||||
Constraint::Percentage(40),
|
||||
Constraint::Percentage(15),
|
||||
Constraint::Percentage(12),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(20),
|
||||
]);
|
||||
|
||||
if [
|
||||
ActiveRadarrBlock::SearchHistory,
|
||||
ActiveRadarrBlock::FilterHistory,
|
||||
]
|
||||
.contains(&active_radarr_block)
|
||||
{
|
||||
history_table.show_cursor(f, area);
|
||||
}
|
||||
|
||||
f.render_widget(history_table, area);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_history_item_details_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let current_selection = if app.data.radarr_data.history.items.is_empty() {
|
||||
RadarrHistoryItem::default()
|
||||
} else {
|
||||
app.data.radarr_data.history.current_selection().clone()
|
||||
};
|
||||
|
||||
let line_vec = create_history_event_details(current_selection);
|
||||
let text = Text::from(line_vec);
|
||||
|
||||
let message = Message::new(text)
|
||||
.title("Details")
|
||||
.style(secondary_style())
|
||||
.alignment(Alignment::Left);
|
||||
|
||||
f.render_widget(Popup::new(message).size(Size::NarrowLongMessage), f.area());
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
---
|
||||
source: src/ui/radarr_ui/history/history_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Source Title ▼ Event Type Language Quality Date
|
||||
=> Test grabbed English HD - 1080p 2022-12-30 07:37:56 UTC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭───────────────── Filter ──────────────────╮
|
||||
│Something │
|
||||
╰─────────────────────────────────────────────╯
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source: src/ui/radarr_ui/history/history_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Source Title ▼ Event Type Language Quality Date
|
||||
=> Test grabbed English HD - 1080p 2022-12-30 07:37:56 UTC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────── Error ───────────────╮
|
||||
│The given filter produced empty results│
|
||||
│ │
|
||||
╰───────────────────────────────────────╯
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/ui/radarr_ui/history/history_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Source Title ▼ Event Type Language Quality Date
|
||||
=> Test grabbed English HD - 1080p 2022-12-30 07:37:56 UTC
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: src/ui/radarr_ui/history/history_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Source Title ▼ Event Type Language Quality Date
|
||||
=> Test grabbed English HD - 1080p 2022-12-30 07:37:56 UTC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭─────────────────────────────────── Details ───────────────────────────────────╮
|
||||
│Source Title: Test │
|
||||
│Event Type: grabbed │
|
||||
│Indexer: DrunkenSlug (Prowlarr) │
|
||||
│Release Group: SPARKS │
|
||||
│NZB Info URL: │
|
||||
│Download Client: transmission │
|
||||
│Age: 0 days │
|
||||
│Published Date: 1970-01-01 00:00:00 UTC │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
---
|
||||
source: src/ui/radarr_ui/history/history_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
Source Title Event Type Language Quality Date
|
||||
=> Test grabbed English HD - 1080p 2022-12-30 07:37:56 UTC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭───────────────────────────────╮
|
||||
│Something │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────╯
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user