404 lines
17 KiB
JavaScript
404 lines
17 KiB
JavaScript
|
|
#!/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 appear now
|
||
|
|
const toastAppearsOnClose = await pdfToast.evaluate(el => el.classList.contains('show'));
|
||
|
|
testResults.push({
|
||
|
|
test: 'Toast appears when modal closed early',
|
||
|
|
passed: toastAppearsOnClose
|
||
|
|
});
|
||
|
|
console.log(toastAppearsOnClose ? '✅ Toast appeared after modal close' : '❌ Toast did not appear');
|
||
|
|
|
||
|
|
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();
|