# Testing Roadmap План покрытия tele-tui тестами с фокусом на интеграционные и e2e тесты. ## Стратегия тестирования ### Подход: Комбо (Snapshot + Integration + E2E) 1. **Snapshot Testing (70%)** — проверка UI рендеринга через insta 2. **Integration Testing (25%)** — проверка логики и flow через FakeTdClient 3. **E2E Smoke Testing (5%)** — базовая проверка что приложение запускается ### Почему не юнит-тесты? - TUI сложно тестировать через юниты (моки, хрупкость) - Интеграционные тесты дают больше уверенности - Snapshots ловят UI регрессии лучше, чем assert координат --- ## Фаза 0: Инфраструктура ### Зависимости - [x] Добавить `insta = "1.34"` в dev-dependencies - [x] Добавить `tokio-test = "0.4"` в dev-dependencies - [x] Настроить `.gitignore` для snapshots (добавить `tests/snapshots/*.new`) ### Helpers и Test Utilities - [x] Создать `tests/helpers/mod.rs` - [x] Создать `tests/helpers/app_builder.rs` — builder для тестового App - [x] Создать `tests/helpers/fake_tdclient.rs` — mock TDLib клиент - [x] Создать `tests/helpers/snapshot_utils.rs` — утилиты для snapshot тестов - [x] Создать `tests/helpers/test_data.rs` — фикстуры данных (чаты, сообщения) ```rust // tests/helpers/mod.rs 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::{render_to_string, assert_ui_snapshot}; pub use test_data::{create_test_chat, create_test_message}; ``` **Файлы для создания**: ``` tests/ ├── helpers/ │ ├── mod.rs │ ├── app_builder.rs │ ├── fake_tdclient.rs │ ├── snapshot_utils.rs │ └── test_data.rs └── snapshots/ # Создаётся insta автоматически ``` --- ## Фаза 1: Snapshot Tests для UI (Приоритет: ВЫСОКИЙ) ### 1.1 Chat List — Список чатов **Файл**: `tests/ui/chat_list_test.rs` - [x] Пустой список чатов - [x] Список с 3 чатами (без индикаторов) - [x] Чат с непрочитанными сообщениями `(5)` - [x] Чат с иконкой закреплённого 📌 - [x] Чат с иконкой mute 🔇 - [x] Чат с индикатором mention @ - [ ] Чат с онлайн-статусом ● - [x] Выбранный чат (с ▌) - [x] Список чатов в режиме поиска - [x] Длинное название чата (обрезка) **Пример теста**: ```rust #[test] fn snapshot_chat_list_with_unread() { let app = TestAppBuilder::new() .with_chat(create_test_chat("Mom", 123, unread: 5)) .with_chat(create_test_chat("Boss", 456, unread: 0)) .build(); assert_ui_snapshot!("chat_list_with_unread", app, |f, app| { render_chat_list(f, f.size(), app); }); } ``` --- ### 1.2 Messages — Область сообщений **Файл**: `tests/messages.rs` - [x] Пустой чат (нет сообщений) - [x] Одно входящее сообщение - [x] Одно исходящее сообщение - [x] Группировка по дате (разделитель "Сегодня") - [x] Группировка по дате (разделитель "Вчера") - [x] Группировка по отправителю (заголовок с именем) - [x] Исходящее сообщение с ✓ (отправлено) - [x] Исходящее сообщение с ✓✓ (прочитано) - [x] Сообщение с индикатором редактирования ✎ - [x] Длинное сообщение (wrap на несколько строк) - [x] Markdown: жирный, курсив, код - [x] Markdown: ссылка, упоминание - [x] Markdown: спойлер - [x] Сообщение с медиа-заглушкой [Фото] - [x] Reply сообщение с превью - [x] Пересланное сообщение (↪ Переслано от) - [x] Сообщение с одной реакцией [👍] - [x] Сообщение с несколькими реакциями [👍] 5 👎 3 - [x] Выбранное сообщение (подсветка) --- ### 1.3 Modals — Модальные окна **Файл**: `tests/modals.rs` - [x] Delete confirmation модалка - [x] Emoji picker (8x6 сетка) - [x] Emoji picker с выбранной реакцией (курсор) - [x] Profile модалка (личный чат) - [x] Profile модалка (группа) - [x] Pinned message вверху чата - [x] Search в чате (с результатами) - [x] Forward mode (список чатов для пересылки) --- ### 1.4 Input Field — Поле ввода **Файл**: `tests/input_field.rs` - [x] Пустое поле ввода - [x] Поле ввода с текстом и курсором █ - [x] Поле ввода с длинным текстом (2 строки) - [x] Поле ввода с длинным текстом (10 строк, максимум) - [x] Режим редактирования (с превью) - [x] Режим reply (с превью сообщения) - [x] Режим поиска (с query) --- ### 1.5 Footer — Нижняя панель ✅ **Файл**: `tests/footer.rs` - [x] Footer в списке чатов (команды навигации) - [x] Footer в открытом чате (команды сообщений) - [x] Footer с индикатором "⚠ Нет сети" - [x] Footer с индикатором "⏳ Подключение к прокси..." - [x] Footer с индикатором "⏳ Подключение..." - [x] Footer в режиме поиска --- ### 1.6 Screens — Полные экраны ✅ **Файл**: `tests/screens.rs` - [x] Loading screen (default) - [x] Loading screen (со статусом) - [x] Auth screen (ввод телефона) - [x] Auth screen (ввод кода) - [x] Auth screen (ввод пароля 2FA) - [x] Main screen (пустой список чатов) - [x] Минимальный размер терминала (предупреждение) --- ## Фаза 2: Integration Tests для логики (Приоритет: ВЫСОКИЙ) ### 2.1 Send Message Flow ✅ **Файл**: `tests/send_message.rs` (6 тестов) - [x] Отправка текстового сообщения - [x] Отправка нескольких сообщений - [x] Отправка с markdown форматированием - [x] Отправка в разные чаты - [x] Получение входящего сообщения - [x] Отправка с reply --- ### 2.2 Edit Message Flow ✅ **Файл**: `tests/edit_message.rs` (6 тестов) - [x] Редактирование текста сообщения - [x] Установка edit_date после редактирования - [x] Проверка can_be_edited перед редактированием - [x] Редактирование только своих сообщений - [x] Множественные редактирования - [x] Редактирование с форматированием --- ### 2.3 Delete Message Flow ✅ **Файл**: `tests/delete_message.rs` (6 тестов) - [x] Удаление сообщения из списка - [x] Множественные удаления - [x] Проверка can_be_deleted - [x] Удаление только своих сообщений - [x] Удаление из разных чатов - [x] Delete with revoke --- ### 2.4 Reply & Forward Flow ✅ **Файл**: `tests/reply_forward.rs` (8 тестов) - [x] Reply на сообщение с превью - [x] Reply сохраняет связь с оригиналом - [x] Forward сообщения - [x] Forward с sender_name - [x] Forward в разные чаты - [x] Reply + Forward комбо - [x] Reply на forwarded сообщение - [x] Forward reply сообщения --- ### 2.5 Reactions Flow ✅ **Файл**: `tests/reactions.rs` (10 тестов) - [x] Добавление реакции на сообщение - [x] Удаление реакции (toggle) - [x] Множественные реакции на одно сообщение - [x] Реакции от разных пользователей - [x] Подсчёт реакций - [x] Chosen реакция (своя) - [x] Реакции обновляются в реальном времени - [x] Получение доступных реакций чата - [x] Реакции на forwarded сообщения - [x] Очистка всех реакций --- ### 2.6 Search Flow ✅ **Файл**: `tests/search.rs` (8 тестов) - [x] Поиск по названию чата - [x] Поиск по @username - [x] Поиск по сообщениям в чате - [x] Навигация по результатам поиска - [x] Case-insensitive поиск - [x] Поиск с пробелами - [x] Поиск возвращает пустой список если нет совпадений - [x] Очистка поиска --- ### 2.7 Drafts Flow ✅ **Файл**: `tests/drafts.rs` (7 тестов) - [x] Сохранение черновика при переключении чатов - [x] Восстановление черновика при возврате - [x] Удаление черновика после отправки - [x] Черновики для разных чатов независимы - [x] Индикатор черновика в списке чатов - [x] Пустой черновик не сохраняется - [x] Черновик сохраняется при закрытии чата --- ### 2.8 Navigation Flow ✅ **Файл**: `tests/navigation.rs` (7 тестов) - [x] Навигация по списку чатов (↑/↓) - [x] Открытие чата (Enter) - [x] Закрытие чата (Esc) - [x] Скролл сообщений (↑/↓) - [x] Переключение между папками (1-9) - [x] Навигация с wrap (переход с конца на начало) - [x] Навигация в пустом списке --- ### 2.9 Profile Flow ✅ **Файл**: `tests/profile.rs` (6 тестов) - [x] Открытие профиля личного чата - [x] Профиль показывает имя и username - [x] Профиль показывает телефон - [x] Открытие профиля группы - [x] Профиль группы показывает участников - [x] Закрытие профиля (Esc) --- ### 2.10 Network & Typing Flow ✅ **Файл**: `tests/network_typing.rs` (9 тестов) - [x] Typing indicator при наборе текста - [x] Отправка typing action - [x] Получение typing статуса - [x] Typing timeout - [x] Network state: WaitingForNetwork - [x] Network state: ConnectingToProxy - [x] Network state: Connecting - [x] Network state: Updating - [x] Network state: Ready --- ### 2.11 Copy Flow ✅ **Файл**: `tests/copy.rs` (9 тестов - ПРЕВЗОШЛИ ПЛАН!) - [x] Форматирование простого сообщения - [x] Форматирование с forward контекстом - [x] Форматирование с reply контекстом - [x] Форматирование с forward + reply одновременно - [x] Форматирование длинного сообщения - [x] Форматирование с markdown entities - [x] Clipboard initialization - [x] Копирование в реальный clipboard (ручное) - [x] Кроссплатформенность clipboard --- ### 2.12 Config Flow ✅ **Файл**: `tests/config.rs` (11 тестов - ПРЕВЗОШЛИ ПЛАН!) - [x] Дефолтные значения конфигурации - [x] Кастомные значения конфигурации - [x] Парсинг валидных цветов - [x] Парсинг light цветов - [x] Парсинг невалидного цвета с fallback - [x] Case-insensitive парсинг цветов - [x] TOML сериализация и десериализация - [x] Частичный TOML использует дефолты - [x] Различные форматы timezone - [x] Загрузка credentials из переменных окружения - [x] Проверка формата ошибки когда credentials не найдены --- ## Фаза 3: E2E Smoke Tests (Приоритет: СРЕДНИЙ) **Файл**: `tests/e2e/smoke_test.rs` - [ ] Приложение запускается без краша - [ ] Приложение рендерит loading screen - [ ] Приложение корректно завершается по Ctrl+C - [ ] Минимальный размер терминала не крашит приложение **Примечание**: E2E тесты опциональны, так как требуют реального TDLib или сложного мока. --- ## Фаза 4: Дополнительные тесты (Приоритет: НИЗКИЙ) ### 4.1 Utils Tests **Файл**: `tests/unit/utils_test.rs` - [ ] `format_timestamp_with_tz` с разными timezone - [ ] `parse_timezone_offset` валидные значения - [ ] `parse_timezone_offset` инвалидные значения (fallback) - [ ] `format_date` для сегодня, вчера, старых дат - [ ] `format_was_online` для разных временных промежутков ### 4.2 Performance Tests **Файл**: `tests/performance/render_bench.rs` - [ ] Benchmark рендеринга 100 сообщений - [ ] Benchmark рендеринга списка 50 чатов - [ ] Benchmark форматирования markdown текста --- ## Метрики прогресса ### Фаза 0: Инфраструктура - [x] 8/8 задач выполнено ✅ ### Фаза 1: Snapshot Tests - [x] 1.1 Chat List: 9/10 (90%) - [x] 1.2 Messages: 18/19 (95%) ✅ - [x] 1.3 Modals: 8/8 (100%) ✅ - [x] 1.4 Input Field: 7/7 (100%) ✅ - [ ] 1.5 Footer: 0/6 - [ ] 1.6 Screens: 0/7 - **Итого: 42/57 snapshot тестов (74%)** ### Фаза 2: Integration Tests ✅ - [x] 2.1 Send Message: 6/6 ✅ - [x] 2.2 Edit Message: 6/6 ✅ - [x] 2.3 Delete Message: 6/6 ✅ - [x] 2.4 Reply & Forward: 8/8 ✅ - [x] 2.5 Reactions: 10/10 ✅ - [x] 2.6 Search: 8/8 ✅ - [x] 2.7 Drafts: 7/7 ✅ - [x] 2.8 Navigation: 7/7 ✅ - [x] 2.9 Profile: 6/6 ✅ - [x] 2.10 Network & Typing: 9/9 ✅ - [x] 2.11 Copy: 9/9 ✅ (вместо 3!) - [x] 2.12 Config: 11/11 ✅ (вместо 8!) - **Итого: 93/93 интеграционных тестов (100%!) — ПРЕВЗОШЛИ ПЛАН!** 🎉 ### Фаза 3: E2E Smoke - [ ] 0/4 smoke тестов ### Фаза 4: Дополнительно - [ ] 4.1 Utils: 0/5 - [ ] 4.2 Performance: 0/3 - **Итого: 0/8 дополнительных тестов** --- ## Общий прогресс **Всего**: 148/151 тестов (98%) — ПРЕВЗОШЛИ ПЛАН! 🎉 **Фаза 0 (Инфраструктура)**: ✅ Завершена (100%) **Фаза 1 (UI Snapshot Tests)**: ✅ 55/55 (100%) **Фаза 2 (Integration Tests)**: ✅ 93/93 (100%!) — ПРЕВЗОШЛИ ПЛАН! - Завершено: 2.1-2.12 ✅ - Превзошли план на 9 тестов: Copy (9 вместо 3), Config (11 вместо 8) **Опционально**: - Фаза 3 (E2E Smoke): 0/4 - Фаза 4 (Utils + Performance): 0/8 --- ## Приоритизация ### Критичные (делать в первую очередь): 1. **Фаза 0**: Инфраструктура (без неё никуда) 2. **1.2**: Messages snapshots (ядро приложения) 3. **2.1**: Send message (основной flow) 4. **2.8**: Navigation (базовая навигация) ### Важные (делать после критичных): 5. **1.1**: Chat list snapshots 6. **2.2**: Edit message 7. **2.3**: Delete message 8. **2.5**: Reactions 9. **2.6**: Search ### Желательные (можно отложить): 10. **1.3-1.6**: Остальные snapshots 11. **2.4, 2.7, 2.9-2.12**: Остальные flows 12. **Фаза 3**: E2E smoke tests ### Опциональные (по желанию): 13. **Фаза 4**: Utils и performance --- ## Технологии ### Основные - **insta** — snapshot testing - **tokio-test** — async testing utilities - **ratatui::backend::TestBackend** — виртуальный терминал ### Дополнительные (опционально) - **expectrl** — для E2E тестов с реальным бинарником - **criterion** — для бенчмарков (фаза 4.2) - **mockall** — если понадобятся моки (скорее всего нет) --- ## Примеры структуры тестов ### Snapshot Test ```rust use insta::assert_snapshot; use ratatui::backend::TestBackend; use ratatui::Terminal; #[test] fn snapshot_messages_with_reactions() { let mut terminal = Terminal::new(TestBackend::new(80, 24)).unwrap(); let app = TestAppBuilder::new() .with_message(create_test_message("Hello!", reactions: vec![ reaction("👍", 1, chosen: true), reaction("👎", 3, chosen: false), ])) .build(); terminal.draw(|f| { render_messages(f, f.size(), &app); }).unwrap(); let buffer = terminal.backend().buffer(); assert_snapshot!(buffer_to_string(buffer)); } ``` ### Integration Test ```rust use crate::helpers::{TestAppBuilder, FakeTdClient}; #[tokio::test] async fn test_send_message_updates_ui() { let fake_client = FakeTdClient::new() .with_chat("Mom", 123); let mut app = TestAppBuilder::new() .with_client(fake_client) .with_selected_chat(123) .build(); // Ввод текста app.input_text = "Hello!".to_string(); // Отправка app.handle_key(KeyCode::Enter).await; // Проверки assert_eq!(app.input_text, ""); // Инпут очистился assert_eq!(app.current_messages().len(), 1); assert_eq!(app.current_messages()[0].text, "Hello!"); assert_eq!(fake_client.sent_messages().len(), 1); } ``` --- ## Команды ```bash # Прогнать все тесты cargo test # Прогнать только snapshot тесты cargo test --test ui # Прогнать только integration тесты cargo test --test integration # Обновить snapshots (после ревью изменений) cargo insta review # Принять все новые snapshots cargo insta accept # Показать diff для изменённых snapshots cargo insta test --review ``` --- ## Правила 1. **Один тест = один сценарий** — не делать мега-тесты 2. **Snapshots коммитим** — они часть тестов 3. **Фикстуры переиспользуем** — общие данные в `test_data.rs` 4. **Тесты изолированы** — каждый тест создаёт свой App 5. **Порядок не важен** — тесты можно запускать в любом порядке --- ## TODO перед началом - [ ] Прочитать документацию insta: https://insta.rs/ - [ ] Решить: нужен ли trait для TdClient или достаточно FakeTdClient - [ ] Обсудить: какие тесты делать в первую очередь --- ## Примечания - Этот документ будет обновляться по мере написания тестов - После завершения фазы — отмечать в метриках - Если тест падает или не актуален — документировать причину - Snapshots хранятся в `tests/snapshots/__snapshots__/`