use ratatui::{Terminal, backend::TestBackend};
use crate::tui::app::dialog_keys::{self, DialogScope};
use crate::tui::render::hints;
#[test]
fn usage_dashboard_footer_is_canonical_key_colon_verb() {
let backend = TestBackend::new(80, 3);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let area = f.area();
hints::render_footer(
f,
ratatui::layout::Rect::new(area.x, area.y + 1, area.width, 1),
dialog_keys::dialog_keys(DialogScope::UsageDashboard),
);
})
.unwrap();
let buf = terminal.backend().buffer().clone();
let row: String = (0..buf.area.width)
.map(|x| buf[(x, 1)].symbol().to_string())
.collect();
assert!(
row.starts_with("Tab: switch"),
"usage footer must start with the canonical first binding, got: {row:?}"
);
assert!(
row.contains("Enter: details"),
"missing Enter binding: {row:?}"
);
assert!(
row.contains("T/W/M/A: period"),
"missing period binding: {row:?}"
);
assert!(row.contains("Esc: close"), "missing Esc binding: {row:?}");
assert!(
!row.contains(" navigate "),
"old bracket-style double-space verbs must be gone: {row:?}"
);
let cell = &buf[(0, 1)];
assert!(
cell.style()
.add_modifier
.contains(ratatui::style::Modifier::BOLD),
"footer keys must be bold, got {:?}",
cell.style()
);
}
#[test]
fn tool_approval_footer_line_advertises_approve_hotkey() {
let line = hints::footer_line(" ", dialog_keys::dialog_keys(DialogScope::ToolApproval));
let text: String = line
.spans
.iter()
.map(|s| s.content.clone())
.collect::<String>();
for needle in [
"a: approve",
"Enter: confirm",
"D/r/Esc: deny",
"V: details",
] {
assert!(
text.contains(needle),
"ToolApproval footer missing {needle:?}: {text:?}"
);
}
assert!(text.starts_with(" "), "indent must be preserved: {text:?}");
}
#[test]
fn approve_policy_menu_footer_has_no_single_key_approve() {
let line = hints::footer_line(
" ",
dialog_keys::dialog_keys(DialogScope::ApprovePolicyMenu),
);
let text: String = line
.spans
.iter()
.map(|s| s.content.clone())
.collect::<String>();
assert!(text.contains("Enter: confirm"), "missing Enter: {text:?}");
assert!(text.contains("Esc: cancel"), "missing Esc: {text:?}");
assert!(
!text.contains("a: approve"),
"menu must not advertise `a`: {text:?}"
);
}
#[test]
fn plan_overlay_states_are_table_suffixes() {
let keys = dialog_keys::dialog_keys(DialogScope::PlanOverlay);
let esc_at = keys
.iter()
.position(|k| k.label == "Esc")
.expect("Esc in table");
let d_at = keys
.iter()
.position(|k| k.label == "d")
.expect("d in table");
assert_eq!(keys[esc_at..].len(), 1);
assert!(d_at > 0);
assert!(d_at < esc_at, "d must come before Esc");
assert_eq!(keys[0].label, "a");
}
#[test]
fn log_viewer_footer_carries_filter_vocabulary() {
let line = hints::footer_line("", dialog_keys::dialog_keys(DialogScope::McLogViewer));
let text: String = line
.spans
.iter()
.map(|s| s.content.clone())
.collect::<String>();
for needle in ["/: search", "g/G", "1-5/e/w/i/d", "[ ]", "Esc"] {
assert!(
text.contains(needle),
"log viewer footer missing {needle:?}: {text:?}"
);
}
}
#[test]
fn ssh_password_footer_is_minimal() {
let keys = dialog_keys::dialog_keys(DialogScope::SshPassword);
assert_eq!(keys.len(), 3);
assert_eq!(keys[0].label, "Enter");
assert_eq!(keys[1].label, "type");
assert!(
keys[1].events.is_empty(),
"display-only pseudo-key must replay nothing"
);
assert_eq!(keys[2].label, "Esc");
}
#[test]
fn usage_dashboard_footer_follows_the_focused_card() {
use crate::usage::cards;
use crate::usage::dashboard::{CACHE_CARD, DashboardState};
let footer_row = |state: &DashboardState| {
let backend = TestBackend::new(80, 3);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let area = f.area();
let row = ratatui::layout::Rect::new(area.x, area.y + 1, area.width, 1);
cards::render_footer(f, row, state);
})
.unwrap();
let buf = terminal.backend().buffer().clone();
(0..buf.area.width)
.map(|x| buf[(x, 1)].symbol().to_string())
.collect::<String>()
};
let on_grid = DashboardState {
focused_card: 0,
..Default::default()
};
let row = footer_row(&on_grid);
assert!(
row.starts_with("Tab: switch"),
"canonical first binding: {row:?}"
);
assert!(
!row.contains("Enter"),
"Enter does nothing off the cache card, so it must not be advertised: {row:?}"
);
assert!(row.contains("T/W/M/A: period"), "{row:?}");
let on_cache = DashboardState {
focused_card: CACHE_CARD,
..Default::default()
};
let row = footer_row(&on_cache);
assert!(
row.contains("Enter: details"),
"Enter opens the prices: {row:?}"
);
let open = DashboardState {
focused_card: CACHE_CARD,
cache_detail_open: true,
..Default::default()
};
let row = footer_row(&open);
assert_eq!(
row.trim_end(),
"Esc: close",
"the open breakdown has exactly one key: {row:?}"
);
}