返回 CodeWhale
body_tests.rs
根目录 / crates / tui / src / tui / work_surface / render / body_tests.rs
1 use super::*;
2 use crate::config::Config;
3 use ratatui::{Terminal, backend::TestBackend, buffer::Buffer, style::Color};
4 use std::path::PathBuf;
5
6 fn app() -> App {
7 let mut app = App::new(
8 crate::test_support::test_tui_options(PathBuf::from(".")),
9 &Config::default(),
10 );
11 app.ui_locale = codewhale_localization::Locale::En;
12 app.ui_theme.text_body = Color::Rgb(29, 47, 83);
13 app.ui_theme.panel_bg = Color::Rgb(13, 31, 59);
14 app.ui_theme.border = Color::Rgb(71, 43, 101);
15 app.ui_theme.status_working = Color::Rgb(47, 109, 67);
16 app
17 }
18 fn paint(app: &mut App, area: Rect, old: bool) -> Buffer {
19 let mut terminal = Terminal::new(TestBackend::new(150, 24)).unwrap();
20 app.viewport.interaction_targets.clear();
21 terminal
22 .draw(|frame| {
23 for cell in &mut frame.buffer_mut().content {
24 cell.set_symbol("~").set_style(
25 Style::default()
26 .fg(Color::Yellow)
27 .bg(Color::Rgb(11, 23, 37))
28 .add_modifier(Modifier::ITALIC),
29 );
30 }
31 if old {
32 super::body_legacy::render(frame, area, app);
33 } else {
34 render(frame, area, app);
35 }
36 })
37 .unwrap();
38 terminal.backend().buffer().clone()
39 }
40 #[test]
41 fn shared_native_body_matches_frozen_whole_buffer_actions_and_hover_receipts() {
42 let mut app = app();
43 for placement in [
44 WorkSurfacePlacement::Top,
45 WorkSurfacePlacement::Bottom,
46 WorkSurfacePlacement::Left,
47 WorkSurfacePlacement::Right,
48 ] {
49 for panel in RailPanel::ORDER
50 .into_iter()
51 .filter(|panel| *panel != RailPanel::Git)
52 {
53 for width in [12, 40, 59, 71, 72, 80, 120] {
54 for height in [1, 2, 3, 5, 12] {
55 app.work_surface.effective_placement = placement;
56 app.work_surface.panel = panel;
57 app.work_surface.explicit_view = true;
58 app.work_surface.focused = true;
59 app.work_surface.scroll_offset = usize::MAX;
60 let area = Rect::new(7, 5, width, height);
61 let expected = paint(&mut app, area, true);
62 let expected_geometry = (
63 app.work_surface.visible_rows,
64 app.work_surface.total_rows,
65 app.work_surface.scroll_offset,
66 );
67 let expected_hitboxes = app
68 .work_surface
69 .hitboxes
70 .iter()
71 .map(|box_| (box_.id.clone(), box_.row_y))
72 .collect::<Vec<_>>();
73 let expected_actions = app
74 .viewport
75 .interaction_targets
76 .iter()
77 .copied()
78 .collect::<Vec<_>>();
79 let expected_hover = app.sidebar_hover.sections.last().map(|section| {
80 (
81 section.content_area,
82 section.lines.clone(),
83 section.rows.clone(),
84 )
85 });
86 let actual = paint(&mut app, area, false);
87 assert_eq!(
88 actual, expected,
89 "placement={placement:?} panel={panel:?} area={area:?}"
90 );
91 assert_eq!(
92 (
93 app.work_surface.visible_rows,
94 app.work_surface.total_rows,
95 app.work_surface.scroll_offset
96 ),
97 expected_geometry
98 );
99 assert_eq!(
100 app.work_surface
101 .hitboxes
102 .iter()
103 .map(|box_| (box_.id.clone(), box_.row_y))
104 .collect::<Vec<_>>(),
105 expected_hitboxes
106 );
107 assert_eq!(
108 app.viewport
109 .interaction_targets
110 .iter()
111 .copied()
112 .collect::<Vec<_>>(),
113 expected_actions
114 );
115 assert_eq!(
116 app.sidebar_hover.sections.last().map(|section| (
117 section.content_area,
118 section.lines.clone(),
119 section.rows.clone()
120 )),
121 expected_hover
122 );
123 }
124 }
125 }
126 }
127 }
128 #[test]
129 fn shared_native_scroll_rail_preserves_custom_ground_and_existing_modifiers() {
130 let app = app();
131 for height in 0..=12 {
132 for total in [0, 1, 8, 128] {
133 let area = Rect::new(7, 5, 1, height);
134 let mut expected = Buffer::empty(Rect::new(2, 3, 30, 20));
135 for cell in &mut expected.content {
136 cell.set_symbol("~").set_style(
137 Style::default()
138 .bg(Color::Green)
139 .add_modifier(Modifier::ITALIC | Modifier::REVERSED),
140 );
141 }
142 let mut actual = expected.clone();
143 // Exact old rail formula with the host's original foreground/background assignments.
144 if height > 0 && total > 0 {
145 let h = usize::from(height);
146 let size = (h * 3 / total).max(1).min(h);
147 let start =
148 2usize.saturating_mul(h.saturating_sub(size)) / total.saturating_sub(3).max(1);
149 for row in 0..h {
150 let active = row >= start && row < start.saturating_add(size);
151 expected[(7, 5 + row as u16)]
152 .set_symbol(if active { "┃" } else { "│" })
153 .set_fg(if active {
154 app.ui_theme.status_working
155 } else {
156 app.ui_theme.border
157 })
158 .set_bg(app.ui_theme.panel_bg);
159 }
160 }
161 codewhale_ratatui::WorkbarScrollbar {
162 offset: 2,
163 visible: 3,
164 total,
165 thumb: "┃",
166 track: "│",
167 thumb_style: Style::default()
168 .fg(app.ui_theme.status_working)
169 .bg(app.ui_theme.panel_bg),
170 track_style: Style::default()
171 .fg(app.ui_theme.border)
172 .bg(app.ui_theme.panel_bg),
173 }
174 .paint(area, &mut actual);
175 assert_eq!(actual, expected, "height={height} total={total}");
176 }
177 }
178 }
179
179 lines RUST