/** * Socket.IO Initialization Timing Test * * Verifies that Socket.IO does NOT block DOMContentLoaded. * This test was added after a fix for CI test timeouts where Socket.IO * initialization was blocking the 'domcontentloaded' event. * * Background: Socket.IO was causing 30-second navigation timeouts in CI because * it initialized immediately on script load, blocking DOMContentLoaded detection. * Fix: Defer initialization until after DOMContentLoaded fires. * * What this test verifies: * 1. Navigation to various pages completes within the timeout (no blocking) * 2. Navigation time is reasonable (<5 seconds per page) * 3. Where socket console messages are available, verify conditional initialization */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); // Short timeout - if Socket.IO blocks DOMContentLoaded, this will fail // The original bug caused 30+ second delays; 10 seconds gives ample margin const NAVIGATION_TIMEOUT = 10000; // Warning threshold - navigation should be much faster than this const NAVIGATION_WARNING_THRESHOLD = 5000; // Test pages - mix of pages where socket is/isn't needed const TEST_PAGES = [ { path: '/', name: 'Home', expectSocket: false }, { path: '/settings/', name: 'Settings', expectSocket: false }, { path: '/metrics/', name: 'Metrics', expectSocket: false }, { path: '/news/', name: 'News', expectSocket: false }, { path: '/research', name: 'Research', expectSocket: true }, ]; /** * Simple delay function */ function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function runTest() { const baseUrl = process.env.TEST_BASE_URL || 'http://127.0.0.1:5000'; let passed = 0; let failed = 0; let warnings = 0; console.log('='.repeat(60)); console.log('Socket.IO Initialization Timing Test'); console.log('='.repeat(60)); console.log(`Base URL: ${baseUrl}`); console.log(`Navigation timeout: ${NAVIGATION_TIMEOUT}ms`); console.log(`Warning threshold: ${NAVIGATION_WARNING_THRESHOLD}ms`); console.log(`Environment: ${process.env.CI ? 'CI' : 'Local'}`); console.log(''); const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); try { // Authenticate console.log('Authenticating...'); const auth = new AuthHelper(page, baseUrl); const testUser = process.env.CI ? { username: 'test_admin', password: 'testpass123' } : { username: 'testuser', password: 'T3st!Secure#2024$LDR' }; await auth.ensureAuthenticated(testUser.username, testUser.password); console.log('Authentication successful\n'); for (const testPage of TEST_PAGES) { const url = `${baseUrl}${testPage.path}`; console.log(`Testing: ${testPage.name} (${testPage.path})`); // Collect console messages for this page only const consoleMessages = []; const pageErrors = []; const consoleHandler = msg => consoleMessages.push(msg.text()); const errorHandler = err => pageErrors.push(err.message); page.on('console', consoleHandler); page.on('pageerror', errorHandler); try { // Primary test: navigation completes within timeout // This is the main thing we're testing - if Socket.IO blocks // DOMContentLoaded, this navigation will timeout const startTime = Date.now(); await page.goto(url, { waitUntil: 'domcontentloaded', timeout: NAVIGATION_TIMEOUT }); const navTime = Date.now() - startTime; // Test passed if navigation completed console.log(` \u2713 Navigation completed: ${navTime}ms`); passed++; // Warn if navigation was slow (but still passed) if (navTime > NAVIGATION_WARNING_THRESHOLD) { console.log(` \u26a0 WARNING: Navigation took ${navTime}ms (threshold: ${NAVIGATION_WARNING_THRESHOLD}ms)`); warnings++; } // Wait a bit for any async socket initialization await delay(300); // Secondary check: look for socket-related console messages // This helps verify the conditional initialization is working const socketMessages = consoleMessages.filter(m => m.toLowerCase().includes('socket') ); if (socketMessages.length > 0) { // Found socket messages - check for expected behavior const notNeededMsg = socketMessages.find(m => m.includes('not needed on this page') ); const initializedMsg = socketMessages.find(m => m.includes('Socket.IO initialized') ); if (!testPage.expectSocket && notNeededMsg) { console.log(` \u2713 Socket correctly skipped on ${testPage.name}`); } else if (testPage.expectSocket && initializedMsg) { console.log(` \u2713 Socket correctly initialized on ${testPage.name}`); } else if (testPage.expectSocket && !initializedMsg) { // Research page didn't show initialization message // This might be due to SafeLogger not being available yet console.log(` \u2139 Socket initialization message not captured`); } } // Note any page errors (informational only) if (pageErrors.length > 0) { console.log(` \u2139 Page errors: ${pageErrors.join(', ').substring(0, 100)}`); } } catch (error) { console.log(` \u2717 Navigation FAILED: ${error.message}`); if (error.message.includes('timeout')) { console.log(' *** Socket.IO may be blocking DOMContentLoaded ***'); } failed++; } finally { page.off('console', consoleHandler); page.off('pageerror', errorHandler); } console.log(''); } } finally { await browser.close(); } console.log('='.repeat(60)); console.log(`Results: ${passed} passed, ${failed} failed, ${warnings} warnings`); console.log(''); if (failed === 0) { console.log('SUCCESS: DOMContentLoaded is not blocked by Socket.IO'); } else { console.log('FAILURE: Some pages failed to load within timeout'); console.log('This may indicate Socket.IO is blocking DOMContentLoaded'); } console.log('='.repeat(60)); process.exit(failed > 0 ? 1 : 0); } runTest().catch(err => { console.error('Test error:', err); process.exit(1); });