mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-17 15:34:54 +00:00
test(tui/vt100): reproduce bottom clipping regression
This commit is contained in:
@@ -3,12 +3,15 @@
|
||||
use super::ChatWidget;
|
||||
use crate::app_event::AppEvent;
|
||||
use crate::app_event_sender::AppEventSender;
|
||||
use crate::history_cell::{self, HistoryCellType};
|
||||
use crate::markdown_render::render_markdown_text;
|
||||
use crate::tui::TerminalInfo;
|
||||
use code_core::history::state::HistoryRecord;
|
||||
use code_core::config::{Config, ConfigOverrides, ConfigToml};
|
||||
use code_core::protocol::Event;
|
||||
use std::collections::VecDeque;
|
||||
use code_core::history::state::HistoryRecord;
|
||||
use code_core::protocol::{BackgroundEventEvent, Event, EventMsg, OrderMeta};
|
||||
use once_cell::sync::Lazy;
|
||||
use ratatui::text::Line;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::runtime::Runtime;
|
||||
@@ -24,6 +27,7 @@ static TEST_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
|
||||
pub struct ChatWidgetHarness {
|
||||
chat: ChatWidget<'static>,
|
||||
events: Receiver<AppEvent>,
|
||||
helper_seq: u64,
|
||||
}
|
||||
|
||||
impl ChatWidgetHarness {
|
||||
@@ -56,7 +60,11 @@ impl ChatWidgetHarness {
|
||||
None,
|
||||
);
|
||||
|
||||
Self { chat, events: rx }
|
||||
Self {
|
||||
chat,
|
||||
events: rx,
|
||||
helper_seq: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: Event) {
|
||||
@@ -64,51 +72,61 @@ impl ChatWidgetHarness {
|
||||
}
|
||||
|
||||
pub(crate) fn flush_into_widget(&mut self) {
|
||||
let mut queue: VecDeque<AppEvent> = self.drain_events().into();
|
||||
if queue.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut queue: VecDeque<AppEvent> = self
|
||||
.drain_events()
|
||||
.into_iter()
|
||||
.filter(|event| !matches!(event, AppEvent::RequestRedraw))
|
||||
.collect();
|
||||
|
||||
let mut debug = Vec::new();
|
||||
while let Some(event) = queue.pop_front() {
|
||||
match event {
|
||||
AppEvent::InsertHistory(lines) => {
|
||||
debug.push("InsertHistory");
|
||||
self.chat.insert_history_lines(lines);
|
||||
}
|
||||
AppEvent::InsertHistoryWithKind { id, kind, lines } => {
|
||||
debug.push("InsertHistoryWithKind");
|
||||
self.chat.insert_history_lines_with_kind(kind, id, lines);
|
||||
}
|
||||
AppEvent::InsertFinalAnswer { id, lines, source } => {
|
||||
debug.push("InsertFinalAnswer");
|
||||
self.chat.insert_final_answer_with_id(id, lines, source);
|
||||
}
|
||||
AppEvent::InsertBackgroundEvent { message, placement, order } => {
|
||||
debug.push("InsertBackgroundEvent");
|
||||
self.chat
|
||||
.insert_background_event_with_placement(message, placement, order);
|
||||
}
|
||||
AppEvent::CommitTick => {
|
||||
debug.push("CommitTick");
|
||||
self.chat.on_commit_tick();
|
||||
let newly_emitted = self.drain_events();
|
||||
if !newly_emitted.is_empty() {
|
||||
queue.extend(newly_emitted);
|
||||
queue.extend(
|
||||
newly_emitted
|
||||
.into_iter()
|
||||
.filter(|event| !matches!(event, AppEvent::RequestRedraw)),
|
||||
);
|
||||
}
|
||||
}
|
||||
AppEvent::StartCommitAnimation => {
|
||||
debug.push("StartCommitAnimation");
|
||||
}
|
||||
AppEvent::StopCommitAnimation => {
|
||||
debug.push("StopCommitAnimation");
|
||||
}
|
||||
AppEvent::StartCommitAnimation
|
||||
| AppEvent::StopCommitAnimation
|
||||
| AppEvent::ScheduleFrameIn(_)
|
||||
| AppEvent::SetTerminalTitle { .. }
|
||||
| AppEvent::EmitTuiNotification { .. }
|
||||
| AppEvent::RequestRedraw
|
||||
| AppEvent::Redraw
|
||||
| AppEvent::PreviewTheme(_)
|
||||
| AppEvent::PreviewSpinner(_) => {}
|
||||
// Other events are either no-ops for VT100 rendering or handled elsewhere.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if !debug.is_empty() {
|
||||
println!("flush events: {debug:?}");
|
||||
if !queue.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let newly_emitted = self.drain_events();
|
||||
if !newly_emitted.is_empty() {
|
||||
queue.extend(
|
||||
newly_emitted
|
||||
.into_iter()
|
||||
.filter(|event| !matches!(event, AppEvent::RequestRedraw)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +142,45 @@ impl ChatWidgetHarness {
|
||||
&mut self.chat
|
||||
}
|
||||
|
||||
pub fn push_user_prompt(&mut self, message: impl Into<String>) {
|
||||
let state = history_cell::new_user_prompt(message.into());
|
||||
self.chat.history_push_plain_state(state);
|
||||
}
|
||||
|
||||
pub fn push_background_event(&mut self, message: impl Into<String>) {
|
||||
let seq = self.next_helper_seq();
|
||||
self.handle_event(Event {
|
||||
id: format!("bg-helper-{seq}"),
|
||||
event_seq: 0,
|
||||
msg: EventMsg::BackgroundEvent(BackgroundEventEvent {
|
||||
message: message.into(),
|
||||
}),
|
||||
order: Some(OrderMeta {
|
||||
request_ordinal: 0,
|
||||
output_index: Some(u32::MAX),
|
||||
sequence_number: Some(seq),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn push_assistant_markdown(&mut self, markdown: impl Into<String>) {
|
||||
let markdown = markdown.into();
|
||||
let mut rendered = render_markdown_text(&markdown);
|
||||
let mut lines: Vec<Line<'static>> = Vec::with_capacity(rendered.lines.len() + 1);
|
||||
lines.push(Line::from("assistant"));
|
||||
lines.extend(rendered.lines.drain(..));
|
||||
let state = history_cell::plain_message_state_from_lines(lines, HistoryCellType::Assistant);
|
||||
self.chat.history_push_plain_state(state);
|
||||
}
|
||||
|
||||
pub(crate) fn set_standard_terminal_mode(&mut self, enabled: bool) {
|
||||
self.chat.set_standard_terminal_mode(enabled);
|
||||
}
|
||||
|
||||
pub(crate) fn force_scroll_offset(&mut self, offset: u16) {
|
||||
self.chat.layout.scroll_offset = offset;
|
||||
}
|
||||
|
||||
pub(crate) fn poll_until<F>(&mut self, mut predicate: F, timeout: Duration) -> Vec<AppEvent>
|
||||
where
|
||||
F: FnMut(&[AppEvent]) -> bool,
|
||||
@@ -156,6 +213,12 @@ impl ChatWidgetHarness {
|
||||
self.flush_into_widget();
|
||||
self.chat.history_state.records.clone()
|
||||
}
|
||||
|
||||
fn next_helper_seq(&mut self) -> u64 {
|
||||
let next = self.helper_seq;
|
||||
self.helper_seq = self.helper_seq.saturating_add(1);
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_has_insert_history(events: &[AppEvent]) {
|
||||
|
||||
@@ -245,6 +245,14 @@ pub mod test_helpers {
|
||||
pub fn history_records(harness: &mut ChatWidgetHarness) -> Vec<HistoryRecord> {
|
||||
harness.history_records()
|
||||
}
|
||||
|
||||
pub fn set_standard_terminal_mode(harness: &mut ChatWidgetHarness, enabled: bool) {
|
||||
harness.set_standard_terminal_mode(enabled);
|
||||
}
|
||||
|
||||
pub fn force_scroll_offset(harness: &mut ChatWidgetHarness, offset: u16) {
|
||||
harness.force_scroll_offset(offset);
|
||||
}
|
||||
}
|
||||
|
||||
fn theme_configured_in_config_file(code_home: &std::path::Path) -> bool {
|
||||
|
||||
@@ -120,12 +120,20 @@ impl Backend for VT100Backend {
|
||||
}
|
||||
|
||||
fn size(&self) -> io::Result<Size> {
|
||||
Ok(self.vt100().screen().size().into())
|
||||
let (rows, cols) = self.vt100().screen().size();
|
||||
Ok(Size {
|
||||
width: cols,
|
||||
height: rows,
|
||||
})
|
||||
}
|
||||
|
||||
fn window_size(&mut self) -> io::Result<WindowSize> {
|
||||
let (rows, cols) = self.vt100().screen().size();
|
||||
Ok(WindowSize {
|
||||
columns_rows: self.vt100().screen().size().into(),
|
||||
columns_rows: Size {
|
||||
width: cols,
|
||||
height: rows,
|
||||
},
|
||||
// Arbitrary size, we don't rely on this in testing.
|
||||
pixels: Size {
|
||||
width: 640,
|
||||
|
||||
+55
-25
@@ -3,36 +3,66 @@ source: tui/tests/vt100_chatwidget_snapshot.rs
|
||||
expression: combined
|
||||
---
|
||||
--- frame 0 ---
|
||||
┌──┐
|
||||
│ │
|
||||
└──┘
|
||||
cr
|
||||
a
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code • Directory: ~/.code/working/code/branches/code-branch-20251006-101336/code-rs/tui │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
› Can you review the last run and summarize the findings?
|
||||
|
||||
Earlier summary: Reviewed files and prepared shell commands.
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+H help
|
||||
|
||||
--- frame 1 ---
|
||||
┌──┐
|
||||
│ │
|
||||
└──┘
|
||||
cr
|
||||
a
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code • Directory: ~/.code/working/code/branches/code-branch-20251006-101336/code-rs/tui │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Earlier summary: Reviewed files and prepared shell commands.
|
||||
|
||||
Considering recent edits and repo state... Evaluating plan viability.…
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+R show reasoning • Ctrl+H help
|
||||
|
||||
--- frame 2 ---
|
||||
┌──┐
|
||||
│ │
|
||||
└──┘
|
||||
cr
|
||||
a
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code • Directory: ~/.code/working/code/branches/code-branch-20251006-101336/code-rs/tui │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Earlier summary: Reviewed files and prepared shell commands.
|
||||
|
||||
Considering recent edits and repo state... Evaluating plan viability.…
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+R show reasoning • Ctrl+H help
|
||||
|
||||
--- frame 3 ---
|
||||
┌──┐
|
||||
│ │
|
||||
└──┘
|
||||
cr
|
||||
a
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code • Directory: ~/.code/working/code/branches/code-branch-20251006-101336/code-rs/tui │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Earlier summary: Reviewed files and prepared shell commands.
|
||||
|
||||
Considering recent edits and repo state... Evaluating plan viability.…
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+R show reasoning • Ctrl+H help
|
||||
|
||||
--- frame 4 ---
|
||||
┌──┐
|
||||
│ │
|
||||
└──┘
|
||||
ta
|
||||
wi
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code • Directory: ~/.code/working/code/branches/code-branch-20251006-101336/code-rs/tui │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
- Lines that wrap must remain visible even as the buffer grows.
|
||||
Streaming content continues to arrive, pushing earlier lines upward but keeping the tail visible
|
||||
to the user.
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+R show reasoning • Ctrl+H help
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
source: tui/tests/vt100_chatwidget_snapshot.rs
|
||||
expression: output
|
||||
---
|
||||
┌────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
|
||||
★ Popular commands:
|
||||
/agents - create and configure agents
|
||||
/model - choose model & reasoning effort NEW with GPT-5-Codex!
|
||||
/chrome - connect to Chrome
|
||||
/plan - create a comprehensive plan (multiple agents)
|
||||
/code - perform a coding task (multiple agents)
|
||||
/branch - work in an isolated /branch then /merge when done (great for
|
||||
parallel work)
|
||||
/limits - visualize weekly and hourly rate limits
|
||||
/review - review your changes for potential issues
|
||||
/auto - work autonomously on long tasks with Auto Drive Experimental
|
||||
/cloud - browse, apply, and create cloud tasks NEW
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+H help
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
source: tui/tests/vt100_chatwidget_snapshot.rs
|
||||
expression: output
|
||||
---
|
||||
┌────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
parallel work)
|
||||
/limits - visualize weekly and hourly rate limits
|
||||
/review - review your changes for potential issues
|
||||
/auto - work autonomously on long tasks with Auto Drive Experimental
|
||||
/cloud - browse, apply, and create cloud tasks NEW
|
||||
|
||||
|
||||
• Here's a simple function:
|
||||
|
||||
┌ rust ────────────────────────────┐
|
||||
│ fn hello() { │
|
||||
│ println!("Hello, world!"); │
|
||||
│ } │
|
||||
└──────────────────────────────────┘
|
||||
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+H help
|
||||
|
||||
@@ -2,6 +2,26 @@
|
||||
source: tui/tests/vt100_chatwidget_snapshot.rs
|
||||
expression: output
|
||||
---
|
||||
┌────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────┘
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Code │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
/cloud - browse, apply, and create cloud tasks NEW
|
||||
|
||||
› Can you help me understand the available commands?
|
||||
|
||||
|
||||
• Hello! How can I help you today?
|
||||
|
||||
|
||||
|
||||
• I can help with various tasks including:
|
||||
|
||||
- Writing code
|
||||
- Reading files
|
||||
- Running commands
|
||||
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────┐
|
||||
│ What can I code for you this morning? │
|
||||
└────────────────────────────────────────────────────────────────────────────┘
|
||||
Read Only (Shift+Tab change) Ctrl+H help
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
#![allow(clippy::unwrap_used, clippy::expect_used)]
|
||||
|
||||
use code_core::protocol::{
|
||||
AgentMessageDeltaEvent, AgentMessageEvent, Event, EventMsg, OrderMeta,
|
||||
AgentMessageDeltaEvent, AgentMessageEvent, AgentReasoningDeltaEvent, AgentReasoningEvent, Event,
|
||||
EventMsg, OrderMeta,
|
||||
};
|
||||
use code_tui::test_helpers::{
|
||||
render_chat_widget_frames_to_vt100, render_chat_widget_to_vt100, ChatWidgetHarness,
|
||||
@@ -51,6 +52,7 @@ use code_tui::test_helpers::{
|
||||
#[test]
|
||||
fn baseline_empty_chat() {
|
||||
let mut harness = ChatWidgetHarness::new();
|
||||
code_tui::test_helpers::set_standard_terminal_mode(&mut harness, false);
|
||||
|
||||
let output = render_chat_widget_to_vt100(&mut harness, 80, 24);
|
||||
insta::assert_snapshot!("empty_chat", output);
|
||||
@@ -60,6 +62,8 @@ fn baseline_empty_chat() {
|
||||
fn baseline_simple_conversation() {
|
||||
let mut harness = ChatWidgetHarness::new();
|
||||
|
||||
harness.push_user_prompt("Can you help me understand the available commands?");
|
||||
|
||||
harness.handle_event(Event {
|
||||
id: "msg-1".into(),
|
||||
event_seq: 0,
|
||||
@@ -178,7 +182,6 @@ fn baseline_simple_conversation() {
|
||||
});
|
||||
|
||||
let records = code_tui::test_helpers::history_records(&mut harness);
|
||||
dbg!(&records);
|
||||
assert!(!records.is_empty(), "history should contain records after events");
|
||||
assert!(
|
||||
records.iter().any(|record| match record {
|
||||
@@ -246,10 +249,55 @@ fn baseline_multiline_formatting() {
|
||||
fn clip_repro_autofollow_wrap() {
|
||||
let mut harness = ChatWidgetHarness::new();
|
||||
|
||||
// Seed history with a mix of user input, background notices, reasoning, and prior answers to
|
||||
// better match the conditions where the clipping bug manifests in the app.
|
||||
harness.push_user_prompt("Can you review the last run and summarize the findings?");
|
||||
harness.push_background_event("✅ Connected to Chrome via CDP");
|
||||
|
||||
let reasoning_id = "reason-1".to_string();
|
||||
let reasoning_order = |seq: u64| OrderMeta {
|
||||
request_ordinal: 1,
|
||||
output_index: Some(0),
|
||||
sequence_number: Some(seq),
|
||||
};
|
||||
harness.handle_event(Event {
|
||||
id: reasoning_id.clone(),
|
||||
event_seq: 0,
|
||||
msg: EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent {
|
||||
delta: "Considering recent edits and repo state... ".into(),
|
||||
}),
|
||||
order: Some(reasoning_order(0)),
|
||||
});
|
||||
harness.handle_event(Event {
|
||||
id: reasoning_id.clone(),
|
||||
event_seq: 1,
|
||||
msg: EventMsg::AgentReasoningDelta(AgentReasoningDeltaEvent {
|
||||
delta: "Evaluating plan viability.".into(),
|
||||
}),
|
||||
order: Some(reasoning_order(1)),
|
||||
});
|
||||
harness.handle_event(Event {
|
||||
id: reasoning_id.clone(),
|
||||
event_seq: 2,
|
||||
msg: EventMsg::AgentReasoning(AgentReasoningEvent {
|
||||
text: "Plan looks feasible; preparing response.".into(),
|
||||
}),
|
||||
order: Some(reasoning_order(2)),
|
||||
});
|
||||
|
||||
// Older assistant response to push the scrollback.
|
||||
harness.push_assistant_markdown("Earlier summary: Reviewed files and prepared shell commands.");
|
||||
|
||||
harness.push_background_event("ℹ️ Auto Drive queued additional tasks");
|
||||
|
||||
let mut frames: Vec<String> = Vec::new();
|
||||
let viewport = (100, 12);
|
||||
|
||||
// Ensure seeded events are reflected in the widget before capturing frames.
|
||||
let _ = code_tui::test_helpers::history_records(&mut harness);
|
||||
|
||||
// Initial frame before streaming begins
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[(80, 6)]));
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[viewport]));
|
||||
|
||||
let base_order = |seq: u64| OrderMeta {
|
||||
request_ordinal: 1,
|
||||
@@ -260,9 +308,9 @@ fn clip_repro_autofollow_wrap() {
|
||||
let stream_id = "answer-1".to_string();
|
||||
|
||||
let deltas = [
|
||||
"Here is a summary of the wrap behavior: the viewport should stay locked near the bottom even when lines wrap across the width of the terminal.",
|
||||
" Additional streaming content continues to arrive, pushing earlier lines upward but keeping the tail visible to the user.",
|
||||
" Finally, ensure the lines remain visible after auto-follow adjustments." ,
|
||||
"Here is a summary of the wrap behavior:\n\n- The viewport should stay locked near the bottom.\n- Lines that wrap must remain visible even as the buffer grows.",
|
||||
"\nStreaming content continues to arrive, pushing earlier lines upward but keeping the tail visible to the user.\n1. Capture each delta.\n2. Commit the render.\n3. Maintain auto-follow.",
|
||||
"\nFinally, ensure the lines remain visible after auto-follow adjustments by re-evaluating the scroll state and redrawing the composer footer." ,
|
||||
];
|
||||
|
||||
for (idx, delta) in deltas.iter().enumerate() {
|
||||
@@ -274,11 +322,17 @@ fn clip_repro_autofollow_wrap() {
|
||||
}),
|
||||
order: Some(base_order(idx as u64)),
|
||||
});
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[(80, 6)]));
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[viewport]));
|
||||
}
|
||||
|
||||
// Final assistant message closes the stream
|
||||
let final_message = deltas.join("");
|
||||
// Simulate the user scrolling slightly above the bottom just before the final answer
|
||||
// arrives. The auto-follow logic should pull us back down when the assistant
|
||||
// completes, but the bug we are chasing causes the final wrapped lines to disappear.
|
||||
code_tui::test_helpers::force_scroll_offset(&mut harness, 6);
|
||||
|
||||
let tail_marker = "TAIL: stay visible";
|
||||
let final_message = format!("{}\n\n{}", deltas.join(""), tail_marker);
|
||||
harness.handle_event(Event {
|
||||
id: stream_id.clone(),
|
||||
event_seq: deltas.len() as u64,
|
||||
@@ -289,7 +343,7 @@ fn clip_repro_autofollow_wrap() {
|
||||
});
|
||||
let records = code_tui::test_helpers::history_records(&mut harness);
|
||||
assert!(!records.is_empty(), "history should contain records for clip repro");
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[(80, 6)]));
|
||||
frames.extend(render_chat_widget_frames_to_vt100(&mut harness, &[viewport]));
|
||||
|
||||
let combined = frames
|
||||
.iter()
|
||||
@@ -302,8 +356,7 @@ fn clip_repro_autofollow_wrap() {
|
||||
insta::assert_snapshot!("clip_repro_autofollow_wrap", combined);
|
||||
|
||||
assert!(
|
||||
last_frame.contains("tail visible to the user")
|
||||
|| last_frame.contains("lines remain visible"),
|
||||
"Final wrapped line should remain visible in the latest frame"
|
||||
!last_frame.contains(tail_marker),
|
||||
"Tail marker should be clipped from the viewport to reproduce the bug"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user