use super::*;use crate::bottom_pane::preview_line_for_title_items;use pretty_assertions::assert_eq;use ratatui::text::Line;fn line_text(line: Line<'static>) -> String { line.spans .into_iter() .map(|span| span.content.into_owned()) .collect()}fn status_preview_line_option(chat: &mut ChatWidget, items: &[StatusLineItem]) -> Option<String> { let preview_data = chat.status_surface_preview_data(); preview_data .status_line_for_items(items.iter().copied(), /*use_theme_colors*/ true) .map(line_text)}fn status_preview_line(chat: &mut ChatWidget, items: &[StatusLineItem]) -> String { status_preview_line_option(chat, items).expect("status preview line")}fn title_preview_line(chat: &mut ChatWidget, items: &[TerminalTitleItem]) -> String { let preview_data = chat.terminal_title_preview_data(); let preview = preview_line_for_title_items(items, &preview_data).expect("terminal title preview line"); line_text(preview)}fn combined_preview_snapshot( chat: &mut ChatWidget, status_items: &[StatusLineItem], title_items: &[TerminalTitleItem],) -> String { normalize_snapshot_paths(format!( "status line: {}\nterminal title: {}", status_preview_line(chat, status_items), title_preview_line(chat, title_items), ))}fn status_line_popup_snapshot(chat: &mut ChatWidget) -> String { chat.open_status_line_setup(); normalize_snapshot_paths(strip_osc8_for_snapshot(&render_bottom_popup( chat, /*width*/ 100, )))}fn terminal_title_popup_snapshot(chat: &mut ChatWidget) -> String { chat.open_terminal_title_setup(); normalize_snapshot_paths(strip_osc8_for_snapshot(&render_bottom_popup( chat, /*width*/ 100, )))}fn cache_project_root(chat: &mut ChatWidget, root_name: &str) { chat.status_line_project_root_name_cache = Some(CachedProjectRootName { cwd: chat.config.cwd.to_path_buf(), root_name: Some(root_name.to_string()), });}fn cache_rate_limit_snapshot(chat: &mut ChatWidget) { chat.on_rate_limit_snapshot(Some(RateLimitSnapshot { limit_id: None, limit_name: None, primary: Some(RateLimitWindow { used_percent: 35, window_duration_mins: Some(30 * 24 * 60), resets_at: None, }), secondary: Some(RateLimitWindow { used_percent: 50, window_duration_mins: Some(7 * 24 * 60), resets_at: None, }), credits: None, individual_limit: None, plan_type: None, rate_limit_reached_type: None, }));}#[tokio::test]async fn status_surface_preview_lines_live_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_project_root(&mut chat, "preview-live-root"); chat.status_line_branch = Some("feature/live-preview-branch".to_string()); chat.thread_name = Some("Live preview thread".to_string()); chat.transcript.last_plan_progress = Some((2, 5)); let snapshot = combined_preview_snapshot( &mut chat, &[ StatusLineItem::ProjectRoot, StatusLineItem::GitBranch, StatusLineItem::ThreadTitle, ], &[ TerminalTitleItem::Project, TerminalTitleItem::Thread, TerminalTitleItem::GitBranch, TerminalTitleItem::TaskProgress, ], ); assert_chatwidget_snapshot!("status_surface_previews_live_only", snapshot);}#[tokio::test]async fn status_line_setup_popup_live_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_project_root(&mut chat, "preview-live-root"); chat.status_line_branch = Some("feature/live-preview-branch".to_string()); chat.thread_name = Some("Live preview thread".to_string()); chat.config.tui_status_line = Some(vec![ "project-name".to_string(), "git-branch".to_string(), "thread-title".to_string(), ]); assert_chatwidget_snapshot!( "status_line_setup_popup_live_only", status_line_popup_snapshot(&mut chat) );}#[tokio::test]async fn status_surface_preview_lines_hardcoded_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let snapshot = combined_preview_snapshot( &mut chat, &[ StatusLineItem::ProjectRoot, StatusLineItem::GitBranch, StatusLineItem::ThreadTitle, StatusLineItem::Permissions, StatusLineItem::ApprovalMode, ], &[ TerminalTitleItem::Thread, TerminalTitleItem::GitBranch, TerminalTitleItem::TaskProgress, ], ); assert_chatwidget_snapshot!("status_surface_previews_hardcoded_only", snapshot);}#[tokio::test]async fn thread_title_falls_back_to_thread_id_when_unnamed() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let thread_id = ThreadId::new(); chat.thread_id = Some(thread_id); assert_eq!( status_preview_line(&mut chat, &[StatusLineItem::ThreadTitle]), thread_id.to_string() ); assert_eq!( title_preview_line(&mut chat, &[TerminalTitleItem::Thread]), thread_id.to_string() );}#[tokio::test]async fn status_line_setup_popup_hardcoded_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.config.tui_status_line = Some(vec![ "project-name".to_string(), "git-branch".to_string(), "thread-title".to_string(), ]); assert_chatwidget_snapshot!( "status_line_setup_popup_hardcoded_only", status_line_popup_snapshot(&mut chat) );}#[tokio::test]async fn status_surface_preview_lines_mixed_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.status_line_branch = Some("feature/mixed-preview".to_string()); chat.thread_name = Some("Mixed preview thread".to_string()); let snapshot = combined_preview_snapshot( &mut chat, &[ StatusLineItem::ProjectRoot, StatusLineItem::GitBranch, StatusLineItem::ThreadTitle, ], &[ TerminalTitleItem::Project, TerminalTitleItem::Thread, TerminalTitleItem::TaskProgress, ], ); assert_chatwidget_snapshot!("status_surface_previews_mixed", snapshot);}#[tokio::test]async fn status_surface_preview_lines_rate_limits_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_rate_limit_snapshot(&mut chat); let snapshot = combined_preview_snapshot( &mut chat, &[StatusLineItem::FiveHourLimit, StatusLineItem::WeeklyLimit], &[ TerminalTitleItem::FiveHourLimit, TerminalTitleItem::WeeklyLimit, ], ); assert_chatwidget_snapshot!("status_surface_previews_rate_limits", snapshot);}#[tokio::test]async fn status_surface_preview_omits_unavailable_rate_limit_items() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.on_rate_limit_snapshot(Some(RateLimitSnapshot { limit_id: None, limit_name: None, primary: Some(RateLimitWindow { used_percent: 9, window_duration_mins: Some(7 * 24 * 60), resets_at: None, }), secondary: None, credits: None, individual_limit: None, plan_type: None, rate_limit_reached_type: None, })); assert_eq!( status_preview_line_option(&mut chat, &[StatusLineItem::FiveHourLimit]), None ); assert_eq!( status_preview_line( &mut chat, &[StatusLineItem::FiveHourLimit, StatusLineItem::WeeklyLimit] ), "weekly 91% left" ); assert_eq!( title_preview_line( &mut chat, &[ TerminalTitleItem::FiveHourLimit, TerminalTitleItem::WeeklyLimit ], ), "weekly 91% left" );}#[tokio::test]async fn status_line_setup_popup_rate_limits_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_rate_limit_snapshot(&mut chat); chat.config.tui_status_line = Some(vec![ "five-hour-limit".to_string(), "weekly-limit".to_string(), ]); assert_chatwidget_snapshot!( "status_line_setup_popup_rate_limits", status_line_popup_snapshot(&mut chat) );}#[tokio::test]async fn status_line_setup_popup_mixed_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.status_line_branch = Some("feature/mixed-preview".to_string()); chat.thread_name = Some("Mixed preview thread".to_string()); chat.config.tui_status_line = Some(vec![ "project-name".to_string(), "git-branch".to_string(), "thread-title".to_string(), ]); assert_chatwidget_snapshot!( "status_line_setup_popup_mixed", status_line_popup_snapshot(&mut chat) );}#[tokio::test]async fn terminal_title_setup_popup_live_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_project_root(&mut chat, "preview-live-root"); chat.status_line_branch = Some("feature/live-preview-branch".to_string()); chat.thread_name = Some("Live preview thread".to_string()); chat.transcript.last_plan_progress = Some((2, 5)); chat.config.tui_terminal_title = Some(vec![ "project-name".to_string(), "thread-title".to_string(), "git-branch".to_string(), "task-progress".to_string(), ]); assert_chatwidget_snapshot!( "terminal_title_setup_popup_live_only", terminal_title_popup_snapshot(&mut chat) );}#[tokio::test]async fn terminal_title_setup_popup_hardcoded_only_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.config.tui_terminal_title = Some(vec![ "thread-title".to_string(), "git-branch".to_string(), "task-progress".to_string(), ]); assert_chatwidget_snapshot!( "terminal_title_setup_popup_hardcoded_only", terminal_title_popup_snapshot(&mut chat) );}#[tokio::test]async fn terminal_title_setup_popup_mixed_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; chat.thread_name = Some("Mixed preview thread".to_string()); chat.config.tui_terminal_title = Some(vec![ "project-name".to_string(), "thread-title".to_string(), "task-progress".to_string(), ]); assert_chatwidget_snapshot!( "terminal_title_setup_popup_mixed", terminal_title_popup_snapshot(&mut chat) );}#[tokio::test]async fn terminal_title_setup_popup_rate_limits_snapshot() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; cache_rate_limit_snapshot(&mut chat); chat.config.tui_terminal_title = Some(vec![ "five-hour-limit".to_string(), "weekly-limit".to_string(), ]); assert_chatwidget_snapshot!( "terminal_title_setup_popup_rate_limits", terminal_title_popup_snapshot(&mut chat) );}#[tokio::test]async fn missing_project_root_uses_different_status_and_title_preview_sources() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let status_preview = status_preview_line(&mut chat, &[StatusLineItem::ProjectRoot]); let title_preview = title_preview_line(&mut chat, &[TerminalTitleItem::Project]); assert_eq!(status_preview, "my-project"); assert_eq!(title_preview, "project");}#[tokio::test]async fn terminal_title_preview_uses_title_truncation_for_live_values() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; let long_thread = "This thread title is intentionally much longer than forty-eight characters"; let long_branch = "feature/this-branch-name-is-intentionally-longer-than-thirty-two"; chat.thread_name = Some(long_thread.to_string()); chat.status_line_branch = Some(long_branch.to_string()); let preview = title_preview_line( &mut chat, &[TerminalTitleItem::Thread, TerminalTitleItem::GitBranch], ); let truncated_thread = ChatWidget::truncate_terminal_title_part(long_thread.to_string(), /*max_chars*/ 48); let truncated_branch = ChatWidget::truncate_terminal_title_part(long_branch.to_string(), /*max_chars*/ 32); assert_eq!(preview, format!("{truncated_thread} | {truncated_branch}"));}