import { test, expect } from '@playwright/test'; /** * LiveComponents: Real-time Updates Tests * * Tests SSE (Server-Sent Events) and real-time component updates */ test.describe('LiveComponents Real-time Updates', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); }); test('should establish SSE connection (if present)', async ({ page }) => { // Listen for EventSource connections const sseConnections: string[] = []; page.on('request', (request) => { if (request.url().includes('/sse') || request.headers()['accept']?.includes('text/event-stream')) { sseConnections.push(request.url()); } }); // Wait for potential SSE connections await page.waitForTimeout(2000); // If SSE is implemented, we should have connections // This is informational - not all pages may have SSE console.log('SSE Connections:', sseConnections); }); test('should handle component updates via HTMX', async ({ page }) => { // Look for HTMX-enabled elements const htmxElements = page.locator('[hx-get], [hx-post], [hx-trigger]'); const count = await htmxElements.count(); if (count === 0) { test.skip(); return; } // Track HTMX requests let htmxRequestMade = false; page.on('request', (request) => { const headers = request.headers(); if (headers['hx-request'] === 'true') { htmxRequestMade = true; } }); // Trigger first HTMX element const firstElement = htmxElements.first(); await firstElement.click(); // Wait for request await page.waitForTimeout(1000); // HTMX request should have been made expect(htmxRequestMade).toBe(true); }); test('should update DOM without full page reload', async ({ page }) => { const htmxElements = page.locator('[hx-get], [hx-post]'); const count = await htmxElements.count(); if (count === 0) { test.skip(); return; } // Track page loads let pageReloaded = false; page.on('load', () => { pageReloaded = true; }); // Get initial page state const initialUrl = page.url(); // Trigger HTMX action await htmxElements.first().click(); await page.waitForTimeout(1500); // URL should not change (unless it's a navigation) const currentUrl = page.url(); // Page should not have reloaded for simple updates // (this is a soft check as some actions might navigate) if (currentUrl === initialUrl) { expect(pageReloaded).toBe(false); } }); test('should handle loading states during updates', async ({ page }) => { // Look for loading indicators const loadingIndicators = page.locator('[hx-indicator], .htmx-request, [data-loading]'); // Trigger update if HTMX elements exist const htmxElements = page.locator('[hx-get], [hx-post]'); const count = await htmxElements.count(); if (count === 0) { test.skip(); return; } // Trigger action and check for loading state await htmxElements.first().click(); // During request, loading indicator might be visible await page.waitForTimeout(100); // After request, loading should be hidden await page.waitForTimeout(2000); const loadingCount = await loadingIndicators.count(); console.log('Loading indicators found:', loadingCount); }); test('should preserve form data during partial updates', async ({ page }) => { const forms = page.locator('form'); const formCount = await forms.count(); if (formCount === 0) { test.skip(); return; } const form = forms.first(); const inputs = form.locator('input[type="text"], input[type="email"]'); const inputCount = await inputs.count(); if (inputCount === 0) { test.skip(); return; } // Fill some data const testValue = 'test-value-123'; await inputs.first().fill(testValue); // Wait a bit await page.waitForTimeout(500); // Value should still be present const currentValue = await inputs.first().inputValue(); expect(currentValue).toBe(testValue); }); test('should handle WebSocket connections (if present)', async ({ page }) => { // Track WebSocket connections const wsConnections: string[] = []; page.on('websocket', (ws) => { wsConnections.push(ws.url()); ws.on('framesent', (event) => { console.log('WebSocket sent:', event.payload); }); ws.on('framereceived', (event) => { console.log('WebSocket received:', event.payload); }); }); // Wait for potential WebSocket connections await page.waitForTimeout(2000); // Log WebSocket connections (informational) console.log('WebSocket Connections:', wsConnections); // If WebSocket is used, we should have connections if (wsConnections.length > 0) { expect(wsConnections.length).toBeGreaterThan(0); } }); test('should handle connection errors gracefully', async ({ page }) => { // Monitor console errors const errors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') { errors.push(msg.text()); } }); // Navigate and wait await page.goto('/'); await page.waitForTimeout(3000); // Filter out known/expected errors const criticalErrors = errors.filter( (error) => !error.includes('favicon') && !error.includes('DevTools') ); // Should not have critical JavaScript errors expect(criticalErrors.length).toBe(0); }); });