Основные изменения: - Добавлено AJAX обновление логов с чекбоксом 'Auto-update logs' - Добавлена опция 'All logs' в выпадающий список tail lines - Исправлено отображение длинных названий контейнеров в multi-view режиме - Восстановлена загрузка истории логов при включенном AJAX обновлении Новые функции: - Чекбокс 'Auto-update logs' в секции Options (включен по умолчанию) - Настройка интервала обновления через LOGBOARD_AJAX_UPDATE_INTERVAL - API эндпоинт /api/settings для получения настроек приложения - Поддержка параметра tail=all для загрузки всех логов - Автоматический запуск AJAX обновления при включении чекбокса Исправления UI: - Кнопки LogLevels не уезжают вправо при длинных названиях контейнеров - Добавлено обрезание длинных названий с многоточием - Фиксированная высота заголовков в multi-view режиме - Защита от сжатия кнопок LogLevels Тестирование: - Добавлены тесты для AJAX обновления (test_ajax_update.py) - Тест multi-view AJAX обновления (test_multi_view_ajax.py) - Тест опции 'all logs' (test_all_logs.py) - Тест отображения длинных названий (test_multi_view_layout.py) - Команды make test-ajax, make test-multi-view-ajax, make test-all-logs, make test-multi-view-layout Документация: - Создана подробная документация AJAX обновления (app/docs/ajax-update.md) - Обновлен CHANGELOG.md с версиями 1.3.0, 1.5.0, 1.6.0 - Обновлен README.md с описанием новых функций Автор: Сергей Антропов Сайт: https://devops.org.ru
155 lines
7.7 KiB
Python
155 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Тест для проверки корректного отображения длинных названий контейнеров
|
||
в multi-view режиме
|
||
Автор: Сергей Антропов
|
||
Сайт: https://devops.org.ru
|
||
"""
|
||
|
||
import asyncio
|
||
import aiohttp
|
||
import json
|
||
import time
|
||
from datetime import datetime
|
||
|
||
async def test_multi_view_layout():
|
||
"""Тестирование отображения длинных названий в multi-view режиме"""
|
||
|
||
print("🧪 Тестирование отображения длинных названий в multi-view режиме")
|
||
print("=" * 70)
|
||
|
||
url = "http://localhost:9001"
|
||
username = "admin"
|
||
password = "admin"
|
||
|
||
print(f"📡 URL: {url}")
|
||
print(f"👤 Пользователь: {username}")
|
||
print("=" * 50)
|
||
|
||
async with aiohttp.ClientSession() as session:
|
||
try:
|
||
# 1. Получаем токен авторизации
|
||
print("🔐 Получение токена авторизации...")
|
||
auth_data = {'username': username, 'password': password}
|
||
async with session.post(f'{url}/api/auth/login', json=auth_data) as response:
|
||
if (response.status != 200):
|
||
print(f"❌ Ошибка авторизации: {response.status}")
|
||
return False
|
||
|
||
auth_response = await response.json()
|
||
token = auth_response.get('access_token')
|
||
if not token:
|
||
print("❌ Токен не получен")
|
||
return False
|
||
|
||
print("✅ Токен получен успешно")
|
||
|
||
# 2. Получаем список сервисов
|
||
print("\n📋 Получение списка сервисов...")
|
||
headers = {'Authorization': f'Bearer {token}'}
|
||
async with session.get(f'{url}/api/services', headers=headers) as response:
|
||
if response.status != 200:
|
||
print(f"❌ Ошибка получения сервисов: {response.status}")
|
||
return False
|
||
|
||
services = await response.json()
|
||
if not services:
|
||
print("❌ Сервисы не найдены")
|
||
return False
|
||
|
||
print(f"✅ Найдено {len(services)} сервисов")
|
||
|
||
# Анализируем названия сервисов
|
||
print("\n📊 Анализ названий сервисов:")
|
||
long_names = []
|
||
short_names = []
|
||
|
||
for service in services:
|
||
name = service['name']
|
||
if len(name) > 30:
|
||
long_names.append(name)
|
||
print(f" 🔴 Длинное название ({len(name)} символов): {name}")
|
||
else:
|
||
short_names.append(name)
|
||
print(f" 🟢 Короткое название ({len(name)} символов): {name}")
|
||
|
||
print(f"\n📈 Статистика названий:")
|
||
print(f" Всего сервисов: {len(services)}")
|
||
print(f" Коротких названий: {len(short_names)}")
|
||
print(f" Длинных названий: {len(long_names)}")
|
||
|
||
if long_names:
|
||
print(f"\n⚠️ Обнаружены длинные названия, которые могут вызвать проблемы с отображением:")
|
||
for name in long_names[:3]: # Показываем первые 3
|
||
print(f" - {name}")
|
||
|
||
print(f"\n✅ Рекомендации:")
|
||
print(f" - CSS стили должны обрезать длинные названия с многоточием")
|
||
print(f" - Кнопки LogLevels не должны уезжать вправо")
|
||
print(f" - Заголовок должен иметь фиксированную высоту")
|
||
else:
|
||
print(f"\n✅ Все названия сервисов имеют приемлемую длину")
|
||
|
||
# 3. Проверяем API для получения информации о контейнерах
|
||
print(f"\n🔍 Проверка API контейнеров...")
|
||
async with session.get(f'{url}/api/containers', headers=headers) as response:
|
||
if response.status == 200:
|
||
containers = await response.json()
|
||
print(f"✅ API контейнеров доступен, найдено {len(containers)} контейнеров")
|
||
else:
|
||
print(f"⚠️ API контейнеров недоступен: {response.status}")
|
||
|
||
# 4. Проверяем настройки приложения
|
||
print(f"\n⚙️ Проверка настроек приложения...")
|
||
async with session.get(f'{url}/api/settings', headers=headers) as response:
|
||
if response.status == 200:
|
||
settings = await response.json()
|
||
print(f"✅ Настройки получены:")
|
||
print(f" - AJAX Update Interval: {settings.get('ajax_update_interval')}ms")
|
||
print(f" - Default Tail: {settings.get('default_tail')}")
|
||
print(f" - Skip Unhealthy: {settings.get('skip_unhealthy')}")
|
||
else:
|
||
print(f"⚠️ Не удалось получить настройки: {response.status}")
|
||
|
||
# 5. Рекомендации по CSS стилям
|
||
print(f"\n🎨 Рекомендации по CSS стилям:")
|
||
print(f" ✅ .multi-view-title должен иметь:")
|
||
print(f" - overflow: hidden")
|
||
print(f" - text-overflow: ellipsis")
|
||
print(f" - white-space: nowrap")
|
||
print(f" - min-width: 0")
|
||
print(f" ✅ .multi-view-levels должен иметь:")
|
||
print(f" - flex-shrink: 0")
|
||
print(f" ✅ .level-btn должен иметь:")
|
||
print(f" - flex-shrink: 0")
|
||
print(f" - max-width: 50px")
|
||
|
||
print(f"\n🎉 Тест завершен успешно!")
|
||
print(f"✅ Анализ названий сервисов выполнен")
|
||
return True
|
||
|
||
except Exception as e:
|
||
print(f"❌ Ошибка тестирования: {e}")
|
||
return False
|
||
|
||
async def main():
|
||
"""Основная функция"""
|
||
print("🚀 Запуск теста отображения длинных названий в multi-view")
|
||
print("=" * 70)
|
||
|
||
result = await test_multi_view_layout()
|
||
|
||
print("\n" + "=" * 70)
|
||
if result:
|
||
print("🎉 Все тесты прошли успешно!")
|
||
print("✅ Анализ названий сервисов завершен")
|
||
else:
|
||
print("❌ Тесты завершились с ошибками")
|
||
|
||
return result
|
||
|
||
if __name__ == "__main__":
|
||
import sys
|
||
result = asyncio.run(main())
|
||
sys.exit(0 if result else 1)
|