#!/usr/bin/env node /** * Simple Build System Test * * Basic validation that the build system is working correctly * without complex Playwright dependencies */ const fs = require('fs').promises; const path = require('path'); const { execSync } = require('child_process'); const BUILD_CONFIG = { PROJECT_ROOT: path.resolve(__dirname), BUILD_OUTPUT: path.resolve(__dirname, 'assets/js/dist'), WEBPACK_CONFIG: path.resolve(__dirname, 'webpack.config.js'), MANIFEST_PATH: path.resolve(__dirname, 'assets/js/dist/manifest.json'), EXPECTED_BUNDLES: [ 'hvac-core.bundle.js', 'hvac-dashboard.bundle.js', 'hvac-certificates.bundle.js', 'hvac-master.bundle.js', 'hvac-trainer.bundle.js', 'hvac-events.bundle.js', 'hvac-admin.bundle.js', 'hvac-safari-compat.bundle.js' ] }; class SimpleBuildSystemTest { constructor() { this.results = { passed: 0, failed: 0, tests: [] }; } log(message, type = 'info') { const colors = { info: '\x1b[37m', // white success: '\x1b[32m', // green error: '\x1b[31m', // red warning: '\x1b[33m', // yellow reset: '\x1b[0m' }; console.log(`${colors[type]}${message}${colors.reset}`); } async test(name, testFn) { try { this.log(`๐Ÿงช Testing: ${name}`, 'info'); await testFn(); this.log(`โœ… PASS: ${name}`, 'success'); this.results.passed++; this.results.tests.push({ name, status: 'pass' }); } catch (error) { this.log(`โŒ FAIL: ${name} - ${error.message}`, 'error'); this.results.failed++; this.results.tests.push({ name, status: 'fail', error: error.message }); } } async testWebpackConfigExists() { const configExists = await fs.access(BUILD_CONFIG.WEBPACK_CONFIG).then(() => true).catch(() => false); if (!configExists) { throw new Error('webpack.config.js not found'); } // Test webpack config can be loaded const config = require(BUILD_CONFIG.WEBPACK_CONFIG); if (!config.entry || !config.output) { throw new Error('Invalid webpack configuration'); } this.log(` Entry points: ${Object.keys(config.entry).length}`, 'info'); } async testBuildOutputExists() { const distExists = await fs.access(BUILD_CONFIG.BUILD_OUTPUT).then(() => true).catch(() => false); if (!distExists) { throw new Error('Build output directory (assets/js/dist) not found'); } const files = await fs.readdir(BUILD_CONFIG.BUILD_OUTPUT); const bundleFiles = files.filter(f => f.endsWith('.bundle.js')); this.log(` Found ${bundleFiles.length} bundle files`, 'info'); if (bundleFiles.length === 0) { throw new Error('No bundle files found in dist directory'); } } async testExpectedBundlesExist() { const files = await fs.readdir(BUILD_CONFIG.BUILD_OUTPUT); const missing = []; for (const expectedBundle of BUILD_CONFIG.EXPECTED_BUNDLES) { if (!files.includes(expectedBundle)) { missing.push(expectedBundle); } } if (missing.length > 0) { this.log(` Missing bundles: ${missing.join(', ')}`, 'warning'); throw new Error(`Missing expected bundles: ${missing.join(', ')}`); } this.log(` All ${BUILD_CONFIG.EXPECTED_BUNDLES.length} expected bundles found`, 'success'); } async testBundleSizes() { const MAX_SIZE_KB = 250; const oversized = []; for (const bundleName of BUILD_CONFIG.EXPECTED_BUNDLES) { const bundlePath = path.join(BUILD_CONFIG.BUILD_OUTPUT, bundleName); try { const stats = await fs.stat(bundlePath); const sizeKB = Math.round(stats.size / 1024); if (sizeKB > MAX_SIZE_KB) { oversized.push(`${bundleName} (${sizeKB}KB)`); } this.log(` ${bundleName}: ${sizeKB}KB`, sizeKB > MAX_SIZE_KB ? 'warning' : 'info'); } catch (error) { throw new Error(`Cannot read bundle size: ${bundleName}`); } } if (oversized.length > 0) { throw new Error(`Bundles exceed ${MAX_SIZE_KB}KB limit: ${oversized.join(', ')}`); } } async testBundleContent() { // Test at least one bundle has content const bundlePath = path.join(BUILD_CONFIG.BUILD_OUTPUT, 'hvac-core.bundle.js'); try { const content = await fs.readFile(bundlePath, 'utf-8'); if (content.length < 100) { throw new Error('Bundle content too small'); } // Check for WordPress compatibility const hasJQuery = content.includes('jQuery') || content.includes('$'); if (!hasJQuery) { this.log(' Warning: No jQuery reference found', 'warning'); } this.log(` Bundle size: ${Math.round(content.length / 1024)}KB`, 'info'); } catch (error) { throw new Error(`Cannot read bundle content: ${error.message}`); } } async testBuildProcess() { this.log(' Running production build...', 'info'); try { const output = execSync('npm run build', { encoding: 'utf-8', timeout: 60000 // 60 second timeout }); if (output.includes('ERROR') || output.includes('Failed')) { throw new Error('Build process reported errors'); } this.log(' Build completed successfully', 'success'); } catch (error) { throw new Error(`Build process failed: ${error.message}`); } } async testPHPClassExists() { const phpClassPath = path.join(BUILD_CONFIG.PROJECT_ROOT, 'includes/class-hvac-bundled-assets.php'); const classExists = await fs.access(phpClassPath).then(() => true).catch(() => false); if (!classExists) { throw new Error('HVAC_Bundled_Assets PHP class not found'); } const phpContent = await fs.readFile(phpClassPath, 'utf-8'); if (!phpContent.includes('class HVAC_Bundled_Assets')) { throw new Error('HVAC_Bundled_Assets class definition not found'); } // Check for singleton pattern if (!phpContent.includes('public static function instance()')) { throw new Error('Singleton pattern not implemented'); } this.log(' PHP class structure validated', 'success'); } async testSecurityBasics() { const phpClassPath = path.join(BUILD_CONFIG.PROJECT_ROOT, 'includes/class-hvac-bundled-assets.php'); const phpContent = await fs.readFile(phpClassPath, 'utf-8'); const securityIssues = []; // Check for unsanitized file operations if (phpContent.includes('file_get_contents') && !phpContent.includes('wp_safe_remote_get')) { securityIssues.push('Direct file_get_contents usage'); } // Check for unescaped output if (phpContent.includes('echo ') && !phpContent.includes('esc_html')) { securityIssues.push('Potentially unescaped output'); } if (securityIssues.length > 0) { this.log(` Security issues found: ${securityIssues.join(', ')}`, 'warning'); this.log(' โš ๏ธ These should be addressed before production', 'warning'); } else { this.log(' Basic security check passed', 'success'); } } async run() { this.log('\n๐Ÿงช HVAC BUILD SYSTEM - SIMPLE VALIDATION', 'info'); this.log('โ•'.repeat(50), 'info'); await this.test('Webpack Configuration Exists', () => this.testWebpackConfigExists()); await this.test('Build Output Directory Exists', () => this.testBuildOutputExists()); await this.test('Expected Bundles Exist', () => this.testExpectedBundlesExist()); await this.test('Bundle Sizes Within Limits', () => this.testBundleSizes()); await this.test('Bundle Content Validation', () => this.testBundleContent()); await this.test('Build Process Works', () => this.testBuildProcess()); await this.test('PHP Class Exists', () => this.testPHPClassExists()); await this.test('Basic Security Check', () => this.testSecurityBasics()); this.log('\n๐Ÿ“Š TEST RESULTS', 'info'); this.log('โ•'.repeat(50), 'info'); this.log(`โœ… Passed: ${this.results.passed}`, 'success'); this.log(`โŒ Failed: ${this.results.failed}`, this.results.failed > 0 ? 'error' : 'info'); this.log(`๐Ÿ“‹ Total: ${this.results.passed + this.results.failed}`, 'info'); if (this.results.failed > 0) { this.log('\nโŒ FAILED TESTS:', 'error'); this.results.tests.filter(t => t.status === 'fail').forEach(test => { this.log(` โ€ข ${test.name}: ${test.error}`, 'error'); }); } const success = this.results.failed === 0; this.log('\n๐ŸŽฏ OVERALL STATUS', 'info'); if (success) { this.log('โœ… Build system validation PASSED', 'success'); this.log('๐Ÿš€ Build system is ready for further testing', 'success'); } else { this.log('โŒ Build system validation FAILED', 'error'); this.log('โš ๏ธ Fix issues before proceeding with deployment', 'error'); } return success; } } // Run the test if (require.main === module) { const test = new SimpleBuildSystemTest(); test.run().then(success => { process.exit(success ? 0 : 1); }).catch(error => { console.error('Fatal error:', error.message); process.exit(1); }); } module.exports = SimpleBuildSystemTest;