import { test, expect } from '@playwright/test'; import { TEST_CONFIG, loginAsUser, setupErrorCapture } from '../utils/test-helpers'; /** * Chat E2E Test Suite (Audit 2.10) * * Couvre le flow critique du chat : * - Load chat page et UI (sidebar, channels, input) * - État déconnecté quand WebSocket indisponible * - Envoi de message quand WebSocket connecté (skip si non connecté) */ test.describe('Chat Flow', () => { let consoleErrors: string[] = []; let networkErrors: Array<{ url: string; status: number; method: string }> = []; test.beforeEach(async ({ page }) => { const errorCapture = setupErrorCapture(page); consoleErrors = errorCapture.consoleErrors; networkErrors = errorCapture.networkErrors; }); test('should load chat page and show UI', async ({ page }) => { console.log('🧪 [CHAT] Running: Load chat page and show UI'); await loginAsUser(page); await page.waitForTimeout(1000); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/chat`); await page.waitForLoadState('domcontentloaded'); await page.waitForTimeout(3000); const channelsHeader = page.locator('h3:has-text("Channels")'); const loadingState = page.locator('text=/ESTABLISHING UPLINK|Establishing/i'); const errorState = page.locator('text=/Connection Terminated|Access Restricted/i'); const messageInput = page.locator('input[placeholder*="Broadcast"], input[aria-label*="message" i]'); const hasChannels = await channelsHeader.isVisible({ timeout: 5000 }).catch(() => false); const hasLoading = await loadingState.isVisible({ timeout: 2000 }).catch(() => false); const hasError = await errorState.isVisible({ timeout: 2000 }).catch(() => false); const hasInput = await messageInput.isVisible({ timeout: 5000 }).catch(() => false); expect(hasChannels || hasLoading || hasError).toBeTruthy(); if (hasChannels) { expect(hasInput).toBeTruthy(); console.log('✅ [CHAT] Chat UI loaded with Channels and input'); } else if (hasLoading) { console.log('✅ [CHAT] Chat page showing loading state'); } else if (hasError) { console.log('✅ [CHAT] Chat page showing error state (expected when chat server unavailable)'); } }); test('should show disconnected state when WebSocket unavailable', async ({ page }) => { console.log('🧪 [CHAT] Running: Disconnected state indicator'); await loginAsUser(page); await page.waitForTimeout(1000); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/chat`); await page.waitForLoadState('domcontentloaded'); await page.waitForTimeout(5000); const channelsHeader = page.locator('h3:has-text("Channels")'); const statusDot = page.locator('.rounded-full.w-2.h-2, div.w-2.h-2.rounded-full'); const hasChannels = await channelsHeader.isVisible({ timeout: 10000 }).catch(() => false); test.skip(!hasChannels, 'Chat UI not loaded - may be loading or error state'); const hasStatusDot = await statusDot.first().isVisible({ timeout: 3000 }).catch(() => false); expect(hasStatusDot).toBeTruthy(); const dotClasses = await statusDot.first().getAttribute('class').catch(() => ''); const isDisconnected = dotClasses?.includes('bg-destructive') ?? false; const isConnected = dotClasses?.includes('bg-success') ?? false; expect(isDisconnected || isConnected).toBeTruthy(); console.log(`✅ [CHAT] Status indicator visible (connected: ${isConnected}, disconnected: ${isDisconnected})`); }); test('should send and display message when WebSocket connected', async ({ page }) => { console.log('🧪 [CHAT] Running: Send message (when WebSocket connected)'); await loginAsUser(page); await page.waitForTimeout(1000); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/chat`); await page.waitForLoadState('domcontentloaded'); await page.waitForTimeout(5000); const channelsHeader = page.locator('h3:has-text("Channels")'); const statusDot = page.locator('div.w-2.h-2.rounded-full, .rounded-full.w-2.h-2').first(); const conversationItem = page.locator('[data-testid="conversation-item"], button, [role="button"]').filter({ hasText: /general|default|lobby|channel/i }).first(); const messageInput = page.locator('input[placeholder*="Broadcast"], input[aria-label*="message" i]'); const sendButton = page.locator('button[type="submit"]').filter({ has: page.locator('svg') }); const hasChannels = await channelsHeader.isVisible({ timeout: 10000 }).catch(() => false); test.skip(!hasChannels, 'Chat UI not loaded'); const dotClasses = await statusDot.getAttribute('class').catch(() => ''); const isConnected = dotClasses?.includes('bg-success') ?? false; test.skip(!isConnected, 'WebSocket not connected - chat server may be unavailable'); const hasConversation = await conversationItem.isVisible({ timeout: 3000 }).catch(() => false); test.skip(!hasConversation, 'No conversation/channel available to send message'); await conversationItem.click(); await page.waitForTimeout(500); const testMessage = `E2E test ${Date.now()}`; await messageInput.fill(testMessage); await page.waitForTimeout(300); await sendButton.click(); await page.waitForTimeout(2000); const messageVisible = await page.locator(`text="${testMessage}"`).isVisible({ timeout: 5000 }).catch(() => false); expect(messageVisible).toBeTruthy(); console.log('✅ [CHAT] Message sent and displayed'); }); test.afterEach(async ({}, testInfo) => { console.log('\n📊 [CHAT] === Final Verifications ==='); if (consoleErrors.length > 0) { console.log(`🔴 [CHAT] Console errors (${consoleErrors.length}):`); consoleErrors.forEach((e) => console.log(` - ${e}`)); } if (networkErrors.length > 0) { console.log(`🔴 [CHAT] Network errors (${networkErrors.length}):`); networkErrors.forEach((e) => console.log(` - ${e.method} ${e.url}: ${e.status}`)); } }); });