/** * HVAC Community Events - Build System Validation Tests * * Comprehensive test suite for the JavaScript build pipeline including: * - Webpack build process validation * - Bundle generation verification * - Security vulnerability testing * - WordPress integration testing * - Performance and compatibility validation * * @package HVAC_Community_Events * @since 2.0.0 */ const fs = require('fs').promises; const path = require('path'); const { execSync, spawn } = require('child_process'); const { test, expect } = require('@playwright/test'); const BasePage = require('./page-objects/base/BasePage'); // Configuration const BUILD_CONFIG = { PROJECT_ROOT: path.resolve(__dirname, '..'), BUILD_OUTPUT: path.resolve(__dirname, '../assets/js/dist'), SOURCE_DIR: path.resolve(__dirname, '../src/js'), 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' ], // Security test payloads SECURITY_PAYLOADS: { XSS_MANIFEST: '{"hvac-core.js": ""}', MALICIOUS_USER_AGENT: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"; maliciousScript();', PATH_TRAVERSAL: '../../../../etc/passwd', SQL_INJECTION: "'; DROP TABLE wp_users; --" } }; /** * Build System Test Utilities */ class BuildSystemTestUtils { /** * Run webpack build command * @param {string} mode - 'development' or 'production' * @returns {Promise<{success: boolean, output: string, error?: string}>} */ static async runWebpackBuild(mode = 'production') { return new Promise((resolve) => { const buildCommand = mode === 'production' ? 'npm run build' : 'npm run build:dev'; const process = spawn('bash', ['-c', buildCommand], { cwd: BUILD_CONFIG.PROJECT_ROOT, stdio: ['pipe', 'pipe', 'pipe'] }); let output = ''; let errorOutput = ''; process.stdout.on('data', (data) => { output += data.toString(); }); process.stderr.on('data', (data) => { errorOutput += data.toString(); }); process.on('close', (code) => { resolve({ success: code === 0, output, error: code !== 0 ? errorOutput : undefined }); }); // Timeout after 60 seconds setTimeout(() => { process.kill('SIGTERM'); resolve({ success: false, output, error: 'Build process timed out after 60 seconds' }); }, 60000); }); } /** * Analyze bundle sizes * @returns {Promise} */ static async analyzeBundleSizes() { const bundles = {}; for (const bundleName of BUILD_CONFIG.EXPECTED_BUNDLES) { const bundlePath = path.join(BUILD_CONFIG.BUILD_OUTPUT, bundleName); try { const stats = await fs.stat(bundlePath); bundles[bundleName] = { size: stats.size, sizeKB: Math.round(stats.size / 1024), exists: true }; } catch (error) { bundles[bundleName] = { size: 0, sizeKB: 0, exists: false, error: error.message }; } } return bundles; } /** * Validate bundle contents * @param {string} bundleName * @returns {Promise<{isValid: boolean, hasWordPressCompat: boolean, hasErrors: boolean, content?: string}>} */ static async validateBundleContent(bundleName) { const bundlePath = path.join(BUILD_CONFIG.BUILD_OUTPUT, bundleName); try { const content = await fs.readFile(bundlePath, 'utf-8'); return { isValid: content.length > 0, hasWordPressCompat: content.includes('jQuery') || content.includes('wp.'), hasErrors: content.includes('Error:') || content.includes('TypeError:'), hasSourceMap: content.includes('//# sourceMappingURL='), content: content.substring(0, 500) // First 500 chars for inspection }; } catch (error) { return { isValid: false, hasWordPressCompat: false, hasErrors: true, error: error.message }; } } /** * Create malicious manifest for security testing * @param {string} payload */ static async createMaliciousManifest(payload) { const backupPath = BUILD_CONFIG.MANIFEST_PATH + '.backup'; // Backup original manifest try { await fs.copyFile(BUILD_CONFIG.MANIFEST_PATH, backupPath); } catch (error) { // Manifest might not exist } // Write malicious manifest await fs.writeFile(BUILD_CONFIG.MANIFEST_PATH, payload); } /** * Restore manifest from backup */ static async restoreManifest() { const backupPath = BUILD_CONFIG.MANIFEST_PATH + '.backup'; try { await fs.copyFile(backupPath, BUILD_CONFIG.MANIFEST_PATH); await fs.unlink(backupPath); } catch (error) { // Remove malicious manifest if backup doesn't exist try { await fs.unlink(BUILD_CONFIG.MANIFEST_PATH); } catch (e) { // Ignore cleanup errors } } } /** * Simulate missing bundle files * @param {string[]} bundleNames */ static async removeBundles(bundleNames) { const backups = []; for (const bundleName of bundleNames) { const bundlePath = path.join(BUILD_CONFIG.BUILD_OUTPUT, bundleName); const backupPath = bundlePath + '.backup'; try { await fs.copyFile(bundlePath, backupPath); await fs.unlink(bundlePath); backups.push({ original: bundlePath, backup: backupPath }); } catch (error) { console.warn(`Could not backup ${bundleName}:`, error.message); } } return backups; } /** * Restore backed up bundles * @param {Array} backups */ static async restoreBundles(backups) { for (const { original, backup } of backups) { try { await fs.copyFile(backup, original); await fs.unlink(backup); } catch (error) { console.warn(`Could not restore bundle:`, error.message); } } } } /** * WordPress Bundled Assets Test Page */ class WordPressBundledAssetsPage extends BasePage { constructor(page) { super(page); this.bundledAssets = []; this.loadErrors = []; } /** * Monitor asset loading */ async monitorAssetLoading() { // Monitor network requests this.page.on('response', async (response) => { const url = response.url(); if (url.includes('/assets/js/dist/') && url.includes('.bundle.js')) { this.bundledAssets.push({ url, status: response.status(), success: response.ok() }); } }); // Monitor console errors this.page.on('console', (message) => { if (message.type() === 'error') { this.loadErrors.push(message.text()); } }); // Monitor JavaScript errors this.page.on('pageerror', (error) => { this.loadErrors.push(`JavaScript Error: ${error.message}`); }); } /** * Get loaded bundles information */ getLoadedBundles() { return this.bundledAssets; } /** * Get loading errors */ getLoadingErrors() { return this.loadErrors; } /** * Check if specific bundle is loaded * @param {string} bundleName */ isBundleLoaded(bundleName) { return this.bundledAssets.some(asset => asset.url.includes(bundleName) && asset.success ); } /** * Validate bundle loading on WordPress page * @param {string} pageUrl */ async validateBundleLoadingOnPage(pageUrl) { await this.monitorAssetLoading(); await this.page.goto(pageUrl); await this.page.waitForLoadState('networkidle'); // Wait a bit for assets to load await this.page.waitForTimeout(2000); return { loadedBundles: this.getLoadedBundles(), loadingErrors: this.getLoadingErrors(), pageTitle: await this.page.title() }; } } // ============================================================================== // BUILD SYSTEM VALIDATION TESTS // ============================================================================== test.describe('Build System Validation', () => { test('Webpack configuration is valid', async () => { // Test webpack config file exists and is readable const configExists = await fs.access(BUILD_CONFIG.WEBPACK_CONFIG).then(() => true).catch(() => false); expect(configExists).toBe(true); // Test webpack config can be loaded const webpack = require('webpack'); const config = require(BUILD_CONFIG.WEBPACK_CONFIG); expect(config).toBeTruthy(); expect(config.entry).toBeTruthy(); expect(config.output).toBeTruthy(); expect(config.output.path).toBe(BUILD_CONFIG.BUILD_OUTPUT); // Validate entry points const expectedEntries = [ 'hvac-core', 'hvac-dashboard', 'hvac-certificates', 'hvac-master', 'hvac-trainer', 'hvac-events', 'hvac-admin', 'hvac-safari-compat' ]; for (const entry of expectedEntries) { expect(config.entry[entry]).toBeTruthy(); } }); test('Production build generates all expected bundles', async () => { console.log('Running production build...'); const buildResult = await BuildSystemTestUtils.runWebpackBuild('production'); expect(buildResult.success).toBe(true); if (!buildResult.success) { console.error('Build failed:', buildResult.error); console.log('Build output:', buildResult.output); } // Check all expected bundles exist const bundleAnalysis = await BuildSystemTestUtils.analyzeBundleSizes(); for (const bundleName of BUILD_CONFIG.EXPECTED_BUNDLES) { expect(bundleAnalysis[bundleName].exists).toBe(true); expect(bundleAnalysis[bundleName].size).toBeGreaterThan(0); console.log(`โœ… ${bundleName}: ${bundleAnalysis[bundleName].sizeKB}KB`); } }); test('Development build generates readable bundles', async () => { console.log('Running development build...'); const buildResult = await BuildSystemTestUtils.runWebpackBuild('development'); expect(buildResult.success).toBe(true); // Check bundles are readable and have source maps for (const bundleName of BUILD_CONFIG.EXPECTED_BUNDLES) { const validation = await BuildSystemTestUtils.validateBundleContent(bundleName); expect(validation.isValid).toBe(true); expect(validation.hasErrors).toBe(false); // Development builds should have source maps expect(validation.hasSourceMap).toBe(true); } }); test('Bundle sizes are within performance limits', async () => { const bundleAnalysis = await BuildSystemTestUtils.analyzeBundleSizes(); // Each bundle should be under 250KB as per webpack config const MAX_BUNDLE_SIZE_KB = 250; for (const [bundleName, info] of Object.entries(bundleAnalysis)) { if (info.exists) { expect(info.sizeKB).toBeLessThanOrEqual(MAX_BUNDLE_SIZE_KB); console.log(`๐Ÿ“Š ${bundleName}: ${info.sizeKB}KB (limit: ${MAX_BUNDLE_SIZE_KB}KB)`); } } }); test('Bundles contain WordPress-compatible code', async () => { for (const bundleName of BUILD_CONFIG.EXPECTED_BUNDLES) { const validation = await BuildSystemTestUtils.validateBundleContent(bundleName); if (validation.isValid) { // Core bundle should definitely have WordPress compatibility if (bundleName.includes('core')) { expect(validation.hasWordPressCompat).toBe(true); } // No bundles should have build errors expect(validation.hasErrors).toBe(false); console.log(`โœ… ${bundleName}: WordPress compatible: ${validation.hasWordPressCompat}`); } } }); }); // ============================================================================== // SECURITY VULNERABILITY TESTS // ============================================================================== test.describe('Security Vulnerability Tests', () => { test('Manifest integrity vulnerability - XSS payload injection', async ({ page }) => { console.log('๐Ÿ”’ Testing manifest integrity vulnerability...'); // Create malicious manifest with XSS payload await BuildSystemTestUtils.createMaliciousManifest(BUILD_CONFIG.SECURITY_PAYLOADS.XSS_MANIFEST); const bundledAssetsPage = new WordPressBundledAssetsPage(page); try { // Navigate to trainer dashboard which should load bundles const result = await bundledAssetsPage.validateBundleLoadingOnPage( `${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/` ); // Check if XSS payload was executed or sanitized const pageContent = await page.content(); const hasXSSExecuted = pageContent.includes('