Some checks failed
ci/woodpecker/pr/check Pipeline failed
CI / Check (pull_request) Has been cancelled
CI / Format (pull_request) Has been cancelled
CI / Clippy (pull_request) Has been cancelled
CI / Build (macos-latest) (pull_request) Has been cancelled
CI / Build (ubuntu-latest) (pull_request) Has been cancelled
CI / Build (windows-latest) (pull_request) Has been cancelled
610 lines
20 KiB
Rust
610 lines
20 KiB
Rust
//! Tests for Vim Normal/Insert mode feature
|
||
//!
|
||
//! Covers:
|
||
//! - Mode transitions (i→Insert, Esc→Normal, auto-Insert on Reply/Edit)
|
||
//! - Command blocking in Insert mode (vim keys type text)
|
||
//! - Insert mode input handling (NewLine, DeleteWord, MoveToStart, MoveToEnd)
|
||
//! - Close chat resets mode
|
||
//! - Edge cases (Esc cancels Reply/Editing from Insert)
|
||
|
||
mod helpers;
|
||
|
||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||
use helpers::app_builder::TestAppBuilder;
|
||
use helpers::test_data::{create_test_chat, TestMessageBuilder};
|
||
use tele_tui::app::methods::compose::ComposeMethods;
|
||
use tele_tui::app::methods::messages::MessageMethods;
|
||
use tele_tui::app::InputMode;
|
||
use tele_tui::input::handle_main_input;
|
||
|
||
fn key(code: KeyCode) -> KeyEvent {
|
||
KeyEvent::new(code, KeyModifiers::empty())
|
||
}
|
||
|
||
fn ctrl_key(c: char) -> KeyEvent {
|
||
KeyEvent::new(KeyCode::Char(c), KeyModifiers::CONTROL)
|
||
}
|
||
|
||
// ============================================================
|
||
// Mode Transitions
|
||
// ============================================================
|
||
|
||
/// `i` в Normal mode → переход в Insert mode
|
||
#[tokio::test]
|
||
async fn test_i_enters_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('i'))).await;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
// Выходим из MessageSelection
|
||
assert!(!app.is_selecting_message());
|
||
}
|
||
|
||
/// `ш` (русская i) в Normal mode → переход в Insert mode
|
||
#[tokio::test]
|
||
async fn test_russian_i_enters_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('ш'))).await;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
}
|
||
|
||
/// Esc в Insert mode → Normal mode + MessageSelection
|
||
#[tokio::test]
|
||
async fn test_esc_exits_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Esc)).await;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.is_selecting_message());
|
||
}
|
||
|
||
/// Esc в Normal mode → закрывает чат
|
||
#[tokio::test]
|
||
async fn test_esc_in_normal_closes_chat() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.build();
|
||
|
||
assert!(app.selected_chat_id.is_some());
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Esc)).await;
|
||
|
||
assert!(app.selected_chat_id.is_none());
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
}
|
||
|
||
/// close_chat() сбрасывает input_mode
|
||
#[tokio::test]
|
||
async fn test_close_chat_resets_input_mode() {
|
||
use tele_tui::app::methods::navigation::NavigationMethods;
|
||
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
|
||
app.close_chat();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
}
|
||
|
||
/// Auto-Insert при Reply (`r` в MessageSelection)
|
||
#[tokio::test]
|
||
async fn test_reply_auto_enters_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello from friend", 1)
|
||
.sender("Friend")
|
||
.build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
|
||
// `r` → reply
|
||
handle_main_input(&mut app, key(KeyCode::Char('r'))).await;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
assert!(app.is_replying());
|
||
}
|
||
|
||
/// Auto-Insert при Edit (Enter в MessageSelection)
|
||
#[tokio::test]
|
||
async fn test_edit_auto_enters_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("My message", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
|
||
// Enter → edit selected message
|
||
handle_main_input(&mut app, key(KeyCode::Enter)).await;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
assert!(app.is_editing());
|
||
}
|
||
|
||
/// При открытии чата → Normal mode (selected_chat задан builder'ом, как после open)
|
||
#[test]
|
||
fn test_open_chat_defaults_to_normal_mode() {
|
||
// Проверяем что при настройке чата (аналог состояния после open_chat_and_load_data)
|
||
// режим = Normal, и start_message_selection() корректно входит в MessageSelection
|
||
let messages = vec![
|
||
TestMessageBuilder::new("Msg 1", 1).build(),
|
||
TestMessageBuilder::new("Msg 2", 2).build(),
|
||
];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.selected_chat_id.is_some());
|
||
|
||
// open_chat_and_load_data вызывает start_message_selection()
|
||
app.start_message_selection();
|
||
assert!(app.is_selecting_message());
|
||
}
|
||
|
||
/// После отправки сообщения — остаёмся в Insert
|
||
#[tokio::test]
|
||
async fn test_send_message_stays_in_insert() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.message_input("Hello!")
|
||
.build();
|
||
app.cursor_position = 6;
|
||
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
|
||
// Enter → отправить
|
||
handle_main_input(&mut app, key(KeyCode::Enter)).await;
|
||
|
||
// Остаёмся в Insert
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
// Инпут очищен
|
||
assert_eq!(app.message_input, "");
|
||
}
|
||
|
||
// ============================================================
|
||
// Command Blocking in Insert Mode
|
||
// ============================================================
|
||
|
||
/// `j` в Insert mode → набирает символ, НЕ навигация
|
||
#[tokio::test]
|
||
async fn test_j_types_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('j'))).await;
|
||
|
||
assert_eq!(app.message_input, "j");
|
||
}
|
||
|
||
/// `k` в Insert mode → набирает символ
|
||
#[tokio::test]
|
||
async fn test_k_types_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('k'))).await;
|
||
|
||
assert_eq!(app.message_input, "k");
|
||
}
|
||
|
||
/// `d` в Insert mode → набирает "d", НЕ удаляет сообщение
|
||
#[tokio::test]
|
||
async fn test_d_types_in_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('d'))).await;
|
||
|
||
assert_eq!(app.message_input, "d");
|
||
// НЕ вошли в delete confirmation
|
||
assert!(!app.chat_state.is_delete_confirmation());
|
||
}
|
||
|
||
/// `r` в Insert mode → набирает "r", НЕ reply
|
||
#[tokio::test]
|
||
async fn test_r_types_in_insert_mode() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('r'))).await;
|
||
|
||
assert_eq!(app.message_input, "r");
|
||
assert!(!app.is_replying());
|
||
}
|
||
|
||
/// `f` в Insert mode → набирает "f", НЕ forward
|
||
#[tokio::test]
|
||
async fn test_f_types_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('f'))).await;
|
||
|
||
assert_eq!(app.message_input, "f");
|
||
assert!(!app.is_forwarding());
|
||
}
|
||
|
||
/// `q` в Insert mode → набирает "q", НЕ quit
|
||
#[tokio::test]
|
||
async fn test_q_types_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('q'))).await;
|
||
|
||
assert_eq!(app.message_input, "q");
|
||
}
|
||
|
||
/// Ctrl+S в Insert mode → НЕ открывает поиск
|
||
#[tokio::test]
|
||
async fn test_ctrl_s_blocked_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, ctrl_key('s')).await;
|
||
|
||
assert!(!app.is_searching);
|
||
}
|
||
|
||
/// Ctrl+F в Insert mode → НЕ открывает поиск по сообщениям
|
||
#[tokio::test]
|
||
async fn test_ctrl_f_blocked_in_insert_mode() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.build();
|
||
|
||
handle_main_input(&mut app, ctrl_key('f')).await;
|
||
|
||
assert!(!app.chat_state.is_search_in_chat());
|
||
}
|
||
|
||
// ============================================================
|
||
// Normal Mode — commands work
|
||
// ============================================================
|
||
|
||
/// `j` в Normal mode → навигация вниз (MoveDown) в MessageSelection
|
||
#[tokio::test]
|
||
async fn test_j_navigates_in_normal_mode() {
|
||
let messages = vec![
|
||
TestMessageBuilder::new("Msg 1", 1).build(),
|
||
TestMessageBuilder::new("Msg 2", 2).build(),
|
||
TestMessageBuilder::new("Msg 3", 3).build(),
|
||
];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(1)
|
||
.build();
|
||
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert_eq!(app.chat_state.selected_message_index(), Some(1));
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('j'))).await;
|
||
|
||
// j = MoveDown = select_next_message
|
||
assert_eq!(app.chat_state.selected_message_index(), Some(2));
|
||
// Текст НЕ добавился
|
||
assert_eq!(app.message_input, "");
|
||
}
|
||
|
||
/// `k` в Normal mode → навигация вверх в MessageSelection
|
||
#[tokio::test]
|
||
async fn test_k_navigates_in_normal_mode() {
|
||
let messages = vec![
|
||
TestMessageBuilder::new("Msg 1", 1).build(),
|
||
TestMessageBuilder::new("Msg 2", 2).build(),
|
||
TestMessageBuilder::new("Msg 3", 3).build(),
|
||
];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(2)
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('k'))).await;
|
||
|
||
assert_eq!(app.chat_state.selected_message_index(), Some(1));
|
||
assert_eq!(app.message_input, "");
|
||
}
|
||
|
||
/// `d` в Normal mode → показывает подтверждение удаления
|
||
#[tokio::test]
|
||
async fn test_d_deletes_in_normal_mode() {
|
||
let messages = vec![TestMessageBuilder::new("My message", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Char('d'))).await;
|
||
|
||
assert!(app.chat_state.is_delete_confirmation());
|
||
}
|
||
|
||
// ============================================================
|
||
// Insert Mode Input Handling
|
||
// ============================================================
|
||
|
||
/// Ctrl+W → удаляет слово в Insert mode
|
||
#[tokio::test]
|
||
async fn test_ctrl_w_deletes_word_in_insert() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.message_input("Hello World")
|
||
.build();
|
||
app.cursor_position = 11; // конец "Hello World"
|
||
|
||
handle_main_input(&mut app, ctrl_key('w')).await;
|
||
|
||
assert_eq!(app.message_input, "Hello ");
|
||
assert_eq!(app.cursor_position, 6);
|
||
}
|
||
|
||
/// Ctrl+W → удаляет слово + пробелы перед ним
|
||
#[tokio::test]
|
||
async fn test_ctrl_w_deletes_word_with_spaces() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.message_input("one two three")
|
||
.build();
|
||
app.cursor_position = 14; // конец
|
||
|
||
handle_main_input(&mut app, ctrl_key('w')).await;
|
||
|
||
// "one two " → удалили "three", осталось "one two "
|
||
assert_eq!(app.message_input, "one two ");
|
||
assert_eq!(app.cursor_position, 9);
|
||
}
|
||
|
||
/// Ctrl+A → курсор в начало в Insert mode
|
||
#[tokio::test]
|
||
async fn test_ctrl_a_moves_to_start_in_insert() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.message_input("Hello World")
|
||
.build();
|
||
app.cursor_position = 11;
|
||
|
||
handle_main_input(&mut app, ctrl_key('a')).await;
|
||
|
||
assert_eq!(app.cursor_position, 0);
|
||
}
|
||
|
||
/// Ctrl+E → курсор в конец в Insert mode
|
||
#[tokio::test]
|
||
async fn test_ctrl_e_moves_to_end_in_insert() {
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.insert_mode()
|
||
.message_input("Hello World")
|
||
.build();
|
||
app.cursor_position = 0;
|
||
|
||
handle_main_input(&mut app, ctrl_key('e')).await;
|
||
|
||
assert_eq!(app.cursor_position, 11);
|
||
}
|
||
|
||
// ============================================================
|
||
// Edge Cases — Esc from Insert cancels Reply/Editing
|
||
// ============================================================
|
||
|
||
/// Esc из Insert при активном Reply → отменяет reply + Normal + MessageSelection
|
||
#[tokio::test]
|
||
async fn test_esc_from_insert_cancels_reply() {
|
||
let messages = vec![TestMessageBuilder::new("Hello", 1).sender("Friend").build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.insert_mode()
|
||
.replying_to(1)
|
||
.build();
|
||
|
||
assert!(app.is_replying());
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Esc)).await;
|
||
|
||
assert!(!app.is_replying());
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.is_selecting_message());
|
||
}
|
||
|
||
/// Esc из Insert при активном Editing → отменяет editing + Normal + MessageSelection
|
||
#[tokio::test]
|
||
async fn test_esc_from_insert_cancels_editing() {
|
||
let messages = vec![TestMessageBuilder::new("My message", 1).outgoing().build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.insert_mode()
|
||
.editing_message(1, 0)
|
||
.message_input("Edited text")
|
||
.build();
|
||
|
||
assert!(app.is_editing());
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
|
||
handle_main_input(&mut app, key(KeyCode::Esc)).await;
|
||
|
||
assert!(!app.is_editing());
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.is_selecting_message());
|
||
// Инпут очищен (cancel_editing)
|
||
assert_eq!(app.message_input, "");
|
||
}
|
||
|
||
/// Normal mode auto-enters MessageSelection при первом нажатии
|
||
/// Используем `k` (MoveUp), т.к. `j` (MoveDown) на последнем сообщении выходит из selection
|
||
#[tokio::test]
|
||
async fn test_normal_mode_auto_enters_selection_on_any_key() {
|
||
let messages = vec![
|
||
TestMessageBuilder::new("Msg 1", 1).build(),
|
||
TestMessageBuilder::new("Msg 2", 2).build(),
|
||
];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.build();
|
||
|
||
// ChatState::Normal, InputMode::Normal — не в MessageSelection
|
||
assert!(!app.is_selecting_message());
|
||
|
||
// `k` (MoveUp) в Normal mode → auto-enter MessageSelection + move up
|
||
handle_main_input(&mut app, key(KeyCode::Char('k'))).await;
|
||
|
||
assert!(app.is_selecting_message());
|
||
// Начали с последнего (index 1), MoveUp → index 0
|
||
assert_eq!(app.chat_state.selected_message_index(), Some(0));
|
||
}
|
||
|
||
/// Полный цикл: Normal → i → набор текста → Esc → Normal
|
||
#[tokio::test]
|
||
async fn test_full_mode_cycle() {
|
||
let messages = vec![TestMessageBuilder::new("Msg", 1).build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
// 1. Normal mode
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.is_selecting_message());
|
||
|
||
// 2. i → Insert
|
||
handle_main_input(&mut app, key(KeyCode::Char('i'))).await;
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
assert!(!app.is_selecting_message());
|
||
|
||
// 3. Набираем текст
|
||
handle_main_input(&mut app, key(KeyCode::Char('H'))).await;
|
||
handle_main_input(&mut app, key(KeyCode::Char('i'))).await;
|
||
assert_eq!(app.message_input, "Hi");
|
||
|
||
// 4. Esc → Normal + MessageSelection
|
||
handle_main_input(&mut app, key(KeyCode::Esc)).await;
|
||
assert_eq!(app.input_mode, InputMode::Normal);
|
||
assert!(app.is_selecting_message());
|
||
// Текст сохранён (черновик)
|
||
assert_eq!(app.message_input, "Hi");
|
||
}
|
||
|
||
/// Полный цикл: Normal → r (reply) → набор → Enter (отправка) → остаёмся в Insert
|
||
#[tokio::test]
|
||
async fn test_reply_send_stays_insert() {
|
||
let messages = vec![TestMessageBuilder::new("Question?", 1)
|
||
.sender("Friend")
|
||
.build()];
|
||
let mut app = TestAppBuilder::new()
|
||
.with_chats(vec![create_test_chat("Chat", 101)])
|
||
.selected_chat(101)
|
||
.with_messages(101, messages)
|
||
.selecting_message(0)
|
||
.build();
|
||
|
||
// 1. r → auto-Insert + Reply
|
||
handle_main_input(&mut app, key(KeyCode::Char('r'))).await;
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
assert!(app.is_replying());
|
||
|
||
// 2. Набираем ответ
|
||
for c in "Yes!".chars() {
|
||
handle_main_input(&mut app, key(KeyCode::Char(c))).await;
|
||
}
|
||
assert_eq!(app.message_input, "Yes!");
|
||
|
||
// 3. Enter → отправить
|
||
handle_main_input(&mut app, key(KeyCode::Enter)).await;
|
||
|
||
// Остаёмся в Insert после отправки
|
||
assert_eq!(app.input_mode, InputMode::Insert);
|
||
assert_eq!(app.message_input, "");
|
||
}
|