From f77626914ea6b4dfed830d3ca896aa8b7bde13ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Tue, 13 Jan 2026 10:21:48 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20.old=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Deleted chatTestsV2.old.js - Deleted postDetailModal.old.js Финальная очистка deprecated кода завершена --- .../js/components/postDetailModal.old.js | 401 ---------------- backend/static/js/tests/chatTestsV2.old.js | 428 ------------------ 2 files changed, 829 deletions(-) delete mode 100644 backend/static/js/components/postDetailModal.old.js delete mode 100644 backend/static/js/tests/chatTestsV2.old.js diff --git a/backend/static/js/components/postDetailModal.old.js b/backend/static/js/components/postDetailModal.old.js deleted file mode 100644 index 936e89b1..00000000 --- a/backend/static/js/components/postDetailModal.old.js +++ /dev/null @@ -1,401 +0,0 @@ -/** - * postDetailModal.js - * Модуль для открытия деталей поста в модальном окне - * Полностью переработанная версия с правильной логикой определения автора - */ - -(function() { - 'use strict'; - - // Константы - const INITIAL_COMMENTS_COUNT = 10; - const DEBUG = true; // Включаем отладку для диагностики - - // Состояние модуля - let modal = null; - let modalElement = null; - let currentPost = null; // Сохраняем текущий пост - - /** - * Инициализация модального окна - */ - function initPostDetailModal() { - modalElement = document.getElementById('postDetailModal'); - if (!modalElement) { - console.warn('postDetailModal: элемент #postDetailModal не найден'); - return; - } - - modal = new bootstrap.Modal(modalElement); - - // Очистка при закрытии - modalElement.addEventListener('hidden.bs.modal', function() { - const contentDiv = document.getElementById('postDetailContent'); - if (contentDiv) { - contentDiv.innerHTML = ` -
-
- Загрузка... -
-
- `; - } - currentPost = null; - }); - - // Добавляем обработчики на ссылки постов - attachPostLinkHandlers(); - } - - /** - * Получение CSRF токена из cookie - */ - function getCsrfToken() { - const cookies = document.cookie.split(';'); - for (let cookie of cookies) { - const [name, value] = cookie.trim().split('='); - if (name === 'csrftoken') { - return decodeURIComponent(value); - } - } - return null; - } - - /** - * Получение JWT токена из cookie (для загрузки данных) - */ - function getAccessToken() { - const cookies = document.cookie.split(';'); - for (let cookie of cookies) { - const [name, value] = cookie.trim().split('='); - if (name === 'access_token') { - return decodeURIComponent(value); - } - } - return null; - } - - /** - * Загрузка деталей поста через API - */ - async function loadPostDetail(postId) { - const accessToken = getAccessToken(); - const headers = { - 'Accept': 'application/json' - }; - - if (accessToken) { - headers['Authorization'] = `Bearer ${accessToken}`; - } - - try { - const response = await fetch(`/api/v1/posts/${postId}/`, { - method: 'GET', - headers: headers - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const post = await response.json(); - - // Отладочная информация - if (DEBUG) { - console.log('=== POST DETAIL DEBUG ==='); - console.log('Post author:', post.author); - console.log('Post author.id:', post.author?.id, 'type:', typeof post.author?.id); - console.log('Post author_id:', post.author_id, 'type:', typeof post.author_id); - if (post.comments && post.comments.length > 0) { - console.log('First comment:', post.comments[0]); - console.log('First comment author:', post.comments[0].author); - console.log('First comment author.id:', post.comments[0].author?.id, 'type:', typeof post.comments[0].author?.id); - console.log('First comment author_id:', post.comments[0].author_id, 'type:', typeof post.comments[0].author_id); - } - console.log('========================'); - } - - currentPost = post; - renderPostDetail(post); - } catch (error) { - console.error('Ошибка загрузки поста:', error); - showError('Не удалось загрузить пост'); - } - } - - /** - * Отображение деталей поста в модальном окне - */ - function renderPostDetail(post) { - const contentDiv = document.getElementById('postDetailContent'); - - // Определяем ID автора поста (пробуем разные поля) - const postAuthorId = getAuthorId(post.author) || post.author_id; - - if (DEBUG) { - console.log('Rendering post with author ID:', postAuthorId); - } - - // Формируем HTML с кнопкой закрытия - let html = ''; - html += '
'; - - // Заголовок с автором - html += renderPostHeader(post); - - // Изображение - if (post.image) { - html += `
${escapeHtml(post.title || 'Публикация')}
`; - } - - // Тело - html += '
'; - html += `

${escapeHtml(post.title || 'Без названия')}

`; - if (post.body) { - html += `
${formatBody(post.body)}
`; - } - html += '
'; - - // Футер с лайками - html += renderPostFooter(post); - - // Секция комментариев - html += renderCommentsSection(post, postAuthorId); - - html += '
'; - - contentDiv.innerHTML = html; - - // Сохраняем данные для динамической подгрузки - if (post.comments && post.comments.length > 0) { - contentDiv.dataset.allComments = JSON.stringify(post.comments); - contentDiv.dataset.postAuthorId = postAuthorId; - } - - // Прокручиваем к последнему комментарию - scrollToBottom(); - - // Добавляем обработчики - attachLikeHandlers(); - attachLoadMoreCommentsHandler(); - attachCommentFormHandler(); - } - - /** - * Отображение комментария - * @param {Object} comment - Объект комментария - * @param {number} postAuthorId - ID автора поста - */ - function renderComment(comment, postAuthorId) { - // Приводим оба ID к числам для корректного сравнения - const commentAuthorId = comment.author?.id ? parseInt(comment.author.id) : null; - const postAuthorIdNum = postAuthorId ? parseInt(postAuthorId) : null; - const isAuthor = commentAuthorId !== null && postAuthorIdNum !== null && commentAuthorId === postAuthorIdNum; - - const commentClass = isAuthor ? 'comment-item comment-item--author' : 'comment-item'; - - let html = `
`; - html += '
'; - - // Автор и дата - const authorName = comment.author?.full_name || 'Аноним'; - const authorId = comment.author?.id; - - if (authorId) { - html += `${authorName}`; - } else { - html += `${authorName}`; - } - - html += '·'; - html += ``; - html += '
'; - - // Текст комментария - html += `
${formatBody(comment.text)}
`; - html += '
'; - - return html; - } - - /** - * Форматирование даты - */ - function formatDate(dateString) { - const date = new Date(dateString); - const now = new Date(); - const diff = now - date; - const days = Math.floor(diff / (1000 * 60 * 60 * 24)); - - if (days === 0) { - return 'Сегодня, ' + date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }); - } else if (days === 1) { - return 'Вчера, ' + date.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' }); - } else if (days < 7) { - return days + ' дн. назад'; - } else { - return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' }); - } - } - - /** - * Форматирование текста (простая обработка переносов строк) - */ - function formatBody(text) { - return text.replace(/\n/g, '
'); - } - - /** - * Показать ошибку - */ - function showError(message) { - const contentDiv = document.getElementById('postDetailContent'); - contentDiv.innerHTML = ` - - `; - } - - /** - * Открыть модальное окно с постом - */ - function openPostModal(postId) { - if (!modal) { - console.error('Modal не инициализирован'); - return; - } - - modal.show(); - loadPostDetail(postId); - } - - /** - * Добавление обработчиков на ссылки постов - */ - function attachPostLinkHandlers() { - // Обработка кликов по ссылкам на посты - document.addEventListener('click', function(e) { - const link = e.target.closest('a[data-post-link]'); - if (link) { - e.preventDefault(); - const postId = link.dataset.postId; - if (postId) { - openPostModal(postId); - } - } - }); - } - - /** - * Обработчик для кнопки "Показать ещё комментарии" - */ - function attachLoadMoreCommentsHandler() { - const loadMoreBtn = document.querySelector('.load-more-comments'); - if (!loadMoreBtn) return; - - loadMoreBtn.addEventListener('click', function() { - const contentDiv = document.getElementById('postDetailContent'); - const allComments = JSON.parse(contentDiv.dataset.allComments || '[]'); - const postAuthorId = parseInt(contentDiv.dataset.postAuthorId); - const commentsContainer = document.getElementById('commentsListContainer'); - - if (!commentsContainer || allComments.length === 0) return; - - // Сохраняем текущую позицию скролла и высоту - const scrollHeight = commentsContainer.scrollHeight; - const scrollTop = commentsContainer.scrollTop; - - // Показываем все комментарии - let html = ''; - allComments.forEach(comment => { - html += renderComment(comment, postAuthorId); - }); - - commentsContainer.innerHTML = html; - - // Восстанавливаем позицию скролла (с учётом добавленного контента) - const newScrollHeight = commentsContainer.scrollHeight; - const heightDiff = newScrollHeight - scrollHeight; - commentsContainer.scrollTop = scrollTop + heightDiff; - - // Удаляем кнопку после загрузки - this.remove(); - }); - } - - /** - * Обработчики лайков в модальном окне - */ - function attachLikeHandlers() { - const likeButtons = document.querySelectorAll('#postDetailContent .like-btn'); - likeButtons.forEach(btn => { - btn.addEventListener('click', async function(e) { - e.preventDefault(); - const postId = this.dataset.postId; - await toggleLike(postId, this); - }); - }); - } - - /** - * Переключение лайка - */ - async function toggleLike(postId, button) { - const csrfToken = getCsrfToken(); - if (!csrfToken) { - alert('Необходимо авторизоваться'); - return; - } - - const icon = button.querySelector('i'); - const isLiked = icon.classList.contains('bi-heart-fill'); - const endpoint = isLiked ? 'unlike' : 'like'; - - try { - const response = await fetch(`/api/v1/posts/${postId}/${endpoint}/`, { - method: 'POST', - headers: { - 'X-CSRFToken': csrfToken, - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest' - } - }); - - if (response.ok) { - const data = await response.json(); - - // Обновляем иконку и счетчик - if (isLiked) { - icon.classList.remove('bi-heart-fill', 'text-danger'); - icon.classList.add('bi-heart'); - } else { - icon.classList.remove('bi-heart'); - icon.classList.add('bi-heart-fill', 'text-danger'); - } - - // Обновляем счетчик - const countText = button.childNodes[1]; - if (countText) { - countText.textContent = ' ' + (data.likes_count || 0); - } - } else { - alert('Ошибка при обработке лайка'); - } - } catch (error) { - console.error('Ошибка:', error); - alert('Ошибка сети'); - } - } - - // Инициализация при загрузке DOM - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initPostDetailModal); - } else { - initPostDetailModal(); - } - - // Экспорт для использования извне - window.openPostModal = openPostModal; - -})(); diff --git a/backend/static/js/tests/chatTestsV2.old.js b/backend/static/js/tests/chatTestsV2.old.js deleted file mode 100644 index 5438b752..00000000 --- a/backend/static/js/tests/chatTestsV2.old.js +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Тесты для ChatControllerV2 - умный автоскролл - * Запуск: - * .venv/Scripts/python manage.py runserver - * Открыть в браузере: http://localhost:8000/static/js/tests/chatTestsV2.html - */ - -import { ChatControllerV2 } from '../controllers/chatControllerV2.js'; -import { MessageStoreV2 } from '../stores/messageStoreV2.js'; - -class ChatTestSuiteV2 { - constructor() { - this.results = { - passed: 0, - failed: 0, - total: 0, - details: [] - }; - } - - // ==================== Утилиты ==================== - - assert(condition, message) { - this.results.total++; - if (condition) { - this.results.passed++; - console.log(`✅ PASS: ${message}`); - this.results.details.push({ status: 'PASS', message }); - } else { - this.results.failed++; - console.error(`❌ FAIL: ${message}`); - this.results.details.push({ status: 'FAIL', message }); - } - } - - assertEqual(actual, expected, message) { - const condition = actual === expected; - const fullMessage = `${message} (expected: ${expected}, got: ${actual})`; - this.assert(condition, fullMessage); - } - - assertNotNull(value, message) { - this.assert(value !== null && value !== undefined, message); - } - - async sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - resetResults() { - this.results = { - passed: 0, - failed: 0, - total: 0, - details: [] - }; - } - - printResults() { - const { passed, failed, total } = this.results; - const passRate = total > 0 ? parseFloat(((passed / total) * 100).toFixed(1)) : 0; - - console.log('\n' + '='.repeat(60)); - console.log(`📊 Результаты тестов V2:`); - console.log(` ✅ Успешных: ${passed}`); - console.log(` ❌ Провалено: ${failed}`); - console.log(` 📈 Процент: ${passRate}%`); - console.log('='.repeat(60) + '\n'); - - return { passed, failed, total, passRate }; - } - - // ==================== Тесты базовой функциональности V2 ==================== - - async testBasicFunctionalityV2() { - console.log('\n🧪 Тестирование базовой функциональности V2...\n'); - this.resetResults(); - - const store = new MessageStoreV2(); - const chatId = 1; - - // Тест 1: Добавление сообщения - const msg1 = { - id: 1, - chat_id: chatId, - author_id: 1, - author_name: 'User 1', - content: 'Test message', - created_ts: Date.now(), - created: new Date().toISOString() - }; - - store.addMessage(msg1); - this.assert(store.hasMessage(1), 'Сообщение должно быть добавлено'); - - // Тест 2: Batch добавление (V2 фича) - const messages = [ - { id: 2, chat_id: chatId, author_id: 1, content: 'Msg 2', created_ts: Date.now() + 1000, created: new Date().toISOString() }, - { id: 3, chat_id: chatId, author_id: 1, content: 'Msg 3', created_ts: Date.now() + 2000, created: new Date().toISOString() }, - { id: 4, chat_id: chatId, author_id: 1, content: 'Msg 4', created_ts: Date.now() + 3000, created: new Date().toISOString() } - ]; - - store.addMessages(messages); - this.assertEqual(store.messages.size, 4, 'Должно быть 4 сообщения после batch'); - - // Тест 3: Обновление сообщения - store.updateMessage(1, { content: 'Updated', is_edited: true }); - const updated = store.getMessage(1); - this.assertEqual(updated.content, 'Updated', 'Сообщение должно быть обновлено'); - - // Тест 4: Удаление сообщения - store.removeMessage(1); - this.assert(!store.hasMessage(1), 'Сообщение должно быть удалено'); - - this.printResults(); - return this.results; - } - - // ==================== Тесты умного автоскролла V2 ==================== - - async testSmartAutoscrollV2() { - console.log('\n🧪 Тестирование умного автоскролла V2 (8 сценариев, ~18 проверок)...\n'); - this.resetResults(); - - // Создаем реальный контейнер - const container = document.createElement('div'); - container.id = 'test-chat-container-v2'; - container.style.cssText = 'height: 400px; overflow-y: auto; position: relative;'; - document.body.appendChild(container); - - const currentUserId = 1; - const controller = new ChatControllerV2({ - chatId: 1, - scrollElement: container, - containerId: 'test-chat-container-v2', - currentUserId: currentUserId - }); - - // Инициализируем контроллер - try { - await controller.init(); - } catch (e) { - console.log('[Test V2] Init failed (expected without backend):', e.message); - controller._initializing = false; - controller._initialized = false; - - // ВАЖНО: Инициализируем scroll watcher вручную для тестов - // т.к. init() упал, но watcher нужен для Теста 5 - controller._initScrollWatcher(); - } - - // Тест 1: Своё сообщение - всегда скроллим вниз - console.log('\n--- Тест 1: Своё сообщение скроллит вниз ---'); - - // Добавляем несколько сообщений для высоты - for (let i = 0; i < 20; i++) { - controller.store.addMessage({ - id: i + 1, - chat_id: 1, - author_id: 2, - author_name: 'Other User', - content: `Message ${i + 1}`, - created_ts: Date.now() + i * 1000, - created: new Date(Date.now() + i * 1000).toISOString() - }); - } - - controller.renderer.render(1); - await this.sleep(100); - - // Прокручиваем вверх (читаем историю) - container.scrollTop = 50; - await this.sleep(50); - - const wasAtBottom = controller.scrollManager.isNearBottom(); - this.assert(!wasAtBottom, 'До отправки своего сообщения должны быть НЕ внизу'); - - // Добавляем своё сообщение - controller.store.addMessage({ - id: 100, - chat_id: 1, - author_id: currentUserId, - author_name: 'Me', - content: 'My message', - created_ts: Date.now() + 30000, - created: new Date(Date.now() + 30000).toISOString() - }); - - await this.sleep(500); - - const isNowAtBottom = controller.scrollManager.isNearBottom(); - this.assert(isNowAtBottom, 'После своего сообщения должны быть внизу (автоскролл)'); - - // Тест 2: Чужое сообщение + внизу - скроллим - console.log('\n--- Тест 2: Чужое сообщение + внизу = скролл ---'); - - // Убеждаемся что внизу - controller.scrollManager.scrollToBottom({ instant: true, force: true }); - await this.sleep(100); - - const wasAtBottomBefore = controller.scrollManager.isNearBottom(); - this.assert(wasAtBottomBefore, 'Должны быть внизу перед получением чужого сообщения'); - - // Добавляем чужое сообщение - controller.store.addMessage({ - id: 101, - chat_id: 1, - author_id: 2, - author_name: 'Other User', - content: 'Other message', - created_ts: Date.now() + 31000, - created: new Date(Date.now() + 31000).toISOString() - }); - - await this.sleep(500); - - const stillAtBottom = controller.scrollManager.isNearBottom(); - this.assert(stillAtBottom, 'Должны остаться внизу после чужого сообщения (автоскролл)'); - - // Тест 3: Чужое сообщение + читаем историю - показываем индикатор - console.log('\n--- Тест 3: Чужое сообщение + история = индикатор ---'); - - // Прокручиваем вверх (читаем историю) - container.scrollTop = 100; - await this.sleep(100); - - const readingHistory = !controller.scrollManager.isNearBottom(); - this.assert(readingHistory, 'Должны читать историю (не внизу)'); - - // Сбрасываем счётчик индикатора - controller._newMessagesCount = 0; - - // Добавляем чужое сообщение - controller.store.addMessage({ - id: 102, - chat_id: 1, - author_id: 2, - author_name: 'Other User', - content: 'Another message', - created_ts: Date.now() + 32000, - created: new Date(Date.now() + 32000).toISOString() - }); - - await this.sleep(500); - - // Проверяем что индикатор показан - this.assert(controller._newMessagesCount > 0, 'Счётчик новых сообщений должен увеличиться'); - - const indicator = document.getElementById('new-messages-btn'); - if (indicator) { - const isVisible = indicator.style.display !== 'none'; - this.assert(isVisible, 'Индикатор новых сообщений должен быть видимым'); - } else { - console.warn('[Test V2] Индикатор не найден (может создаваться при первом показе)'); - } - - // Тест 4: Клик по индикатору - скролл вниз и скрытие - console.log('\n--- Тест 4: Клик по индикатору ---'); - - if (indicator && controller._newMessagesCount > 0) { - // Кликаем по индикатору - indicator.click(); - - // Ждём завершения анимации скролла - await this.sleep(500); - - // Проверяем что прокрутили вниз - const isAtBottomNow = controller.scrollManager.isNearBottom(); - this.assert(isAtBottomNow, 'После клика по индикатору должны быть внизу'); - - // Проверяем что счётчик сброшен - this.assertEqual(controller._newMessagesCount, 0, 'Счётчик должен быть сброшен'); - - // Проверяем что индикатор скрыт - const isHiddenNow = indicator.style.display === 'none'; - this.assert(isHiddenNow, 'Индикатор должен быть скрыт'); - } - - // Тест 5: Ручной скролл вниз - скрытие индикатора - console.log('\n--- Тест 5: Ручной скролл вниз ---'); - - // Прокручиваем вверх - container.scrollTop = 50; - controller._newMessagesCount = 5; // Устанавливаем счётчик вручную - - if (indicator) { - indicator.style.display = 'flex'; - const badge = indicator.querySelector('.badge'); - if (badge) badge.textContent = '5'; - } - - await this.sleep(100); - - const countBefore = controller._newMessagesCount; - this.assertEqual(countBefore, 5, 'Счётчик установлен корректно'); - - // Прокручиваем вниз вручную (programmatic scroll) - controller.scrollManager.scrollToBottom({ instant: false, force: true }); - - // Ждем скролл event + обработку - await this.sleep(800); - - // Проверяем что индикатор скрылся - this.assertEqual(controller._newMessagesCount, 0, 'Счётчик должен сброситься при ручном скролле'); - - // Тест 6: Оптимистичное сообщение - скроллим - console.log('\n--- Тест 6: Оптимистичное сообщение ---'); - - // Прокручиваем вверх - container.scrollTop = 80; - await this.sleep(100); - - const notAtBottomBefore = !controller.scrollManager.isNearBottom(); - this.assert(notAtBottomBefore, 'Должны быть не внизу перед оптимистичным сообщением'); - - // Добавляем оптимистичное сообщение (своё) - controller.store.addMessage({ - id: 'temp_123', - chat_id: 1, - author_id: currentUserId, - author_name: 'Me', - content: 'Optimistic message', - created_ts: Date.now() + 33000, - created: new Date(Date.now() + 33000).toISOString(), - is_optimistic: true - }, true); // true = optimistic - - await this.sleep(500); - - const atBottomAfter = controller.scrollManager.isNearBottom(); - this.assert(atBottomAfter, 'Оптимистичное сообщение должно скроллить вниз'); - - // Тест 7: Методы индикатора существуют - console.log('\n--- Тест 7: API индикатора ---'); - - this.assert(typeof controller._showNewMessagesIndicator === 'function', '_showNewMessagesIndicator должен быть функцией'); - this.assert(typeof controller._hideNewMessagesIndicator === 'function', '_hideNewMessagesIndicator должен быть функцией'); - this.assert(typeof controller._findOrCreateNewMessagesButton === 'function', '_findOrCreateNewMessagesButton должен быть функцией'); - this.assert(typeof controller._initScrollWatcher === 'function', '_initScrollWatcher должен быть функцией'); - - // Тест 8: Счётчик новых сообщений инкрементируется - console.log('\n--- Тест 8: Счётчик сообщений ---'); - - container.scrollTop = 100; - controller._newMessagesCount = 0; - await this.sleep(100); - - // Добавляем 3 чужих сообщения подряд - for (let i = 0; i < 3; i++) { - controller.store.addMessage({ - id: 200 + i, - chat_id: 1, - author_id: 2, - author_name: 'Other User', - content: `Batch message ${i}`, - created_ts: Date.now() + 40000 + i * 100, - created: new Date(Date.now() + 40000 + i * 100).toISOString() - }); - await this.sleep(50); - } - - await this.sleep(500); - - this.assertEqual(controller._newMessagesCount, 3, 'Счётчик должен показывать 3 новых сообщения'); - - // Cleanup - controller.destroy(); - if (indicator && indicator.parentElement) { - indicator.parentElement.removeChild(indicator); - } - document.body.removeChild(container); - - this.printResults(); - return this.results; - } - - // ==================== Публичное API ==================== - - async runAll() { - console.log('\n' + '='.repeat(60)); - console.log('🚀 Запуск всех тестов ChatControllerV2'); - console.log('='.repeat(60)); - - const basicResults = await this.testBasicFunctionalityV2(); - const smartAutoscrollResults = await this.testSmartAutoscrollV2(); - - const totalPassed = basicResults.passed + smartAutoscrollResults.passed; - const totalFailed = basicResults.failed + smartAutoscrollResults.failed; - const totalTests = basicResults.total + smartAutoscrollResults.total; - const totalRate = totalTests > 0 ? parseFloat(((totalPassed / totalTests) * 100).toFixed(1)) : 0; - - console.log('\n' + '='.repeat(60)); - console.log('📊 ИТОГОВЫЕ РЕЗУЛЬТАТЫ V2:'); - console.log(` ✅ Успешных: ${totalPassed}`); - console.log(` ❌ Провалено: ${totalFailed}`); - console.log(` 📈 Процент: ${totalRate}%`); - console.log('='.repeat(60) + '\n'); - - return { - basic: basicResults, - smartAutoscroll: smartAutoscrollResults, - total: { passed: totalPassed, failed: totalFailed, total: totalTests, passRate: totalRate } - }; - } -} - -// Создаем глобальный экземпляр -const chatTestsV2 = new ChatTestSuiteV2(); - -// Экспортируем для использования в модулях -export default chatTestsV2; - -// Добавляем в window для вызова из консоли -window.ChatTestsV2 = { - runAll: () => chatTestsV2.runAll(), - testBasicFunctionalityV2: () => chatTestsV2.testBasicFunctionalityV2(), - testSmartAutoscrollV2: () => chatTestsV2.testSmartAutoscrollV2() -}; - -console.log('✅ ChatTestsV2 loaded!'); -console.log('💡 Запуск из консоли:'); -console.log(' window.ChatTestsV2.runAll() - Все тесты V2'); -console.log(' window.ChatTestsV2.testBasicFunctionalityV2() - Базовая функциональность'); -console.log(' window.ChatTestsV2.testSmartAutoscrollV2() - Умный автоскролл'); -console.log(''); -console.log('⚠️ ПРИМЕЧАНИЕ: Это сокращенная версия тестов для V2.'); -console.log(' Полная версия (11 тестов, 1732 строки) доступна в chatTests.js'); -console.log(' V2 тесты фокусируются на новых фичах: smart autoscroll, batch ops, retry logic');