#[allow(clippy::wildcard_imports)]
use super::super::*;
#[allow(clippy::wildcard_imports)]
use super::helpers::*;
#[test]
fn detail_esc_back_to_board() {
let (tmp, mut app) = make_app();
let dir = tmp.path().join("issues");
write_issue(&dir, "2026-01-01-a.md", 1, "A", "todo", None);
let config = app.store.config.clone();
app.board = BoardState::build(&app.store, config.default_config());
open_detail(&mut app);
let _ = app.handle_key(press(KeyCode::Esc)).unwrap();
assert!(matches!(app.mode, Mode::Board));
assert!(app.detail.is_none());
}
#[test]
fn detail_page_keys_scroll_one_page_and_stop_at_the_end() {
let (_tmp, mut app) = make_mouse_app();
let (x, y) = card_cell(&app, 0, 0);
app.handle_mouse(down_left(x, y)).unwrap();
app.handle_mouse(up_left(x, y)).unwrap();
app.handle_mouse(down_left(x, y)).unwrap();
assert!(matches!(app.mode, Mode::Detail));
if let Some(d) = app.detail.as_mut() {
d.viewport.set(20);
d.max_scroll.set(45);
}
let page = app.detail.as_ref().unwrap().page();
assert_eq!(page, 19);
app.handle_key(press(KeyCode::PageDown)).unwrap();
assert_eq!(app.detail.as_ref().unwrap().scroll_offset, 19);
app.handle_key(press(KeyCode::PageDown)).unwrap();
assert_eq!(app.detail.as_ref().unwrap().scroll_offset, 38);
app.handle_key(press(KeyCode::PageDown)).unwrap();
assert_eq!(app.detail.as_ref().unwrap().scroll_offset, 45);
app.handle_key(press(KeyCode::PageUp)).unwrap();
assert_eq!(app.detail.as_ref().unwrap().scroll_offset, 26);
}
#[test]
fn resolve_render_size_falls_back_for_non_tty() {
assert_eq!(
resolve_render_size(None),
(RENDER_FALLBACK_WIDTH, RENDER_FALLBACK_HEIGHT)
);
assert_eq!(resolve_render_size(Some((0, 0))), (1, 1));
}
#[test]
fn navigation_toggle_cycles_display_states() {
let (_tmp, mut app) = make_app();
assert_eq!(app.nav_display, NavDisplay::Default);
let toggle = |app: &mut App| {
app.handle_key(press_with_modifiers(
KeyCode::Char('n'),
crossterm::event::KeyModifiers::CONTROL,
))
};
toggle(&mut app).unwrap();
assert_eq!(app.nav_display, NavDisplay::Off);
toggle(&mut app).unwrap();
assert_eq!(app.nav_display, NavDisplay::Small);
toggle(&mut app).unwrap();
assert_eq!(app.nav_display, NavDisplay::Default);
}
#[test]
fn navigation_renders_in_all_three_display_states() {
let (_tmp, mut app) = make_app();
app.nav_display = NavDisplay::Default;
let cells = draw_board(&app, 80, 24);
assert!(
flush_line(&cells, 80, 0).starts_with("F1 New Issue"),
"default navigation shows the full label, got {:?}",
flush_line(&cells, 80, 0)
);
app.nav_display = NavDisplay::Small;
let cells = draw_board(&app, 80, 24);
let row = flush_line(&cells, 80, 0);
assert!(
row.starts_with("F1 ") && !row.starts_with("F1 New Issue"),
"small navigation shows only the chord, got {row:?}"
);
app.nav_display = NavDisplay::Off;
let cells = draw_board(&app, 80, 24);
assert!(
!flush_line(&cells, 80, 0).starts_with("F1"),
"off navigation draws nothing in the left column"
);
}
#[test]
fn rail_right_and_l_activate_the_focused_row() {
let (_tmp, mut app) = make_branchy_app();
let board_trunk = app
.nav_rows()
.iter()
.position(|action| *action == Action::SwitchMode(TopMode::Board))
.expect("the Board trunk is a rail row");
let _ = app.handle_key(press(KeyCode::Char('h'))).unwrap();
let _ = app.handle_key(press(KeyCode::Char('j'))).unwrap();
let _ = app.handle_key(press(KeyCode::Char('j'))).unwrap();
assert_eq!(
app.nav_rows()[board_trunk + 2],
Action::SelectBoard("release".into())
);
let _ = app.handle_key(press(KeyCode::Char('l'))).unwrap();
assert_eq!(app.active_board_name(), "release");
assert_eq!(
app.nav_focus_index(),
None,
"`l` opens the row and hands the keyboard to the content"
);
let (_tmp, mut app) = make_branchy_app();
let _ = app.handle_key(press(KeyCode::Char('h'))).unwrap();
let _ = app.handle_key(press(KeyCode::Char('j'))).unwrap();
let _ = app.handle_key(press(KeyCode::Right)).unwrap();
assert_eq!(app.active_board_name(), "default");
assert_eq!(app.nav_focus_index(), None, "`→` opens the row too");
let (_tmp, mut app) = make_branchy_app();
let _ = app.handle_key(press(KeyCode::Char('h'))).unwrap();
let _ = app.handle_key(press(KeyCode::Esc)).unwrap();
assert_eq!(app.nav_focus_index(), None);
}
#[test]
fn the_rail_cursor_is_visible_on_the_active_trunk() {
let (_tmp, mut app) = make_branchy_app();
app.nav_display = NavDisplay::Default;
let board_trunk = app
.nav_rows()
.iter()
.position(|action| *action == Action::SwitchMode(TopMode::Board))
.expect("the Board trunk is a rail row");
let _ = app.handle_key(press(KeyCode::Char('h'))).unwrap();
assert_eq!(app.nav_focus_index(), Some(board_trunk));
let buffer = draw_buffer(&app, 80, 24);
assert_eq!(
buffer[(0, board_trunk as u16)].style().bg,
Some(Color::DarkGray),
"the cursor on the active trunk is highlighted, not left as active-cyan"
);
}
#[test]
fn the_focused_rail_draws_a_focus_accent() {
let (_tmp, mut app) = make_branchy_app();
app.nav_display = NavDisplay::Default;
let edge = app.nav_width() - 1;
let unfocused = draw_buffer(&app, 80, 24);
assert_ne!(
unfocused[(edge, 0)].style().fg,
Some(Color::Yellow),
"the unfocused rail draws no focus accent"
);
let _ = app.handle_key(press(KeyCode::Char('h'))).unwrap();
let focused = draw_buffer(&app, 80, 24);
assert_eq!(
focused[(edge, 0)].style().fg,
Some(Color::Yellow),
"the focused rail's right edge is accented"
);
}