test(tui): add off-screen render coverage #93

Merged
Lochnair merged 4 commits from test/tui-rendering into main 2026-08-15 02:55:09 +00:00
Showing only changes of commit c12617a669 - Show all commits
+65 -5
View File
16
@@ -2960,6 +2960,25 @@ mod tests {
render_rows(state, width, height).join("\n") render_rows(state, width, height).join("\n")
} }
/// The target table's own rows, sliced to that panel's columns.
///
/// The table shares its band with the session panel, so a whole-row search
/// would happily match a label rendered on the left. Rows are cut at the
/// panel separator and stop at the band's bottom border.
fn target_table_rows(rows: &[String]) -> Vec<String> {
let header = rows
.iter()
.position(|row| {
row.contains("target") && row.contains("status") && row.contains("warn")
})
.expect("the multi-target layout renders a target table header");
rows[header + 1..]
.iter()
.take_while(|row| row.contains("\u{2502}\u{2502}"))
.filter_map(|row| row.rsplit("\u{2502}\u{2502}").next().map(str::to_owned))
.collect()
}
/// A single-target state carrying a session, one reply, and one warning. /// A single-target state carrying a session, one reply, and one warning.
/// ///
/// The TUI opens in the graph view, so dashboard tests toggle it. /// The TUI opens in the graph view, so dashboard tests toggle it.
@@ -3016,6 +3035,7 @@ mod tests {
let text = render_text(&state, MIN_WIDTH, MIN_HEIGHT - 1); let text = render_text(&state, MIN_WIDTH, MIN_HEIGHT - 1);
assert!(text.contains("terminal too small")); assert!(text.contains("terminal too small"));
assert!(!text.contains("packets"));
assert!(!text.contains("q quit")); assert!(!text.contains("q quit"));
} }
@@ -3047,7 +3067,7 @@ mod tests {
// that only fit in the large layout. // that only fit in the large layout.
assert!(!text.contains("recent events")); assert!(!text.contains("recent events"));
assert!(!text.contains("last warning:")); assert!(!text.contains("last warning:"));
assert!(!text.contains("effective RTT ")); assert!(!text.contains("timing"));
// The status line is the bottom bordered row of the layout. // The status line is the bottom bordered row of the layout.
assert!(rows[22].contains("q quit")); assert!(rows[22].contains("q quit"));
} }
@@ -3084,9 +3104,20 @@ mod tests {
// Session identity from the header. // Session identity from the header.
assert!(text.contains("0xabcd")); assert!(text.contains("0xabcd"));
assert!(text.contains("127.0.0.1:2112")); assert!(text.contains("127.0.0.1:2112"));
// The most recent sample and its effective RTT. // The most recent sample, with its effective RTT on the sample panel's
// own metric row rather than anywhere on the frame.
assert!(text.contains("last seq: 11")); assert!(text.contains("last seq: 11"));
assert!(text.contains("2.5ms")); let sample_metrics = rows
.iter()
.find(|row| row.contains("raw / adjusted / effective:"))
.expect("the large layout renders a sample metric row");
// The whole row, so a regression in any one field is caught: the
// fixture's adjusted and effective RTT share a value, and asserting
chatgpt-codex-connector[bot] commented 2026-08-14 18:55:00 +00:00 (Migrated from github.com)
Review

P2 Badge Assert that dashboard panels are absent below minimum height

If the short-terminal path regresses to rendering the resize notice together with ordinary dashboard content while still omitting the status line, this test continues to pass because it only checks for the notice and the absence of q quit. The corresponding minimum-width test explicitly rejects the packet panel; add an equivalent negative dashboard-panel assertion here so the test actually protects its stated resize-notice-only behavior.

AGENTS.md reference: AGENTS.md:L85-L89

Useful? React with 👍 / 👎.

**<sub><sub>![P2 Badge](https://img.shields.io/badge/P2-yellow?style=flat)</sub></sub> Assert that dashboard panels are absent below minimum height** If the short-terminal path regresses to rendering the resize notice together with ordinary dashboard content while still omitting the status line, this test continues to pass because it only checks for the notice and the absence of `q quit`. The corresponding minimum-width test explicitly rejects the packet panel; add an equivalent negative dashboard-panel assertion here so the test actually protects its stated resize-notice-only behavior. AGENTS.md reference: [AGENTS.md:L85-L89](https://github.com/Lochnair/irtt-rs/blob/2062535c20459a097a098d664a8f75e17c9cfcff/AGENTS.md#L85-L89) Useful? React with 👍 / 👎.
Lochnair commented 2026-08-14 19:01:32 +00:00 (Migrated from github.com)
Review

Agreed — the minimum-height case now rejects dashboard content (packets) as the minimum-width case does.

🤖 Addressed by Claude Code

Agreed — the minimum-height case now rejects dashboard content (`packets`) as the minimum-width case does. _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_
// "2.5ms" alone would still pass with the effective field dropped.
assert!(
sample_metrics.contains("raw / adjusted / effective: 1.5ms / 2.5ms / 2.5ms"),
"unexpected sample metric row: {sample_metrics}"
);
// The warning reaches the recent-events panel... // The warning reaches the recent-events panel...
assert!(rows assert!(rows
.iter() .iter()
@@ -3124,12 +3155,41 @@ mod tests {
state.toggle_view(); state.toggle_view();
state.process_target_event(&target_instance("beta"), &reply(4, 3_000_000)); state.process_target_event(&target_instance("beta"), &reply(4, 3_000_000));
let text = render_text(&state, 140, 40); let rows = render_rows(&state, 140, 40);
let text = rows.join("\n");
assert!(text.contains("targets")); assert!(text.contains("targets"));
// Every target gets its own table row. Searching the whole frame would
// not prove this: the first target is also named in the session header,
// and a target with a reply appears in recent events and the legend.
let table = target_table_rows(&rows);
for label in ["alpha", "beta", "gamma"] { for label in ["alpha", "beta", "gamma"] {
assert!(text.contains(label), "missing target {label} in:\n{text}"); let row = table
.iter()
.find(|row| {
row.trim_start_matches('\u{2502}')
.trim_start()
.starts_with(label)
})
.unwrap_or_else(|| panic!("no target table row for {label} in:\n{text}"));
assert!(
row.contains("opening"),
"target row for {label} lost its status column: {row}"
);
} }
// The target that replied shows its last sample; the others do not.
let beta = table
.iter()
.find(|row| {
row.trim_start_matches('\u{2502}')
.trim_start()
.starts_with("beta")
})
.expect("beta has a target table row");
assert!(
beta.contains("3.0ms"),
"beta row lost its last sample: {beta}"
);
assert!(text.contains("timing - first target")); assert!(text.contains("timing - first target"));
assert!(text.contains("sample - first target")); assert!(text.contains("sample - first target"));
} }