Files
cv-site/tests/mjs/29-pdf-toast-notifications.test.mjs
T
juanatsap 531433f54c fix: Remove toast notification from PDF download flow
Simplified PDF download UX to use only the modal loading overlay,
removing the redundant toast notification that appeared when the modal
was closed during download. Updated tests to reflect the new behavior.

Changes:
- Removed toast trigger logic from PDF modal download function
- Removed modal close event listener for toast display
- Updated toast notification test expectations
- Fixed recommended card outline styling
2025-11-20 14:35:22 +00:00

405 lines
18 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 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();