/** * Test for toast notifications when saving settings * * This test verifies that toast notifications appear when checkbox settings are changed. */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); const TEST_CONFIG = { TIMEOUTS: { SETTINGS_LOAD: 5000, TOAST_APPEAR: 5000, AJAX_SAVE: 3000, NAVIGATION: 30000 }, URLS: { BASE: 'http://localhost:5000', SETTINGS: 'http://localhost:5000/settings' }, SELECTORS: { CHECKBOX: 'input[type="checkbox"].ldr-settings-checkbox', TOAST_CONTAINER: '#toast-container', TOAST: '.toast, .ldr-toast', TOAST_VISIBLE: '.toast.visible, .ldr-toast.visible' } }; (async () => { let browser; let page; const consoleLogs = []; const networkRequests = []; const networkResponses = []; try { console.log('=== Testing Toast Notifications ===\n'); browser = await puppeteer.launch(getPuppeteerLaunchOptions()); page = await browser.newPage(); await page.setViewport({ width: 1920, height: 1080 }); // Capture ONLY toast-related console logs page.on('console', msg => { const text = msg.text(); const type = msg.type(); // Only log toast-related items if (text.includes('showMessage') || text.includes('[Notification]') || text.includes('toast') || text.includes('Toast') || text.includes('[DEBUG]') || text.includes('[submitSettingsData]') || text.includes('originalSettings cache')) { consoleLogs.push({ type, text, timestamp: Date.now() }); console.log(`[BROWSER] ${text}`); } }); // Capture page errors page.on('pageerror', error => { console.error(`[PAGE ERROR] ${error.message}`); consoleLogs.push({ type: 'pageerror', text: error.message, timestamp: Date.now() }); }); // Capture network requests page.on('request', req => { if (req.url().includes('save_all_settings') || req.url().includes('settings')) { networkRequests.push({ url: req.url(), method: req.method(), timestamp: Date.now() }); } }); // Capture network responses page.on('response', async resp => { if (resp.url().includes('save_all_settings')) { try { const text = await resp.text(); networkResponses.push({ url: resp.url(), status: resp.status(), body: text.substring(0, 500), timestamp: Date.now() }); console.log(`[NETWORK] Response from save_all_settings: ${resp.status()}`); } catch { // Response already consumed } } }); // Authenticate with a consistent user console.log('šŸ” Authenticating...'); const authHelper = new AuthHelper(page); const testUser = 'toast_test_user'; const testPassword = 'T3st!Secure#2024$LDR'; // Try to register/login this specific user try { await authHelper.ensureAuthenticated(testUser, testPassword); } catch { console.log('Initial auth failed, trying direct registration...'); try { await authHelper.register(testUser, testPassword); } catch { console.log('Registration failed (user may exist), trying login...'); await authHelper.login(testUser, testPassword); } } console.log('āœ… Authenticated\n'); // Navigate to settings console.log('šŸ“„ Navigating to settings page...'); await page.goto(TEST_CONFIG.URLS.SETTINGS, { waitUntil: 'domcontentloaded', timeout: TEST_CONFIG.TIMEOUTS.NAVIGATION }); // Check current URL let currentUrl = page.url(); console.log(`Current URL: ${currentUrl}`); // Check if we got redirected to login - try login with our test user if (currentUrl.includes('/auth/login')) { console.log('āš ļø Redirected to login, attempting login...'); await authHelper.login(testUser, testPassword); await page.goto(TEST_CONFIG.URLS.SETTINGS, { waitUntil: 'domcontentloaded', timeout: TEST_CONFIG.TIMEOUTS.NAVIGATION }); currentUrl = page.url(); console.log(`After re-login URL: ${currentUrl}`); } // Wait longer for settings to load (they load dynamically) console.log('Waiting for settings page to fully load...'); await new Promise(resolve => setTimeout(resolve, 5000)); // Debug: check page content const pageDebug = await page.evaluate(() => ({ title: document.title, url: window.location.href, allCheckboxes: document.querySelectorAll('input[type="checkbox"]').length, ldrCheckboxes: document.querySelectorAll('input[type="checkbox"].ldr-settings-checkbox').length, anyInputs: document.querySelectorAll('input').length, settingsContent: !!document.getElementById('settings-content'), bodyText: document.body.textContent.substring(0, 500) })); console.log('Page debug info:', JSON.stringify(pageDebug, null, 2)); // Try different selectors let checkboxSelector = TEST_CONFIG.SELECTORS.CHECKBOX; let checkboxes = await page.$$(checkboxSelector); if (checkboxes.length === 0) { console.log('No ldr-settings-checkbox found, trying any checkbox...'); checkboxSelector = 'input[type="checkbox"]'; checkboxes = await page.$$(checkboxSelector); } if (checkboxes.length === 0) { // Take screenshot for debugging await page.screenshot({ path: '/tmp/settings_no_checkboxes.png', fullPage: true }); console.log('šŸ“ø Screenshot saved to /tmp/settings_no_checkboxes.png'); throw new Error(`No checkboxes found. Page has ${pageDebug.anyInputs} inputs total.`); } console.log(`āœ… Settings page loaded - found ${checkboxes.length} checkboxes\n`); // Check if window.ui is available const uiCheck = await page.evaluate(() => { return { windowUi: !!window.ui, showMessage: !!(window.ui && window.ui.showMessage), toastContainer: !!document.getElementById('toast-container') }; }); console.log('UI availability check:', JSON.stringify(uiCheck, null, 2)); // Get first checkbox info const firstCheckbox = checkboxes[0]; const checkboxInfo = await firstCheckbox.evaluate(el => ({ name: el.name, checked: el.checked, id: el.id })); console.log('Testing checkbox:', JSON.stringify(checkboxInfo, null, 2)); // Clear console logs before toggling consoleLogs.length = 0; networkRequests.length = 0; networkResponses.length = 0; console.log('\nšŸ”„ Toggling checkbox...'); const initialState = checkboxInfo.checked; await firstCheckbox.click(); // Wait for AJAX save to complete console.log('ā³ Waiting for AJAX save...'); await new Promise(resolve => setTimeout(resolve, TEST_CONFIG.TIMEOUTS.AJAX_SAVE)); // Check checkbox state changed const newState = await firstCheckbox.evaluate(el => el.checked); console.log(`Checkbox state: ${initialState} → ${newState}`); // Check for toast in DOM const toastCheck = await page.evaluate(() => { const container = document.getElementById('toast-container'); const toasts = document.querySelectorAll('.toast, .ldr-toast'); const visibleToasts = document.querySelectorAll('.toast.visible, .ldr-toast.visible'); let containerStyles = null; if (container) { const computed = window.getComputedStyle(container); containerStyles = { position: computed.position, top: computed.top, right: computed.right, zIndex: computed.zIndex, display: computed.display, visibility: computed.visibility }; } const toastInfo = []; toasts.forEach((t, i) => { const computed = window.getComputedStyle(t); toastInfo.push({ index: i, className: t.className, innerHTML: t.innerHTML.substring(0, 100), opacity: computed.opacity, transform: computed.transform, display: computed.display, visibility: computed.visibility }); }); return { containerExists: !!container, containerStyles, totalToasts: toasts.length, visibleToasts: visibleToasts.length, toastInfo }; }); console.log('\nšŸ“‹ Toast DOM state:'); console.log(JSON.stringify(toastCheck, null, 2)); // Wait a bit more for toast animation await new Promise(resolve => setTimeout(resolve, 500)); // Check again for visible toasts const finalToastCheck = await page.evaluate(() => { const visibleToasts = document.querySelectorAll('.toast.visible, .ldr-toast.visible'); return { visibleToastsCount: visibleToasts.length, visibleToastsHTML: Array.from(visibleToasts).map(t => t.innerHTML.substring(0, 100)) }; }); console.log('\nšŸ“‹ Final toast check:'); console.log(JSON.stringify(finalToastCheck, null, 2)); // Print relevant console logs console.log('\nšŸ“ Relevant browser console logs:'); const relevantLogs = consoleLogs.filter(log => log.text.includes('showMessage') || log.text.includes('Notification') || log.text.includes('toast') || log.text.includes('DEBUG') || log.text.includes('submitSettingsData') || log.text.includes('originalSettings') || log.type === 'error' ); if (relevantLogs.length === 0) { console.log(' (No relevant logs captured - this is suspicious!)'); } else { relevantLogs.forEach(log => { console.log(` [${log.type}] ${log.text}`); }); } // Print network activity console.log('\n🌐 Network activity:'); console.log(` Requests: ${networkRequests.length}`); networkRequests.forEach(req => { console.log(` ${req.method} ${req.url}`); }); console.log(` Responses: ${networkResponses.length}`); networkResponses.forEach(resp => { console.log(` ${resp.status} ${resp.url}`); if (resp.body) { console.log(` Body: ${resp.body.substring(0, 200)}`); } }); // Take screenshot const screenshotPath = '/tmp/toast_test_screenshot.png'; await page.screenshot({ path: screenshotPath, fullPage: false }); console.log(`\nšŸ“ø Screenshot saved to: ${screenshotPath}`); // Toggle back to original state console.log('\nšŸ”„ Restoring original checkbox state...'); await firstCheckbox.click(); await new Promise(resolve => setTimeout(resolve, 1000)); // Final verdict console.log('\n' + '='.repeat(60)); if (finalToastCheck.visibleToastsCount > 0) { console.log('āœ… SUCCESS: Toast notification appeared!'); } else if (toastCheck.totalToasts > 0) { console.log('āš ļø PARTIAL: Toast created but not visible (CSS issue)'); } else if (networkResponses.length > 0 && networkResponses[0].status === 200) { console.log('āŒ ISSUE: Save succeeded but no toast was created'); console.log(' Check if notification code is being reached'); } else { console.log('āŒ ISSUE: No save request or toast notification detected'); } console.log('='.repeat(60)); } catch (error) { console.error('\nāŒ Test failed:', error.message); console.error(error.stack); // Print all console logs on failure console.log('\nšŸ“ All browser console logs:'); consoleLogs.forEach(log => { console.log(` [${log.type}] ${log.text}`); }); process.exit(1); } finally { if (browser) { await browser.close(); } } })();