use super::*;
use crate::tui::render::modals::modal_text_area;
#[test]
fn sessions_modal_renders_title_help_current_marker_and_selection() {
let mut state = MissionControlState::default();
state.open_session_picker(
vec![
crate::tui::state::SessionPickerRow {
id: "abcd1234efgh".into(),
label: "abcd...efgh".into(),
is_current: true,
preview: None,
},
crate::tui::state::SessionPickerRow {
id: "wxyz9876lmno".into(),
label: "wxyz...lmno".into(),
is_current: false,
preview: None,
},
],
TEST_PICKER_VISIBLE_ROWS,
);
state.modals.session_picker.as_mut().unwrap().selected = 1;
let backend = TestBackend::new(120, 30);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Sessions"), "{rendered}");
assert!(
rendered.contains("[Tab] focus • [↑/↓] move/scroll"),
"{rendered}"
);
assert!(rendered.contains("● current abcd...efgh"), "{rendered}");
assert!(rendered.contains("> wxyz...lmno"), "{rendered}");
}
#[test]
fn sessions_modal_uses_two_columns_when_space_permits() {
let areas = sessions_modal_areas(ratatui::layout::Rect::new(0, 0, 100, 30)).unwrap();
let preview = areas.preview.unwrap();
assert!(areas.list.width < preview.width);
assert!(areas.list.height > 0);
assert!(preview.height > 0);
}
#[test]
fn sessions_modal_empty_state_and_small_terminal_do_not_panic() {
let mut state = MissionControlState::default();
state.open_session_picker(Vec::new(), TEST_PICKER_VISIBLE_ROWS);
let backend = TestBackend::new(120, 30);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("No saved sessions found"), "{rendered}");
for (width, height) in [(10, 4), (12, 5)] {
let backend = TestBackend::new(width, height);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
}
}
#[test]
fn skills_modal_renders_title_toggles_help_selection_and_empty_state() {
let mut state = MissionControlState::default();
state.open_skills_modal(
vec![
crate::tui::state::SkillToggleRow {
name: "enabled-skill".to_string(),
source: "test".to_string(),
enabled: true,
},
crate::tui::state::SkillToggleRow {
name: "disabled-skill".to_string(),
source: "test".to_string(),
enabled: false,
},
],
TEST_PICKER_VISIBLE_ROWS,
);
state.modals.skills_modal.as_mut().unwrap().selected = 1;
let backend = TestBackend::new(80, 20);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Skills"), "{rendered}");
assert!(rendered.contains("[x] enabled-skill (test)"), "{rendered}");
assert!(rendered.contains("[ ] disabled-skill (test)"), "{rendered}");
assert!(rendered.contains("[↑/↓] move"), "{rendered}");
assert!(
rendered.contains("> [ ] disabled-skill (test)"),
"{rendered}"
);
state.open_skills_modal(Vec::new(), TEST_PICKER_VISIBLE_ROWS);
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("No skills discovered"), "{rendered}");
}
#[test]
fn skills_modal_small_viewports_do_not_panic_or_overflow() {
for (width, height) in [(10, 4), (12, 5)] {
let mut state = MissionControlState::default();
state.open_skills_modal(
vec![crate::tui::state::SkillToggleRow {
name: "tiny-skill".to_string(),
source: "test".to_string(),
enabled: true,
}],
TEST_PICKER_VISIBLE_ROWS,
);
let backend = TestBackend::new(width, height);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
}
}
#[test]
fn tools_modal_renders_title_toggles_kind_help_selection_and_empty_state() {
let mut state = MissionControlState::default();
state.open_tools_modal(
vec![
crate::tui::state::ToolToggleRow {
name: "bash".to_string(),
kind: "built-in".to_string(),
enabled: true,
},
crate::tui::state::ToolToggleRow {
name: "mcp__mock__echo".to_string(),
kind: "mcp:mock".to_string(),
enabled: false,
},
],
TEST_PICKER_VISIBLE_ROWS,
);
state.modals.tools_modal.as_mut().unwrap().selected = 1;
let backend = TestBackend::new(90, 20);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Tools"), "{rendered}");
assert!(rendered.contains("[x] bash (built-in)"), "{rendered}");
assert!(
rendered.contains("[ ] mcp__mock__echo (mcp:mock)"),
"{rendered}"
);
assert!(rendered.contains("> [ ] mcp__mock__echo"), "{rendered}");
state.open_tools_modal(Vec::new(), TEST_PICKER_VISIBLE_ROWS);
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("No tools available"), "{rendered}");
}
fn connect_provider_render_state(
stage: crate::tui::state::ConnectProviderStage,
) -> MissionControlState {
let mut state = MissionControlState::default();
state.open_connect_provider(Vec::new(), None);
match stage {
crate::tui::state::ConnectProviderStage::ChooseProvider => {}
crate::tui::state::ConnectProviderStage::ConfigureCustom => {
state.start_connect_custom_configuration();
}
crate::tui::state::ConnectProviderStage::AnthropicGuidance => {
state.start_connect_anthropic_guidance();
}
crate::tui::state::ConnectProviderStage::OAuth => {
state.start_connect_oauth("openai-codex".to_string());
}
crate::tui::state::ConnectProviderStage::ReplacementConfirmation => {
state.start_connect_custom_configuration();
state.open_connect_provider_replacement_confirmation(
"local-ai".to_string(),
"Old Local AI".to_string(),
"Local AI".to_string(),
"https://provider.example/v1".to_string(),
Some("LOCAL_AI_API_KEY".to_string()),
);
}
crate::tui::state::ConnectProviderStage::Saving => {
state.set_connect_provider_saving("local-ai".to_string());
}
crate::tui::state::ConnectProviderStage::Success => {
state.set_connect_provider_saving("local-ai".to_string());
assert!(state.set_connect_provider_success(
"local-ai",
"Local AI connected".to_string(),
false,
));
}
crate::tui::state::ConnectProviderStage::Error => {
state.start_connect_custom_configuration();
assert!(state.set_connect_provider_error("custom provider setup failed"));
}
}
state
}
#[test]
fn connect_provider_modal_stages_render_with_requested_proportions() {
let cases = [
(
crate::tui::state::ConnectProviderStage::ChooseProvider,
"Choose a provider",
Some("[↑/↓] move"),
),
(
crate::tui::state::ConnectProviderStage::ConfigureCustom,
"Configure custom provider metadata",
Some("[Tab/Shift-Tab] field"),
),
(
crate::tui::state::ConnectProviderStage::AnthropicGuidance,
"Anthropic uses an API key",
Some("[Enter] providers"),
),
(
crate::tui::state::ConnectProviderStage::OAuth,
"Waiting for OAuth instructions…",
Some("[Tab] focus"),
),
(
crate::tui::state::ConnectProviderStage::ReplacementConfirmation,
"Replace",
Some("[Enter] replace"),
),
(
crate::tui::state::ConnectProviderStage::Saving,
"Saving is in progress",
None,
),
(
crate::tui::state::ConnectProviderStage::Success,
"Local AI connected",
Some("[Enter] close"),
),
(
crate::tui::state::ConnectProviderStage::Error,
"Connection failed",
Some("[Enter] retry"),
),
];
assert_eq!(
modal_rect(
ratatui::layout::Rect::new(0, 0, 60, 18),
CONNECT_PROVIDER_MODAL,
),
Some(ratatui::layout::Rect::new(12, 5, 36, 7))
);
for (stage, content, controls) in cases {
for (width, height) in [(160, 60), (200, 70)] {
let state = connect_provider_render_state(stage);
let backend = TestBackend::new(width, height);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(
rendered.contains("Connect Provider"),
"stage={stage:?} size={width}x{height}: {rendered}"
);
assert!(
rendered.contains(content),
"stage={stage:?} size={width}x{height}: {rendered}"
);
let modal = modal_rect(Rect::new(0, 0, width, height), CONNECT_PROVIDER_MODAL).unwrap();
let buffer = terminal.backend().buffer();
let row = |y| {
(modal.x..modal.right())
.map(|x| buffer[(x, y)].symbol())
.collect::<String>()
};
assert_eq!(buffer[(modal.x, modal.y)].symbol(), "┌");
assert_eq!(
buffer[(modal.x + 1, modal.y + 1)].bg,
state.theme.app().bg.unwrap()
);
assert_eq!(
row(modal.y).contains("[Esc]"),
stage != crate::tui::state::ConnectProviderStage::Saving
);
if let Some(controls) = controls {
assert!(row(modal.bottom() - 1).contains(controls));
assert!((modal.y + 1..modal.bottom() - 1).all(|y| !row(y).contains(controls)));
}
if let Some(controls) = controls {
assert!(
rendered.contains(controls),
"stage={stage:?} size={width}x{height}: {rendered}"
);
}
}
}
}
#[test]
fn connect_provider_modal_is_safe_below_supported_minimum() {
assert!(
modal_rect(
ratatui::layout::Rect::new(0, 0, 59, 18),
CONNECT_PROVIDER_MODAL
)
.is_none()
);
assert!(
modal_rect(
ratatui::layout::Rect::new(0, 0, 60, 17),
CONNECT_PROVIDER_MODAL
)
.is_none()
);
let stages = [
crate::tui::state::ConnectProviderStage::ChooseProvider,
crate::tui::state::ConnectProviderStage::ConfigureCustom,
crate::tui::state::ConnectProviderStage::AnthropicGuidance,
crate::tui::state::ConnectProviderStage::OAuth,
crate::tui::state::ConnectProviderStage::ReplacementConfirmation,
crate::tui::state::ConnectProviderStage::Saving,
crate::tui::state::ConnectProviderStage::Success,
crate::tui::state::ConnectProviderStage::Error,
];
for stage in stages {
let state = connect_provider_render_state(stage);
for (width, height) in [(59, 18), (60, 17), (20, 10), (60, 18), (80, 24)] {
let backend = TestBackend::new(width, height);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
}
}
}
#[test]
fn connect_provider_oauth_modal_wraps_long_url_and_shows_keyboard_actions() {
let mut state = MissionControlState::default();
state.open_connect_provider(Vec::new(), None);
state.start_connect_oauth("openai-codex".to_string());
let url = "https://auth.openai.com/oauth/authorize?code_challenge=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz&originator=pi";
state.set_connect_provider_instructions(
url.to_string(),
format!(
"OpenAI Codex login: open this URL in your browser:\n{url}\nWaiting for browser callback. If callback cannot reach this terminal, paste the final redirect URL or authorization code into the active login prompt."
),
);
let backend = TestBackend::new(120, 50);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("[Ctrl-Y] copy URL"));
assert!(rendered.contains("[Open Browser] [Alt-O]"));
assert!(rendered.contains("[Copy URL]"));
assert!(rendered.contains("copy the url you are redirected back to so you can paste it below"));
assert!(rendered.contains("OAuth redirect or code"));
assert!(rendered.contains("paste redirect URL or authorization code"));
let text_area = modal_text_area(
Rect::new(0, 0, 120, 50),
"Connect Provider",
CONNECT_PROVIDER_MODAL,
)
.unwrap();
let lines = rendered_text_in_area(terminal.backend().buffer(), text_area);
let url_lines = lines
.lines()
.skip_while(|line| !line.starts_with("URL: "))
.take(2)
.map(str::trim_end)
.collect::<String>();
assert_eq!(url_lines, format!("URL: {url}"));
assert_eq!(rendered.matches("https://auth.openai.com").count(), 1);
}
#[test]
fn connect_provider_custom_modal_shows_metadata_fields_and_redacts_no_key_value() {
let fake_secret = "sk-tui-custom-provider-secret-7f3c";
let mut state = MissionControlState::default();
state.open_connect_provider(Vec::new(), None);
state.start_connect_custom_configuration();
{
let connect = state.modals.connect_provider.as_mut().unwrap();
connect.label.set_value("Local Llama");
connect.provider_id = Some("local-llama".to_string());
connect.selected_field = crate::tui::state::ConnectProviderField::ApiKeyEnvVar;
connect.error = Some("API key environment variable name must match".to_string());
}
let backend = TestBackend::new(160, 60);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Configure custom provider metadata"));
assert!(rendered.contains("Provider label"));
assert!(rendered.contains("Base URL"));
assert!(rendered.contains("API key environment variable"));
assert!(rendered.contains("models.dev provider override"));
assert!(rendered.contains("Error: API key environment variable name must match"));
assert!(
rendered.contains("no API key value is requested"),
"{rendered}"
);
assert_eq!(rendered.matches("API key value").count(), 1, "{rendered}");
assert!(!rendered.contains(fake_secret), "{rendered}");
}
#[test]
fn connect_provider_custom_renderer_masks_direct_secret_env_value_and_reports_error() {
let fake_secret = "sk-direct-render-secret";
let mut state = MissionControlState::default();
state.open_connect_provider(Vec::new(), None);
state.start_connect_custom_configuration();
{
let connect = state.modals.connect_provider.as_mut().unwrap();
connect.label.set_value("Local Llama");
connect.base_url.set_value("https://provider.example/v1");
connect.selected_field = crate::tui::state::ConnectProviderField::ApiKeyEnvVar;
connect.api_key_env_var.set_value(fake_secret);
}
let backend = TestBackend::new(120, 40);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(!rendered.contains(fake_secret), "{rendered}");
assert!(rendered.contains("looks like a secret value"), "{rendered}");
assert!(rendered.contains('•'), "{rendered}");
}
#[test]
fn render_prompt_prompts_login_when_provider_is_not_ready() {
let state = MissionControlState {
provider: "openai-codex".to_string(),
model: "gpt-5.5".to_string(),
..Default::default()
};
let backend = TestBackend::new(100, 20);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let buffer = terminal.backend().buffer();
let metadata = find_text(buffer, "/login").unwrap();
let layout = layout_for(Rect::new(0, 0, 100, 20), &state);
assert_eq!(metadata.1, layout.prompt.y);
assert!(metadata.0 > layout.prompt.x);
assert!(find_text(buffer, "openai-codex/gpt-5.5").is_none());
}
#[test]
fn render_logout_confirmation_shows_provider_and_keyboard_controls() {
let mut state = MissionControlState {
provider: "openai-codex".to_string(),
model: "gpt-5.5".to_string(),
provider_ready: true,
..Default::default()
};
state.open_logout_confirmation("openai-codex".to_string(), "OpenAI Codex".to_string());
let backend = TestBackend::new(100, 24);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Confirm Logout"));
assert!(rendered.contains("Remove local auth for OpenAI Codex (openai-codex)?"));
assert!(rendered.contains("[Enter] remove local auth"));
assert!(rendered.contains("[Esc] cancel"));
assert!(rendered.contains("[Alt-M] openai-codex/gpt-5.5"));
}
#[test]
fn render_logout_confirmation_requires_complete_target_before_action_row() {
let mut state = MissionControlState::default();
let label = "OpenAI Codex";
let provider_id = "openai-codex";
state.open_logout_confirmation(provider_id.to_string(), label.to_string());
for (width, height, action_visible) in [
(12, 7, false),
(89, 7, false),
(80, 20, true),
(90, 20, true),
] {
let area = Rect::new(0, 0, width, height);
let backend = TestBackend::new(area.width, area.height);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = rendered_text_in_area(terminal.backend().buffer(), area);
if action_visible {
assert!(rendered.contains("Remove"), "{width}x{height}: {rendered}");
}
let action_area = logout_confirmation_action_row_area(area, label, provider_id);
if action_visible {
let action_area = action_area.expect("action row geometry at the visible boundary");
assert!(
rendered_text_in_area(terminal.backend().buffer(), action_area)
.contains("[Enter] remove local auth"),
"{width}x{height}: {rendered}"
);
assert!(rendered.contains("[Esc] cancel"));
} else {
assert!(action_area.is_none());
assert!(!rendered.contains("[Enter] remove local auth"));
}
}
}
#[test]
fn render_model_picker_modal_shows_current_model_and_available_models() {
let mut state = MissionControlState {
provider: "openai-codex".to_string(),
model: "gpt-current".to_string(),
..Default::default()
};
state.open_model_picker(
vec![
crate::model_catalog::ModelCatalogEntry::new_codex("gpt-current"),
crate::model_catalog::ModelCatalogEntry::new_codex("gpt-next"),
],
std::collections::BTreeSet::new(),
Some("using stale openai-codex model catalog".to_string()),
5,
);
let backend = TestBackend::new(100, 30);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("Model Picker"));
assert!(rendered.contains("current: openai-codex/gpt-current"));
assert!(rendered.contains("openai-codex/gpt-next"));
assert!(!rendered.contains("[disabled]"));
assert!(rendered.contains("using stale openai-codex model catalog"));
}
#[test]
fn picker_redraw_preserves_scroll_state_and_keeps_offset_visible() {
let mut state = MissionControlState::default();
state.open_skills_modal(
(0..12)
.map(|index| crate::tui::state::SkillToggleRow {
name: format!("skill-{index}"),
source: "test".to_string(),
enabled: true,
})
.collect(),
TEST_PICKER_VISIBLE_ROWS,
);
state.modals.skills_modal.as_mut().unwrap().selected = 3;
state.set_scroll_offset(&state.scroll_views.skills_modal, 3);
let before = *state.scroll_views.skills_modal.borrow();
let backend = TestBackend::new(100, 24);
let mut terminal = ratatui::Terminal::new(backend).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
assert_eq!(*state.scroll_views.skills_modal.borrow(), before);
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains("> [x] skill-3 (test)"), "{rendered}");
terminal.draw(|frame| draw(frame, &state)).unwrap();
assert_eq!(*state.scroll_views.skills_modal.borrow(), before);
}
#[test]
fn compact_login_keeps_active_inputs_and_oauth_controls_visible() {
for stage in [
crate::tui::state::ConnectProviderStage::ConfigureCustom,
crate::tui::state::ConnectProviderStage::OAuth,
] {
let state = connect_provider_render_state(stage);
let mut terminal = ratatui::Terminal::new(TestBackend::new(80, 24)).unwrap();
terminal.draw(|frame| draw(frame, &state)).unwrap();
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(!rendered.contains("Enlarge the terminal"));
let area = Rect::new(0, 0, 80, 24);
assert!(crate::tui::render::connect_provider_custom_field_visible(
area
));
assert!(crate::tui::render::connect_provider_oauth_fallback_visible(
area
));
if stage == crate::tui::state::ConnectProviderStage::OAuth {
assert!(rendered.contains("[Copy URL]"));
assert!(rendered.contains("OAuth redirect or code"));
} else {
assert!(rendered.contains("Provider label"));
}
}
}
#[test]
fn oauth_production_length_url_is_readable_across_pages() {
for padding in [358, 800] {
let url = format!(
"https://auth.openai.com/oauth/authorize?client_id={}&state=final-marker",
"a".repeat(padding)
);
assert!(url.len() >= 424);
for (width, height) in [(80, 24), (120, 40)] {
let mut state =
connect_provider_render_state(crate::tui::state::ConnectProviderStage::OAuth);
state.modals.connect_provider.as_mut().unwrap().url = url.clone();
let mut terminal = ratatui::Terminal::new(TestBackend::new(width, height)).unwrap();
let [open, _] = crate::tui::render::connect_provider_oauth_button_areas(Rect::new(
0, 0, width, height,
))
.unwrap();
let mut collected = String::new();
for page in 0..20 {
state
.modals
.connect_provider
.as_mut()
.unwrap()
.oauth_url_page = page;
terminal.draw(|frame| draw(frame, &state)).unwrap();
let buffer = terminal.backend().buffer();
let modal =
modal_rect(Rect::new(0, 0, width, height), CONNECT_PROVIDER_MODAL).unwrap();
let heading_y = modal.y + 2;
let heading =
rendered_text_in_area(buffer, Rect::new(open.x, heading_y, modal.width - 4, 1));
if height == 24 || padding == 800 {
assert!(heading.contains("[PgUp/PgDn] more"), "{heading}");
}
if page > 0 && heading.starts_with("URL 1/") {
break;
}
let help_height = if height == 24 { 0 } else { 1 };
let lines = rendered_text_in_area(
buffer,
Rect::new(
open.x,
heading_y + 1,
modal.width - 4,
open.y - heading_y - 1 - help_height,
),
);
collected.extend(lines.lines().map(str::trim_end));
if !heading.contains("[PgUp/PgDn] more") {
break;
}
}
assert_eq!(collected, format!("URL: {url}"));
}
}
}
#[test]
fn compact_custom_form_paints_each_focused_field_and_cursor() {
let mut state = MissionControlState::default();
state.open_connect_provider(Vec::new(), None);
state.start_connect_custom_configuration();
let mut terminal = ratatui::Terminal::new(TestBackend::new(80, 24)).unwrap();
for label in [
"Provider label",
"Base URL",
"API key environment variable",
"models.dev provider override",
] {
state.insert_modal_field_text("example");
terminal.draw(|frame| draw(frame, &state)).unwrap();
let cursor =
ratatui::backend::Backend::get_cursor_position(terminal.backend_mut()).unwrap();
let modal = modal_rect(Rect::new(0, 0, 80, 24), CONNECT_PROVIDER_MODAL).unwrap();
assert!(modal.contains(cursor));
let rendered = terminal
.backend()
.buffer()
.content()
.iter()
.map(|cell| cell.symbol())
.collect::<String>();
assert!(rendered.contains(label), "{rendered}");
assert!(rendered.contains("example"), "{rendered}");
state.connect_provider_next_field();
}
}