Something went wrong. Try again.
A 5e storytelling engine with an LLM DM
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254//! Tests for a terminal whose width disagrees with the round's: a//! terminal resized while a round streams, and a table too wide for any//! terminal. These drive a `Transcript` directly, because the harness in//! `screen_tests.rs` plays keys and turn events, and a width change is//! neither.
use ratatui::backend::TestBackend;use ratatui::layout::Position;use ratatui::{TerminalOptions, Viewport};
use crate::play::screen::tests::buffer_text;
use super::*;
/// A paragraph that wraps to four rows at [`NARROW`] and to one at/// [`WIDE`].const PARAGRAPH: &str = "one two three four five six seven eight nine ten eleven twelve";
/// The block after [`PARAGRAPH`], which finishes the round.const AFTER: &str = "thirteen fourteen";
const NARROW: u16 = 20;
const WIDE: u16 = 80;
/// A tail area with room for every row these rounds form, so nothing/// spills and the width is the only thing under test.const DEEP_TAIL: u16 = 10;
/// How many rows the test terminals hold. The viewport fills the screen,/// so every row the transcript emits lands in the scrollback.const HEIGHT: u16 = 4;
/// A terminal `width` columns wide.////// A round gathers its rows against one of these and places them on it,/// so the rows of a round that spans a resize land on the terminal that/// was there when they were gathered.////// A resize is a second terminal here, not `TestBackend::resize`. That/// call re-slices the scrollback to the new width, which garbles every/// row already in it and leaves nothing to assert against. The width the/// terminal reports is the only thing a resize changes for a/// `Transcript`, and each terminal keeps a scrollback of its own, so the/// rows that went out before the change stay legible next to the ones/// that went out after.fn terminal(width: u16) -> Terminal<TestBackend> { let mut backend = TestBackend::new(width, HEIGHT); backend.set_cursor_position(Position::new(0, 0)).unwrap(); Terminal::with_options( backend, TerminalOptions { viewport: Viewport::Inline(HEIGHT), }, ) .unwrap()}
/// The rows of `terminal`'s scrollback.fn scrollback(terminal: &Terminal<TestBackend>) -> Vec<String> { buffer_text(terminal.backend().scrollback()) .lines() .map(str::to_string) .collect()}
#[test]fn a_round_that_widens_mid_stream_still_says_what_it_has_left() { let mut narrow = terminal(NARROW); let mut wide = terminal(WIDE); let mut transcript = Transcript::default();
transcript.delta(&format!("{PARAGRAPH}\n\n{AFTER}")); transcript.stream(&narrow, DEEP_TAIL).unwrap(); transcript.place(&mut narrow).unwrap(); transcript.delta(" fifteen"); transcript.stream(&wide, DEEP_TAIL).unwrap(); transcript.flush(&wide).unwrap(); transcript.place(&mut wide).unwrap();
assert_eq!( scrollback(&narrow), [ "one two three four", "five six seven eight", "nine ten eleven", "twelve", ] ); assert_eq!(scrollback(&wide), ["", "thirteen fourteen", "fifteen"]);}
#[test]fn a_round_takes_the_width_it_first_has_something_to_say_at() { let wide = terminal(WIDE); let mut narrow = terminal(NARROW); let mut transcript = Transcript::default();
// The play loop streams several times a second, whether or not the // DM has said anything yet. transcript.stream(&wide, DEEP_TAIL).unwrap(); transcript.delta(PARAGRAPH); transcript.stream(&narrow, 1).unwrap(); transcript.place(&mut narrow).unwrap();
// The paragraph takes four rows at twenty columns, and a tail area // of one row spills the first three of them. A round holding the // eighty columns of the idle pass would render one row and spill // none of it. assert_eq!( scrollback(&narrow), [ "one two three four", "five six seven eight", "nine ten eleven", ] );}
#[test]fn a_round_that_narrows_mid_stream_says_nothing_a_second_time() { let mut wide = terminal(WIDE); let mut narrow = terminal(NARROW); let mut transcript = Transcript::default();
transcript.delta(&format!("{PARAGRAPH}\n\n{AFTER}")); transcript.stream(&wide, DEEP_TAIL).unwrap(); transcript.place(&mut wide).unwrap(); transcript.flush(&narrow).unwrap(); transcript.place(&mut narrow).unwrap();
assert_eq!(scrollback(&wide), [PARAGRAPH]); assert_eq!(scrollback(&narrow), ["", AFTER]);}
#[test]fn a_row_too_wide_for_the_terminal_it_lands_on_keeps_every_word() { let mut wide = terminal(WIDE); let mut narrow = terminal(NARROW); let mut transcript = Transcript::default();
transcript.delta(&format!("{AFTER}\n\n{PARAGRAPH}")); transcript.stream(&wide, DEEP_TAIL).unwrap(); transcript.place(&mut wide).unwrap(); transcript.flush(&narrow).unwrap(); transcript.place(&mut narrow).unwrap();
// The paragraph rendered as one row at eighty columns. All of it is // here, over the four rows twenty columns takes, and the row that // separates it from the block above is one blank row of spacing. assert_eq!(scrollback(&wide), [AFTER]); assert_eq!( scrollback(&narrow), [ "", "one two three four", "five six seven eight", "nine ten eleven", "twelve", ] );}
/// A paragraph of two-column characters. It prints in twenty-three/// columns, which [`WIDE`] holds and [`NARROW`] does not, and in fourteen/// characters, which both of them hold: a wrap that counts characters/// leaves it too wide for [`NARROW`].const WIDE_CHARACTERS: &str = "日本語 の 文字 は 二 列";
/// The rows waiting to go out, as the text they carry.fn queued_rows(transcript: &Transcript) -> Vec<String> { transcript .queued .iter() .map(|queued| queued.row.to_string()) .collect()}
/// A transcript holding one round of [`WIDE_CHARACTERS`], rendered at/// [`WIDE`] and then flushed onto a terminal [`NARROW`] columns wide, so/// its one row has to wrap again to reach the screen whole.fn wide_round_flushed_narrow() -> Transcript { let wide = terminal(WIDE); let narrow = terminal(NARROW); let mut transcript = Transcript::default(); transcript.delta(WIDE_CHARACTERS); transcript.stream(&wide, DEEP_TAIL).unwrap(); transcript.flush(&narrow).unwrap(); transcript}
#[test]fn a_row_of_wide_characters_wraps_to_the_columns_the_terminal_has() { let transcript = wide_round_flushed_narrow();
assert_eq!(queued_rows(&transcript), ["日本語 の 文字 は 二", "列"]);}
#[test]fn every_row_of_a_wrapped_wide_row_fits_the_terminal() { let transcript = wide_round_flushed_narrow();
let widest = transcript .queued .iter() .map(|queued| queued.row.width()) .max(); assert_eq!(widest, Some(NARROW as usize));}
#[test]fn a_row_of_zero_width_characters_still_goes_out() { let wide = terminal(WIDE); let mut transcript = Transcript::default();
transcript .insert(&wide, "\u{200b}", aside(), Kind::Aside) .unwrap();
assert_eq!(queued_rows(&transcript), ["\u{200b}"]);}
/// A table of four columns, each already at the narrowest a column/// shrinks to. It renders twenty-five columns wide at any width, so/// [`NARROW`] cannot hold it.const WIDE_TABLE: &str = "| abc | def | ghi | jkl |\n|-----|-----|-----|-----|\n| mno | pqr | stu | vwx |\n";
#[test]fn a_table_too_wide_for_the_screen_clips_every_row_at_the_same_column() { let mut narrow = terminal(NARROW); let mut transcript = Transcript::default();
transcript.delta(WIDE_TABLE); transcript.flush(&narrow).unwrap(); transcript.place(&mut narrow).unwrap();
// One scrollback row per table row, every one of them cut at column // twenty, so the three columns that fit still line up. The content // rows show nineteen characters because the twentieth is the space // that opens the fourth column, and `scrollback` trims a trailing // space. assert_eq!( scrollback(&narrow), [ "┌─────┬─────┬─────┬─", "│ abc │ def │ ghi │", "├─────┼─────┼─────┼─", "│ mno │ pqr │ stu │", "└─────┴─────┴─────┴─", ] );}