fixes
This commit is contained in:
277
tests/helpers/app_builder.rs
Normal file
277
tests/helpers/app_builder.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
280
tests/helpers/fake_tdclient.rs
Normal file
280
tests/helpers/fake_tdclient.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
// Fake TDLib client for testing
|
||||
|
||||
use std::collections::HashMap;
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, FolderInfo, NetworkState};
|
||||
|
||||
/// Упрощённый mock TDLib клиента для тестов
|
||||
#[derive(Clone)]
|
||||
pub struct FakeTdClient {
|
||||
pub chats: Vec<ChatInfo>,
|
||||
pub messages: HashMap<i64, Vec<MessageInfo>>,
|
||||
pub folders: Vec<FolderInfo>,
|
||||
pub user_names: HashMap<i64, String>,
|
||||
pub network_state: NetworkState,
|
||||
pub typing_chat_id: Option<i64>,
|
||||
pub sent_messages: Vec<SentMessage>,
|
||||
pub edited_messages: Vec<EditedMessage>,
|
||||
pub deleted_messages: Vec<i64>,
|
||||
pub reactions: HashMap<i64, Vec<String>>, // message_id -> emojis
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SentMessage {
|
||||
pub chat_id: i64,
|
||||
pub text: String,
|
||||
pub reply_to: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EditedMessage {
|
||||
pub message_id: i64,
|
||||
pub new_text: String,
|
||||
}
|
||||
|
||||
impl Default for FakeTdClient {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl FakeTdClient {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chats: vec![],
|
||||
messages: HashMap::new(),
|
||||
folders: vec![
|
||||
FolderInfo {
|
||||
id: 0,
|
||||
name: "All".to_string(),
|
||||
},
|
||||
],
|
||||
user_names: HashMap::new(),
|
||||
network_state: NetworkState::Ready,
|
||||
typing_chat_id: None,
|
||||
sent_messages: vec![],
|
||||
edited_messages: vec![],
|
||||
deleted_messages: vec![],
|
||||
reactions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Добавить чат
|
||||
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 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
|
||||
}
|
||||
|
||||
/// Добавить папку
|
||||
pub fn with_folder(mut self, id: i32, name: &str) -> Self {
|
||||
self.folders.push(FolderInfo {
|
||||
id,
|
||||
name: name.to_string(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Добавить пользователя
|
||||
pub fn with_user(mut self, id: i64, name: &str) -> Self {
|
||||
self.user_names.insert(id, name.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Установить состояние сети
|
||||
pub fn with_network_state(mut self, state: NetworkState) -> Self {
|
||||
self.network_state = state;
|
||||
self
|
||||
}
|
||||
|
||||
/// Получить чаты
|
||||
pub fn get_chats(&self) -> &[ChatInfo] {
|
||||
&self.chats
|
||||
}
|
||||
|
||||
/// Получить сообщения для чата
|
||||
pub fn get_messages(&self, chat_id: i64) -> Vec<MessageInfo> {
|
||||
self.messages
|
||||
.get(&chat_id)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Получить папки
|
||||
pub fn get_folders(&self) -> &[FolderInfo] {
|
||||
&self.folders
|
||||
}
|
||||
|
||||
/// Отправить сообщение (мок)
|
||||
pub fn send_message(&mut self, chat_id: i64, text: String, reply_to: Option<i64>) -> i64 {
|
||||
let message_id = (self.sent_messages.len() as i64) + 1000;
|
||||
|
||||
self.sent_messages.push(SentMessage {
|
||||
chat_id,
|
||||
text: text.clone(),
|
||||
reply_to,
|
||||
});
|
||||
|
||||
// Добавляем сообщение в список сообщений чата
|
||||
let message = MessageInfo {
|
||||
id: message_id,
|
||||
sender_name: "You".to_string(),
|
||||
is_outgoing: true,
|
||||
content: text,
|
||||
entities: vec![],
|
||||
date: 1640000000,
|
||||
edit_date: 0,
|
||||
is_read: true,
|
||||
can_be_edited: true,
|
||||
can_be_deleted_only_for_self: true,
|
||||
can_be_deleted_for_all_users: true,
|
||||
reply_to: None,
|
||||
forward_from: None,
|
||||
reactions: vec![],
|
||||
};
|
||||
|
||||
self.messages
|
||||
.entry(chat_id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(message);
|
||||
|
||||
message_id
|
||||
}
|
||||
|
||||
/// Редактировать сообщение (мок)
|
||||
pub fn edit_message(&mut self, chat_id: i64, message_id: i64, new_text: String) {
|
||||
self.edited_messages.push(EditedMessage {
|
||||
message_id,
|
||||
new_text: new_text.clone(),
|
||||
});
|
||||
|
||||
// Обновляем сообщение в списке
|
||||
if let Some(messages) = self.messages.get_mut(&chat_id) {
|
||||
if let Some(msg) = messages.iter_mut().find(|m| m.id == message_id) {
|
||||
msg.content = new_text;
|
||||
msg.edit_date = msg.date + 60;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Удалить сообщение (мок)
|
||||
pub fn delete_message(&mut self, chat_id: i64, message_id: i64) {
|
||||
self.deleted_messages.push(message_id);
|
||||
|
||||
// Удаляем сообщение из списка
|
||||
if let Some(messages) = self.messages.get_mut(&chat_id) {
|
||||
messages.retain(|m| m.id != message_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Добавить реакцию (мок)
|
||||
pub fn add_reaction(&mut self, message_id: i64, emoji: String) {
|
||||
self.reactions
|
||||
.entry(message_id)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(emoji);
|
||||
}
|
||||
|
||||
/// Установить статус "печатает"
|
||||
pub fn set_typing(&mut self, chat_id: Option<i64>) {
|
||||
self.typing_chat_id = chat_id;
|
||||
}
|
||||
|
||||
/// Получить список отправленных сообщений
|
||||
pub fn sent_messages(&self) -> &[SentMessage] {
|
||||
&self.sent_messages
|
||||
}
|
||||
|
||||
/// Получить список отредактированных сообщений
|
||||
pub fn edited_messages(&self) -> &[EditedMessage] {
|
||||
&self.edited_messages
|
||||
}
|
||||
|
||||
/// Получить список удалённых сообщений
|
||||
pub fn deleted_messages(&self) -> &[i64] {
|
||||
&self.deleted_messages
|
||||
}
|
||||
|
||||
/// Очистить историю действий
|
||||
pub fn clear_history(&mut self) {
|
||||
self.sent_messages.clear();
|
||||
self.edited_messages.clear();
|
||||
self.deleted_messages.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::helpers::test_data::create_test_chat;
|
||||
|
||||
#[test]
|
||||
fn test_fake_client_creation() {
|
||||
let client = FakeTdClient::new();
|
||||
assert_eq!(client.chats.len(), 0);
|
||||
assert_eq!(client.folders.len(), 1); // Default "All" folder
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fake_client_with_chat() {
|
||||
let chat = create_test_chat("Mom", 123);
|
||||
let client = FakeTdClient::new().with_chat(chat);
|
||||
|
||||
assert_eq!(client.chats.len(), 1);
|
||||
assert_eq!(client.chats[0].title, "Mom");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_message() {
|
||||
let mut client = FakeTdClient::new();
|
||||
let msg_id = client.send_message(123, "Hello".to_string(), None);
|
||||
|
||||
assert_eq!(client.sent_messages().len(), 1);
|
||||
assert_eq!(client.sent_messages()[0].text, "Hello");
|
||||
assert_eq!(client.get_messages(123).len(), 1);
|
||||
assert_eq!(client.get_messages(123)[0].id, msg_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_message() {
|
||||
let mut client = FakeTdClient::new();
|
||||
let msg_id = client.send_message(123, "Hello".to_string(), None);
|
||||
client.edit_message(123, msg_id, "Hello World".to_string());
|
||||
|
||||
assert_eq!(client.edited_messages().len(), 1);
|
||||
assert_eq!(client.get_messages(123)[0].content, "Hello World");
|
||||
assert!(client.get_messages(123)[0].edit_date > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_message() {
|
||||
let mut client = FakeTdClient::new();
|
||||
let msg_id = client.send_message(123, "Hello".to_string(), None);
|
||||
client.delete_message(123, msg_id);
|
||||
|
||||
assert_eq!(client.deleted_messages().len(), 1);
|
||||
assert_eq!(client.get_messages(123).len(), 0);
|
||||
}
|
||||
}
|
||||
11
tests/helpers/mod.rs
Normal file
11
tests/helpers/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
// Test helpers module
|
||||
|
||||
pub mod app_builder;
|
||||
pub mod fake_tdclient;
|
||||
pub mod snapshot_utils;
|
||||
pub mod test_data;
|
||||
|
||||
pub use app_builder::TestAppBuilder;
|
||||
pub use fake_tdclient::FakeTdClient;
|
||||
pub use snapshot_utils::{buffer_to_string, render_to_buffer};
|
||||
pub use test_data::{create_test_chat, create_test_message, create_test_user};
|
||||
89
tests/helpers/snapshot_utils.rs
Normal file
89
tests/helpers/snapshot_utils.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
// Snapshot testing utilities
|
||||
|
||||
use ratatui::backend::TestBackend;
|
||||
use ratatui::Terminal;
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
|
||||
/// Конвертирует Buffer в читаемую строку для snapshot тестов
|
||||
pub fn buffer_to_string(buffer: &Buffer) -> String {
|
||||
let area = buffer.area();
|
||||
let mut result = String::new();
|
||||
|
||||
for y in 0..area.height {
|
||||
let mut line = String::new();
|
||||
for x in 0..area.width {
|
||||
line.push_str(buffer[(x, y)].symbol());
|
||||
}
|
||||
// Убираем trailing spaces в конце строки
|
||||
result.push_str(line.trim_end());
|
||||
if y < area.height - 1 {
|
||||
result.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Создаёт TestBackend с заданным размером и рендерит UI
|
||||
pub fn render_to_buffer<F>(width: u16, height: u16, render_fn: F) -> Buffer
|
||||
where
|
||||
F: FnOnce(&mut ratatui::Frame),
|
||||
{
|
||||
let backend = TestBackend::new(width, height);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
|
||||
terminal
|
||||
.draw(render_fn)
|
||||
.unwrap();
|
||||
|
||||
terminal.backend().buffer().clone()
|
||||
}
|
||||
|
||||
/// Макрос для упрощения snapshot тестов
|
||||
#[macro_export]
|
||||
macro_rules! assert_ui_snapshot {
|
||||
($name:expr, $width:expr, $height:expr, $render_fn:expr) => {{
|
||||
use $crate::helpers::snapshot_utils::{render_to_buffer, buffer_to_string};
|
||||
let buffer = render_to_buffer($width, $height, $render_fn);
|
||||
let output = buffer_to_string(&buffer);
|
||||
insta::assert_snapshot!($name, output);
|
||||
}};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ratatui::widgets::{Block, Borders};
|
||||
|
||||
#[test]
|
||||
fn test_buffer_to_string_simple() {
|
||||
let buffer = render_to_buffer(10, 3, |f| {
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title("Hi");
|
||||
f.render_widget(block, f.area());
|
||||
});
|
||||
|
||||
let result = buffer_to_string(&buffer);
|
||||
assert!(result.contains("Hi"));
|
||||
assert!(result.contains("┌"));
|
||||
assert!(result.contains("└"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffer_to_string_removes_trailing_spaces() {
|
||||
let buffer = render_to_buffer(20, 3, |f| {
|
||||
let block = Block::default().title("Test");
|
||||
f.render_widget(block, Rect::new(0, 0, 10, 3));
|
||||
});
|
||||
|
||||
let result = buffer_to_string(&buffer);
|
||||
let lines: Vec<&str> = result.lines().collect();
|
||||
|
||||
// Проверяем что trailing spaces убраны
|
||||
for line in lines {
|
||||
assert!(!line.ends_with(' ') || line.trim().is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
241
tests/helpers/test_data.rs
Normal file
241
tests/helpers/test_data.rs
Normal file
@@ -0,0 +1,241 @@
|
||||
// Test data builders and fixtures
|
||||
|
||||
use tele_tui::tdlib::{ChatInfo, MessageInfo, ReactionInfo, ReplyInfo, ForwardInfo, ProfileInfo};
|
||||
|
||||
/// Builder для создания тестового чата
|
||||
pub struct TestChatBuilder {
|
||||
id: i64,
|
||||
title: String,
|
||||
username: Option<String>,
|
||||
last_message: String,
|
||||
last_message_date: i32,
|
||||
unread_count: i32,
|
||||
unread_mention_count: i32,
|
||||
is_pinned: bool,
|
||||
order: i64,
|
||||
last_read_outbox_message_id: i64,
|
||||
folder_ids: Vec<i32>,
|
||||
is_muted: bool,
|
||||
draft_text: Option<String>,
|
||||
}
|
||||
|
||||
impl TestChatBuilder {
|
||||
pub fn new(title: &str, id: i64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
title: title.to_string(),
|
||||
username: None,
|
||||
last_message: "".to_string(),
|
||||
last_message_date: 1640000000,
|
||||
unread_count: 0,
|
||||
unread_mention_count: 0,
|
||||
is_pinned: false,
|
||||
order: id,
|
||||
last_read_outbox_message_id: 0,
|
||||
folder_ids: vec![0],
|
||||
is_muted: false,
|
||||
draft_text: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn username(mut self, username: &str) -> Self {
|
||||
self.username = Some(username.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn last_message(mut self, text: &str) -> Self {
|
||||
self.last_message = text.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unread_count(mut self, count: i32) -> Self {
|
||||
self.unread_count = count;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unread_mentions(mut self, count: i32) -> Self {
|
||||
self.unread_mention_count = count;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pinned(mut self) -> Self {
|
||||
self.is_pinned = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn muted(mut self) -> Self {
|
||||
self.is_muted = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn draft(mut self, text: &str) -> Self {
|
||||
self.draft_text = Some(text.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn folder(mut self, folder_id: i32) -> Self {
|
||||
self.folder_ids = vec![folder_id];
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ChatInfo {
|
||||
ChatInfo {
|
||||
id: self.id,
|
||||
title: self.title,
|
||||
username: self.username,
|
||||
last_message: self.last_message,
|
||||
last_message_date: self.last_message_date,
|
||||
unread_count: self.unread_count,
|
||||
unread_mention_count: self.unread_mention_count,
|
||||
is_pinned: self.is_pinned,
|
||||
order: self.order,
|
||||
last_read_outbox_message_id: self.last_read_outbox_message_id,
|
||||
folder_ids: self.folder_ids,
|
||||
is_muted: self.is_muted,
|
||||
draft_text: self.draft_text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder для создания тестового сообщения
|
||||
pub struct TestMessageBuilder {
|
||||
id: i64,
|
||||
sender_name: String,
|
||||
is_outgoing: bool,
|
||||
content: String,
|
||||
entities: Vec<tdlib_rs::types::TextEntity>,
|
||||
date: i32,
|
||||
edit_date: i32,
|
||||
is_read: bool,
|
||||
can_be_edited: bool,
|
||||
can_be_deleted_only_for_self: bool,
|
||||
can_be_deleted_for_all_users: bool,
|
||||
reply_to: Option<ReplyInfo>,
|
||||
forward_from: Option<ForwardInfo>,
|
||||
reactions: Vec<ReactionInfo>,
|
||||
}
|
||||
|
||||
impl TestMessageBuilder {
|
||||
pub fn new(content: &str, id: i64) -> Self {
|
||||
Self {
|
||||
id,
|
||||
sender_name: "User".to_string(),
|
||||
is_outgoing: false,
|
||||
content: content.to_string(),
|
||||
entities: vec![],
|
||||
date: 1640000000,
|
||||
edit_date: 0,
|
||||
is_read: true,
|
||||
can_be_edited: false,
|
||||
can_be_deleted_only_for_self: true,
|
||||
can_be_deleted_for_all_users: false,
|
||||
reply_to: None,
|
||||
forward_from: None,
|
||||
reactions: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outgoing(mut self) -> Self {
|
||||
self.is_outgoing = true;
|
||||
self.sender_name = "You".to_string();
|
||||
self.can_be_edited = true;
|
||||
self.can_be_deleted_for_all_users = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sender(mut self, name: &str) -> Self {
|
||||
self.sender_name = name.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn date(mut self, timestamp: i32) -> Self {
|
||||
self.date = timestamp;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn edited(mut self) -> Self {
|
||||
self.edit_date = self.date + 60;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn unread(mut self) -> Self {
|
||||
self.is_read = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reply_to(mut self, message_id: i64, sender: &str, text: &str) -> Self {
|
||||
self.reply_to = Some(ReplyInfo {
|
||||
message_id,
|
||||
sender_name: sender.to_string(),
|
||||
text: text.to_string(),
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn forwarded_from(mut self, sender: &str) -> Self {
|
||||
self.forward_from = Some(ForwardInfo {
|
||||
sender_name: sender.to_string(),
|
||||
date: self.date - 3600,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reaction(mut self, emoji: &str, count: i32, chosen: bool) -> Self {
|
||||
self.reactions.push(ReactionInfo {
|
||||
emoji: emoji.to_string(),
|
||||
count,
|
||||
is_chosen: chosen,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> MessageInfo {
|
||||
MessageInfo {
|
||||
id: self.id,
|
||||
sender_name: self.sender_name,
|
||||
is_outgoing: self.is_outgoing,
|
||||
content: self.content,
|
||||
entities: self.entities,
|
||||
date: self.date,
|
||||
edit_date: self.edit_date,
|
||||
is_read: self.is_read,
|
||||
can_be_edited: self.can_be_edited,
|
||||
can_be_deleted_only_for_self: self.can_be_deleted_only_for_self,
|
||||
can_be_deleted_for_all_users: self.can_be_deleted_for_all_users,
|
||||
reply_to: self.reply_to,
|
||||
forward_from: self.forward_from,
|
||||
reactions: self.reactions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Хелперы для быстрого создания тестовых данных
|
||||
|
||||
pub fn create_test_chat(title: &str, id: i64) -> ChatInfo {
|
||||
TestChatBuilder::new(title, id).build()
|
||||
}
|
||||
|
||||
pub fn create_test_message(content: &str, id: i64) -> MessageInfo {
|
||||
TestMessageBuilder::new(content, id).build()
|
||||
}
|
||||
|
||||
pub fn create_test_user(name: &str, id: i64) -> (i64, String) {
|
||||
(id, name.to_string())
|
||||
}
|
||||
|
||||
/// Хелпер для создания профиля
|
||||
pub fn create_test_profile(title: &str, chat_id: i64) -> ProfileInfo {
|
||||
ProfileInfo {
|
||||
chat_id,
|
||||
title: title.to_string(),
|
||||
username: None,
|
||||
bio: None,
|
||||
phone_number: None,
|
||||
chat_type: "Личный чат".to_string(),
|
||||
member_count: None,
|
||||
description: None,
|
||||
invite_link: None,
|
||||
is_group: false,
|
||||
online_status: None,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user