This commit is contained in:
Mikhail Kilin
2026-01-28 11:39:21 +03:00
parent 051c4a0265
commit 68a2b7a982
56 changed files with 4424 additions and 5 deletions

View File

@@ -0,0 +1,277 @@
// Test App builder
use tele_tui::app::{App, AppScreen};
use tele_tui::config::Config;
use tele_tui::tdlib::{ChatInfo, MessageInfo};
use ratatui::widgets::ListState;
use std::collections::HashMap;
/// Builder для создания тестового App
///
/// Примечание: Так как App содержит реальный TdClient,
/// этот билдер подходит только для UI/snapshot тестов.
/// Для интеграционных тестов логики понадобится рефакторинг
/// с выделением trait для TdClient.
pub struct TestAppBuilder {
config: Config,
screen: AppScreen,
chats: Vec<ChatInfo>,
selected_chat_id: Option<i64>,
message_input: String,
is_searching: bool,
search_query: String,
editing_message_id: Option<i64>,
replying_to_message_id: Option<i64>,
is_reaction_picker_mode: bool,
is_profile_mode: bool,
confirm_delete_message_id: Option<i64>,
messages: HashMap<i64, Vec<MessageInfo>>,
selected_message_index: Option<usize>,
message_search_mode: bool,
message_search_query: String,
forwarding_message_id: Option<i64>,
is_selecting_forward_chat: bool,
}
impl Default for TestAppBuilder {
fn default() -> Self {
Self::new()
}
}
impl TestAppBuilder {
pub fn new() -> Self {
Self {
config: Config::default(),
screen: AppScreen::Main,
chats: vec![],
selected_chat_id: None,
message_input: String::new(),
is_searching: false,
search_query: String::new(),
editing_message_id: None,
replying_to_message_id: None,
is_reaction_picker_mode: false,
is_profile_mode: false,
confirm_delete_message_id: None,
messages: HashMap::new(),
selected_message_index: None,
message_search_mode: false,
message_search_query: String::new(),
forwarding_message_id: None,
is_selecting_forward_chat: false,
}
}
/// Установить экран
pub fn screen(mut self, screen: AppScreen) -> Self {
self.screen = screen;
self
}
/// Установить конфиг
pub fn config(mut self, config: Config) -> Self {
self.config = config;
self
}
/// Добавить чат
pub fn with_chat(mut self, chat: ChatInfo) -> Self {
self.chats.push(chat);
self
}
/// Добавить несколько чатов
pub fn with_chats(mut self, chats: Vec<ChatInfo>) -> Self {
self.chats.extend(chats);
self
}
/// Выбрать чат
pub fn selected_chat(mut self, chat_id: i64) -> Self {
self.selected_chat_id = Some(chat_id);
self
}
/// Установить текст в инпуте
pub fn message_input(mut self, text: &str) -> Self {
self.message_input = text.to_string();
self
}
/// Режим поиска
pub fn searching(mut self, query: &str) -> Self {
self.is_searching = true;
self.search_query = query.to_string();
self
}
/// Режим редактирования сообщения
pub fn editing_message(mut self, message_id: i64) -> Self {
self.editing_message_id = Some(message_id);
self
}
/// Режим ответа на сообщение
pub fn replying_to(mut self, message_id: i64) -> Self {
self.replying_to_message_id = Some(message_id);
self
}
/// Режим выбора реакции
pub fn reaction_picker(mut self) -> Self {
self.is_reaction_picker_mode = true;
self
}
/// Режим профиля
pub fn profile_mode(mut self) -> Self {
self.is_profile_mode = true;
self
}
/// Подтверждение удаления
pub fn delete_confirmation(mut self, message_id: i64) -> Self {
self.confirm_delete_message_id = Some(message_id);
self
}
/// Добавить сообщение для чата
pub fn with_message(mut self, chat_id: i64, message: MessageInfo) -> Self {
self.messages.entry(chat_id).or_insert_with(Vec::new).push(message);
self
}
/// Добавить несколько сообщений для чата
pub fn with_messages(mut self, chat_id: i64, messages: Vec<MessageInfo>) -> Self {
self.messages.entry(chat_id).or_insert_with(Vec::new).extend(messages);
self
}
/// Установить выбранное сообщение (режим selection)
pub fn selecting_message(mut self, message_index: usize) -> Self {
self.selected_message_index = Some(message_index);
self
}
/// Режим поиска по сообщениям в чате
pub fn message_search(mut self, query: &str) -> Self {
self.message_search_mode = true;
self.message_search_query = query.to_string();
self
}
/// Режим пересылки сообщения
pub fn forward_mode(mut self, message_id: i64) -> Self {
self.forwarding_message_id = Some(message_id);
self.is_selecting_forward_chat = true;
self
}
/// Построить App
///
/// ВАЖНО: Этот метод создаёт App с реальным TdClient,
/// поэтому он подходит только для UI тестов, где мы
/// не вызываем методы TdClient.
pub fn build(self) -> App {
let mut app = App::new(self.config);
app.screen = self.screen;
app.chats = self.chats;
app.selected_chat_id = self.selected_chat_id;
app.message_input = self.message_input;
app.is_searching = self.is_searching;
app.search_query = self.search_query;
app.editing_message_id = self.editing_message_id;
app.replying_to_message_id = self.replying_to_message_id;
app.is_reaction_picker_mode = self.is_reaction_picker_mode;
app.is_profile_mode = self.is_profile_mode;
app.confirm_delete_message_id = self.confirm_delete_message_id;
app.selected_message_index = self.selected_message_index;
app.is_message_search_mode = self.message_search_mode;
app.message_search_query = self.message_search_query;
app.forwarding_message_id = self.forwarding_message_id;
app.is_selecting_forward_chat = self.is_selecting_forward_chat;
// Выбираем первый чат если есть
if !app.chats.is_empty() {
let mut list_state = ListState::default();
list_state.select(Some(0));
app.chat_list_state = list_state;
}
// Применяем сообщения к текущему открытому чату
if let Some(chat_id) = self.selected_chat_id {
if let Some(messages) = self.messages.get(&chat_id) {
app.td_client.current_chat_messages = messages.clone();
app.td_client.current_chat_id = Some(chat_id);
}
}
app
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::helpers::test_data::create_test_chat;
#[test]
fn test_builder_defaults() {
let app = TestAppBuilder::new().build();
assert_eq!(app.screen, AppScreen::Main);
assert_eq!(app.chats.len(), 0);
assert_eq!(app.selected_chat_id, None);
assert_eq!(app.message_input, "");
}
#[test]
fn test_builder_with_chats() {
let chat1 = create_test_chat("Mom", 123);
let chat2 = create_test_chat("Boss", 456);
let app = TestAppBuilder::new()
.with_chat(chat1)
.with_chat(chat2)
.build();
assert_eq!(app.chats.len(), 2);
assert_eq!(app.chats[0].title, "Mom");
assert_eq!(app.chats[1].title, "Boss");
}
#[test]
fn test_builder_with_selected_chat() {
let chat = create_test_chat("Mom", 123);
let app = TestAppBuilder::new()
.with_chat(chat)
.selected_chat(123)
.build();
assert_eq!(app.selected_chat_id, Some(123));
}
#[test]
fn test_builder_editing_mode() {
let app = TestAppBuilder::new()
.editing_message(999)
.message_input("Edited text")
.build();
assert_eq!(app.editing_message_id, Some(999));
assert_eq!(app.message_input, "Edited text");
}
#[test]
fn test_builder_search_mode() {
let app = TestAppBuilder::new()
.searching("test query")
.build();
assert!(app.is_searching);
assert_eq!(app.search_query, "test query");
}
}