mirror of
https://github.com/talwat/lowfi
synced 2025-12-09 16:34:12 +00:00
255 lines
6.8 KiB
Rust
255 lines
6.8 KiB
Rust
/* The lowfi UI:
|
|
┌─────────────────────────────┐
|
|
│ loading │
|
|
│ [ ] 00:00/00:00 │
|
|
│ [s]kip [p]ause [q]uit │
|
|
└─────────────────────────────┘
|
|
*/
|
|
|
|
#[cfg(test)]
|
|
mod components {
|
|
use crate::ui;
|
|
|
|
use std::time::Duration;
|
|
|
|
#[test]
|
|
fn format_duration_works() {
|
|
let d = Duration::from_secs(62);
|
|
assert_eq!(ui::components::format_duration(&d), "01:02");
|
|
}
|
|
|
|
#[test]
|
|
fn format_duration_zero() {
|
|
let d = Duration::from_secs(0);
|
|
assert_eq!(ui::components::format_duration(&d), "00:00");
|
|
}
|
|
|
|
#[test]
|
|
fn format_duration_hours_wrap() {
|
|
let d = Duration::from_secs(3661); // 1:01:01
|
|
assert_eq!(ui::components::format_duration(&d), "61:01");
|
|
}
|
|
|
|
#[test]
|
|
fn audio_bar_contains_percentage() {
|
|
let s = ui::components::audio_bar(10, 0.5, "50%");
|
|
assert!(s.contains("50%"));
|
|
assert!(s.starts_with(" volume:"));
|
|
}
|
|
|
|
#[test]
|
|
fn audio_bar_muted_volume() {
|
|
let s = ui::components::audio_bar(8, 0.0, "0%");
|
|
assert!(s.contains("0%"));
|
|
}
|
|
|
|
#[test]
|
|
fn audio_bar_full_volume() {
|
|
let s = ui::components::audio_bar(10, 1.0, "100%");
|
|
assert!(s.contains("100%"));
|
|
}
|
|
|
|
#[test]
|
|
fn controls_has_items() {
|
|
let s = ui::components::controls(30);
|
|
assert!(s.contains("[s]"));
|
|
assert!(s.contains("[p]"));
|
|
assert!(s.contains("[q]"));
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod window {
|
|
use crate::ui::window::Window;
|
|
|
|
#[test]
|
|
fn new_border_strings() {
|
|
let w = Window::new(10, false);
|
|
assert!(w.borders[0].starts_with('┌'));
|
|
assert!(w.borders[1].starts_with('└'));
|
|
|
|
let w2 = Window::new(5, true);
|
|
assert!(w2.borders[0].is_empty());
|
|
assert!(w2.borders[1].is_empty());
|
|
}
|
|
|
|
fn sided(text: &str) -> String {
|
|
return format!("│ {text} │");
|
|
}
|
|
|
|
#[test]
|
|
fn simple() {
|
|
let mut w = Window::new(3, false);
|
|
let (render, height) = w.render(vec![String::from("abc")], false, true).unwrap();
|
|
|
|
const MIDDLE: &str = "─────";
|
|
assert_eq!(format!("┌{MIDDLE}┐\n{}\n└{MIDDLE}┘", sided("abc")), render);
|
|
assert_eq!(height, 3);
|
|
}
|
|
|
|
#[test]
|
|
fn spaced() {
|
|
let mut w = Window::new(3, false);
|
|
let (render, height) = w
|
|
.render(
|
|
vec![String::from("abc"), String::from(" b"), String::from("c")],
|
|
true,
|
|
true,
|
|
)
|
|
.unwrap();
|
|
|
|
const MIDDLE: &str = "─────";
|
|
assert_eq!(
|
|
format!(
|
|
"┌{MIDDLE}┐\n{}\n{}\n{}\n└{MIDDLE}┘",
|
|
sided("abc"),
|
|
sided(" b "),
|
|
sided("c "),
|
|
),
|
|
render
|
|
);
|
|
assert_eq!(height, 5);
|
|
}
|
|
|
|
#[test]
|
|
fn zero_width_window() {
|
|
let w = Window::new(0, false);
|
|
assert!(!w.borders[0].is_empty());
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod interface {
|
|
use crossterm::style::Stylize;
|
|
use std::{sync::Arc, time::Duration};
|
|
use tokio::time::Instant;
|
|
|
|
use crate::{
|
|
download::PROGRESS,
|
|
player::Current,
|
|
tracks,
|
|
ui::{
|
|
interface::{self, Params},
|
|
State,
|
|
},
|
|
};
|
|
|
|
#[test]
|
|
fn loading() {
|
|
let sink = Arc::new(rodio::Sink::new().0);
|
|
let mut state = State::initial(sink, 3, Current::Loading(None), String::from("test"));
|
|
let menu = interface::menu(&mut state, Params::default());
|
|
|
|
assert_eq!(menu[0], "loading ");
|
|
assert_eq!(menu[1], " [ ] 00:00/00:00 ");
|
|
assert_eq!(
|
|
menu[2],
|
|
format!(
|
|
"{}kip {}ause {}uit",
|
|
"[s]".bold(),
|
|
"[p]".bold(),
|
|
"[q]".bold()
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn volume() {
|
|
let sink = Arc::new(rodio::Sink::new().0);
|
|
sink.set_volume(0.5);
|
|
let mut state = State::initial(sink, 3, Current::Loading(None), String::from("test"));
|
|
state.timer = Some(Instant::now());
|
|
|
|
let menu = interface::menu(&mut state, Params::default());
|
|
|
|
assert_eq!(menu[0], "loading ");
|
|
assert_eq!(menu[1], " volume: [///// ] 50% ");
|
|
assert_eq!(
|
|
menu[2],
|
|
format!(
|
|
"{}kip {}ause {}uit",
|
|
"[s]".bold(),
|
|
"[p]".bold(),
|
|
"[q]".bold()
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn progress() {
|
|
let sink = Arc::new(rodio::Sink::new().0);
|
|
PROGRESS.store(50, std::sync::atomic::Ordering::Relaxed);
|
|
let mut state = State::initial(
|
|
sink,
|
|
3,
|
|
Current::Loading(Some(&PROGRESS)),
|
|
String::from("test"),
|
|
);
|
|
let menu = interface::menu(&mut state, Params::default());
|
|
|
|
assert_eq!(menu[0], format!("loading {} ", "50%".bold()));
|
|
assert_eq!(menu[1], " [ ] 00:00/00:00 ");
|
|
assert_eq!(
|
|
menu[2],
|
|
format!(
|
|
"{}kip {}ause {}uit",
|
|
"[s]".bold(),
|
|
"[p]".bold(),
|
|
"[q]".bold()
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn track() {
|
|
let sink = Arc::new(rodio::Sink::new().0);
|
|
let track = tracks::Info {
|
|
path: "/path".to_owned(),
|
|
display: "Test Track".to_owned(),
|
|
width: 4 + 1 + 5,
|
|
duration: Some(Duration::from_secs(8)),
|
|
};
|
|
|
|
let current = Current::Track(track.clone());
|
|
let mut state = State::initial(sink, 3, current, String::from("test"));
|
|
let menu = interface::menu(&mut state, Params::default());
|
|
|
|
assert_eq!(
|
|
menu[0],
|
|
format!("playing {} ", track.display.bold())
|
|
);
|
|
assert_eq!(menu[1], " [ ] 00:00/00:08 ");
|
|
assert_eq!(
|
|
menu[2],
|
|
format!(
|
|
"{}kip {}ause {}uit",
|
|
"[s]".bold(),
|
|
"[p]".bold(),
|
|
"[q]".bold()
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod environment {
|
|
use crate::ui::Environment;
|
|
|
|
#[test]
|
|
fn ready_and_cleanup_no_panic() {
|
|
// Try to create the environment but don't fail the test if the
|
|
// terminal isn't available. We just assert the API exists.
|
|
if let Ok(env) = Environment::ready(false) {
|
|
// cleanup should succeed
|
|
let _ = env.cleanup(true);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ready_with_alternate_screen() {
|
|
if let Ok(env) = Environment::ready(true) {
|
|
let _ = env.cleanup(false);
|
|
}
|
|
}
|
|
}
|