20 KiB
Testing Roadmap
План покрытия tele-tui тестами с фокусом на интеграционные и e2e тесты.
Стратегия тестирования
Подход: Комбо (Snapshot + Integration + E2E)
- Snapshot Testing (70%) — проверка UI рендеринга через insta
- Integration Testing (25%) — проверка логики и flow через FakeTdClient
- E2E Smoke Testing (5%) — базовая проверка что приложение запускается
Почему не юнит-тесты?
- TUI сложно тестировать через юниты (моки, хрупкость)
- Интеграционные тесты дают больше уверенности
- Snapshots ловят UI регрессии лучше, чем assert координат
Фаза 0: Инфраструктура
Зависимости
- Добавить
insta = "1.34"в dev-dependencies - Добавить
tokio-test = "0.4"в dev-dependencies - Настроить
.gitignoreдля snapshots (добавитьtests/snapshots/*.new)
Helpers и Test Utilities
- Создать
tests/helpers/mod.rs - Создать
tests/helpers/app_builder.rs— builder для тестового App - Создать
tests/helpers/fake_tdclient.rs— mock TDLib клиент - Создать
tests/helpers/snapshot_utils.rs— утилиты для snapshot тестов - Создать
tests/helpers/test_data.rs— фикстуры данных (чаты, сообщения)
// 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
- Пустой список чатов
- Список с 3 чатами (без индикаторов)
- Чат с непрочитанными сообщениями
(5) - Чат с иконкой закреплённого 📌
- Чат с иконкой mute 🔇
- Чат с индикатором mention @
- Чат с онлайн-статусом ●
- Выбранный чат (с ▌)
- Список чатов в режиме поиска
- Длинное название чата (обрезка)
Пример теста:
#[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
- Пустой чат (нет сообщений)
- Одно входящее сообщение
- Одно исходящее сообщение
- Группировка по дате (разделитель "Сегодня")
- Группировка по дате (разделитель "Вчера")
- Группировка по отправителю (заголовок с именем)
- Исходящее сообщение с ✓ (отправлено)
- Исходящее сообщение с ✓✓ (прочитано)
- Сообщение с индикатором редактирования ✎
- Длинное сообщение (wrap на несколько строк)
- Markdown: жирный, курсив, код
- Markdown: ссылка, упоминание
- Markdown: спойлер
- Сообщение с медиа-заглушкой [Фото]
- Reply сообщение с превью
- Пересланное сообщение (↪ Переслано от)
- Сообщение с одной реакцией [👍]
- Сообщение с несколькими реакциями [👍] 5 👎 3
- Выбранное сообщение (подсветка)
1.3 Modals — Модальные окна
Файл: tests/modals.rs
- Delete confirmation модалка
- Emoji picker (8x6 сетка)
- Emoji picker с выбранной реакцией (курсор)
- Profile модалка (личный чат)
- Profile модалка (группа)
- Pinned message вверху чата
- Search в чате (с результатами)
- Forward mode (список чатов для пересылки)
1.4 Input Field — Поле ввода
Файл: tests/ui/input_test.rs
- Пустое поле ввода
- Поле ввода с текстом и курсором █
- Поле ввода с длинным текстом (2 строки)
- Поле ввода с длинным текстом (10 строк, максимум)
- Режим редактирования (с превью)
- Режим reply (с превью сообщения)
- Режим поиска (с query)
1.5 Footer — Нижняя панель
Файл: tests/ui/footer_test.rs
- Footer в списке чатов (команды навигации)
- Footer в открытом чате (команды сообщений)
- Footer с индикатором "⚠ Нет сети"
- Footer с индикатором "⏳ Подключение..."
- Footer в режиме поиска
- Footer в режиме выбора сообщения
1.6 Screens — Полные экраны
Файл: tests/ui/screens_test.rs
- Loading screen
- Auth screen (ввод телефона)
- Auth screen (ввод кода)
- Auth screen (ввод пароля 2FA)
- Main screen (папки + чаты + пустая область)
- Main screen (папки + чаты + открытый чат)
- Минимальный размер терминала (предупреждение)
Фаза 2: Integration Tests для логики (Приоритет: ВЫСОКИЙ)
2.1 Send Message Flow
Файл: tests/integration/send_message_test.rs
- Отправка текстового сообщения
- Отправка сообщения обновляет UI
- Отправка пустого сообщения игнорируется
- Отправка с markdown форматированием
- Счётчик непрочитанных обнуляется при открытии чата
- Новое сообщение появляется в реальном времени
2.2 Edit Message Flow
Файл: tests/integration/edit_message_test.rs
- ↑ при пустом инпуте активирует режим выбора
- Enter в режиме выбора начинает редактирование
- Изменение текста и Enter сохраняет
- Esc отменяет редактирование
- Редактирование только своих сообщений
- Индикатор ✎ появляется после редактирования
2.3 Delete Message Flow
Файл: tests/integration/delete_message_test.rs
- d в режиме выбора открывает модалку
- y в модалке удаляет сообщение
- n в модалке отменяет удаление
- Esc отменяет удаление
- Сообщение исчезает из списка после удаления
- Удаление только своих сообщений
2.4 Reply & Forward Flow
Файл: tests/integration/reply_forward_test.rs
- r в режиме выбора активирует reply mode
- Превью сообщения отображается в инпуте
- Отправка reply создаёт связь с оригиналом
- Esc отменяет reply mode
- f в режиме выбора активирует forward mode
- Выбор чата стрелками в forward mode
- Enter пересылает сообщение
- Пересланное сообщение показывает "↪ Переслано от"
2.5 Reactions Flow
Файл: tests/integration/reactions_test.rs
- e открывает emoji picker
- Навигация стрелками по сетке эмодзи
- Enter добавляет реакцию
- Повторный Enter удаляет реакцию (toggle)
- Esc закрывает emoji picker
- Реакция появляется под сообщением
- Своя реакция в рамках [👍]
- Чужая реакция без рамок 👍
- Реакция 1 человека: только эмодзи
- Реакция 2+ людей: эмодзи + счётчик
2.6 Search Flow
Файл: tests/integration/search_test.rs
- Ctrl+S активирует поиск по чатам
- Фильтрация чатов по названию
- Фильтрация чатов по @username
- Esc закрывает поиск
- Ctrl+F активирует поиск в чате
- n переходит к следующему результату
- N переходит к предыдущему результату
- Подсветка найденных совпадений
2.7 Drafts Flow
Файл: tests/integration/drafts_test.rs
- Переключение между чатами сохраняет текст
- Возврат в чат восстанавливает текст
- Отправка сообщения удаляет черновик
- Индикатор черновика в списке чатов
2.8 Navigation Flow
Файл: tests/integration/navigation_test.rs
- ↑/↓ навигация по списку чатов
- Enter открывает чат
- Esc закрывает чат
- 1-9 переключение между папками
- ↑/↓ скролл сообщений в чате
- Подгрузка старых сообщений при скролле вверх
- Русская раскладка (р о л д)
2.9 Profile Flow
Файл: tests/integration/profile_test.rs
- i открывает профиль в личном чате
- Профиль показывает имя, username, телефон
- i открывает профиль в группе
- Профиль группы показывает название, описание, участников
- Esc закрывает профиль
2.10 Copy Flow
Файл: tests/integration/copy_test.rs
- y в режиме выбора копирует текст
- Clipboard содержит правильный текст
- Копирование работает на разных платформах
2.11 Typing Indicator Flow
Файл: tests/integration/typing_test.rs
- Ввод текста отправляет статус "печатает"
- Получение статуса показывает "печатает..." в UI
- Статус исчезает через timeout
2.12 Config Flow
Файл: tests/integration/config_test.rs
- Загрузка конфига из ~/.config/tele-tui/config.toml
- Создание дефолтного конфига если отсутствует
- Применение timezone к отображению времени
- Применение цветов к сообщениям
- Валидация невалидного timezone
- Валидация невалидного цвета
- Загрузка credentials: приоритет XDG → .env
- Ошибка если 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с разными timezoneparse_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: Инфраструктура
- 8/8 задач выполнено ✅
Фаза 1: Snapshot Tests
- 1.1 Chat List: 9/10 (90%)
- 1.2 Messages: 18/19 (95%) ✅
- 1.3 Modals: 8/8 (100%) ✅
- 1.4 Input Field: 0/7
- 1.5 Footer: 0/6
- 1.6 Screens: 0/7
- Итого: 35/57 snapshot тестов (61%)
Фаза 2: Integration Tests
- 2.1 Send Message: 0/6
- 2.2 Edit Message: 0/6
- 2.3 Delete Message: 0/6
- 2.4 Reply & Forward: 0/8
- 2.5 Reactions: 0/10
- 2.6 Search: 0/8
- 2.7 Drafts: 0/4
- 2.8 Navigation: 0/7
- 2.9 Profile: 0/5
- 2.10 Copy: 0/3
- 2.11 Typing: 0/3
- 2.12 Config: 0/8
- Итого: 0/74 интеграционных тестов
Фаза 3: E2E Smoke
- 0/4 smoke тестов
Фаза 4: Дополнительно
- 4.1 Utils: 0/5
- 4.2 Performance: 0/3
- Итого: 0/8 дополнительных тестов
Общий прогресс
Всего: 35/151 тестов (23%)
Фаза 0 (Инфраструктура): ✅ Завершена Фаза 1.1 (Chat List): 9/10 (90%) Фаза 1.2 (Messages): 18/19 (95%) ✅ Фаза 1.3 (Modals): 8/8 (100%) ✅
Приоритизация
Критичные (делать в первую очередь):
- Фаза 0: Инфраструктура (без неё никуда)
- 1.2: Messages snapshots (ядро приложения)
- 2.1: Send message (основной flow)
- 2.8: Navigation (базовая навигация)
Важные (делать после критичных):
- 1.1: Chat list snapshots
- 2.2: Edit message
- 2.3: Delete message
- 2.5: Reactions
- 2.6: Search
Желательные (можно отложить):
- 1.3-1.6: Остальные snapshots
- 2.4, 2.7, 2.9-2.12: Остальные flows
- Фаза 3: E2E smoke tests
Опциональные (по желанию):
- Фаза 4: Utils и performance
Технологии
Основные
- insta — snapshot testing
- tokio-test — async testing utilities
- ratatui::backend::TestBackend — виртуальный терминал
Дополнительные (опционально)
- expectrl — для E2E тестов с реальным бинарником
- criterion — для бенчмарков (фаза 4.2)
- mockall — если понадобятся моки (скорее всего нет)
Примеры структуры тестов
Snapshot Test
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
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);
}
Команды
# Прогнать все тесты
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
Правила
- Один тест = один сценарий — не делать мега-тесты
- Snapshots коммитим — они часть тестов
- Фикстуры переиспользуем — общие данные в
test_data.rs - Тесты изолированы — каждый тест создаёт свой App
- Порядок не важен — тесты можно запускать в любом порядке
TODO перед началом
- Прочитать документацию insta: https://insta.rs/
- Решить: нужен ли trait для TdClient или достаточно FakeTdClient
- Обсудить: какие тесты делать в первую очередь
Примечания
- Этот документ будет обновляться по мере написания тестов
- После завершения фазы — отмечать в метриках
- Если тест падает или не актуален — документировать причину
- Snapshots хранятся в
tests/snapshots/__snapshots__/