#!/usr/bin/env bun /** * PDF TOAST NOTIFICATIONS TEST * ============================= * Tests the toast notification system for PDF downloads * Validates both modal overlay and toast notification flows */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; async function testPDFToastNotifications() { console.log('๐Ÿงช PDF TOAST NOTIFICATIONS TEST\n'); console.log('='.repeat(70)); const browser = await chromium.launch({ headless: false }); const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); const errors = []; const testResults = []; // Track console errors page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); console.log(`โŒ CONSOLE ERROR: ${msg.text()}`); } }); try { // ===================================================================== // SETUP: Navigate to page // ===================================================================== console.log('\n๐Ÿ“ STEP 1: Navigate to CV page'); await page.goto(URL); await page.waitForLoadState('networkidle'); console.log('โœ… Page loaded successfully'); // ===================================================================== // TEST 1: Toast Elements Exist // ===================================================================== console.log('\n๐Ÿ“ STEP 2: Verify toast elements exist in DOM'); const pdfToast = await page.locator('#pdf-toast'); const toastExists = await pdfToast.count() > 0; testResults.push({ test: 'PDF toast element exists', passed: toastExists }); console.log(toastExists ? 'โœ… PDF toast element found' : 'โŒ PDF toast element not found'); const toastIcon = await page.locator('#pdf-toast-icon'); const iconExists = await toastIcon.count() > 0; testResults.push({ test: 'Toast icon element exists', passed: iconExists }); console.log(iconExists ? 'โœ… Toast icon element found' : 'โŒ Toast icon not found'); const toastTitle = await page.locator('#pdf-toast-title'); const titleExists = await toastTitle.count() > 0; testResults.push({ test: 'Toast title element exists', passed: titleExists }); console.log(titleExists ? 'โœ… Toast title element found' : 'โŒ Toast title not found'); const toastMessage = await page.locator('#pdf-toast-message'); const messageExists = await toastMessage.count() > 0; testResults.push({ test: 'Toast message element exists', passed: messageExists }); console.log(messageExists ? 'โœ… Toast message element found' : 'โŒ Toast message not found'); const progressBar = await page.locator('#pdf-toast-progress'); const progressExists = await progressBar.count() > 0; testResults.push({ test: 'Toast progress bar exists', passed: progressExists }); console.log(progressExists ? 'โœ… Toast progress bar found' : 'โŒ Progress bar not found'); // ===================================================================== // TEST 2: Toast Initially Hidden // ===================================================================== console.log('\n๐Ÿ“ STEP 3: Verify toast is initially hidden'); const isInitiallyHidden = !(await pdfToast.evaluate(el => el.classList.contains('show'))); testResults.push({ test: 'Toast initially hidden', passed: isInitiallyHidden }); console.log(isInitiallyHidden ? 'โœ… Toast is hidden initially' : 'โŒ Toast is visible (should be hidden)'); // ===================================================================== // TEST 3: JavaScript Functions Available // ===================================================================== console.log('\n๐Ÿ“ STEP 4: Verify JavaScript toast functions exist'); const showPDFToastExists = await page.evaluate(() => typeof window.showPDFToast === 'function'); testResults.push({ test: 'showPDFToast() function exists', passed: showPDFToastExists }); console.log(showPDFToastExists ? 'โœ… showPDFToast() function available' : 'โŒ showPDFToast() not found'); const hidePDFToastExists = await page.evaluate(() => typeof window.hidePDFToast === 'function'); testResults.push({ test: 'hidePDFToast() function exists', passed: hidePDFToastExists }); console.log(hidePDFToastExists ? 'โœ… hidePDFToast() function available' : 'โŒ hidePDFToast() not found'); // ===================================================================== // TEST 4: Manual Toast Trigger Test // ===================================================================== console.log('\n๐Ÿ“ STEP 5: Test manual toast trigger'); await page.evaluate(() => { window.showPDFToast({ icon: '๐Ÿงช', title: 'Test Toast', message: 'This is a test notification', duration: 3000 }); }); await page.waitForTimeout(500); const isToastVisible = await pdfToast.evaluate(el => el.classList.contains('show')); testResults.push({ test: 'Toast appears when showPDFToast() called', passed: isToastVisible }); console.log(isToastVisible ? 'โœ… Toast appears correctly' : 'โŒ Toast did not appear'); const toastIconText = await toastIcon.textContent(); const iconCorrect = toastIconText === '๐Ÿงช'; testResults.push({ test: 'Toast icon updates correctly', passed: iconCorrect }); console.log(iconCorrect ? `โœ… Toast icon: "${toastIconText}"` : `โŒ Icon incorrect: "${toastIconText}"`); const toastTitleText = await toastTitle.textContent(); const titleCorrect = toastTitleText === 'Test Toast'; testResults.push({ test: 'Toast title updates correctly', passed: titleCorrect }); console.log(titleCorrect ? `โœ… Toast title: "${toastTitleText}"` : `โŒ Title incorrect: "${toastTitleText}"`); // Wait for auto-hide console.log('โณ Waiting 3.5 seconds for toast auto-hide...'); await page.waitForTimeout(3500); const isToastHidden = !(await pdfToast.evaluate(el => el.classList.contains('show'))); testResults.push({ test: 'Toast auto-hides after duration', passed: isToastHidden }); console.log(isToastHidden ? 'โœ… Toast auto-hid correctly' : 'โŒ Toast still visible (should auto-hide)'); // ===================================================================== // TEST 5: PDF Modal Integration - Scenario A (Stay in Modal) // ===================================================================== console.log('\n๐Ÿ“ STEP 6: Test PDF modal integration (Scenario A: Stay in Modal)'); // Open PDF modal const pdfButton = await page.locator('button[onclick*="openPdfModal"], .download-btn').first(); await pdfButton.click(); await page.waitForTimeout(500); const modal = await page.locator('#pdf-modal[open]'); const isModalOpen = await modal.count() > 0; testResults.push({ test: 'PDF modal opens', passed: isModalOpen }); console.log(isModalOpen ? 'โœ… PDF modal opened' : 'โŒ PDF modal did not open'); // Select Default CV const defaultCard = await page.locator('.pdf-option-card[data-cv-format="default"]'); await defaultCard.click(); await page.waitForTimeout(300); // Click download but DON'T close modal const downloadBtn = await page.locator('#pdf-download-btn'); await downloadBtn.click(); await page.waitForTimeout(300); // Check overlay appears const overlay = await page.locator('#pdf-loading-overlay'); const isOverlayActive = await overlay.evaluate(el => el.classList.contains('active')); testResults.push({ test: 'Loading overlay appears on download', passed: isOverlayActive }); console.log(isOverlayActive ? 'โœ… Loading overlay is active' : 'โŒ Loading overlay did not activate'); // Toast should NOT appear yet (modal still open) await page.waitForTimeout(500); const toastShouldNotShow = !(await pdfToast.evaluate(el => el.classList.contains('show'))); testResults.push({ test: 'Toast does not appear when modal stays open', passed: toastShouldNotShow }); console.log(toastShouldNotShow ? 'โœ… Toast correctly hidden (modal open)' : 'โš ๏ธ Toast appeared (should wait for modal close)'); // Wait for modal to close automatically console.log('โณ Waiting 5 seconds for modal auto-close...'); await page.waitForTimeout(5000); const isModalClosed = await page.locator('#pdf-modal[open]').count() === 0; testResults.push({ test: 'Modal auto-closes after download', passed: isModalClosed }); console.log(isModalClosed ? 'โœ… Modal closed automatically' : 'โŒ Modal still open (should auto-close)'); // ===================================================================== // TEST 6: PDF Modal Integration - Scenario B (Close Modal Early) // ===================================================================== console.log('\n๐Ÿ“ STEP 7: Test PDF modal integration (Scenario B: Close Modal Early)'); // Re-open PDF modal await pdfButton.click(); await page.waitForTimeout(500); // Select Short CV (faster for testing) const shortCard = await page.locator('.pdf-option-card[data-cv-format="short"]'); await shortCard.click(); await page.waitForTimeout(300); // Click download await downloadBtn.click(); await page.waitForTimeout(300); // Immediately close modal (ESC key) await page.keyboard.press('Escape'); await page.waitForTimeout(500); // Toast should NOT appear (behavior changed - modal only, no toast) const toastDoesNotAppear = !(await pdfToast.evaluate(el => el.classList.contains('show'))); testResults.push({ test: 'Toast does not appear when modal closed early (new behavior)', passed: toastDoesNotAppear }); console.log(toastDoesNotAppear ? 'โœ… Toast correctly hidden (no toast on modal close)' : 'โŒ Toast appeared (should not show)'); // Skip toast content validation since toast should not appear if (false) { // Previously: if (toastAppearsOnClose) // Check toast content const currentIcon = await toastIcon.textContent(); const iconIsPreparing = currentIcon === '๐Ÿ“ฅ'; testResults.push({ test: 'Toast shows preparing icon (๐Ÿ“ฅ)', passed: iconIsPreparing }); console.log(iconIsPreparing ? `โœ… Toast icon: "${currentIcon}"` : `โš ๏ธ Unexpected icon: "${currentIcon}"`); const currentTitle = await toastTitle.textContent(); const titleIsPreparing = currentTitle.includes('Preparing') || currentTitle.includes('Preparando'); testResults.push({ test: 'Toast shows preparing title', passed: titleIsPreparing }); console.log(titleIsPreparing ? `โœ… Toast title: "${currentTitle}"` : `โš ๏ธ Title: "${currentTitle}"`); // Check progress bar animation const hasProgressAnimation = await page.evaluate(() => { const bar = document.getElementById('pdf-toast-progress'); const style = window.getComputedStyle(bar); return style.animation && style.animation !== 'none'; }); testResults.push({ test: 'Progress bar animates', passed: hasProgressAnimation }); console.log(hasProgressAnimation ? 'โœ… Progress bar is animating' : 'โŒ Progress bar not animated'); // Wait for toast to update to success console.log('โณ Waiting 4 seconds for toast to update to success...'); await page.waitForTimeout(4000); const finalIcon = await toastIcon.textContent(); const iconIsSuccess = finalIcon === 'โœ…'; testResults.push({ test: 'Toast updates to success icon (โœ…)', passed: iconIsSuccess }); console.log(iconIsSuccess ? `โœ… Toast updated: "${finalIcon}"` : `โš ๏ธ Final icon: "${finalIcon}"`); const finalTitle = await toastTitle.textContent(); const titleIsReady = finalTitle.includes('Ready') || finalTitle.includes('Listo'); testResults.push({ test: 'Toast shows ready title', passed: titleIsReady }); console.log(titleIsReady ? `โœ… Toast title: "${finalTitle}"` : `โš ๏ธ Title: "${finalTitle}"`); // Wait for final auto-dismiss console.log('โณ Waiting 4 seconds for final toast auto-dismiss...'); await page.waitForTimeout(4000); const finallyHidden = !(await pdfToast.evaluate(el => el.classList.contains('show'))); testResults.push({ test: 'Toast auto-dismisses after success', passed: finallyHidden }); console.log(finallyHidden ? 'โœ… Toast dismissed successfully' : 'โš ๏ธ Toast still visible'); } // ===================================================================== // TEST 7: CSS Animations // ===================================================================== console.log('\n๐Ÿ“ STEP 8: Verify CSS animations are defined'); const hasSlideInAnimation = await page.evaluate(() => { const sheet = Array.from(document.styleSheets).find(s => s.href?.includes('toasts.css')); if (!sheet) return false; const rules = Array.from(sheet.cssRules || []); return rules.some(r => r.name === 'toastSlideIn'); }); testResults.push({ test: 'toastSlideIn animation defined', passed: hasSlideInAnimation }); console.log(hasSlideInAnimation ? 'โœ… toastSlideIn animation exists' : 'โŒ Animation not found'); const hasLifecycleAnimation = await page.evaluate(() => { const sheet = Array.from(document.styleSheets).find(s => s.href?.includes('toasts.css')); if (!sheet) return false; const rules = Array.from(sheet.cssRules || []); return rules.some(r => r.name === 'toastLifecycle'); }); testResults.push({ test: 'toastLifecycle animation defined', passed: hasLifecycleAnimation }); console.log(hasLifecycleAnimation ? 'โœ… toastLifecycle animation exists' : 'โŒ Animation not found'); const hasProgressAnimation = await page.evaluate(() => { const sheet = Array.from(document.styleSheets).find(s => s.href?.includes('toasts.css')); if (!sheet) return false; const rules = Array.from(sheet.cssRules || []); return rules.some(r => r.name === 'progressShrink'); }); testResults.push({ test: 'progressShrink animation defined', passed: hasProgressAnimation }); console.log(hasProgressAnimation ? 'โœ… progressShrink animation exists' : 'โŒ Animation not found'); } catch (error) { console.error('\nโŒ Test execution error:', error.message); errors.push(error.message); } // ===================================================================== // SUMMARY // ===================================================================== console.log("\n" + "=".repeat(70)); console.log("๐Ÿ“Š TEST SUMMARY\n"); const passedTests = testResults.filter(t => t.passed).length; const totalTests = testResults.length; testResults.forEach(result => { const icon = result.passed ? 'โœ…' : 'โŒ'; console.log(`${icon} ${result.test}`); }); console.log(`\n๐Ÿ“ˆ Results: ${passedTests}/${totalTests} tests passed`); if (errors.length > 0) { console.log(`\nโš ๏ธ ${errors.length} console errors detected`); errors.forEach(err => console.log(` - ${err}`)); } if (passedTests === totalTests && errors.length === 0) { console.log('\n๐ŸŽ‰ ALL TESTS PASSED! Toast notification system works perfectly!'); } else if (passedTests >= totalTests * 0.9) { console.log('\nโœ… MOST TESTS PASSED! Minor issues detected.'); } else { console.log('\nโš ๏ธ SOME TESTS FAILED - Review needed'); } console.log("\n" + "=".repeat(70)); console.log("\n๐Ÿ’ก Browser will stay open for manual inspection."); console.log(" You can:"); console.log(" 1. Test both scenarios manually"); console.log(" 2. Try different PDF formats (Short, Default, Long)"); console.log(" 3. Test close button on toast"); console.log(" 4. Check responsive behavior (resize window)"); console.log(" 5. Verify progress bar animations"); console.log("\nPress Ctrl+C when done.\n"); await new Promise(() => {}); // Keep browser open } await testPDFToastNotifications();