use ratatui_core::text::{Line, Span};
use crate::components::{Boxed, Flex, Scroll, ScrollState, StatusBar, Text};
use crate::overlay::OverlaySpec;
use crate::style::Theme;
use crate::surface::Surface;
use crate::tests::support::{buffer, row};
use crate::view::{RenderCtx, View, element};
#[test]
fn an_inherited_theme_reaches_the_painted_cells() {
use ratatui_core::style::Color;
use crate::term::palette::TerminalPalette;
use crate::themes;
let palette = TerminalPalette::parse(
b"\x1b]11;rgb:1c1c/1e1e/2626\x1b\\\x1b]10;rgb:d5d5/d0d0/c8c8\x1b\\\
\x1b]4;12;rgb:5c5c/9f9f/ecec\x1b\\\x1b[?62c",
);
let derived = Theme::from_terminal(&palette).expect("a complete theme");
let panel = element(Boxed::new(element(Text::raw("body"))));
let buf = crate::testing::render(panel.as_ref(), 12, 3, &derived);
assert_eq!(
buf[(5, 1)].bg,
Color::Rgb(0x1c, 0x1e, 0x26),
"the screen fill is the reported background, verbatim"
);
assert_eq!(buf[(0, 0)].fg, derived.border);
assert_ne!(
derived.border, derived.text,
"the border is a derived tone, not one of the two reported colors"
);
let buf = crate::testing::render(panel.as_ref(), 12, 3, &themes::TERMINAL);
assert_eq!(
buf[(5, 1)].bg,
Color::Reset,
"the terminal's own background shows through"
);
assert_eq!(
buf[(0, 0)].fg,
Color::Indexed(8),
"the border is an ANSI slot the terminal resolves"
);
let log = crate::components::ConsoleLog::new(8);
log.line("entry");
let console = crate::components::Console::new(&log).title("t");
let paint_console = |theme: &Theme| {
let mut buf = buffer(12, 3);
let area = buf.area;
let ctx = RenderCtx::new(theme);
let mut surface = Surface::new(&mut buf, area);
console.render(area, &mut surface, &ctx);
buf
};
let buf = paint_console(&derived);
assert_eq!(
buf[(0, 2)].fg,
Color::Rgb(0xd5, 0xd0, 0xc8),
"body text is the reported foreground, verbatim"
);
assert_eq!(
buf[(0, 0)].fg,
Color::Rgb(0x5c, 0x9f, 0xec),
"the accent is the bright blue it reported"
);
assert_eq!(buf[(0, 2)].bg, derived.surface);
assert_ne!(
derived.surface, derived.background,
"the raised fill is derived, and distinct from the base"
);
let buf = paint_console(&themes::TERMINAL);
assert_eq!(
buf[(0, 2)].fg,
Color::Reset,
"body text is the terminal's own foreground"
);
assert_eq!(buf[(0, 0)].fg, Color::Indexed(4), "so is the accent");
}
#[test]
fn scroll_and_overlay_survive_tiny_screens() {
use ratatui_core::layout::Rect;
let lines: Vec<Line<'static>> = (0..50).map(|i| Line::from(format!("l{i}"))).collect();
let mut st = ScrollState::new();
st.clamp(50, 1);
let theme = Theme::default();
let ctx = RenderCtx::new(&theme);
let mut buf = buffer(1, 1);
let area = buf.area;
let mut surface = Surface::new(&mut buf, area);
Scroll::new(lines, &st).render(area, &mut surface, &ctx);
for (w, h) in [(0u16, 0u16), (1, 1), (2, 3), (5, 5)] {
let screen = Rect::new(0, 0, w, h);
let rect = OverlaySpec::centered(80, 80).min_size(4, 4).resolve(screen);
assert!(rect.right() <= screen.right(), "overlay exceeds {screen:?}");
assert!(
rect.bottom() <= screen.bottom(),
"overlay exceeds {screen:?}"
);
}
}
#[test]
fn nested_flex_tree_lays_out_status_and_body() {
let theme = Theme::default();
let mut buf = buffer(24, 6);
let area = buf.area;
let body = Boxed::new(element(Text::raw("hi"))).title("Body");
let status = StatusBar::new().left(vec![Span::raw("model: sim")]);
let tree = Flex::column()
.grow(1, element(body))
.fixed(1, element(status));
let ctx = RenderCtx::new(&theme);
let mut surface = Surface::new(&mut buf, area);
tree.render(area, &mut surface, &ctx);
assert!(row(&buf, 5).starts_with("model: sim"));
assert!(row(&buf, 0).contains("Body"));
assert_eq!(buf[(0, 0)].symbol(), "╭");
}
#[test]
fn a_split_footer_composes_components_over_published_markdown() {
use ratatui_core::backend::{Backend, TestBackend};
use ratatui_core::layout::Position;
use ratatui_core::terminal::{Terminal, TerminalOptions};
use crate::components::Markdown;
use crate::screen::{ScreenMode, Scrollback, close_footer, pin_footer};
let theme = Theme::default();
let mut backend = TestBackend::new(28, 10);
backend
.set_cursor_position(Position::new(0, 0))
.expect("place cursor");
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: ScreenMode::split_footer(3).viewport(),
},
)
.expect("inline terminal");
pin_footer(&mut terminal).expect("pin");
let scrollback = Scrollback::new();
scrollback.write(|_width| element(Markdown::new("# Answer\n\nIt was the **cache**.")));
assert!(scrollback.flush(&mut terminal, &theme).expect("flush"));
let draw = |terminal: &mut Terminal<TestBackend>| {
terminal
.draw(|frame| {
let area = frame.area();
let tree = Flex::column()
.grow(
1,
element(Boxed::new(element(Text::raw("ready"))).title("codex")),
)
.fixed(
1,
element(StatusBar::new().left(vec![Span::raw("ctrl-c quit")])),
);
crate::paint(frame.buffer_mut(), area, &Theme::default(), &tree, &[]);
})
.expect("draw footer");
};
draw(&mut terminal);
let lines: Vec<String> = {
let buffer = terminal.backend().buffer();
(0..10).map(|y| row(buffer, y)).collect()
};
assert!(
lines[..7].iter().any(|line| line.contains("Answer")),
"the heading was published: {lines:?}"
);
assert!(
lines[..7].iter().any(|line| line.contains("cache")),
"the body was published: {lines:?}"
);
assert!(
!lines.iter().any(|line| line.contains("**")),
"markdown was rendered, not printed as source: {lines:?}"
);
assert!(lines[7].contains("codex"), "footer border row: {lines:?}");
assert!(lines[9].starts_with("ctrl-c quit"), "status row: {lines:?}");
close_footer(&mut terminal).expect("close");
let after: Vec<String> = {
let buffer = terminal.backend().buffer();
(0..10).map(|y| row(buffer, y)).collect()
};
assert!(
after[7..].iter().all(|line| line.is_empty()),
"the footer's rows go back to the terminal: {after:?}"
);
assert!(
after[..7].iter().any(|line| line.contains("cache")),
"the published answer is the user's to keep: {after:?}"
);
}