/** * CSS Bundling Test * * Tests for Lightning CSS bundling in production vs development mode. * Verifies correct CSS loading based on GO_ENV environment variable. */ import { chromium } from 'playwright'; const BASE_URL = process.env.BASE_URL || 'http://localhost:1999'; console.log('\n============================================================'); console.log('CSS BUNDLING TEST'); console.log('============================================================\n'); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext(); const page = await context.newPage(); let passed = 0; let failed = 0; const results = []; function logResult(name, success, details = '') { const status = success ? '✅ PASS' : '❌ FAIL'; console.log(`${status} - ${name}${details ? ': ' + details : ''}`); results.push({ name, success, details }); if (success) passed++; else failed++; } try { // Test 1: Check CSS files are loading (development mode) console.log('1️⃣ Testing CSS Loading...\n'); await page.goto(BASE_URL, { waitUntil: 'networkidle' }); // Get all CSS links const cssLinks = await page.evaluate(() => { return Array.from(document.querySelectorAll('link[rel="stylesheet"]')) .map(link => link.href); }); console.log(' CSS Links found:'); cssLinks.forEach(link => console.log(` - ${link}`)); console.log(''); // Check if using bundled or modular CSS const hasBundledCSS = cssLinks.some(href => href.includes('/dist/bundle')); const hasModularCSS = cssLinks.some(href => href.includes('/css/main.css')); const hasPrintCSS = cssLinks.some(href => href.includes('/css/print.css')); logResult( 'CSS Loading Strategy', hasBundledCSS || hasModularCSS, hasBundledCSS ? 'Production bundle' : 'Development modular' ); logResult( 'Print CSS Separate', hasPrintCSS, hasPrintCSS ? 'Loaded separately with media="print"' : 'Missing!' ); // Test 2: Verify print.css has media="print" attribute console.log('\n2️⃣ Testing Print CSS Media Attribute...\n'); const printLinkMedia = await page.evaluate(() => { const printLink = document.querySelector('link[href*="print.css"]'); return printLink ? printLink.media : null; }); logResult( 'Print CSS media="print"', printLinkMedia === 'print', printLinkMedia ? `media="${printLinkMedia}"` : 'No media attribute' ); // Test 3: Verify CSS is rendering correctly (basic visual check) console.log('\n3️⃣ Testing CSS Rendering...\n'); const bodyStyles = await page.evaluate(() => { const body = document.body; const computed = window.getComputedStyle(body); return { fontFamily: computed.fontFamily, color: computed.color, backgroundColor: window.getComputedStyle(document.documentElement).getPropertyValue('--page-bg') }; }); const hasQuicksandFont = bodyStyles.fontFamily.toLowerCase().includes('quicksand'); logResult( 'Font Family Applied', hasQuicksandFont || bodyStyles.fontFamily.includes('system-ui'), bodyStyles.fontFamily.substring(0, 50) + '...' ); const hasThemeColor = bodyStyles.backgroundColor && bodyStyles.backgroundColor !== ''; logResult( 'CSS Variables Working', hasThemeColor, `--page-bg: ${bodyStyles.backgroundColor || 'not set'}` ); // Test 4: Verify no CSS 404 errors console.log('\n4️⃣ Testing CSS Resources Load Successfully...\n'); const cssResponses = []; page.on('response', response => { if (response.url().includes('.css')) { cssResponses.push({ url: response.url(), status: response.status() }); } }); // Reload to capture all CSS requests await page.reload({ waitUntil: 'networkidle' }); const all200 = cssResponses.every(r => r.status === 200); const css404s = cssResponses.filter(r => r.status === 404); logResult( 'All CSS Resources Load (200)', all200, css404s.length > 0 ? `404s: ${css404s.map(r => r.url).join(', ')}` : `${cssResponses.length} CSS files OK` ); // Test 5: Check CSS size (if bundled) console.log('\n5️⃣ Testing CSS Size...\n'); let totalCSSSize = 0; for (const cssLink of cssLinks) { try { const response = await page.evaluate(async (url) => { const res = await fetch(url); const text = await res.text(); return text.length; }, cssLink); totalCSSSize += response; } catch (e) { // External CSS (like Google Fonts) won't be fetchable } } const sizeKB = (totalCSSSize / 1024).toFixed(1); const sizeOK = totalCSSSize > 0 && totalCSSSize < 500 * 1024; // Under 500KB is reasonable logResult( 'CSS Size Reasonable', sizeOK, `${sizeKB} KB total (local CSS)` ); // Test 6: Verify ITCSS layers (check for specific class names) console.log('\n6️⃣ Testing CSS Architecture (ITCSS Layers)...\n'); const cssClasses = await page.evaluate(() => { const classes = { foundation: document.querySelector('[class*="cv-"]') !== null, layout: document.querySelector('.cv-container') !== null, components: document.querySelector('.cv-header') !== null || document.querySelector('.cv-paper') !== null, interactive: document.querySelector('.modal') !== null || document.querySelector('.toggle-switch') !== null, responsive: window.matchMedia('(max-width: 768px)').matches !== undefined }; return classes; }); const layersPresent = Object.values(cssClasses).filter(v => v).length; logResult( 'ITCSS Layers Present', layersPresent >= 3, `${layersPresent}/5 layers detected` ); } catch (error) { console.error('Test error:', error.message); logResult('Test Execution', false, error.message); } finally { await browser.close(); } // Summary console.log('\n============================================================'); console.log('TEST SUMMARY'); console.log('============================================================\n'); results.forEach(r => { const icon = r.success ? '✅' : '❌'; console.log(` ${icon} ${r.name}`); }); console.log(`\n Total: ${passed}/${passed + failed} tests passed\n`); if (failed > 0) { console.log('❌ SOME TESTS FAILED\n'); process.exit(1); } else { console.log('✅ ALL TESTS PASSED\n'); process.exit(0); }