/** * Unified Configuration Manager for HVAC Testing Framework * * Provides centralized configuration management with environment-specific settings, * WordPress integration, and test execution parameters. * * @package HVAC_Community_Events * @version 2.0.0 * @created 2025-08-27 */ const fs = require('fs'); const path = require('path'); class ConfigManager { /** * Configuration manager instance */ static instance = null; /** * Get configuration manager instance (Singleton pattern) */ static getInstance() { if (!ConfigManager.instance) { ConfigManager.instance = new ConfigManager(); } return ConfigManager.instance; } constructor() { this.config = null; this.environment = process.env.NODE_ENV || process.env.HVAC_TEST_ENV || 'staging'; this.loadConfiguration(); } /** * Load configuration based on environment */ loadConfiguration() { const configPath = path.resolve(__dirname, '../../environments/', `${this.environment}.config.js`); try { // Load environment-specific config const envConfig = require(configPath); // Load base configuration const baseConfig = this.getBaseConfiguration(); // Merge configurations (environment overrides base) this.config = this.deepMerge(baseConfig, envConfig); console.log(`📋 Loaded configuration for environment: ${this.environment}`); } catch (error) { console.warn(`⚠️ Could not load environment config for ${this.environment}, using defaults`); this.config = this.getBaseConfiguration(); } } /** * Get base configuration that applies to all environments */ getBaseConfiguration() { return { // Test Framework Settings framework: { name: 'HVAC WordPress Testing Framework', version: '2.0.0', timeout: 30000, retries: 2, parallel: true, maxWorkers: 4, testDataIsolation: true, screenshotsOnFailure: true, videosOnFailure: true, reportFormat: 'html' }, // WordPress Integration wordpress: { cliPath: 'wp', adminPath: '/wp-admin/', loginPath: '/wp-login.php', customLoginPath: '/training-login/', apiPath: '/wp-json/', databaseIsolation: true, flushRewriteRules: true, clearCache: true }, // User Roles and Capabilities roles: { trainer: { role: 'hvac_trainer', capabilities: [ 'read', 'manage_hvac_events', 'edit_hvac_profile', 'view_hvac_dashboard', 'manage_attendees', 'email_attendees' ], defaultPage: '/trainer/dashboard/' }, masterTrainer: { role: 'hvac_master_trainer', capabilities: [ 'read', 'manage_hvac_events', 'edit_hvac_profile', 'view_hvac_dashboard', 'manage_attendees', 'email_attendees', 'manage_all_events', 'approve_trainers', 'edit_trainer_profiles', 'manage_announcements', 'view_reports', 'manage_imports' ], defaultPage: '/master-trainer/master-dashboard/' }, admin: { role: 'administrator', capabilities: ['manage_options'], defaultPage: '/wp-admin/' } }, // Browser Settings browser: { type: 'chromium', headless: process.env.CI === 'true', slowMo: process.env.CI === 'true' ? 0 : 100, viewport: { width: 1280, height: 720 }, mobileViewport: { width: 375, height: 667 }, args: [ '--disable-dev-shm-usage', '--no-sandbox', '--disable-setuid-sandbox' ] }, // Screenshot and Video Settings media: { screenshotDir: './test-results/screenshots', videoDir: './test-results/videos', screenshotMode: 'only-on-failure', videoMode: 'retain-on-failure', quality: 80, fullPage: false }, // Test Data Management testData: { seedData: true, cleanupAfterTests: true, isolateTransactions: true, backupDatabase: false, fixtures: { users: './fixtures/test-users.json', events: './fixtures/test-events.json', venues: './fixtures/test-venues.json' } }, // Selectors for stable element identification selectors: { dataTestIdAttribute: 'data-testid', roleAttribute: 'data-role', pageAttribute: 'data-page', formAttribute: 'data-form', buttonAttribute: 'data-action' }, // Reporting reporting: { outputDir: './test-results/reports', formats: ['html', 'json', 'junit'], includeScreenshots: true, includeTimings: true, groupByCategory: true } }; } /** * Get configuration value by path (e.g., 'browser.viewport.width') */ get(path, defaultValue = null) { return this.getNestedValue(this.config, path.split('.'), defaultValue); } /** * Set configuration value by path */ set(path, value) { this.setNestedValue(this.config, path.split('.'), value); } /** * Get environment name */ getEnvironment() { return this.environment; } /** * Get full configuration object */ getAll() { return { ...this.config }; } /** * Get user configuration for specific role */ getUserConfig(role) { const roleConfig = this.get(`roles.${role}`); const envConfig = this.get(`environments.users.${role}`); return { ...roleConfig, ...envConfig }; } /** * Get WordPress CLI configuration */ getWpCliConfig() { return { path: this.get('wordpress.cliPath'), url: this.get('app.baseUrl'), dbPrefix: this.get('database.prefix', 'wp_'), adminUser: this.get('environments.users.admin.username'), adminEmail: this.get('environments.users.admin.email') }; } /** * Get browser launch configuration */ getBrowserConfig() { return { ...this.get('browser'), timeout: this.get('framework.timeout'), actionTimeout: this.get('framework.timeout') * 0.75 }; } /** * Check if feature is enabled */ isFeatureEnabled(feature) { return this.get(`features.${feature}`, false); } /** * Get environment-specific database configuration */ getDatabaseConfig() { return this.get('database', {}); } /** * Get test execution parameters */ getExecutionConfig() { return { timeout: this.get('framework.timeout'), retries: this.get('framework.retries'), parallel: this.get('framework.parallel'), maxWorkers: this.get('framework.maxWorkers'), globalTimeout: this.get('framework.timeout') * 10 }; } /** * Validate configuration completeness */ validate() { const required = [ 'app.baseUrl', 'environments.users.trainer', 'environments.users.masterTrainer' ]; const missing = required.filter(path => this.get(path) === null); if (missing.length > 0) { throw new Error(`Missing required configuration: ${missing.join(', ')}`); } return true; } /** * Deep merge two configuration objects */ deepMerge(target, source) { const result = { ...target }; for (const key in source) { if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = this.deepMerge(target[key] || {}, source[key]); } else { result[key] = source[key]; } } return result; } /** * Get nested value from object by path array */ getNestedValue(obj, path, defaultValue) { let current = obj; for (const key of path) { if (current === null || current === undefined || !current.hasOwnProperty(key)) { return defaultValue; } current = current[key]; } return current; } /** * Set nested value in object by path array */ setNestedValue(obj, path, value) { let current = obj; for (let i = 0; i < path.length - 1; i++) { const key = path[i]; if (!current[key] || typeof current[key] !== 'object') { current[key] = {}; } current = current[key]; } current[path[path.length - 1]] = value; } /** * Export configuration to file for debugging */ exportToFile(filePath = null) { const outputPath = filePath || path.resolve('./test-results/config-export.json'); const configData = { environment: this.environment, timestamp: new Date().toISOString(), config: this.config }; fs.writeFileSync(outputPath, JSON.stringify(configData, null, 2)); console.log(`📁 Configuration exported to: ${outputPath}`); return outputPath; } } // Export singleton instance module.exports = ConfigManager.getInstance();