feat(readarr): add the system UI
The system tab renders the tasks table, the queued events table and the log list, and overlays the log details, tasks, queued events, task start confirmation and updates popups. SystemUi and SystemDetailsUi ship together because SystemUi::accepts covers every block that only SystemDetailsUi draws. Landing them apart would advertise five blocks that nothing renders. Log level extraction is pulled into a named function so it can be unit tested directly: the level only ever reaches the output as a colour, and the rendered snapshots are plain strings that discard it.
This commit is contained in:
31 files changed
+2156
-47
No files matched your search
@@ -25,6 +25,7 @@ use super::{
|
||||
use crate::ui::readarr_ui::blocklist::BlocklistUi;
|
||||
use crate::ui::readarr_ui::downloads::DownloadsUi;
|
||||
use crate::ui::readarr_ui::root_folders::RootFoldersUi;
|
||||
use crate::ui::readarr_ui::system::SystemUi;
|
||||
use crate::ui::utils::{extract_monitored_disk_space_vec, extract_monitored_root_folders};
|
||||
use crate::{
|
||||
app::App,
|
||||
@@ -44,6 +45,7 @@ mod history;
|
||||
mod library;
|
||||
mod readarr_ui_utils;
|
||||
mod root_folders;
|
||||
mod system;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "readarr_ui_tests.rs"]
|
||||
@@ -66,6 +68,7 @@ impl DrawUi for ReadarrUi {
|
||||
_ if BlocklistUi::accepts(route) => BlocklistUi::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),
|
||||
_ if SystemUi::accepts(route) => SystemUi::draw(f, app, content_area),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
+47
-47
@@ -4,51 +4,51 @@ expression: output
|
||||
---
|
||||
╭ Authors ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Library │ Downloads │ Blocklist │ History │ Root Folders │ Indexers │ System │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│╭ Tasks ───────────────────────────────────────────────────────────────────────╮╭ Queued Events ──────────────────────────────────────────────────────────────╮│
|
||||
││Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration ││
|
||||
││Backup 1 hour now 59 minutes ││manual completed Refresh Monitored 4 minutes ago 4 minutes a 00:03:03 ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
││ ││ ││
|
||||
│╰────────────────────────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────────────────────╯│
|
||||
│╭ Logs ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮│
|
||||
││2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|No list items to process ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
││ ││
|
||||
│╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯│
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
@@ -0,0 +1,203 @@
|
||||
use std::ops::Sub;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use chrono::Utc;
|
||||
use ratatui::text::{Span, Text};
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
use ratatui::{
|
||||
Frame,
|
||||
layout::{Constraint, Layout, Rect},
|
||||
widgets::ListItem,
|
||||
};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
use crate::models::readarr_models::ReadarrTask;
|
||||
use crate::models::servarr_data::readarr::readarr_data::ActiveReadarrBlock;
|
||||
use crate::models::servarr_models::QueueEvent;
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::readarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::styles::{ManagarrStyle, default_style};
|
||||
#[cfg(test)]
|
||||
use crate::ui::ui_test_utils::test_utils::Utc;
|
||||
use crate::ui::utils::{convert_to_minutes_hours_days, style_log_list_item, title_block};
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::widgets::selectable_list::SelectableList;
|
||||
|
||||
mod system_details_ui;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "system_ui_tests.rs"]
|
||||
mod system_ui_tests;
|
||||
|
||||
pub(super) const TASK_TABLE_HEADERS: [&str; 4] =
|
||||
["Name", "Interval", "Last Execution", "Next Execution"];
|
||||
|
||||
pub(super) const TASK_TABLE_CONSTRAINTS: [Constraint; 4] = [
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(23),
|
||||
Constraint::Percentage(23),
|
||||
Constraint::Percentage(23),
|
||||
];
|
||||
|
||||
pub(super) struct SystemUi;
|
||||
|
||||
impl DrawUi for SystemUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
let Route::Readarr(active_readarr_block, _) = route else {
|
||||
return false;
|
||||
};
|
||||
SystemDetailsUi::accepts(route) || active_readarr_block == ActiveReadarrBlock::System
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let route = app.get_current_route();
|
||||
draw_system_ui_layout(f, app, area);
|
||||
|
||||
if SystemDetailsUi::accepts(route) {
|
||||
SystemDetailsUi::draw(f, app, area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_system_ui_layout(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let [activities_area, logs_area] =
|
||||
Layout::vertical([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(area);
|
||||
|
||||
let [tasks_area, events_area] =
|
||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(activities_area);
|
||||
|
||||
draw_tasks(f, app, tasks_area);
|
||||
draw_queued_events(f, app, events_area);
|
||||
draw_logs(f, app, logs_area);
|
||||
}
|
||||
|
||||
fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let tasks_row_mapping = |task: &ReadarrTask| {
|
||||
let task_props = extract_task_props(task);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.readarr_data.tasks), tasks_row_mapping)
|
||||
.block(title_block("Tasks"))
|
||||
.loading(app.is_loading)
|
||||
.highlight_rows(false)
|
||||
.headers(TASK_TABLE_HEADERS)
|
||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||
|
||||
f.render_widget(tasks_table, area);
|
||||
}
|
||||
|
||||
pub(super) fn draw_queued_events(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let events_row_mapping = |event: &QueueEvent| {
|
||||
let queued = convert_to_minutes_hours_days(Utc::now().sub(event.queued).num_minutes());
|
||||
let queued_string = if queued != "now" {
|
||||
format!("{queued} ago")
|
||||
} else {
|
||||
queued
|
||||
};
|
||||
let started_string = if let Some(date_time) = event.started {
|
||||
let started = convert_to_minutes_hours_days(Utc::now().sub(date_time).num_minutes());
|
||||
|
||||
if started != "now" {
|
||||
format!("{started} ago")
|
||||
} else {
|
||||
started
|
||||
}
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let duration = if let Some(dur) = &event.duration {
|
||||
&dur[..8]
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(event.trigger.clone()),
|
||||
Cell::from(event.status.clone()),
|
||||
Cell::from(event.command_name.clone()),
|
||||
Cell::from(queued_string),
|
||||
Cell::from(started_string),
|
||||
Cell::from(duration.to_owned()),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let events_table = ManagarrTable::new(
|
||||
Some(&mut app.data.readarr_data.queued_events),
|
||||
events_row_mapping,
|
||||
)
|
||||
.block(title_block("Queued Events"))
|
||||
.loading(app.is_loading)
|
||||
.highlight_rows(false)
|
||||
.headers(["Trigger", "Status", "Name", "Queued", "Started", "Duration"])
|
||||
.constraints([
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(13),
|
||||
Constraint::Percentage(30),
|
||||
Constraint::Percentage(16),
|
||||
Constraint::Percentage(14),
|
||||
Constraint::Percentage(14),
|
||||
]);
|
||||
|
||||
f.render_widget(events_table, area);
|
||||
}
|
||||
|
||||
fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let block = title_block("Logs");
|
||||
|
||||
if app.data.readarr_data.logs.items.is_empty() {
|
||||
f.render_widget(LoadingBlock::new(app.is_loading, block), area);
|
||||
return;
|
||||
}
|
||||
|
||||
let logs_box = SelectableList::new(&mut app.data.readarr_data.logs, |log| {
|
||||
let log_line = log.to_string();
|
||||
let level = extract_log_level(&log_line);
|
||||
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
})
|
||||
.block(block)
|
||||
.highlight_style(default_style());
|
||||
|
||||
f.render_widget(logs_box, area);
|
||||
}
|
||||
|
||||
pub(super) fn extract_log_level(log_line: &str) -> String {
|
||||
log_line.split('|').collect::<Vec<&str>>()[1].to_string()
|
||||
}
|
||||
|
||||
pub(super) struct TaskProps {
|
||||
pub(super) name: String,
|
||||
pub(super) interval: String,
|
||||
pub(super) last_execution: String,
|
||||
pub(super) next_execution: String,
|
||||
}
|
||||
|
||||
pub(super) fn extract_task_props(task: &ReadarrTask) -> TaskProps {
|
||||
let interval = convert_to_minutes_hours_days(task.interval);
|
||||
let next_execution =
|
||||
convert_to_minutes_hours_days((task.next_execution - Utc::now()).num_minutes());
|
||||
let last_execution =
|
||||
convert_to_minutes_hours_days((Utc::now() - task.last_execution).num_minutes());
|
||||
let last_execution_string = if last_execution != "now" {
|
||||
format!("{last_execution} ago")
|
||||
} else {
|
||||
last_execution
|
||||
};
|
||||
|
||||
TaskProps {
|
||||
name: task.name.clone(),
|
||||
interval,
|
||||
last_execution: last_execution_string,
|
||||
next_execution,
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha │
|
||||
│2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta │
|
||||
│2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮
|
||||
│Trigger Status Name Queued Started Duration │
|
||||
│manual completed queued event one 1 hour ago 30 minutes ag 00:29:14 │
|
||||
│scheduled started queued event two 15 minutes ago 4 minutes ago 01:02:03 │
|
||||
│external queued queued event three 1 minute ago │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name Interval Last Execution Next Execution │
|
||||
│=> Backup task 2 hours 30 minutes ago 1 hour │
|
||||
│ Rss sync task 15 minutes 8 minutes ago 3 minutes │
|
||||
│ Housekeeping task 1 day 4 hours ago 18 hours │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭────────────────────── Start Task ───────────────────────╮ │
|
||||
│ │ Do you want to manually start this task: Backup task? │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │╭────────────────────────────╮╭───────────────────────────╮│ │
|
||||
│ ││ Yes ││ No ││ │
|
||||
│ │╰────────────────────────────╯╰───────────────────────────╯│ │
|
||||
│ ╰───────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name Interval Last Execution Next Execution │
|
||||
│=> Backup task 2 hours 30 minutes ago 1 hour │
|
||||
│ Rss sync task 15 minutes 8 minutes ago 3 minutes │
|
||||
│ Housekeeping task 1 day 4 hours ago 18 hours │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│The latest version of Sonarr is already installed │
|
||||
│ │
|
||||
│4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed) │
|
||||
│--------------------------------------------------------------------------------------------------------------------------│
|
||||
│New: │
|
||||
│ * Cool new thing │
|
||||
│Fixed: │
|
||||
│ * Some bugs killed │
|
||||
│ │
|
||||
│ │
|
||||
│3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed) │
|
||||
│--------------------------------------------------------------------------------------------------------------------------│
|
||||
│New: │
|
||||
│ * Cool new thing (old) │
|
||||
│ * Other cool new thing (old) │
|
||||
│ │
|
||||
│ │
|
||||
│2.1.0 - 2023-04-15 02:02:53 UTC │
|
||||
│--------------------------------------------------------------------------------------------------------------------------│
|
||||
│Fixed: │
|
||||
│ * Killed bug 1 │
|
||||
│ * Fixed bug 2 │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name Interval Last Execution Next Execution │
|
||||
│ Backup task 2 hours 30 minutes ago 1 hour │
|
||||
│ Rss sync task 15 minutes 8 minutes ago 3 minutes │
|
||||
│=> Housekeeping task 1 day 4 hours ago 18 hours │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ ╭────────────────────── Start Task ───────────────────────╮ │
|
||||
│ │Do you want to manually start this task: Housekeeping task?│ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │╭────────────────────────────╮╭───────────────────────────╮│ │
|
||||
│ ││ Yes ││ No ││ │
|
||||
│ │╰────────────────────────────╯╰───────────────────────────╯│ │
|
||||
│ ╰───────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_details_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ╭ Log Details ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||
│ │2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha │ │
|
||||
│ │2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta │ │
|
||||
│ │2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
╰───────────────────│ │────────────────────╯
|
||||
╭ Logs ───────────│ │────────────────────╮
|
||||
│2025-12-16 16:40:59│ │ │
|
||||
│2025-12-16 16:41:12│ │ │
|
||||
│2025-12-16 16:41:30│ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ╭ Queued Events ────────────────────────────────────────────────────────────────────────────────╮ │
|
||||
│ │Trigger Status Name Queued Started Duration │ │
|
||||
│ │manual completed queued event one 1 hour ago 30 minutes ag 00:29:14 │ │
|
||||
│ │scheduled started queued event two 15 minutes ago 4 minutes ago 01:02:03 │ │
|
||||
│ │external queued queued event three 1 minute ago │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
╰────────────────────────────────│ │────────────────────────────────╯
|
||||
╭ Logs ────────────────────────│ │────────────────────────────────╮
|
||||
│2025-12-16 16:40:59 UTC|INFO|Imp│ │ │
|
||||
│2025-12-16 16:41:12 UTC|WARN|Boo│ │ │
|
||||
│2025-12-16 16:41:30 UTC|FATAL|Co│ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||
│ │ Name Interval Last Execution Next Execution │ │
|
||||
│ │=> Backup task 2 hours 30 minutes ago 1 hour │ │
|
||||
│ │ Rss sync task 15 minutes 8 minutes ago 3 minutes │ │
|
||||
│ │ Housekeeping task 1 day 4 hours ago 18 hours │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ ╭────────────────────── Start Task ───────────────────────╮ │ │
|
||||
│ │ │ Do you want to manually start this task: Backup task? │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
╰───────────────────│ │ │ │────────────────────╯
|
||||
╭ Logs ───────────│ │ │ │────────────────────╮
|
||||
│2025-12-16 16:40:59│ │ │ │ │
|
||||
│2025-12-16 16:41:12│ │ │ │ │
|
||||
│2025-12-16 16:41:30│ │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │╭────────────────────────────╮╭───────────────────────────╮│ │ │
|
||||
│ │ ││ Yes ││ No ││ │ │
|
||||
│ │ │╰────────────────────────────╯╰───────────────────────────╯│ │ │
|
||||
│ │ ╰───────────────────────────────────────────────────────────╯ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ╭ Tasks ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||
│ │ Name Interval Last Execution Next Execution │ │
|
||||
│ │=> Backup task 2 hours 30 minutes ago 1 hour │ │
|
||||
│ │ Rss sync task 15 minutes 8 minutes ago 3 minutes │ │
|
||||
│ │ Housekeeping task 1 day 4 hours ago 18 hours │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
╰───────────────────│ │────────────────────╯
|
||||
╭ Logs ───────────│ │────────────────────╮
|
||||
│2025-12-16 16:40:59│ │ │
|
||||
│2025-12-16 16:41:12│ │ │
|
||||
│2025-12-16 16:41:30│ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ╭ Updates ───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │
|
||||
│ │The latest version of Sonarr is already installed │ │
|
||||
│ │ │ │
|
||||
│ │4.3.2.1 - 2023-04-15 02:02:53 UTC (Currently Installed) │ │
|
||||
│ │--------------------------------------------------------------------------------------------------------------------------│ │
|
||||
│ │New: │ │
|
||||
│ │ * Cool new thing │ │
|
||||
│ │Fixed: │ │
|
||||
│ │ * Some bugs killed │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │3.2.1.0 - 2023-04-15 02:02:53 UTC (Previously Installed) │ │
|
||||
│ │--------------------------------------------------------------------------------------------------------------------------│ │
|
||||
│ │New: │ │
|
||||
│ │ * Cool new thing (old) │ │
|
||||
│ │ * Other cool new thing (old) │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
╰───────────────────│2.1.0 - 2023-04-15 02:02:53 UTC │────────────────────╯
|
||||
╭ Logs ───────────│--------------------------------------------------------------------------------------------------------------------------│────────────────────╮
|
||||
│2025-12-16 16:40:59│Fixed: │ │
|
||||
│2025-12-16 16:41:12│ * Killed bug 1 │ │
|
||||
│2025-12-16 16:41:30│ * Fixed bug 2 │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ Loading ... ││ Loading ... │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│Name Interval Last Execution Next Execution ││Trigger Status Name Queued Started Duration │
|
||||
│Backup task 2 hours 30 minutes ago 1 hour ││manual completed queued event one 1 hour ago 30 minutes 00:29:14 │
|
||||
│Rss sync task 15 minutes 8 minutes ago 3 minutes ││scheduled started queued event two 15 minutes ag 4 minutes a 01:02:03 │
|
||||
│Housekeeping task 1 day 4 hours ago 18 hours ││external queued queued event three 1 minute ago │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha │
|
||||
│2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta │
|
||||
│2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ Loading ... ││ Loading ... │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ │
|
||||
│ │
|
||||
│ Loading ... │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: src/ui/readarr_ui/system/system_ui_tests.rs
|
||||
expression: output
|
||||
---
|
||||
╭ Tasks ────────────────────────────────────────────────────────────────────────╮╭ Queued Events ───────────────────────────────────────────────────────────────╮
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ Loading ... ││ Loading ... │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
│ ││ │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
╭ Logs ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha │
|
||||
│2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta │
|
||||
│2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
@@ -0,0 +1,147 @@
|
||||
use ratatui::Frame;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::text::{Span, Text};
|
||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::Route;
|
||||
use crate::models::readarr_models::ReadarrTask;
|
||||
use crate::models::servarr_data::readarr::readarr_data::{
|
||||
ActiveReadarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||
};
|
||||
use crate::ui::readarr_ui::system::{
|
||||
TASK_TABLE_CONSTRAINTS, TASK_TABLE_HEADERS, draw_queued_events, extract_log_level,
|
||||
extract_task_props,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, style_log_list_item, title_block};
|
||||
use crate::ui::widgets::confirmation_prompt::ConfirmationPrompt;
|
||||
use crate::ui::widgets::loading_block::LoadingBlock;
|
||||
use crate::ui::widgets::managarr_table::ManagarrTable;
|
||||
use crate::ui::widgets::popup::{Popup, Size};
|
||||
use crate::ui::widgets::selectable_list::SelectableList;
|
||||
use crate::ui::{DrawUi, draw_popup};
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "system_details_ui_tests.rs"]
|
||||
mod system_details_ui_tests;
|
||||
|
||||
pub(super) struct SystemDetailsUi;
|
||||
|
||||
impl DrawUi for SystemDetailsUi {
|
||||
fn accepts(route: Route) -> bool {
|
||||
let Route::Readarr(active_readarr_block, _) = route else {
|
||||
return false;
|
||||
};
|
||||
SYSTEM_DETAILS_BLOCKS.contains(&active_readarr_block)
|
||||
}
|
||||
|
||||
fn draw(f: &mut Frame<'_>, app: &mut App<'_>, _area: Rect) {
|
||||
if let Route::Readarr(active_readarr_block, _) = app.get_current_route() {
|
||||
match active_readarr_block {
|
||||
ActiveReadarrBlock::SystemLogs => {
|
||||
draw_logs_popup(f, app);
|
||||
}
|
||||
ActiveReadarrBlock::SystemTasks | ActiveReadarrBlock::SystemTaskStartConfirmPrompt => {
|
||||
draw_popup(f, app, draw_tasks_popup, Size::Large)
|
||||
}
|
||||
ActiveReadarrBlock::SystemQueuedEvents => {
|
||||
draw_popup(f, app, draw_queued_events, Size::Medium)
|
||||
}
|
||||
ActiveReadarrBlock::SystemUpdates => {
|
||||
draw_updates_popup(f, app);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let block = title_block("Log Details");
|
||||
|
||||
if app.data.readarr_data.log_details.items.is_empty() {
|
||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
return;
|
||||
}
|
||||
|
||||
let logs_list = SelectableList::new(&mut app.data.readarr_data.log_details, |log| {
|
||||
let log_line = log.to_string();
|
||||
let level = extract_log_level(&log.text);
|
||||
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
})
|
||||
.block(borderless_block());
|
||||
let popup = Popup::new(logs_list)
|
||||
.size(Size::Large)
|
||||
.block(block)
|
||||
.margin(1);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
|
||||
fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let tasks_row_mapping = |task: &ReadarrTask| {
|
||||
let task_props = extract_task_props(task);
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(task_props.name),
|
||||
Cell::from(task_props.interval),
|
||||
Cell::from(task_props.last_execution),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.primary()
|
||||
};
|
||||
let tasks_table = ManagarrTable::new(Some(&mut app.data.readarr_data.tasks), tasks_row_mapping)
|
||||
.loading(app.is_loading)
|
||||
.margin(1)
|
||||
.headers(TASK_TABLE_HEADERS)
|
||||
.constraints(TASK_TABLE_CONSTRAINTS);
|
||||
|
||||
f.render_widget(title_block("Tasks"), area);
|
||||
f.render_widget(tasks_table, area);
|
||||
|
||||
if matches!(
|
||||
app.get_current_route(),
|
||||
Route::Readarr(ActiveReadarrBlock::SystemTaskStartConfirmPrompt, _)
|
||||
) {
|
||||
let prompt = format!(
|
||||
"Do you want to manually start this task: {}?",
|
||||
app.data.readarr_data.tasks.current_selection().name
|
||||
);
|
||||
let confirmation_prompt = ConfirmationPrompt::new()
|
||||
.title("Start Task")
|
||||
.prompt(&prompt)
|
||||
.yes_no_value(app.data.readarr_data.prompt_confirm);
|
||||
|
||||
f.render_widget(
|
||||
Popup::new(confirmation_prompt).size(Size::MediumPrompt),
|
||||
f.area(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
let updates = app.data.readarr_data.updates.get_text();
|
||||
let block = title_block("Updates");
|
||||
|
||||
if !updates.is_empty() && !app.is_loading {
|
||||
let updates_paragraph = Paragraph::new(Text::from(updates))
|
||||
.block(borderless_block())
|
||||
.scroll((app.data.readarr_data.updates.offset, 0));
|
||||
let popup = Popup::new(updates_paragraph)
|
||||
.size(Size::Large)
|
||||
.block(block)
|
||||
.margin(1);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
} else {
|
||||
let loading = LoadingBlock::new(app.is_loading, borderless_block());
|
||||
let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
|
||||
|
||||
f.render_widget(popup, f.area());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::DateTime;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::readarr_models::{ReadarrTask, ReadarrTaskName};
|
||||
use crate::models::servarr_data::readarr::readarr_data::{
|
||||
ActiveReadarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::QueueEvent;
|
||||
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::readarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_system_details_ui_accepts() {
|
||||
ActiveReadarrBlock::iter().for_each(|active_readarr_block| {
|
||||
if SYSTEM_DETAILS_BLOCKS.contains(&active_readarr_block) {
|
||||
assert!(SystemDetailsUi::accepts(active_readarr_block.into()));
|
||||
} else {
|
||||
assert!(!SystemDetailsUi::accepts(active_readarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use super::*;
|
||||
use crate::models::ScrollableText;
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
fn test_system_details_ui_popups(
|
||||
#[values(
|
||||
ActiveReadarrBlock::SystemLogs,
|
||||
ActiveReadarrBlock::SystemQueuedEvents,
|
||||
ActiveReadarrBlock::SystemTasks,
|
||||
ActiveReadarrBlock::SystemTaskStartConfirmPrompt,
|
||||
ActiveReadarrBlock::SystemUpdates
|
||||
)]
|
||||
active_readarr_block: ActiveReadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.push_navigation_stack(active_readarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("popup_{active_readarr_block}"), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_details_ui_task_start_confirm_prompt_names_the_selected_task() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.data.readarr_data.tasks.select_index(Some(2));
|
||||
app.push_navigation_stack(ActiveReadarrBlock::SystemTaskStartConfirmPrompt.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
assert!(output.contains("Do you want to manually start this task: Housekeeping task?"));
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_details_ui_updates_popup_is_loading_when_updates_are_present() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveReadarrBlock::SystemUpdates.into());
|
||||
|
||||
assert!(!app.data.readarr_data.updates.get_text().is_empty());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_system_details_ui_loading(
|
||||
#[values(
|
||||
ActiveReadarrBlock::SystemLogs,
|
||||
ActiveReadarrBlock::SystemQueuedEvents,
|
||||
ActiveReadarrBlock::SystemTasks,
|
||||
ActiveReadarrBlock::SystemUpdates
|
||||
)]
|
||||
active_readarr_block: ActiveReadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(active_readarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("loading_{active_readarr_block}"), output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_details_ui_updates_popup_loading_when_empty() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveReadarrBlock::SystemUpdates.into());
|
||||
app.data.readarr_data.updates = ScrollableText::with_string("".to_string());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_system_details_ui_popups_empty(
|
||||
#[values(
|
||||
ActiveReadarrBlock::SystemLogs,
|
||||
ActiveReadarrBlock::SystemQueuedEvents,
|
||||
ActiveReadarrBlock::SystemTasks
|
||||
)]
|
||||
active_readarr_block: ActiveReadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(active_readarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemDetailsUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(format!("empty_{active_readarr_block}"), output);
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_system_data(app: &mut App<'_>) {
|
||||
app.data.readarr_data.tasks.set_items(tasks_vec());
|
||||
app
|
||||
.data
|
||||
.readarr_data
|
||||
.queued_events
|
||||
.set_items(queued_events_vec());
|
||||
app.data.readarr_data.logs.set_items(log_lines_vec());
|
||||
app.data.readarr_data.log_details.set_items(log_lines_vec());
|
||||
}
|
||||
|
||||
fn tasks_vec() -> Vec<ReadarrTask> {
|
||||
vec![
|
||||
ReadarrTask {
|
||||
name: "Backup task".to_owned(),
|
||||
task_name: ReadarrTaskName::Backup,
|
||||
interval: 120,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:00:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:00.5111547".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T22:30:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
ReadarrTask {
|
||||
name: "Rss sync task".to_owned(),
|
||||
task_name: ReadarrTaskName::RssSync,
|
||||
interval: 15,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:22:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:01.2233445".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:33:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
ReadarrTask {
|
||||
name: "Housekeeping task".to_owned(),
|
||||
task_name: ReadarrTaskName::Housekeeping,
|
||||
interval: 1440,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T17:30:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:09.9887766".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-21T15:30:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn queued_events_vec() -> Vec<QueueEvent> {
|
||||
vec![
|
||||
QueueEvent {
|
||||
trigger: "manual".to_owned(),
|
||||
name: "event name one".to_owned(),
|
||||
command_name: "queued event one".to_owned(),
|
||||
status: "completed".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T20:30:00Z").unwrap()),
|
||||
started: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:00:00Z").unwrap(),
|
||||
)),
|
||||
ended: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:29:14Z").unwrap(),
|
||||
)),
|
||||
duration: Some("00:29:14.1234567".to_owned()),
|
||||
},
|
||||
QueueEvent {
|
||||
trigger: "scheduled".to_owned(),
|
||||
name: "event name two".to_owned(),
|
||||
command_name: "queued event two".to_owned(),
|
||||
status: "started".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:15:00Z").unwrap()),
|
||||
started: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:26:00Z").unwrap(),
|
||||
)),
|
||||
ended: None,
|
||||
duration: Some("01:02:03.7654321".to_owned()),
|
||||
},
|
||||
QueueEvent {
|
||||
trigger: "external".to_owned(),
|
||||
name: "event name three".to_owned(),
|
||||
command_name: "queued event three".to_owned(),
|
||||
status: "queued".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:00Z").unwrap()),
|
||||
started: None,
|
||||
ended: None,
|
||||
duration: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn log_lines_vec() -> Vec<HorizontallyScrollableText> {
|
||||
vec![
|
||||
"2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha".into(),
|
||||
"2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta".into(),
|
||||
"2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma".into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::DateTime;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::app::App;
|
||||
use crate::models::HorizontallyScrollableText;
|
||||
use crate::models::readarr_models::{ReadarrTask, ReadarrTaskName};
|
||||
use crate::models::servarr_data::readarr::readarr_data::{
|
||||
ActiveReadarrBlock, SYSTEM_DETAILS_BLOCKS,
|
||||
};
|
||||
use crate::models::servarr_models::QueueEvent;
|
||||
use crate::ui::DrawUi;
|
||||
use crate::ui::readarr_ui::system::SystemUi;
|
||||
use crate::ui::ui_test_utils::test_utils::render_to_string_with_app;
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_accepts() {
|
||||
let mut system_ui_blocks = Vec::new();
|
||||
system_ui_blocks.push(ActiveReadarrBlock::System);
|
||||
system_ui_blocks.extend(SYSTEM_DETAILS_BLOCKS);
|
||||
|
||||
ActiveReadarrBlock::iter().for_each(|active_readarr_block| {
|
||||
if system_ui_blocks.contains(&active_readarr_block) {
|
||||
assert!(SystemUi::accepts(active_readarr_block.into()));
|
||||
} else {
|
||||
assert!(!SystemUi::accepts(active_readarr_block.into()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mod test_extract_log_level {
|
||||
use pretty_assertions::assert_str_eq;
|
||||
|
||||
use crate::ui::readarr_ui::system::extract_log_level;
|
||||
|
||||
#[test]
|
||||
fn test_extract_log_level_returns_the_field_after_the_timestamp() {
|
||||
let level = extract_log_level("2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|Message");
|
||||
|
||||
assert_str_eq!(level, "INFO");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_log_level_preserves_case() {
|
||||
let level = extract_log_level("2025-12-16 16:41:30 UTC|FaTaL|CommandExecutor|Message");
|
||||
|
||||
assert_str_eq!(level, "FaTaL");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_log_level_ignores_later_fields() {
|
||||
let level = extract_log_level("timestamp|WARN|logger|message|with|extra|pipes");
|
||||
|
||||
assert_str_eq!(level, "WARN");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_log_level_returns_an_empty_string_when_the_level_is_absent() {
|
||||
let level = extract_log_level("2025-12-16 16:41:12 UTC||BookScanService|Message");
|
||||
|
||||
assert_str_eq!(level, "");
|
||||
}
|
||||
}
|
||||
|
||||
mod snapshot_tests {
|
||||
use crate::models::stateful_list::StatefulList;
|
||||
use crate::ui::ui_test_utils::test_utils::TerminalSize;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_renders_system_tab_loading() {
|
||||
let mut app = App::test_default();
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveReadarrBlock::System.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_renders_logs_loading() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.is_loading = true;
|
||||
app.push_navigation_stack(ActiveReadarrBlock::System.into());
|
||||
app.data.readarr_data.logs = StatefulList::default();
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_renders_system_tab_task_and_events_loading() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.push_navigation_stack(ActiveReadarrBlock::System.into());
|
||||
app.is_loading = true;
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_renders_system_tab() {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.push_navigation_stack(ActiveReadarrBlock::System.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_ui_renders_system_tab_empty() {
|
||||
let mut app = App::test_default();
|
||||
app.push_navigation_stack(ActiveReadarrBlock::System.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn test_system_details_ui_renders_popups_over_system_ui(
|
||||
#[values(
|
||||
ActiveReadarrBlock::SystemLogs,
|
||||
ActiveReadarrBlock::SystemQueuedEvents,
|
||||
ActiveReadarrBlock::SystemTasks,
|
||||
ActiveReadarrBlock::SystemTaskStartConfirmPrompt,
|
||||
ActiveReadarrBlock::SystemUpdates
|
||||
)]
|
||||
active_readarr_block: ActiveReadarrBlock,
|
||||
) {
|
||||
let mut app = App::test_default_fully_populated();
|
||||
populate_system_data(&mut app);
|
||||
app.push_navigation_stack(active_readarr_block.into());
|
||||
|
||||
let output = render_to_string_with_app(TerminalSize::Large, &mut app, |f, app| {
|
||||
SystemUi::draw(f, app, f.area());
|
||||
});
|
||||
|
||||
insta::assert_snapshot!(
|
||||
format!("popups_over_system_ui_{active_readarr_block}"),
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_system_data(app: &mut App<'_>) {
|
||||
app.data.readarr_data.tasks.set_items(tasks_vec());
|
||||
app
|
||||
.data
|
||||
.readarr_data
|
||||
.queued_events
|
||||
.set_items(queued_events_vec());
|
||||
app.data.readarr_data.logs.set_items(log_lines_vec());
|
||||
app.data.readarr_data.log_details.set_items(log_lines_vec());
|
||||
}
|
||||
|
||||
fn tasks_vec() -> Vec<ReadarrTask> {
|
||||
vec![
|
||||
ReadarrTask {
|
||||
name: "Backup task".to_owned(),
|
||||
task_name: ReadarrTaskName::Backup,
|
||||
interval: 120,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:00:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:00.5111547".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T22:30:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
ReadarrTask {
|
||||
name: "Rss sync task".to_owned(),
|
||||
task_name: ReadarrTaskName::RssSync,
|
||||
interval: 15,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:22:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:01.2233445".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:33:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
ReadarrTask {
|
||||
name: "Housekeeping task".to_owned(),
|
||||
task_name: ReadarrTaskName::Housekeeping,
|
||||
interval: 1440,
|
||||
last_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T17:30:00Z").unwrap(),
|
||||
),
|
||||
last_duration: "00:00:09.9887766".to_owned(),
|
||||
next_execution: DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-21T15:30:00Z").unwrap(),
|
||||
),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn queued_events_vec() -> Vec<QueueEvent> {
|
||||
vec![
|
||||
QueueEvent {
|
||||
trigger: "manual".to_owned(),
|
||||
name: "event name one".to_owned(),
|
||||
command_name: "queued event one".to_owned(),
|
||||
status: "completed".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T20:30:00Z").unwrap()),
|
||||
started: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:00:00Z").unwrap(),
|
||||
)),
|
||||
ended: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:29:14Z").unwrap(),
|
||||
)),
|
||||
duration: Some("00:29:14.1234567".to_owned()),
|
||||
},
|
||||
QueueEvent {
|
||||
trigger: "scheduled".to_owned(),
|
||||
name: "event name two".to_owned(),
|
||||
command_name: "queued event two".to_owned(),
|
||||
status: "started".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:15:00Z").unwrap()),
|
||||
started: Some(DateTime::from(
|
||||
DateTime::parse_from_rfc3339("2023-05-20T21:26:00Z").unwrap(),
|
||||
)),
|
||||
ended: None,
|
||||
duration: Some("01:02:03.7654321".to_owned()),
|
||||
},
|
||||
QueueEvent {
|
||||
trigger: "external".to_owned(),
|
||||
name: "event name three".to_owned(),
|
||||
command_name: "queued event three".to_owned(),
|
||||
status: "queued".to_owned(),
|
||||
queued: DateTime::from(DateTime::parse_from_rfc3339("2023-05-20T21:29:00Z").unwrap()),
|
||||
started: None,
|
||||
ended: None,
|
||||
duration: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn log_lines_vec() -> Vec<HorizontallyScrollableText> {
|
||||
vec![
|
||||
"2025-12-16 16:40:59 UTC|INFO|ImportListSyncService|log line alpha".into(),
|
||||
"2025-12-16 16:41:12 UTC|WARN|BookScanService|log line beta".into(),
|
||||
"2025-12-16 16:41:30 UTC|FATAL|CommandExecutor|log line gamma".into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user