Files
telegram-tui/tests/helpers/app_builder.rs
Mikhail Kilin 3b7ef41cae
Some checks failed
ci/woodpecker/pr/check Pipeline was successful
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
fix: resolve all 40 clippy warnings (dead_code, unused_imports, lints)
- Add #[allow(unused_imports)] on pub re-exports used only by lib/tests
- Add #[allow(dead_code)] on public API items unused in binary target
- Fix collapsible_if, redundant_closure, unnecessary_map_or in main.rs
- Prefix unused test variables with underscore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 17:50:18 +03:00

359 lines
11 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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_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, 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");
}
}