/** * Authentication Flow Test * Tests registration, login, and logout functionality * CI-compatible: Works in both local and CI environments */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); const fs = require('fs'); const path = require('path'); // NAVIGATION NOTE: Using 'domcontentloaded' instead of 'networkidle2' for page.goto() // because networkidle2 waits for no network activity for 500ms, but WebSocket // connections and background polling keep the network active, causing infinite hangs. // See: test_login_validation.js and auth_helper.js for detailed explanation. async function testAuthFlow() { const isCI = !!process.env.CI; console.log(`๐Ÿงช Running authentication flow test (CI mode: ${isCI})`); // Create screenshots directory if it doesn't exist const screenshotsDir = path.join(__dirname, 'screenshots'); if (!fs.existsSync(screenshotsDir)) { fs.mkdirSync(screenshotsDir, { recursive: true }); } const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); const baseUrl = 'http://127.0.0.1:5000'; const authHelper = new AuthHelper(page, baseUrl); // Increase default timeout in CI if (isCI) { page.setDefaultTimeout(60000); page.setDefaultNavigationTimeout(60000); } // Test credentials - use a strong password that meets requirements const testUser = { username: `testuser_${Date.now()}`, // Unique username password: 'Test@Pass123!' // pragma: allowlist secret - meets complexity requirements }; console.log('๐Ÿงช Starting authentication flow test...\n'); try { // Test 1: Registration console.log('๐Ÿ“ Test 1: Registration'); await authHelper.register(testUser.username, testUser.password); // Verify we're logged in after registration console.log('Current URL after registration:', page.url()); // Wait a bit for any redirects to complete await new Promise(resolve => setTimeout(resolve, isCI ? 5000 : 2000)); const isLoggedIn = await authHelper.isLoggedIn(); if (isLoggedIn) { console.log('โœ… Registration successful and auto-logged in'); } else { // Debug: Check what's on the page const pageTitle = await page.title(); console.log('Page title:', pageTitle); // Check for any alerts or messages const alerts = await page.$$('.alert'); console.log('Number of alerts on page:', alerts.length); // In CI, try to navigate to home to verify login if (isCI) { console.log(' CI: Navigating to home to verify login status...'); await page.goto(baseUrl, { waitUntil: 'domcontentloaded', timeout: 30000 }); await new Promise(resolve => setTimeout(resolve, 2000)); const isLoggedInAfterNav = await authHelper.isLoggedIn(); if (!isLoggedInAfterNav) { throw new Error('Not logged in after registration'); } console.log('โœ… Registration successful (verified after navigation)'); } else { throw new Error('Not logged in after registration'); } } // Take screenshot of logged-in state (skip in CI โ€” diagnostic only) if (!isCI) { try { await page.screenshot({ path: path.join(screenshotsDir, 'after_registration.png') }); } catch (screenshotError) { console.log('โš ๏ธ Could not take screenshot:', screenshotError.message); } } // Test 2: Logout console.log('\n๐Ÿšช Test 2: Logout'); await authHelper.logout(); // Verify we're logged out const isLoggedOut = !(await authHelper.isLoggedIn()); if (isLoggedOut) { console.log('โœ… Logout successful'); } else { throw new Error('Still logged in after logout'); } // Test 3: Login console.log('\n๐Ÿ” Test 3: Login'); await authHelper.login(testUser.username, testUser.password); // Verify we're logged in const isLoggedInAgain = await authHelper.isLoggedIn(); if (isLoggedInAgain) { console.log('โœ… Login successful'); } else { throw new Error('Not logged in after login'); } // Test 4: Navigate to protected pages console.log('\n๐Ÿ“„ Test 4: Access protected pages'); const protectedPages = [ { url: '/', name: 'Home' }, { url: '/settings/', name: 'Settings' }, { url: '/metrics/', name: 'Metrics' }, { url: '/history/', name: 'History' } ]; for (const pageInfo of protectedPages) { await page.goto(`${baseUrl}${pageInfo.url}`, { waitUntil: 'domcontentloaded', timeout: 30000 }); // Check we didn't get redirected to login const currentUrl = page.url(); if (!currentUrl.includes('/auth/login')) { console.log(`โœ… Successfully accessed ${pageInfo.name} page`); } else { throw new Error(`Redirected to login when accessing ${pageInfo.name}`); } } // Test 5: Test with existing user (should login, not register) console.log('\n๐Ÿ”„ Test 5: Ensure authenticated with existing user'); await authHelper.ensureAuthenticated(testUser.username, testUser.password); console.log('โœ… Ensure authenticated works with existing user'); console.log('\n๐ŸŽ‰ All authentication tests passed!'); } catch (error) { console.error('\nโŒ Test failed:', error.message); // Take error screenshot (skip in CI โ€” diagnostic only) if (!isCI) { try { await page.screenshot({ path: path.join(screenshotsDir, 'auth_error.png') }); console.log('๐Ÿ“ธ Error screenshot saved'); } catch (screenshotError) { console.log('โš ๏ธ Could not take error screenshot:', screenshotError.message); } } // Check current URL for debugging try { console.log('Current URL:', page.url()); } catch { console.log('Could not get current URL'); } // Check for error messages on the page try { const errorText = await page.$eval('.alert-danger, .error-message', el => el.textContent); console.log('Error message on page:', errorText); } catch { // No error message found } await browser.close(); process.exit(1); } await browser.close(); console.log('\nโœ… Test completed successfully'); process.exit(0); } // Run the test testAuthFlow().catch(err => { console.error(err); process.exit(1); });