Complete TUI coverage and security audit
This commit is contained in:
@@ -18,6 +18,29 @@ use crate::{
|
||||
const MINIMUM_WIDTH: u16 = 40;
|
||||
const MINIMUM_HEIGHT: u16 = 8;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ColorCapability {
|
||||
Color,
|
||||
Monochrome,
|
||||
}
|
||||
|
||||
impl ColorCapability {
|
||||
pub fn detect() -> Self {
|
||||
Self::from_environment(
|
||||
std::env::var_os("TERM").as_deref(),
|
||||
std::env::var_os("NO_COLOR").is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
fn from_environment(term: Option<&std::ffi::OsStr>, no_color: bool) -> Self {
|
||||
if no_color || term.is_some_and(|term| term == "dumb") {
|
||||
Self::Monochrome
|
||||
} else {
|
||||
Self::Color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum LayoutClass {
|
||||
TooSmall,
|
||||
@@ -39,6 +62,19 @@ pub fn layout_class(area: Rect) -> LayoutClass {
|
||||
}
|
||||
|
||||
pub fn draw(frame: &mut Frame, app: &App) {
|
||||
draw_with_color_capability(frame, app, ColorCapability::Color);
|
||||
}
|
||||
|
||||
pub fn draw_with_color_capability(frame: &mut Frame, app: &App, capability: ColorCapability) {
|
||||
draw_inner(frame, app);
|
||||
if capability == ColorCapability::Monochrome {
|
||||
for cell in &mut frame.buffer_mut().content {
|
||||
cell.set_fg(Color::Reset).set_bg(Color::Reset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_inner(frame: &mut Frame, app: &App) {
|
||||
let area = frame.area();
|
||||
if layout_class(area) == LayoutClass::TooSmall {
|
||||
frame.render_widget(
|
||||
@@ -77,7 +113,8 @@ fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(matrix) = app.qr_popup() {
|
||||
if let Some(popup) = app.qr_popup() {
|
||||
let matrix = popup.matrix();
|
||||
let padded_width = matrix.width() + 8;
|
||||
let needed_width =
|
||||
u16::try_from(padded_width.saturating_mul(2).saturating_add(2)).unwrap_or(u16::MAX);
|
||||
@@ -98,7 +135,7 @@ fn render_content(frame: &mut Frame, app: &App, area: Rect) {
|
||||
};
|
||||
frame.render_widget(
|
||||
Paragraph::new(text)
|
||||
.block(Block::bordered().title("OTP QR — Esc closes"))
|
||||
.block(Block::bordered().title(format!("{} — Esc closes", popup.title())))
|
||||
.wrap(Wrap { trim: false }),
|
||||
area,
|
||||
);
|
||||
@@ -654,6 +691,8 @@ fn prompt_line(app: &App) -> Paragraph<'static> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use ratatui::{Terminal, backend::TestBackend};
|
||||
|
||||
use super::*;
|
||||
@@ -685,6 +724,49 @@ mod tests {
|
||||
assert_eq!(layout_class(Rect::new(0, 0, 140, 20)), LayoutClass::Wide);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_capability_has_a_complete_monochrome_fallback() {
|
||||
assert_eq!(
|
||||
ColorCapability::from_environment(Some(OsStr::new("xterm-256color")), false),
|
||||
ColorCapability::Color
|
||||
);
|
||||
assert_eq!(
|
||||
ColorCapability::from_environment(Some(OsStr::new("dumb")), false),
|
||||
ColorCapability::Monochrome
|
||||
);
|
||||
assert_eq!(
|
||||
ColorCapability::from_environment(Some(OsStr::new("xterm")), true),
|
||||
ColorCapability::Monochrome
|
||||
);
|
||||
|
||||
let app = App::new();
|
||||
let backend = TestBackend::new(100, 20);
|
||||
let mut terminal = Terminal::new(backend).expect("test terminal");
|
||||
terminal
|
||||
.draw(|frame| draw_with_color_capability(frame, &app, ColorCapability::Monochrome))
|
||||
.expect("monochrome draw");
|
||||
assert!(
|
||||
terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.all(|cell| { cell.fg == Color::Reset && cell.bg == Color::Reset })
|
||||
);
|
||||
|
||||
terminal
|
||||
.draw(|frame| draw_with_color_capability(frame, &app, ColorCapability::Color))
|
||||
.expect("color draw");
|
||||
assert!(
|
||||
terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.any(|cell| { cell.fg != Color::Reset || cell.bg != Color::Reset })
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimum_size_message_is_clear() {
|
||||
let output = render(39, 8, &App::new());
|
||||
|
||||
Reference in New Issue
Block a user