use super::super::*;
use super::support::*;
#[test]
fn alphanumeric_and_slash_typing_focuses_prompt_and_keeps_first_character() {
for pane in [
TuiFocusPane::Transcript,
TuiFocusPane::ActivityTree,
TuiFocusPane::ActivityDetail,
TuiFocusPane::Summary,
] {
for (event, expected) in [
(key(KeyCode::Char('a')), "ax!"),
(key(KeyCode::Char('7')), "7x!"),
(key(KeyCode::Char('/')), "/x!"),
(modified_key(KeyCode::Char('a'), KeyModifiers::SHIFT), "Ax!"),
(key(KeyCode::Char('é')), "éx!"),
(
key_with_kind(KeyCode::Char('r'), KeyEventKind::Repeat),
"rx!",
),
] {
let mut state = MissionControlState {
focus_pane: pane,
prompt_editor: crate::tui::state::PromptEditor::new("!", 0),
..Default::default()
};
handle_key(event, &mut state);
handle_key(key(KeyCode::Char('x')), &mut state);
assert!(state.is_prompt_focused());
assert_eq!(state.prompt_plain_text(), expected);
}
}
}
#[test]
fn unrelated_keys_do_not_autofocus_prompt() {
for event in [
key(KeyCode::Char(' ')),
key(KeyCode::Char('!')),
modified_key(KeyCode::Char('/'), KeyModifiers::CONTROL),
modified_key(KeyCode::Char('/'), KeyModifiers::ALT),
key_with_kind(KeyCode::Char('/'), KeyEventKind::Release),
modified_key(KeyCode::Char('1'), KeyModifiers::SHIFT),
modified_key(KeyCode::Char('x'), KeyModifiers::CONTROL),
modified_key(KeyCode::Char('x'), KeyModifiers::ALT),
key_with_kind(KeyCode::Char('a'), KeyEventKind::Release),
] {
let mut state = MissionControlState {
focus_pane: TuiFocusPane::Transcript,
..Default::default()
};
handle_key(event, &mut state);
assert_eq!(state.focus_pane, TuiFocusPane::Transcript);
assert_eq!(state.prompt_plain_text(), "");
}
}
#[test]
fn typed_chars_enter_prompt_by_default_and_enter_submits() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key(key(KeyCode::Char('h')), &mut state),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "h");
assert!(matches!(
handle_key(key(KeyCode::Char('f')), &mut state),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "hf");
match handle_key(key(KeyCode::Enter), &mut state) {
InputAction::Submit(prompt) => assert_eq!(prompt, "hf"),
_ => panic!("expected submit"),
}
}
#[test]
fn key_release_events_do_not_edit_prompt() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
prompt_editor: crate::tui::state::PromptEditor::new("ab", 2),
..Default::default()
};
assert!(matches!(
handle_key(
key_with_kind(KeyCode::Char('/'), crossterm::event::KeyEventKind::Release),
&mut state
),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "ab");
assert_eq!(state.prompt_cursor(), 2);
assert!(matches!(
handle_key(
key_with_kind(KeyCode::Backspace, crossterm::event::KeyEventKind::Release),
&mut state
),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "ab");
assert_eq!(state.prompt_cursor(), 2);
}
#[test]
fn selected_prompt_ctrl_c_returns_clipboard_action_without_editing() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("hello", 5),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert_eq!(
handle_key(modified_key(KeyCode::Left, KeyModifiers::SHIFT), &mut state,),
InputAction::None
);
assert_eq!(
handle_key(modified_key(KeyCode::Left, KeyModifiers::SHIFT), &mut state,),
InputAction::None
);
assert_eq!(state.prompt_selection_text().as_deref(), Some("lo"));
assert_eq!(
handle_key(
modified_key(KeyCode::Char('c'), KeyModifiers::CONTROL),
&mut state,
),
InputAction::CopySelectedPrompt("lo".to_string())
);
assert_eq!(state.prompt_plain_text(), "hello");
assert_eq!(state.prompt_selection_text().as_deref(), Some("lo"));
}
#[test]
fn selected_prompt_ctrl_c_copies_after_focus_moves_away_from_prompt() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("hello", 5),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert_eq!(
handle_key(modified_key(KeyCode::Left, KeyModifiers::SHIFT), &mut state,),
InputAction::None
);
assert_eq!(
handle_key(modified_key(KeyCode::Left, KeyModifiers::SHIFT), &mut state,),
InputAction::None
);
let before_text = state.prompt_plain_text();
let before_cursor = state.prompt_cursor();
let before_selection = state.prompt_selection_text();
state.focus_transcript();
assert_eq!(
handle_key(
modified_key(KeyCode::Char('c'), KeyModifiers::CONTROL),
&mut state,
),
InputAction::CopySelectedPrompt("lo".to_string())
);
assert_eq!(state.prompt_plain_text(), before_text);
assert_eq!(state.prompt_cursor(), before_cursor);
assert_eq!(state.prompt_selection_text(), before_selection);
}
#[test]
fn question_mark_is_prompt_text_not_help() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key(key(KeyCode::Char('?')), &mut state),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "?");
assert!(!state.show_help);
}
#[test]
fn shifted_printable_prompt_characters_insert_shifted_text() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
for (input, expected) in [
('a', 'A'),
('z', 'Z'),
('1', '!'),
('/', '?'),
('-', '_'),
('=', '+'),
('A', 'A'),
('!', '!'),
('é', 'é'),
('界', '界'),
] {
assert_eq!(
handle_key(
modified_key(KeyCode::Char(input), KeyModifiers::SHIFT),
&mut state
),
InputAction::None
);
let prompt = state.prompt_plain_text();
assert!(prompt.ends_with(expected));
state.backspace_prompt_char(1, 80);
}
assert!(state.prompt_plain_text().is_empty());
}
#[test]
fn shift_alt_and_shift_control_printable_keys_do_not_insert_prompt_text() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert_eq!(
handle_key(
modified_key(KeyCode::Char('a'), KeyModifiers::SHIFT | KeyModifiers::ALT),
&mut state,
),
InputAction::None
);
assert_eq!(
handle_key(
modified_key(
KeyCode::Char('a'),
KeyModifiers::SHIFT | KeyModifiers::CONTROL
),
&mut state,
),
InputAction::None
);
assert!(state.prompt_plain_text().is_empty());
}
#[test]
fn unmodified_command_letters_are_prompt_text_not_actions() {
let mut state = state_with_activity();
state.focus_prompt();
for ch in ['h', 'l', 'e', 'c', 'f', 'r', '?'] {
assert!(matches!(
handle_key(key(KeyCode::Char(ch)), &mut state),
InputAction::None
));
}
assert_eq!(state.prompt_plain_text(), "hlecfr?");
assert_eq!(state.selected, 0);
assert!(!state.show_help);
}
#[test]
fn f1_toggles_help_and_quit_submit_still_exits() {
let mut state = MissionControlState {
..Default::default()
};
assert!(matches!(
handle_key(key(KeyCode::F(1)), &mut state),
InputAction::None
));
assert!(state.show_help);
assert_eq!(state.help_offset(), 0);
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("/quit", 0),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key(key(KeyCode::Enter), &mut state),
InputAction::Exit
));
}
#[test]
fn alt_p_focuses_prompt_clears_help_and_then_accepts_typing() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::ActivityTree,
show_help: true,
prompt_editor: crate::tui::state::PromptEditor::new_with_visibility("", 0, false),
..Default::default()
};
let alt_p = modified_key(KeyCode::Char('p'), KeyModifiers::ALT);
assert_eq!(handle_key(alt_p, &mut state), InputAction::None);
assert!(state.is_prompt_focused());
assert!(!state.show_help);
assert!(state.prompt_cursor_visible());
assert!(state.last_standalone_ctrl.is_none());
assert_eq!(
handle_key(key(KeyCode::Char('x')), &mut state),
InputAction::None
);
assert_eq!(state.prompt_plain_text(), "x");
}
#[test]
fn ctrl_p_does_not_focus_prompt() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::ActivityTree,
show_help: true,
prompt_editor: crate::tui::state::PromptEditor::new_with_visibility("", 0, false),
..Default::default()
};
let ctrl_p = modified_key(KeyCode::Char('p'), KeyModifiers::CONTROL);
assert_eq!(handle_key(ctrl_p, &mut state), InputAction::None);
assert!(state.is_activity_tree_focused());
assert!(state.show_help);
assert!(!state.prompt_cursor_visible());
assert!(state.last_standalone_ctrl.is_none());
}
#[test]
fn ctrl_c_clears_input_then_exits_when_empty() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("abc", 0),
..Default::default()
};
let ctrl_c = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
assert!(matches!(handle_key(ctrl_c, &mut state), InputAction::None));
assert!(state.prompt_plain_text().is_empty());
assert!(matches!(handle_key(ctrl_c, &mut state), InputAction::Exit));
}
#[test]
fn prompt_typing_backspace_delete_edit_at_cursor() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("ac界", 1),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
let viewport = Some(PromptViewport {
visible_rows: 2,
wrap_width: 10,
});
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Char('é')), &mut state, viewport, &[]),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "aéc界");
assert_eq!(state.prompt_cursor(), "aé".len());
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Delete), &mut state, viewport, &[]),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "aé界");
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Backspace), &mut state, viewport, &[]),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "a界");
}
#[test]
fn prompt_plain_arrows_move_cursor_without_scrolling_other_panes() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new(
"alpha\nbeta\ngamma",
"alpha\nbeta".len(),
),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
state.set_scroll_offset(&state.scroll_views.transcript, 2);
state.set_scroll_offset(&state.scroll_views.detail, 3);
state.set_scroll_offset(&state.scroll_views.help, 4);
state.open_system_prompt_modal("modal".to_string());
state.close_system_prompt_modal();
let viewport = Some(PromptViewport {
visible_rows: 2,
wrap_width: 80,
});
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Up), &mut state, viewport, &[]),
InputAction::None
));
assert!(state.prompt_cursor() < "alpha\nbeta".len());
assert_eq!(state.transcript_scroll_offset(), 2);
assert_eq!(state.detail_offset(), 3);
assert_eq!(state.help_offset(), 4);
assert!(state.modals.system_prompt_modal.is_none());
}
#[test]
fn prompt_alt_up_down_scroll_transcript_not_prompt_or_detail_when_prompt_focused() {
let mut state = state_with_activity();
state.focus_prompt();
state.set_prompt_text("x".repeat(30).as_str(), 0);
for line in 0..10 {
state.transcript.push_back(format!("user: line {line}"));
}
let prompt_viewport = Some(PromptViewport {
visible_rows: 2,
wrap_width: 5,
});
let transcript_viewport = Some(TranscriptViewport {
visible_rows: 4,
wrap_width: 80,
});
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Up, KeyModifiers::ALT),
&mut state,
prompt_viewport,
transcript_viewport,
&[]
),
InputAction::None
));
assert_eq!(state.transcript_scroll_offset(), 1);
assert_eq!(state.detail_offset(), 0);
assert!(state.is_prompt_focused());
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Down, KeyModifiers::ALT),
&mut state,
prompt_viewport,
transcript_viewport,
&[]
),
InputAction::None
));
assert_eq!(state.transcript_scroll_offset(), 0);
assert_eq!(state.detail_offset(), 0);
}
#[test]
fn prompt_alt_up_down_transcript_scroll_clamps_at_boundaries() {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
for line in 0..6 {
state.transcript.push_back(format!("user: line {line}"));
}
let transcript_viewport = Some(TranscriptViewport {
visible_rows: 4,
wrap_width: 80,
});
let overflow = state.transcript_scroll_overflow(4, 80);
assert!(overflow > 0);
while state.transcript_scroll_offset() < overflow {
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Up, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&[]
),
InputAction::None
));
}
assert_eq!(state.transcript_scroll_offset(), overflow);
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Up, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&[]
),
InputAction::None
));
assert_eq!(state.transcript_scroll_offset(), overflow);
while state.transcript_scroll_offset() > 0 {
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Down, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&[]
),
InputAction::None
));
}
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Down, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&[]
),
InputAction::None
));
assert_eq!(state.transcript_scroll_offset(), 0);
}
#[test]
fn prompt_alt_up_down_preserve_prompt_text_cursor_focus_and_autocomplete() {
let candidates = autocomplete_candidates();
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("/q", 2),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
for line in 0..10 {
state.transcript.push_back(format!("user: line {line}"));
}
state.recompute_autocomplete(&candidates);
state.autocomplete.as_mut().unwrap().selected = 1;
state.autocomplete_dismissed_token = Some("/ignored".to_string());
let autocomplete_before = state.autocomplete.clone();
let dismissed_before = state.autocomplete_dismissed_token.clone();
let transcript_viewport = Some(TranscriptViewport {
visible_rows: 4,
wrap_width: 80,
});
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Up, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&candidates
),
InputAction::None
));
assert!(matches!(
handle_key_with_viewports(
modified_key(KeyCode::Down, KeyModifiers::ALT),
&mut state,
None,
transcript_viewport,
&candidates
),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "/q");
assert_eq!(state.prompt_cursor(), 2);
assert!(state.is_prompt_focused());
assert_eq!(state.autocomplete, autocomplete_before);
assert_eq!(state.autocomplete_dismissed_token, dismissed_before);
}
#[test]
fn shift_enter_inserts_newline_at_cursor_and_plain_enter_submits_multiline_prompt() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("helloworld", "hello".len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key_with_viewport(
modified_key(KeyCode::Enter, KeyModifiers::SHIFT),
&mut state,
None,
&[]
),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "hello\nworld");
assert_eq!(state.prompt_cursor(), "hello\n".len());
match handle_key_with_viewport(key(KeyCode::Enter), &mut state, None, &[]) {
InputAction::Submit(prompt) => assert_eq!(prompt, "hello\nworld"),
_ => panic!("expected submit"),
}
}
#[test]
fn shifted_enter_with_extra_modifier_bits_inserts_newline_without_submitting() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("helloworld", "hello".len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key_with_viewport(
modified_key(KeyCode::Enter, KeyModifiers::SHIFT | KeyModifiers::CONTROL),
&mut state,
None,
&[]
),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "hello\nworld");
assert_eq!(state.prompt_cursor(), "hello\n".len());
}
#[test]
fn prompt_paste_multiline_inserts_newlines_without_submitting() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("alphaomega", "alpha".len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_paste_with_viewport("\nbeta\ngamma", &mut state, None, &[]),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), "alpha\nbeta\ngammaomega");
assert_eq!(state.prompt_cursor(), "alpha\nbeta\ngamma".len());
}
#[test]
fn oversized_prompt_paste_sets_exact_status_without_editing_prompt() {
let text = "x".repeat(crate::tui::state::MAX_PROMPT_BYTES);
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new(&text, text.len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
let before_cursor = state.prompt_cursor();
assert_eq!(
handle_paste_with_viewport("y", &mut state, None, &[]),
InputAction::None
);
assert_eq!(state.prompt_plain_text(), text);
assert_eq!(state.prompt_cursor(), before_cursor);
assert_eq!(state.status, crate::tui::state::PROMPT_TOO_LARGE_STATUS);
assert_eq!(
handle_key(key(KeyCode::Char('z')), &mut state),
InputAction::None
);
assert_eq!(state.prompt_plain_text(), text);
assert_eq!(state.status, crate::tui::state::PROMPT_TOO_LARGE_STATUS);
}
#[test]
fn explicit_paste_and_key_burst_use_the_same_prompt_normalization() {
let source = "a\0\x07\x08\x0b\x0c\x1b\x7f\t\n\ré界";
let expected = "a\t\n\né界";
let mut pasted = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
handle_paste_with_viewport(source, &mut pasted, None, &[]);
let keys = vec![
key(KeyCode::Char('a')),
key(KeyCode::Char('\0')),
key(KeyCode::Char('\x07')),
key(KeyCode::Char('\x08')),
key(KeyCode::Char('\x0b')),
key(KeyCode::Char('\x0c')),
key(KeyCode::Char('\x1b')),
key(KeyCode::Char('\x7f')),
key(KeyCode::Tab),
key(KeyCode::Enter),
key(KeyCode::Char('\r')),
key(KeyCode::Char('é')),
key(KeyCode::Char('界')),
];
let burst = text_for_key_burst(&keys).expect("all keys are text");
let mut burst_state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
handle_paste_with_viewport(&burst, &mut burst_state, None, &[]);
assert_eq!(pasted.prompt_plain_text(), expected);
assert_eq!(burst_state.prompt_plain_text(), expected);
}
#[test]
fn prompt_paste_is_ignored_while_rewind_modal_is_active() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("draft", "draft".len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
state.open_rewind_modal("rewind".to_string(), None, false);
assert_eq!(
handle_paste_with_viewport("hidden", &mut state, None, &[]),
InputAction::None
);
assert_eq!(state.prompt_plain_text(), "draft");
assert_eq!(state.prompt_cursor(), "draft".len());
}
#[test]
fn submit_after_middle_insertion_returns_canonical_text_and_resets_editor_state() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("ac", 1),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Char('b')), &mut state, None, &[]),
InputAction::None
));
match handle_key_with_viewport(key(KeyCode::Enter), &mut state, None, &[]) {
InputAction::Submit(prompt) => assert_eq!(prompt, "abc"),
_ => panic!("expected submit"),
}
assert!(state.prompt_plain_text().is_empty());
assert_eq!(state.prompt_cursor(), 0);
}
#[test]
fn quit_submits_as_exit_even_when_cursor_is_not_at_end() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("/quit", 1),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key_with_viewport(key(KeyCode::Enter), &mut state, None, &[]),
InputAction::Exit
));
}
#[test]
fn quit_with_trailing_args_submits_as_exit() {
let mut state = MissionControlState {
prompt_editor: crate::tui::state::PromptEditor::new("/quit extra", "/quit extra".len()),
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key_with_viewports(key(KeyCode::Enter), &mut state, None, None, &[]),
InputAction::Exit
));
}
#[test]
fn native_paste_expands_for_submit_and_copy_actions() {
let payload = "one\ntwo\nthree";
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_paste_with_viewport(payload, &mut state, None, &[]),
InputAction::None
));
assert_eq!(state.prompt_plain_text(), payload);
state.select_all_prompt();
assert_eq!(
handle_key(
modified_key(KeyCode::Char('c'), KeyModifiers::CONTROL),
&mut state,
),
InputAction::CopySelectedPrompt(payload.to_string())
);
match handle_key(key(KeyCode::Enter), &mut state) {
InputAction::Submit(prompt) => assert_eq!(prompt, payload),
other => panic!("expected submit, got {other:?}"),
}
assert!(state.prompt_plain_text().is_empty());
}
#[test]
fn paste_after_typed_command_prefix_stays_visible_text() {
let payload = "one\ntwo\nthree";
for prefix in ['/', '!'] {
let mut state = MissionControlState {
focus_pane: crate::tui::state::TuiFocusPane::Prompt,
..Default::default()
};
assert!(matches!(
handle_key(key(KeyCode::Char(prefix)), &mut state),
InputAction::None
));
assert!(matches!(
handle_paste_with_viewport(payload, &mut state, None, &[]),
InputAction::None
));
assert_eq!(
state.prompt_editor.visible_text(),
format!("{prefix}{payload}")
);
}
}