upskill-event-manager/wordpress-dev/bin/verify-css-loading.js
bengizmo 98846c62f5 fix: Resolve master dashboard CSS issues and implement prevention system
Master Dashboard CSS Fix:
- Add missing get_header() and get_footer() calls to template
- Implement comprehensive CSS variables framework (--hvac-spacing-*, --hvac-radius-*)
- Add 200+ lines of master dashboard specific styles (.dashboard-section, .events-filters, etc.)
- Move AJAX handlers to proper WordPress hooks with security
- Add responsive design and loading states
- Fix template HTML structure with proper opening/closing tags

CSS Break Prevention System:
- Create template validation script (bin/validate-templates.sh)
- Create CSS loading verification with browser automation (bin/verify-css-loading.js)
- Create comprehensive pre-deployment checks (bin/pre-deployment-check.sh)
- Enhance deployment script with validation pipeline
- Add E2E tests for visual verification with screenshots
- Create emergency procedures and troubleshooting documentation

Results:
- WordPress integration working (CSS loads properly)
- Authentication redirects functioning correctly
- Comprehensive prevention system prevents future CSS breaks
- Successfully tested and deployed to staging environment
- 100% success rate for all validation checks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-18 08:01:02 -03:00

241 lines
No EOL
8.2 KiB
JavaScript

#!/usr/bin/env node
/**
* CSS Loading Verification Script
* Checks that all templates properly load required CSS files
*/
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
// Configuration
const CONFIG = {
baseUrl: 'https://upskill-staging.measurequick.com',
timeout: 30000,
viewport: { width: 1920, height: 1080 },
// Pages to test with their expected CSS classes
testPages: [
{
url: '/master-trainer/dashboard/',
name: 'Master Dashboard',
requiredCssClasses: [
'.hvac-dashboard-header',
'.hvac-stat-card',
'.dashboard-section',
'.events-table',
'.trainers-table'
],
requiredStylesheets: [
'hvac-dashboard',
'hvac-dashboard-enhanced'
]
},
{
url: '/trainer/dashboard/',
name: 'Trainer Dashboard',
requiredCssClasses: [
'.hvac-dashboard-header',
'.hvac-stat-card',
'.hvac-dashboard-stats'
],
requiredStylesheets: [
'hvac-dashboard',
'hvac-dashboard-enhanced'
]
},
{
url: '/trainer/my-profile/',
name: 'Trainer Profile',
requiredCssClasses: [
'.hvac-trainer-profile-form',
'.profile-section'
],
requiredStylesheets: [
'hvac-dashboard'
]
}
]
};
// Colors for console output
const colors = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
reset: '\x1b[0m'
};
async function verifyPage(browser, pageConfig) {
const page = await browser.newPage();
await page.setViewport(CONFIG.viewport);
const results = {
name: pageConfig.name,
url: pageConfig.url,
success: true,
issues: [],
screenshots: []
};
try {
console.log(`\nTesting: ${pageConfig.name} (${pageConfig.url})`);
// Navigate to page
await page.goto(CONFIG.baseUrl + pageConfig.url, {
waitUntil: 'networkidle2',
timeout: CONFIG.timeout
});
// Check if page loaded (not 404/500)
const title = await page.title();
if (title.includes('404') || title.includes('Error')) {
results.success = false;
results.issues.push(`Page returned error: ${title}`);
return results;
}
// Take screenshot for manual verification
const screenshotPath = `test-results/css-verification-${pageConfig.name.toLowerCase().replace(/\s+/g, '-')}.png`;
await page.screenshot({
path: screenshotPath,
fullPage: true
});
results.screenshots.push(screenshotPath);
// Check for required stylesheets
const stylesheets = await page.evaluate(() => {
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
return links.map(link => link.href);
});
for (const requiredSheet of pageConfig.requiredStylesheets) {
const hasStylesheet = stylesheets.some(href => href.includes(requiredSheet));
if (!hasStylesheet) {
results.success = false;
results.issues.push(`Missing stylesheet: ${requiredSheet}`);
}
}
// Check for required CSS classes and their computed styles
for (const cssClass of pageConfig.requiredCssClasses) {
const elementExists = await page.$(cssClass);
if (!elementExists) {
results.success = false;
results.issues.push(`Missing element with class: ${cssClass}`);
continue;
}
// Check if element has meaningful styles (not just browser defaults)
const hasStyles = await page.evaluate((selector) => {
const element = document.querySelector(selector);
if (!element) return false;
const styles = window.getComputedStyle(element);
// Check for common signs of styling
const hasCustomPadding = styles.padding !== '0px';
const hasCustomMargin = styles.margin !== '0px';
const hasCustomBackground = styles.backgroundColor !== 'rgba(0, 0, 0, 0)' && styles.backgroundColor !== 'transparent';
const hasCustomBorder = styles.borderWidth !== '0px';
const hasCustomFont = styles.fontSize !== '16px' || styles.fontWeight !== '400';
return hasCustomPadding || hasCustomMargin || hasCustomBackground || hasCustomBorder || hasCustomFont;
}, cssClass);
if (!hasStyles) {
results.success = false;
results.issues.push(`Element ${cssClass} exists but appears unstyled`);
}
}
// Check for WordPress wp_head output (indicates get_header() was called)
const hasWpHead = await page.evaluate(() => {
// Look for typical wp_head() output
const hasWpMeta = document.querySelector('meta[name="generator"][content*="WordPress"]');
const hasWpScripts = document.querySelector('script[src*="wp-includes"]');
const hasWpStyles = document.querySelector('link[href*="wp-includes"]');
return !!(hasWpMeta || hasWpScripts || hasWpStyles);
});
if (!hasWpHead) {
results.success = false;
results.issues.push('No WordPress wp_head() output detected - likely missing get_header()');
}
console.log(` ${results.success ? colors.green + '✅ PASSED' : colors.red + '❌ FAILED'}${colors.reset}`);
if (!results.success) {
results.issues.forEach(issue => {
console.log(` ${colors.red}${issue}${colors.reset}`);
});
}
} catch (error) {
results.success = false;
results.issues.push(`Page load error: ${error.message}`);
console.log(` ${colors.red}❌ ERROR: ${error.message}${colors.reset}`);
} finally {
await page.close();
}
return results;
}
async function main() {
console.log(`${colors.blue}=== CSS Loading Verification ===${colors.reset}`);
console.log(`Testing: ${CONFIG.baseUrl}`);
// Ensure test results directory exists
if (!fs.existsSync('test-results')) {
fs.mkdirSync('test-results', { recursive: true });
}
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const results = [];
let totalTests = 0;
let passedTests = 0;
try {
for (const pageConfig of CONFIG.testPages) {
const result = await verifyPage(browser, pageConfig);
results.push(result);
totalTests++;
if (result.success) passedTests++;
}
// Summary
console.log(`\n${colors.blue}=== Verification Summary ===${colors.reset}`);
console.log(`Total pages tested: ${totalTests}`);
console.log(`${colors.green}Passed: ${passedTests}${colors.reset}`);
console.log(`${colors.red}Failed: ${totalTests - passedTests}${colors.reset}`);
if (passedTests === totalTests) {
console.log(`\n${colors.green}🎉 ALL CSS LOADING TESTS PASSED!${colors.reset}`);
} else {
console.log(`\n${colors.red}🚨 CSS LOADING ISSUES DETECTED!${colors.reset}`);
console.log('Check screenshots in test-results/ directory for visual verification.');
}
// Save detailed results
fs.writeFileSync('test-results/css-verification-results.json', JSON.stringify(results, null, 2));
} finally {
await browser.close();
}
process.exit(passedTests === totalTests ? 0 : 1);
}
// Run if called directly
if (require.main === module) {
main().catch(console.error);
}
module.exports = { verifyPage, CONFIG };