use super::*;
use crate::ai::ai_state::lifecycle::TEST_MAX_CONTEXT_LENGTH;
use crate::ai::ai_state::{Suggestion, SuggestionType};
use ratatui::Terminal;
use ratatui::backend::TestBackend;
use ratatui::layout::Rect;
fn render_and_get_popup_height(ai_state: &mut AiState, width: u16, height: u16) -> Option<u16> {
let backend = TestBackend::new(width, height);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let input_area = Rect {
x: 0,
y: height - 4,
width,
height: 3,
};
render_popup(ai_state, f, input_area);
})
.unwrap();
let buffer = terminal.backend().buffer();
let mut top_border = None;
let mut bottom_border = None;
for y in 0..buffer.area.height {
let mut row_text = String::new();
for x in 0..buffer.area.width {
let idx = (y * buffer.area.width + x) as usize;
if idx < buffer.content.len() {
row_text.push_str(buffer.content[idx].symbol());
}
}
if row_text.contains("╭") && top_border.is_none() {
top_border = Some(y);
}
if row_text.contains("╰") {
bottom_border = Some(y);
}
}
if let (Some(top), Some(bottom)) = (top_border, bottom_border) {
Some(bottom - top + 1)
} else {
None
}
}
#[test]
fn test_height_stored_after_rendering_suggestions() {
let mut state = AiState::new_with_config(
true,
true,
"Anthropic".to_string(),
"claude-3-5-sonnet-20241022".to_string(),
TEST_MAX_CONTEXT_LENGTH,
);
state.visible = true;
state.response = "AI response".to_string();
state.suggestions = vec![
Suggestion {
query: ".first".to_string(),
description: "First".to_string(),
suggestion_type: SuggestionType::Fix,
},
Suggestion {
query: ".second".to_string(),
description: "Second".to_string(),
suggestion_type: SuggestionType::Next,
},
];
assert_eq!(state.previous_popup_height, None);
let _height = render_and_get_popup_height(&mut state, 100, 30);
assert!(
state.previous_popup_height.is_some(),
"previous_popup_height should be stored after rendering suggestions"
);
}
#[test]
fn test_height_maintained_during_loading() {
let mut state = AiState::new_with_config(
true,
true,
"Anthropic".to_string(),
"claude-3-5-sonnet-20241022".to_string(),
TEST_MAX_CONTEXT_LENGTH,
);
state.visible = true;
state.response = "AI response".to_string();
state.suggestions = vec![Suggestion {
query: ".test".to_string(),
description: "Test".to_string(),
suggestion_type: SuggestionType::Fix,
}];
let height_with_suggestions = render_and_get_popup_height(&mut state, 100, 30);
assert!(height_with_suggestions.is_some());
let baseline_height = height_with_suggestions.unwrap();
state.loading = true;
state.suggestions.clear();
state.previous_response = Some(state.response.clone());
state.response.clear();
let height_during_loading = render_and_get_popup_height(&mut state, 100, 30);
assert!(height_during_loading.is_some());
assert_eq!(
height_during_loading.unwrap(),
baseline_height,
"Popup height should remain consistent during loading transition"
);
}
#[test]
fn test_height_adjusts_with_new_suggestions() {
let mut state = AiState::new_with_config(
true,
true,
"Anthropic".to_string(),
"claude-3-5-sonnet-20241022".to_string(),
TEST_MAX_CONTEXT_LENGTH,
);
state.visible = true;
state.response = "AI response".to_string();
state.suggestions = vec![
Suggestion {
query: ".a".to_string(),
description: "Short".to_string(),
suggestion_type: SuggestionType::Fix,
},
Suggestion {
query: ".b".to_string(),
description: "Short".to_string(),
suggestion_type: SuggestionType::Next,
},
];
let height_with_short = render_and_get_popup_height(&mut state, 100, 50);
let short_height = height_with_short.unwrap();
state.suggestions = vec![
Suggestion {
query: ".query1 with longer text".to_string(),
description: "Longer description that explains more".to_string(),
suggestion_type: SuggestionType::Fix,
},
Suggestion {
query: ".query2 with longer text".to_string(),
description: "Longer description that explains more".to_string(),
suggestion_type: SuggestionType::Next,
},
Suggestion {
query: ".query3 with longer text".to_string(),
description: "Longer description that explains more".to_string(),
suggestion_type: SuggestionType::Optimize,
},
Suggestion {
query: ".query4 with longer text".to_string(),
description: "Longer description that explains more".to_string(),
suggestion_type: SuggestionType::Fix,
},
Suggestion {
query: ".query5 with longer text".to_string(),
description: "Longer description that explains more".to_string(),
suggestion_type: SuggestionType::Next,
},
];
let height_with_long = render_and_get_popup_height(&mut state, 100, 50);
let long_height = height_with_long.unwrap();
assert!(
long_height > short_height,
"Popup should expand when more/longer suggestions are shown"
);
}
#[test]
fn test_no_height_stored_for_error_state() {
let mut state = AiState::new_with_config(
true,
true,
"Anthropic".to_string(),
"claude-3-5-sonnet-20241022".to_string(),
TEST_MAX_CONTEXT_LENGTH,
);
state.visible = true;
state.error = Some("API Error".to_string());
let _height = render_and_get_popup_height(&mut state, 100, 30);
assert_eq!(
state.previous_popup_height, None,
"Height should not be stored for error state"
);
}
#[test]
fn test_height_persistence_across_multiple_loads() {
let mut state = AiState::new_with_config(
true,
true,
"Anthropic".to_string(),
"claude-3-5-sonnet-20241022".to_string(),
TEST_MAX_CONTEXT_LENGTH,
);
state.visible = true;
state.response = "AI response".to_string();
state.suggestions = vec![Suggestion {
query: ".test".to_string(),
description: "Test suggestion".to_string(),
suggestion_type: SuggestionType::Fix,
}];
let height1 = render_and_get_popup_height(&mut state, 100, 30).unwrap();
state.loading = true;
state.suggestions.clear();
let height2 = render_and_get_popup_height(&mut state, 100, 30).unwrap();
state.loading = false;
state.suggestions = vec![Suggestion {
query: ".test".to_string(),
description: "Test suggestion".to_string(),
suggestion_type: SuggestionType::Fix,
}];
let height3 = render_and_get_popup_height(&mut state, 100, 30).unwrap();
assert_eq!(height1, height2, "Height should persist during loading");
assert_eq!(
height2, height3,
"Height should remain consistent after loading"
);
}