lowfi/src/tests/ui.rs
Tal 02a4e688bd chore: update dependencies
for some reason, rodio decided it would be a great idea to change all of
the core terminology to be less technical. this is frankly strange, but
it's best not to use an outdated version of the dependency.

rodio is for some reason switching to a "breaking change every single
update" versioning model. why? i have frankly no idea, semver exists for
a reason and they decided just not to use it. 0.x.x is stupid, and
people should realize semver exists for a reason. to the total of zero
people who will read this, thanks for reading my short rant.
2026-03-05 20:34:45 +01:00

227 lines
6.2 KiB
Rust

/* The lowfi UI:
┌─────────────────────────────┐
│ loading │
│ [ ] 00:00/00:00 │
│ [s]kip [p]ause [q]uit │
└─────────────────────────────┘
*/
#[cfg(test)]
mod components {
use crate::ui::interface;
use std::time::Duration;
#[test]
fn format_duration_works() {
let d = Duration::from_secs(62);
assert_eq!(interface::components::format_duration(&d), "01:02");
}
#[test]
fn format_duration_zero() {
let d = Duration::from_secs(0);
assert_eq!(interface::components::format_duration(&d), "00:00");
}
#[test]
fn format_duration_hours_wrap() {
let d = Duration::from_secs(3661); // 1:01:01
assert_eq!(interface::components::format_duration(&d), "61:01");
}
#[test]
fn audio_bar_contains_percentage() {
let s = interface::components::audio_bar(10, 0.5, "50%");
assert!(s.contains("50%"));
assert!(s.starts_with(" volume:"));
}
#[test]
fn audio_bar_muted_volume() {
let s = interface::components::audio_bar(8, 0.0, "0%");
assert!(s.contains("0%"));
}
#[test]
fn audio_bar_full_volume() {
let s = interface::components::audio_bar(10, 1.0, "100%");
assert!(s.contains("100%"));
}
#[test]
fn controls_has_items() {
let s = interface::components::controls(30);
assert!(s.contains("[s]"));
assert!(s.contains("[p]"));
assert!(s.contains("[q]"));
}
}
#[cfg(test)]
mod window {
use crate::ui::interface::Window;
#[test]
fn new_border_strings() {
let w = Window::new(10, false, false, false);
assert!(w.titlebar.content.starts_with('┌'));
assert!(w.statusbar.starts_with('└'));
let w2 = Window::new(5, true, false, false);
assert!(w2.titlebar.content.is_empty());
assert!(w2.statusbar.is_empty());
}
fn sided(text: &str) -> String {
return format!("{text}");
}
#[test]
fn simple() {
let w = Window::new(3, false, false, false);
let (render, height) = w.render(vec![String::from("abc")]).unwrap();
const MIDDLE: &str = "─────";
assert_eq!(format!("{MIDDLE}\n{}\n{MIDDLE}", sided("abc")), render);
assert_eq!(height, 3);
}
#[test]
fn spaced() {
let w = Window::new(3, false, true, false);
let (render, height) = w
.render(vec![
String::from("abc"),
String::from(" b"),
String::from("c"),
])
.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, false, false);
assert!(!w.titlebar.content.is_empty());
}
}
#[cfg(test)]
mod interface {
use crossterm::style::Stylize;
use std::{sync::Arc, time::Duration};
use tokio::time::Instant;
use crate::{
downloader::Progress,
player::Current,
tracks,
ui::{Interface, State},
};
#[tokio::test]
async fn loading() {
let sink = Arc::new(rodio::Player::new().0);
let mut state = State::initial(sink, String::from("test"));
let menu = Interface::default().menu(&mut state);
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()
)
);
}
#[tokio::test]
async fn volume() {
let sink = Arc::new(rodio::Player::new().0);
sink.set_volume(0.5);
let mut state = State::initial(sink, String::from("test"));
state.volume_timer = Some(Instant::now());
let menu = Interface::default().menu(&mut state);
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()
)
);
}
#[tokio::test]
async fn progress() {
let sink = Arc::new(rodio::Player::new().0);
Progress::new().set(0.5);
let mut state = State::initial(sink, String::from("test"));
state.current = Current::Loading(Some(Progress::new()));
let menu = Interface::default().menu(&mut state);
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()
)
);
}
#[tokio::test]
async fn track() {
let sink = Arc::new(rodio::Player::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 mut state = State::initial(sink, String::from("test"));
state.current = Current::Track(track.clone());
let menu = Interface::default().menu(&mut state);
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()
)
);
}
}