Split core and TUI crates
This commit is contained in:
352
crates/tele-tui/tests/helpers/app_builder.rs
Normal file
352
crates/tele-tui/tests/helpers/app_builder.rs
Normal file
@@ -0,0 +1,352 @@
|
||||
// Test App builder
|
||||
|
||||
use super::FakeTdClient;
|
||||
use ratatui::widgets::ListState;
|
||||
use std::collections::HashMap;
|
||||
use tele_tui::app::{App, AppScreen, ChatState, InputMode};
|
||||
use tele_tui::config::Config;
|
||||
use tele_tui::tdlib::AuthState;
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo};
|
||||
use tele_tui::types::{ChatId, MessageId};
|
||||
|
||||
/// Builder для создания тестового App с FakeTdClient\n///\n/// Использует trait-based DI для подмены TdClient на FakeTdClient в тестах.
|
||||
#[allow(dead_code)]
|
||||
pub struct TestAppBuilder {
|
||||
config: Config,
|
||||
screen: AppScreen,
|
||||
chats: Vec<ChatInfo>,
|
||||
selected_chat_id: Option<i64>,
|
||||
message_input: String,
|
||||
is_searching: bool,
|
||||
search_query: String,
|
||||
chat_state: Option<ChatState>,
|
||||
input_mode: Option<InputMode>,
|
||||
messages: HashMap<i64, Vec<MessageInfo>>,
|
||||
status_message: Option<String>,
|
||||
auth_state: Option<AuthState>,
|
||||
phone_input: Option<String>,
|
||||
code_input: Option<String>,
|
||||
password_input: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for TestAppBuilder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
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(),
|
||||
chat_state: None,
|
||||
input_mode: None,
|
||||
messages: HashMap::new(),
|
||||
status_message: None,
|
||||
auth_state: None,
|
||||
phone_input: None,
|
||||
code_input: None,
|
||||
password_input: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Установить экран
|
||||
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, selected_index: usize) -> Self {
|
||||
self.chat_state = Some(ChatState::Editing {
|
||||
message_id: MessageId::new(message_id),
|
||||
selected_index,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Режим ответа на сообщение
|
||||
pub fn replying_to(mut self, message_id: i64) -> Self {
|
||||
self.chat_state = Some(ChatState::Reply { message_id: MessageId::new(message_id) });
|
||||
self
|
||||
}
|
||||
|
||||
/// Режим выбора реакции
|
||||
pub fn reaction_picker(mut self, message_id: i64, available_reactions: Vec<String>) -> Self {
|
||||
self.chat_state = Some(ChatState::ReactionPicker {
|
||||
message_id: MessageId::new(message_id),
|
||||
available_reactions,
|
||||
selected_index: 0,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Режим профиля
|
||||
pub fn profile_mode(mut self, info: tele_tui::tdlib::ProfileInfo) -> Self {
|
||||
self.chat_state = Some(ChatState::Profile {
|
||||
info,
|
||||
selected_action: 0,
|
||||
leave_group_confirmation_step: 0,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Подтверждение удаления
|
||||
pub fn delete_confirmation(mut self, message_id: i64) -> Self {
|
||||
self.chat_state =
|
||||
Some(ChatState::DeleteConfirmation { message_id: MessageId::new(message_id) });
|
||||
self
|
||||
}
|
||||
|
||||
/// Добавить сообщение для чата
|
||||
pub fn with_message(mut self, chat_id: i64, message: MessageInfo) -> Self {
|
||||
self.messages.entry(chat_id).or_default().push(message);
|
||||
self
|
||||
}
|
||||
|
||||
/// Добавить несколько сообщений для чата
|
||||
pub fn with_messages(mut self, chat_id: i64, messages: Vec<MessageInfo>) -> Self {
|
||||
self.messages.entry(chat_id).or_default().extend(messages);
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить выбранное сообщение (режим selection)
|
||||
pub fn selecting_message(mut self, selected_index: usize) -> Self {
|
||||
self.chat_state = Some(ChatState::MessageSelection { selected_index });
|
||||
self
|
||||
}
|
||||
|
||||
/// Режим поиска по сообщениям в чате
|
||||
pub fn message_search(mut self, query: &str) -> Self {
|
||||
self.chat_state = Some(ChatState::SearchInChat {
|
||||
query: query.to_string(),
|
||||
results: Vec::new(),
|
||||
selected_index: 0,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить Insert mode
|
||||
pub fn insert_mode(mut self) -> Self {
|
||||
self.input_mode = Some(InputMode::Insert);
|
||||
self
|
||||
}
|
||||
|
||||
/// Режим пересылки сообщения
|
||||
pub fn forward_mode(mut self, message_id: i64) -> Self {
|
||||
self.chat_state = Some(ChatState::Forward { message_id: MessageId::new(message_id) });
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить статус сообщение (для loading screen)
|
||||
pub fn status_message(mut self, message: &str) -> Self {
|
||||
self.status_message = Some(message.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить auth state
|
||||
pub fn auth_state(mut self, state: AuthState) -> Self {
|
||||
self.auth_state = Some(state);
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить phone input
|
||||
pub fn phone_input(mut self, phone: &str) -> Self {
|
||||
self.phone_input = Some(phone.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить code input
|
||||
pub fn code_input(mut self, code: &str) -> Self {
|
||||
self.code_input = Some(code.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить password input
|
||||
pub fn password_input(mut self, password: &str) -> Self {
|
||||
self.password_input = Some(password.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Построить App с FakeTdClient
|
||||
///
|
||||
/// Создаёт App с FakeTdClient, подходит для любых тестов включая
|
||||
/// интеграционные тесты логики.
|
||||
pub fn build(self) -> App<FakeTdClient> {
|
||||
// Создаём FakeTdClient с чатами и сообщениями
|
||||
let mut fake_client = FakeTdClient::new();
|
||||
|
||||
// Добавляем чаты
|
||||
for chat in &self.chats {
|
||||
fake_client = fake_client.with_chat(chat.clone());
|
||||
}
|
||||
|
||||
// Добавляем сообщения
|
||||
for (chat_id, messages) in self.messages {
|
||||
fake_client = fake_client.with_messages(chat_id, messages);
|
||||
}
|
||||
|
||||
// Устанавливаем текущий чат если нужно
|
||||
if let Some(chat_id) = self.selected_chat_id {
|
||||
*fake_client.current_chat_id.lock().unwrap() = Some(chat_id);
|
||||
}
|
||||
|
||||
// Устанавливаем auth state если нужно
|
||||
if let Some(auth_state) = self.auth_state {
|
||||
fake_client = fake_client.with_auth_state(auth_state);
|
||||
}
|
||||
|
||||
// Создаём App с FakeTdClient
|
||||
let mut app = App::with_client(self.config, fake_client);
|
||||
|
||||
app.screen = self.screen;
|
||||
app.chats = self.chats;
|
||||
app.selected_chat_id = self.selected_chat_id.map(ChatId::new);
|
||||
app.message_input = self.message_input;
|
||||
app.is_searching = self.is_searching;
|
||||
app.search_query = self.search_query;
|
||||
|
||||
// Применяем chat_state если он установлен
|
||||
if let Some(chat_state) = self.chat_state {
|
||||
app.chat_state = chat_state;
|
||||
}
|
||||
|
||||
// Применяем input_mode если он установлен
|
||||
if let Some(input_mode) = self.input_mode {
|
||||
app.input_mode = input_mode;
|
||||
}
|
||||
|
||||
// Применяем status_message
|
||||
if let Some(status) = self.status_message {
|
||||
app.status_message = Some(status);
|
||||
}
|
||||
|
||||
// Применяем auth inputs
|
||||
if let Some(phone) = self.phone_input {
|
||||
app.set_phone_input(phone);
|
||||
}
|
||||
if let Some(code) = self.code_input {
|
||||
app.set_code_input(code);
|
||||
}
|
||||
if let Some(password) = self.password_input {
|
||||
app.set_password_input(password);
|
||||
}
|
||||
|
||||
// Выбираем первый чат если есть
|
||||
if !app.chats.is_empty() {
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select(Some(0));
|
||||
app.chat_list_state = list_state;
|
||||
}
|
||||
|
||||
app
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::helpers::test_data::create_test_chat;
|
||||
use tele_tui::app::methods::messages::MessageMethods;
|
||||
|
||||
#[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(ChatId::new(123)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_editing_mode() {
|
||||
let app = TestAppBuilder::new()
|
||||
.editing_message(999, 0)
|
||||
.message_input("Edited text")
|
||||
.build();
|
||||
|
||||
assert!(app.is_editing());
|
||||
assert_eq!(app.chat_state.selected_message_id(), Some(MessageId::new(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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user