#!/usr/bin/env bun /** * SKELETON LOADERS TEST * ====================== * Tests skeleton loader animations during language transitions * - Verifies skeleton loaders appear during language switching * - Checks component-wrapper structure exists * - Validates .loading class is added/removed correctly * - Tests skeleton appears on multiple consecutive switches * - Ensures no visual glitches or stuck loading states */ import { chromium } from 'playwright'; const URL = "http://localhost:1999"; async function testSkeletonLoaders() { console.log('πŸ’€ SKELETON LOADERS 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 = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); console.log(`❌ ERROR: ${msg.text()}`); } }); console.log("\n1️⃣ Loading page (English default)..."); await page.goto(URL); await page.waitForTimeout(2000); // ======================================================================== // TEST 1: Component wrapper structure exists // ======================================================================== console.log("\n2️⃣ Testing Component Wrapper Structure..."); const structure = await page.evaluate(() => { const wrappers = document.querySelectorAll('.component-wrapper'); const hasActual = Array.from(wrappers).every(w => w.querySelector('.actual-content')); const hasSkeleton = Array.from(wrappers).every(w => w.querySelector('.skeleton-content')); return { wrapperCount: wrappers.length, allHaveActual: hasActual, allHaveSkeleton: hasSkeleton }; }); console.log(` Component wrappers found: ${structure.wrapperCount}`); console.log(` All have .actual-content: ${structure.allHaveActual ? 'βœ…' : '❌'}`); console.log(` All have .skeleton-content: ${structure.allHaveSkeleton ? 'βœ…' : '❌'}`); const structurePassed = structure.wrapperCount > 0 && structure.allHaveActual && structure.allHaveSkeleton; console.log(` ${structurePassed ? 'βœ… PASS' : '❌ FAIL'} - Dual-state structure exists`); testResults.push({ test: 'Component Wrapper Structure', passed: structurePassed }); // ======================================================================== // TEST 2: Skeleton CSS exists and is loaded // ======================================================================== console.log("\n3️⃣ Testing Skeleton CSS..."); const cssCheck = await page.evaluate(() => { const skeleton = document.querySelector('.skeleton-content .skeleton'); if (!skeleton) return { exists: false }; const styles = window.getComputedStyle(skeleton); return { exists: true, hasAnimation: styles.animation !== 'none' && styles.animation !== '', background: styles.background, borderRadius: styles.borderRadius }; }); console.log(` Skeleton elements exist: ${cssCheck.exists ? 'βœ…' : '❌'}`); console.log(` Has shimmer animation: ${cssCheck.hasAnimation ? 'βœ…' : '❌'}`); console.log(` ${cssCheck.exists && cssCheck.hasAnimation ? 'βœ… PASS' : '❌ FAIL'} - Skeleton CSS loaded`); testResults.push({ test: 'Skeleton CSS', passed: cssCheck.exists && cssCheck.hasAnimation }); // ======================================================================== // TEST 3: Monitor parent container .loading class during language switch // ======================================================================== console.log("\n4️⃣ Testing First Language Switch (EN β†’ ES)..."); // Set up console log monitoring to track our JavaScript skeleton loader messages const consoleMessages = []; page.on('console', msg => { const text = msg.text(); if (text.includes('Skeleton loader:')) { consoleMessages.push(text); } }); // Click Spanish button await page.click('.selector-btn[aria-label="EspaΓ±ol"]'); await page.waitForTimeout(800); // Check the console messages const addedMessages1 = consoleMessages.filter(m => m.includes('Added .loading')).length; const removedMessages1 = consoleMessages.filter(m => m.includes('Removed .loading')).length; console.log(` Skeleton loader added .loading: ${addedMessages1 > 0 ? 'βœ…' : '❌'} (${addedMessages1} events)`); console.log(` Skeleton loader removed .loading: ${removedMessages1 > 0 ? 'βœ…' : '❌'} (${removedMessages1} events)`); const switch1Passed = addedMessages1 > 0 && removedMessages1 > 0; console.log(` ${switch1Passed ? 'βœ… PASS' : '❌ FAIL'} - Skeleton displayed during transition`); testResults.push({ test: 'First Language Switch', passed: switch1Passed }); // ======================================================================== // TEST 4: Second language switch (ES β†’ EN) // ======================================================================== console.log("\n5️⃣ Testing Second Language Switch (ES β†’ EN)..."); // Clear console messages const beforeSwitch2 = consoleMessages.length; // Click English button await page.click('.selector-btn[aria-label="English"]'); await page.waitForTimeout(800); // Check new console messages since last switch const newMessages2 = consoleMessages.slice(beforeSwitch2); const addedMessages2 = newMessages2.filter(m => m.includes('Added .loading')).length; const removedMessages2 = newMessages2.filter(m => m.includes('Removed .loading')).length; console.log(` Skeleton loader added .loading: ${addedMessages2 > 0 ? 'βœ…' : '❌'} (${addedMessages2} events)`); console.log(` Skeleton loader removed .loading: ${removedMessages2 > 0 ? 'βœ…' : '❌'} (${removedMessages2} events)`); const switch2Passed = addedMessages2 > 0 && removedMessages2 > 0; console.log(` ${switch2Passed ? 'βœ… PASS' : '❌ FAIL'} - Skeleton still works on second switch`); testResults.push({ test: 'Second Language Switch', passed: switch2Passed }); // ======================================================================== // TEST 5: Third language switch (EN β†’ ES) - consistency check // ======================================================================== console.log("\n6️⃣ Testing Third Language Switch (EN β†’ ES)..."); // Clear console messages const beforeSwitch3 = consoleMessages.length; // Click Spanish button await page.click('.selector-btn[aria-label="EspaΓ±ol"]'); await page.waitForTimeout(800); // Check new console messages since last switch const newMessages3 = consoleMessages.slice(beforeSwitch3); const addedMessages3 = newMessages3.filter(m => m.includes('Added .loading')).length; const removedMessages3 = newMessages3.filter(m => m.includes('Removed .loading')).length; console.log(` Skeleton loader added .loading: ${addedMessages3 > 0 ? 'βœ…' : '❌'} (${addedMessages3} events)`); console.log(` Skeleton loader removed .loading: ${removedMessages3 > 0 ? 'βœ…' : '❌'} (${removedMessages3} events)`); const switch3Passed = addedMessages3 > 0 && removedMessages3 > 0; console.log(` ${switch3Passed ? 'βœ… PASS' : '❌ FAIL'} - Consistent behavior on third switch`); testResults.push({ test: 'Third Language Switch', passed: switch3Passed }); // ======================================================================== // TEST 6: No stuck loading states // ======================================================================== console.log("\n7️⃣ Testing for Stuck Loading States..."); const finalState = await page.evaluate(() => { const page1 = document.querySelector('#cv-inner-content-page-1'); const page2 = document.querySelector('#cv-inner-content-page-2'); const wrappers = document.querySelectorAll('.component-wrapper'); return { page1HasLoading: page1?.classList.contains('loading') || false, page2HasLoading: page2?.classList.contains('loading') || false, anyWrapperHasLoading: Array.from(wrappers).some(w => w.classList.contains('loading')) }; }); console.log(` Page 1 stuck with .loading: ${finalState.page1HasLoading ? '❌ BUG' : 'βœ… Clean'}`); console.log(` Page 2 stuck with .loading: ${finalState.page2HasLoading ? '❌ BUG' : 'βœ… Clean'}`); console.log(` Any wrapper stuck with .loading: ${finalState.anyWrapperHasLoading ? '❌ BUG' : 'βœ… Clean'}`); const noStuckStates = !finalState.page1HasLoading && !finalState.page2HasLoading && !finalState.anyWrapperHasLoading; console.log(` ${noStuckStates ? 'βœ… PASS' : '❌ FAIL'} - No stuck loading states`); testResults.push({ test: 'No Stuck Loading States', passed: noStuckStates }); // ======================================================================== // TEST 7: JavaScript event handlers work // ======================================================================== console.log("\n8️⃣ Testing JavaScript Event Handlers..."); const jsCheck = await page.evaluate(() => { // Check if main.js loaded and languageSwitching variable exists const hasLanguageSwitchingFlag = typeof window.languageSwitching !== 'undefined'; // Verify the flag is in clean state (false) const flagIsClean = window.languageSwitching === false; return { hasLanguageSwitchingFlag, flagIsClean }; }); console.log(` JavaScript languageSwitching flag exists: ${jsCheck.hasLanguageSwitchingFlag ? 'βœ…' : '❌'}`); console.log(` Flag is in clean state (false): ${jsCheck.flagIsClean ? 'βœ…' : '❌'}`); const jsPassed = jsCheck.hasLanguageSwitchingFlag && jsCheck.flagIsClean; console.log(` ${jsPassed ? 'βœ… PASS' : '❌ FAIL'} - JavaScript event handlers configured`); testResults.push({ test: 'JavaScript Event Handlers', passed: jsPassed }); // ======================================================================== // FINAL SUMMARY // ======================================================================== console.log("\n" + "=".repeat(70)); console.log("πŸ“Š TEST SUMMARY\n"); const totalTests = testResults.length; const passedTests = testResults.filter(r => r.passed).length; const failedTests = totalTests - passedTests; testResults.forEach(result => { console.log(` ${result.passed ? 'βœ…' : '❌'} ${result.test}`); }); console.log(`\n Total: ${passedTests}/${totalTests} tests passed`); if (errors.length === 0) { console.log("\nβœ… NO CONSOLE ERRORS"); } else { console.log(`\n⚠️ ${errors.length} CONSOLE ERRORS`); } console.log("=".repeat(70) + "\n"); if (failedTests === 0) { console.log("πŸŽ‰ SKELETON LOADERS VALIDATED!"); } else { console.log("⚠️ SOME TESTS FAILED - See details above"); } console.log("\nBrowser will stay open for manual inspection."); console.log("Press Ctrl+C when done.\n"); await new Promise(() => {}); // Keep browser open } await testSkeletonLoaders();