ratatui 0.26.2

A library that's all about cooking up terminal user interfaces
Documentation
use ratatui::{
    backend::TestBackend,
    buffer::Buffer,
    layout::{Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style, Stylize},
    symbols,
    text::Span,
    widgets::{Block, Borders, Gauge, LineGauge},
    Terminal,
};

#[test]
fn widgets_gauge_renders() {
    let backend = TestBackend::new(40, 10);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal
        .draw(|f| {
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .margin(2)
                .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
                .split(f.size());

            let gauge = Gauge::default()
                .block(Block::default().title("Percentage").borders(Borders::ALL))
                .gauge_style(Style::default().bg(Color::Blue).fg(Color::Red))
                .use_unicode(true)
                .percent(43);
            f.render_widget(gauge, chunks[0]);
            let gauge = Gauge::default()
                .block(Block::default().title("Ratio").borders(Borders::ALL))
                .gauge_style(Style::default().bg(Color::Blue).fg(Color::Red))
                .use_unicode(true)
                .ratio(0.511_313_934_313_1);
            f.render_widget(gauge, chunks[1]);
        })
        .unwrap();
    let mut expected = Buffer::with_lines(vec![
        "                                        ",
        "                                        ",
        "  ┌Percentage────────────────────────┐  ",
        "  │██████████████▋43%                │  ",
        "  └──────────────────────────────────┘  ",
        "  ┌Ratio─────────────────────────────┐  ",
        "  │███████████████51%                │  ",
        "  └──────────────────────────────────┘  ",
        "                                        ",
        "                                        ",
    ]);
    expected.set_style(Rect::new(3, 3, 34, 1), Style::new().red().on_blue());

    expected.set_style(Rect::new(3, 6, 15, 1), Style::new().red().on_blue());
    // Note that filled part of the gauge only covers the 5 and the 1, not the % symbol
    expected.set_style(Rect::new(18, 6, 2, 1), Style::new().blue().on_red());
    expected.set_style(Rect::new(20, 6, 17, 1), Style::new().red().on_blue());

    terminal.backend().assert_buffer(&expected);
}

#[test]
fn widgets_gauge_renders_no_unicode() {
    let backend = TestBackend::new(40, 10);
    let mut terminal = Terminal::new(backend).unwrap();

    terminal
        .draw(|f| {
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .margin(2)
                .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
                .split(f.size());

            let gauge = Gauge::default()
                .block(Block::default().title("Percentage").borders(Borders::ALL))
                .percent(43)
                .use_unicode(false);
            f.render_widget(gauge, chunks[0]);
            let gauge = Gauge::default()
                .block(Block::default().title("Ratio").borders(Borders::ALL))
                .ratio(0.211_313_934_313_1)
                .use_unicode(false);
            f.render_widget(gauge, chunks[1]);
        })
        .unwrap();
    let expected = Buffer::with_lines(vec![
        "                                        ",
        "                                        ",
        "  ┌Percentage────────────────────────┐  ",
        "  │███████████████43%                │  ",
        "  └──────────────────────────────────┘  ",
        "  ┌Ratio─────────────────────────────┐  ",
        "  │███████        21%                │  ",
        "  └──────────────────────────────────┘  ",
        "                                        ",
        "                                        ",
    ]);
    terminal.backend().assert_buffer(&expected);
}

#[test]
fn widgets_gauge_applies_styles() {
    let backend = TestBackend::new(12, 5);
    let mut terminal = Terminal::new(backend).unwrap();

    terminal
        .draw(|f| {
            let gauge = Gauge::default()
                .block(
                    Block::default()
                        .title(Span::styled("Test", Style::default().fg(Color::Red)))
                        .borders(Borders::ALL),
                )
                .gauge_style(Style::default().fg(Color::Blue).bg(Color::Red))
                .percent(43)
                .label(Span::styled(
                    "43%",
                    Style::default()
                        .fg(Color::Green)
                        .add_modifier(Modifier::BOLD),
                ));
            f.render_widget(gauge, f.size());
        })
        .unwrap();
    let mut expected = Buffer::with_lines(vec![
        "┌Test──────┐",
        "│████      │",
        "│███43%    │",
        "│████      │",
        "└──────────┘",
    ]);
    // title
    expected.set_style(Rect::new(1, 0, 4, 1), Style::default().fg(Color::Red));
    // gauge area
    expected.set_style(
        Rect::new(1, 1, 10, 3),
        Style::default().fg(Color::Blue).bg(Color::Red),
    );
    // filled area
    expected.set_style(
        Rect::new(1, 1, 4, 3),
        Style::default().fg(Color::Blue).bg(Color::Red),
    );
    // label (foreground and modifier from label style)
    expected.set_style(
        Rect::new(4, 2, 1, 1),
        Style::default()
            .fg(Color::Green)
            // "4" is in the filled area so background is gauge_style foreground
            .bg(Color::Blue)
            .add_modifier(Modifier::BOLD),
    );
    expected.set_style(
        Rect::new(5, 2, 2, 1),
        Style::default()
            .fg(Color::Green)
            // "3%" is not in the filled area so background is gauge_style background
            .bg(Color::Red)
            .add_modifier(Modifier::BOLD),
    );
    terminal.backend().assert_buffer(&expected);
}

#[test]
fn widgets_gauge_supports_large_labels() {
    let backend = TestBackend::new(10, 1);
    let mut terminal = Terminal::new(backend).unwrap();

    terminal
        .draw(|f| {
            let gauge = Gauge::default()
                .percent(43)
                .label("43333333333333333333333333333%");
            f.render_widget(gauge, f.size());
        })
        .unwrap();
    let expected = Buffer::with_lines(vec!["4333333333"]);
    terminal.backend().assert_buffer(&expected);
}

#[test]
fn widgets_line_gauge_renders() {
    let backend = TestBackend::new(20, 4);
    let mut terminal = Terminal::new(backend).unwrap();
    terminal
        .draw(|f| {
            let gauge = LineGauge::default()
                .gauge_style(Style::default().fg(Color::Green).bg(Color::White))
                .ratio(0.43);
            f.render_widget(
                gauge,
                Rect {
                    x: 0,
                    y: 0,
                    width: 20,
                    height: 1,
                },
            );
            let gauge = LineGauge::default()
                .block(Block::default().title("Gauge 2").borders(Borders::ALL))
                .gauge_style(Style::default().fg(Color::Green))
                .line_set(symbols::line::THICK)
                .ratio(0.211_313_934_313_1);
            f.render_widget(
                gauge,
                Rect {
                    x: 0,
                    y: 1,
                    width: 20,
                    height: 3,
                },
            );
        })
        .unwrap();
    let mut expected = Buffer::with_lines(vec![
        "43% ────────────────",
        "┌Gauge 2───────────┐",
        "│21% ━━━━━━━━━━━━━━│",
        "└──────────────────┘",
    ]);
    for col in 4..10 {
        expected.get_mut(col, 0).set_fg(Color::Green);
    }
    for col in 10..20 {
        expected.get_mut(col, 0).set_fg(Color::White);
    }
    for col in 5..7 {
        expected.get_mut(col, 2).set_fg(Color::Green);
    }
    terminal.backend().assert_buffer(&expected);
}