Skip to main content

TestBackend

Struct TestBackend 

Source
pub struct TestBackend { /* private fields */ }
Expand description

Headless rendering backend for tests.

Renders a UI closure to an in-memory Buffer without a real terminal. Use render to run one frame, then inspect the output with line, assert_contains, or to_string_trimmed. Session state persists across renders, so multi-frame tests can exercise hooks, focus, and previous-frame hit testing.

§Example

use slt::TestBackend;

let mut backend = TestBackend::new(40, 10);
backend.render(|ui| {
    ui.text("hello");
});
backend.assert_contains("hello");

Implementations§

Source§

impl TestBackend

Source

pub fn new(width: u32, height: u32) -> Self

Create a test backend with the given terminal dimensions.

Source

pub fn record_frames(self) -> Self

Enable frame recording.

After this call, every subsequent render, render_with_events, and run_with_events call appends a FrameRecord to the internal history. Disabled by default so tests that don’t need history pay zero memory overhead.

Returns self for chaining.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(20, 3).record_frames();
for n in 0..3 {
    tb.render(|ui| {
        ui.text(format!("frame {n}"));
    });
}
assert_eq!(tb.frames().len(), 3);
tb.frames()[0].assert_contains("frame 0");
tb.frames()[2].assert_contains("frame 2");
Source

pub fn frames(&self) -> &[FrameRecord]

Return all captured frame snapshots in chronological order.

Returns an empty slice if record_frames was never called on this backend.

Source

pub fn render(&mut self, f: impl FnOnce(&mut Context))

Run a UI closure for one frame and render to the internal buffer.

Source

pub fn render_with_events( &mut self, events: Vec<Event>, focus_index: usize, prev_focus_count: usize, f: impl FnOnce(&mut Context), )

Render with injected events and focus state for interaction testing.

Source

pub fn run_with_events( &mut self, events: Vec<Event>, f: impl FnOnce(&mut Context), )

Convenience wrapper: render with events using default focus state.

Source

pub fn line(&self, y: u32) -> String

Get the rendered text content of row y (trimmed trailing spaces)

Source

pub fn assert_line(&self, y: u32, expected: &str)

Assert that row y contains expected as a substring

Source

pub fn assert_line_contains(&self, y: u32, expected: &str)

Assert that row y contains expected as a substring

Source

pub fn assert_contains(&self, expected: &str)

Assert that any line in the buffer contains expected

Source

pub fn buffer(&self) -> &Buffer

Access the underlying render buffer.

Source

pub fn width(&self) -> u32

Terminal width used for this backend.

Source

pub fn height(&self) -> u32

Terminal height used for this backend.

Source

pub fn to_string_trimmed(&self) -> String

Return the full rendered buffer as a multi-line string.

Each row is trimmed of trailing spaces and joined with newlines. Useful for snapshot testing with insta::assert_snapshot!.

Source

pub fn assert_not_contains(&self, expected: &str)

Assert that no row in the buffer contains expected as a substring.

Panics with the offending row indices and contents on failure.

Source

pub fn assert_line_not_contains(&self, y: u32, expected: &str)

Assert that row y does NOT contain expected as a substring.

Source

pub fn assert_empty_line(&self, y: u32)

Assert that row y is entirely blank (contains no non-space content).

Useful for verifying that cleared, padded, or overflow-suppressed rows render as empty.

Source

pub fn assert_style_at(&self, x: u32, y: u32, expected: Style)

Assert that the cell at (x, y) carries exactly the expected style.

Useful for focused color/modifier regression checks without committing to a full-buffer snapshot. Panics with (x, y), the actual style, and the expected style on mismatch.

Source

pub fn find_text(&self, needle: &str) -> Option<(u32, u32)>

Find the first buffer position where needle begins in the rendered text grid, scanning rows top-to-bottom and columns left-to-right.

Each cell contributes its glyph at the cell’s own column; empty cells (blanks and wide-char trailing cells) count as a single space so the returned x is the actual buffer column where the match starts. The search is per-row — a needle that wraps across a row boundary is not matched. Returns None if needle is empty or absent.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(20, 2);
tb.render(|ui| {
    ui.text("  hello");
});
assert_eq!(tb.find_text("hello"), Some((2, 0)));
assert_eq!(tb.find_text("nope"), None);
Source

pub fn assert_region(&self, x: u32, y: u32, w: u32, h: u32, expected: &str)

Assert that the rectangular region anchored at (x, y) with width w and height h renders exactly expected (rows joined with \n).

Each region row is the slice of buffer columns x..x+w on buffer row y..y+h, with empty cells rendered as a space and trailing spaces of each region row preserved (so width is significant). Columns or rows that fall outside the buffer are treated as blanks. Panics with an aligned expected-vs-actual diff on mismatch.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(10, 3);
tb.render(|ui| {
    let _ = ui.col(|ui| {
        ui.text("ab");
        ui.text("cd");
    });
});
tb.assert_region(0, 0, 2, 2, "ab\ncd");
Source

pub fn region(&self, x: u32, y: u32, w: u32, h: u32) -> String

Render the rectangular region anchored at (x, y) with width w and height h as a multi-line string (rows joined with \n).

Empty cells render as a single space and trailing spaces are preserved, so the result is exactly w columns wide per row. Columns or rows outside the buffer are blank-filled. Useful for scoping a snapshot to a sub-rectangle without asserting on the full buffer.

Source

pub fn assert_styled_contains( &self, needle: &str, predicate: impl Fn(&Style) -> bool, )

Assert that needle is rendered somewhere in the buffer AND every cell of the matched run satisfies predicate (applied to each cell’s Style).

Combines a content check with a per-cell style check, which is more ergonomic than pairing find_text with repeated assert_style_at calls. The run is located with find_text (per-row, left-to-right), then each of the needle’s char-count cells starting at the match is tested. Panics if the needle is absent or any covered cell fails the predicate.

§Example
use slt::{Color, TestBackend};

let mut tb = TestBackend::new(20, 1);
tb.render(|ui| {
    ui.text("hi").fg(Color::Red).bold();
});
tb.assert_styled_contains("hi", |s| {
    s.fg == Some(Color::Red) && s.modifiers.contains(slt::Modifiers::BOLD)
});
Source

pub fn snapshot(&self) -> String

Produce a stable, plain-text snapshot of the whole buffer.

Every buffer row is rendered exactly width columns wide (empty cells as spaces, no trailing trim) and joined with \n. Unlike to_string_trimmed, no trailing blank rows are dropped and per-row width is fixed, giving a deterministic snapshot suitable for assert_snapshot_eq or external snapshot tooling.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(3, 2);
tb.render(|ui| {
    ui.text("ab");
});
assert_eq!(tb.snapshot(), "ab \n   ");
Source

pub fn assert_snapshot_eq(&self, expected: &str)

Assert the buffer snapshot equals expected, panicking with a unified-diff-style report on mismatch.

Trailing whitespace on each line of expected is ignored (the actual snapshot is right-padded to the buffer width), so callers can write trimmed expected strings. The panic message lists each differing row with - (expected) / + (actual) markers.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(5, 2);
tb.render(|ui| {
    ui.text("hi");
});
tb.assert_snapshot_eq("hi\n");
Source

pub fn sequence(&mut self) -> TestSequence<'_>

Begin building a multi-step interaction sequence.

Each tick (or key) appends an event batch + render closure pair. run executes them in order, advancing FrameState naturally between steps so callers don’t need to thread focus_index / prev_focus_count manually.

§Example
use slt::{KeyCode, TestBackend};

let mut tb = TestBackend::new(20, 3);
tb.sequence()
    .tick(|ui| { ui.text("ready"); })
    .key(KeyCode::Esc, |ui| { ui.text("after esc"); })
    .run();
tb.assert_contains("after esc");
Source

pub fn type_string(&mut self, s: &str, render: impl FnMut(&mut Context))

Simulate typing s one character at a time, rendering with render between each character.

Each character produces a KeyCode::Char event with no modifiers. Focus state is preserved across characters.

§Example
use slt::TestBackend;

let mut tb = TestBackend::new(20, 3);
let mut typed = String::new();
tb.type_string("hi", |ui| {
    ui.text(&typed);
});
// 2 characters → 2 frames rendered.
drop(typed);

Trait Implementations§

Source§

impl Display for TestBackend

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToCompactString for T
where T: Display,

Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.