Some checks failed
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Has been cancelled
Security Monitoring & Compliance / Secrets & Credential Scan (push) Has been cancelled
Security Monitoring & Compliance / WordPress Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Has been cancelled
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Has been cancelled
Security Monitoring & Compliance / Static Code Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Security Compliance Validation (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Notification (push) Has been cancelled
Security Monitoring & Compliance / Security Summary Report (push) Has been cancelled
Security Monitoring & Compliance / Security Team Notification (push) Has been cancelled
- Deploy 6 simultaneous WordPress specialized agents using sequential thinking and Zen MCP - Resolve all critical issues: permissions, jQuery dependencies, CDN mapping, security vulnerabilities - Implement bulletproof jQuery loading system with WordPress hook timing fixes - Create professional MapGeo Safety system with CDN health monitoring and fallback UI - Fix privilege escalation vulnerability with capability-based authorization - Add complete announcement admin system with modal forms and AJAX handling - Enhance import/export functionality (54 trainers successfully exported) - Achieve 100% operational master trainer functionality verified via MCP Playwright E2E testing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
892 lines
No EOL
33 KiB
JavaScript
892 lines
No EOL
33 KiB
JavaScript
/**
|
|
* 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": "<script>alert(\'XSS\')</script>"}',
|
|
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<Object>}
|
|
*/
|
|
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('<script>alert(') ||
|
|
result.loadingErrors.some(err => err.includes('alert'));
|
|
|
|
// VULNERABILITY: XSS payload should NOT execute
|
|
expect(hasXSSExecuted).toBe(false);
|
|
|
|
console.log('Loaded bundles:', result.loadedBundles.length);
|
|
console.log('Loading errors:', result.loadingErrors.length);
|
|
|
|
// Should gracefully handle invalid manifest
|
|
expect(result.loadingErrors).not.toContain(expect.stringMatching(/XSS|alert|script/i));
|
|
|
|
} finally {
|
|
// Always restore manifest
|
|
await BuildSystemTestUtils.restoreManifest();
|
|
}
|
|
});
|
|
|
|
test('User agent security vulnerability - injection attack', async ({ page }) => {
|
|
console.log('🔒 Testing user agent injection vulnerability...');
|
|
|
|
// Set malicious user agent
|
|
await page.setExtraHTTPHeaders({
|
|
'User-Agent': BUILD_CONFIG.SECURITY_PAYLOADS.MALICIOUS_USER_AGENT
|
|
});
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Check for signs of code injection
|
|
const pageContent = await page.content();
|
|
const hasCodeInjection = pageContent.includes('maliciousScript()') ||
|
|
result.loadingErrors.some(err => err.includes('maliciousScript'));
|
|
|
|
// VULNERABILITY: Malicious code should NOT execute
|
|
expect(hasCodeInjection).toBe(false);
|
|
|
|
// Should not crash the page
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
console.log(`Page loaded successfully with suspicious user agent`);
|
|
});
|
|
|
|
test('Missing bundle validation - file not found handling', async ({ page }) => {
|
|
console.log('🔒 Testing missing bundle validation...');
|
|
|
|
// Remove critical core bundle
|
|
const backups = await BuildSystemTestUtils.removeBundles(['hvac-core.bundle.js']);
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
|
|
try {
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// VULNERABILITY: Should gracefully handle missing bundles
|
|
// Page should still load (potentially with degraded functionality)
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
// Should have loading errors but not crash
|
|
const has404Errors = result.loadedBundles.some(bundle => bundle.status === 404);
|
|
if (has404Errors) {
|
|
console.log('Expected 404 errors for missing bundles detected');
|
|
}
|
|
|
|
// Check if page has fallback functionality
|
|
const pageContent = await page.content();
|
|
const hasBasicContent = pageContent.includes('trainer') || pageContent.includes('dashboard');
|
|
expect(hasBasicContent).toBe(true);
|
|
|
|
console.log('Page survived missing core bundle');
|
|
|
|
} finally {
|
|
// Restore removed bundles
|
|
await BuildSystemTestUtils.restoreBundles(backups);
|
|
}
|
|
});
|
|
|
|
test('Manifest tampering - path traversal attack', async ({ page }) => {
|
|
console.log('🔒 Testing manifest path traversal vulnerability...');
|
|
|
|
// Create manifest with path traversal payload
|
|
const traversalManifest = JSON.stringify({
|
|
'hvac-core.js': BUILD_CONFIG.SECURITY_PAYLOADS.PATH_TRAVERSAL + '/malicious.js'
|
|
});
|
|
|
|
await BuildSystemTestUtils.createMaliciousManifest(traversalManifest);
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
|
|
try {
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Check if path traversal was attempted
|
|
const suspiciousRequests = result.loadedBundles.filter(bundle =>
|
|
bundle.url.includes('../') || bundle.url.includes('/etc/')
|
|
);
|
|
|
|
// VULNERABILITY: Path traversal should be blocked
|
|
expect(suspiciousRequests.length).toBe(0);
|
|
|
|
// Page should still load safely
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
console.log('Path traversal attack blocked successfully');
|
|
|
|
} finally {
|
|
await BuildSystemTestUtils.restoreManifest();
|
|
}
|
|
});
|
|
});
|
|
|
|
// ==============================================================================
|
|
// WORDPRESS INTEGRATION TESTS
|
|
// ==============================================================================
|
|
|
|
test.describe('WordPress Integration Tests', () => {
|
|
|
|
test('HVAC_Bundled_Assets class loads correct bundles for trainer dashboard', async ({ page }) => {
|
|
console.log('🔧 Testing bundle loading on trainer dashboard...');
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Should load core bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-core.bundle.js')).toBe(true);
|
|
|
|
// Should load dashboard bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-dashboard.bundle.js')).toBe(true);
|
|
|
|
// Should load trainer bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-trainer.bundle.js')).toBe(true);
|
|
|
|
console.log(`✅ Loaded ${result.loadedBundles.length} bundles successfully`);
|
|
console.log('Bundle URLs:', result.loadedBundles.map(b => b.url.split('/').pop()));
|
|
|
|
// No loading errors
|
|
expect(result.loadingErrors.length).toBe(0);
|
|
});
|
|
|
|
test('HVAC_Bundled_Assets class loads correct bundles for master trainer pages', async ({ page }) => {
|
|
console.log('🔧 Testing bundle loading on master trainer dashboard...');
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/master-trainer/master-dashboard/`
|
|
);
|
|
|
|
// Should load core bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-core.bundle.js')).toBe(true);
|
|
|
|
// Should load master bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-master.bundle.js')).toBe(true);
|
|
|
|
console.log(`✅ Master trainer loaded ${result.loadedBundles.length} bundles`);
|
|
|
|
// No loading errors
|
|
expect(result.loadingErrors.length).toBe(0);
|
|
});
|
|
|
|
test('Safari compatibility bundle loads for Safari browsers', async ({ page, browserName }) => {
|
|
if (browserName !== 'webkit') {
|
|
test.skip('Safari compatibility test only runs on WebKit/Safari');
|
|
}
|
|
|
|
console.log('🦎 Testing Safari compatibility bundle loading...');
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Should load Safari compatibility bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-safari-compat.bundle.js')).toBe(true);
|
|
|
|
console.log('✅ Safari compatibility bundle loaded');
|
|
});
|
|
|
|
test('Bundle localization data is properly injected', async ({ page }) => {
|
|
console.log('🌐 Testing bundle localization...');
|
|
|
|
await page.goto(`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if hvacBundleData is available
|
|
const localizationData = await page.evaluate(() => {
|
|
return window.hvacBundleData || null;
|
|
});
|
|
|
|
expect(localizationData).toBeTruthy();
|
|
expect(localizationData.ajax_url).toBeTruthy();
|
|
expect(localizationData.nonce).toBeTruthy();
|
|
expect(localizationData.rest_url).toBeTruthy();
|
|
|
|
console.log('✅ Localization data:', Object.keys(localizationData));
|
|
});
|
|
});
|
|
|
|
// ==============================================================================
|
|
// PERFORMANCE & COMPATIBILITY TESTS
|
|
// ==============================================================================
|
|
|
|
test.describe('Performance & Compatibility Tests', () => {
|
|
|
|
test('Bundle loading performance benchmarks', async ({ page }) => {
|
|
console.log('⚡ Testing bundle loading performance...');
|
|
|
|
const startTime = Date.now();
|
|
|
|
await page.goto(`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
console.log(`Page loaded in ${loadTime}ms`);
|
|
|
|
// Should load within reasonable time (adjust based on environment)
|
|
expect(loadTime).toBeLessThan(10000); // 10 seconds max
|
|
|
|
// Check bundle sizes loaded
|
|
const bundleRequests = await page.evaluate(() => {
|
|
return performance.getEntriesByType('resource')
|
|
.filter(entry => entry.name.includes('.bundle.js'))
|
|
.map(entry => ({
|
|
name: entry.name.split('/').pop(),
|
|
size: entry.transferSize,
|
|
loadTime: entry.duration
|
|
}));
|
|
});
|
|
|
|
console.log('Bundle performance:', bundleRequests);
|
|
|
|
// Each bundle should load reasonably fast
|
|
bundleRequests.forEach(bundle => {
|
|
expect(bundle.loadTime).toBeLessThan(3000); // 3 seconds per bundle
|
|
});
|
|
});
|
|
|
|
test('Cross-browser bundle compatibility', async ({ page, browserName }) => {
|
|
console.log(`🌐 Testing bundle compatibility on ${browserName}...`);
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Should load without JavaScript errors
|
|
expect(result.loadingErrors.length).toBe(0);
|
|
|
|
// Should load at least core bundle
|
|
expect(bundledAssetsPage.isBundleLoaded('hvac-core.bundle.js')).toBe(true);
|
|
|
|
// Test basic JavaScript functionality
|
|
const jsWorking = await page.evaluate(() => {
|
|
return typeof jQuery !== 'undefined' && typeof $ !== 'undefined';
|
|
});
|
|
|
|
expect(jsWorking).toBe(true);
|
|
|
|
console.log(`✅ ${browserName} compatibility confirmed`);
|
|
});
|
|
|
|
test('Bundle caching behavior', async ({ page }) => {
|
|
console.log('💾 Testing bundle caching...');
|
|
|
|
// First load
|
|
await page.goto(`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const firstLoadRequests = await page.evaluate(() => {
|
|
return performance.getEntriesByType('resource')
|
|
.filter(entry => entry.name.includes('.bundle.js'))
|
|
.length;
|
|
});
|
|
|
|
// Reload page
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const reloadRequests = await page.evaluate(() => {
|
|
return performance.getEntriesByType('resource')
|
|
.filter(entry => entry.name.includes('.bundle.js'))
|
|
.length;
|
|
});
|
|
|
|
console.log(`First load: ${firstLoadRequests} requests, Reload: ${reloadRequests} requests`);
|
|
|
|
// Should have bundle requests on both loads (caching depends on server config)
|
|
expect(firstLoadRequests).toBeGreaterThan(0);
|
|
expect(reloadRequests).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// ==============================================================================
|
|
// ERROR SCENARIO TESTS
|
|
// ==============================================================================
|
|
|
|
test.describe('Error Scenario & Graceful Degradation Tests', () => {
|
|
|
|
test('Handles corrupted manifest.json gracefully', async ({ page }) => {
|
|
console.log('🚨 Testing corrupted manifest handling...');
|
|
|
|
// Create corrupted manifest
|
|
await BuildSystemTestUtils.createMaliciousManifest('corrupted-json{invalid');
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
|
|
try {
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Page should still load (fallback to expected filenames)
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
// Should attempt to load bundles with fallback naming
|
|
const loadAttempts = result.loadedBundles.length +
|
|
result.loadingErrors.filter(err => err.includes('bundle')).length;
|
|
|
|
expect(loadAttempts).toBeGreaterThan(0);
|
|
|
|
console.log('✅ Gracefully handled corrupted manifest');
|
|
|
|
} finally {
|
|
await BuildSystemTestUtils.restoreManifest();
|
|
}
|
|
});
|
|
|
|
test('Handles missing dist directory', async ({ page }) => {
|
|
console.log('🚨 Testing missing dist directory...');
|
|
|
|
// Rename dist directory temporarily
|
|
const distBackupPath = BUILD_CONFIG.BUILD_OUTPUT + '.backup';
|
|
|
|
try {
|
|
await fs.rename(BUILD_CONFIG.BUILD_OUTPUT, distBackupPath);
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Page should still load with graceful degradation
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
// Should have 404 errors for missing bundles
|
|
const has404s = result.loadedBundles.some(bundle => bundle.status === 404);
|
|
console.log('404 errors detected:', has404s);
|
|
|
|
// Basic page content should still be accessible
|
|
const pageContent = await page.content();
|
|
expect(pageContent.length).toBeGreaterThan(100);
|
|
|
|
console.log('✅ Page survived missing dist directory');
|
|
|
|
} finally {
|
|
// Restore dist directory
|
|
try {
|
|
await fs.rename(distBackupPath, BUILD_CONFIG.BUILD_OUTPUT);
|
|
} catch (error) {
|
|
console.warn('Could not restore dist directory:', error.message);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('Handles JavaScript errors in bundles', async ({ page }) => {
|
|
console.log('🚨 Testing JavaScript error handling...');
|
|
|
|
// Monitor for JavaScript errors
|
|
const jsErrors = [];
|
|
page.on('pageerror', (error) => {
|
|
jsErrors.push(error.message);
|
|
});
|
|
|
|
await page.goto(`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Inject a JavaScript error to test error handling
|
|
await page.evaluate(() => {
|
|
if (window.hvacBundleData) {
|
|
try {
|
|
// This should cause an error but not crash the page
|
|
throw new Error('Test bundle error');
|
|
} catch (e) {
|
|
console.error('Bundle error caught:', e.message);
|
|
}
|
|
}
|
|
});
|
|
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Page should still be functional despite errors
|
|
const pageTitle = await page.title();
|
|
expect(pageTitle).toBeTruthy();
|
|
|
|
// Basic navigation should work
|
|
const navigationExists = await page.locator('nav, .navigation, .menu').count();
|
|
expect(navigationExists).toBeGreaterThan(0);
|
|
|
|
console.log(`✅ Page remained functional with ${jsErrors.length} JS errors`);
|
|
});
|
|
|
|
test('Network failure during bundle loading', async ({ page }) => {
|
|
console.log('🚨 Testing network failure during bundle loading...');
|
|
|
|
// Simulate network failure for bundle requests
|
|
await page.route('**/assets/js/dist/*.bundle.js', (route) => {
|
|
// Fail 50% of bundle requests to simulate network issues
|
|
if (Math.random() < 0.5) {
|
|
route.abort('failed');
|
|
} else {
|
|
route.continue();
|
|
}
|
|
});
|
|
|
|
const bundledAssetsPage = new WordPressBundledAssetsPage(page);
|
|
const result = await bundledAssetsPage.validateBundleLoadingOnPage(
|
|
`${process.env.BASE_URL || 'http://localhost:8080'}/trainer/dashboard/`
|
|
);
|
|
|
|
// Some bundles should fail, some should succeed
|
|
const failedBundles = result.loadedBundles.filter(b => !b.success);
|
|
const successBundles = result.loadedBundles.filter(b => b.success);
|
|
|
|
console.log(`Failed: ${failedBundles.length}, Succeeded: ${successBundles.length}`);
|
|
|
|
// Page should still load with partial functionality
|
|
expect(result.pageTitle).toBeTruthy();
|
|
|
|
// Should have some loading errors but not completely crash
|
|
expect(result.loadingErrors.length).toBeGreaterThan(0);
|
|
|
|
console.log('✅ Partial network failure handled gracefully');
|
|
|
|
// Clean up route override
|
|
await page.unroute('**/assets/js/dist/*.bundle.js');
|
|
});
|
|
});
|
|
|
|
console.log('🧪 HVAC Build System Test Suite Loaded');
|
|
console.log('📊 Test Coverage:');
|
|
console.log(' ✅ Build system validation (webpack, bundles, sizes)');
|
|
console.log(' 🔒 Security vulnerability testing (manifest, user agent, path traversal)');
|
|
console.log(' 🔧 WordPress integration (bundle loading, localization)');
|
|
console.log(' ⚡ Performance & compatibility (loading times, cross-browser)');
|
|
console.log(' 🚨 Error scenarios (corruption, missing files, network failures)'); |