Some checks are pending
		
		
	
	HVAC Plugin CI/CD Pipeline / Security Analysis (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Unit Tests (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Integration Tests (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Blocked by required conditions
				
			HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
				
			HVAC Plugin CI/CD Pipeline / Notification (push) Blocked by required conditions
				
			Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Waiting to run
				
			Security Monitoring & Compliance / Secrets & Credential Scan (push) Waiting to run
				
			Security Monitoring & Compliance / WordPress Security Analysis (push) Waiting to run
				
			Security Monitoring & Compliance / Static Code Security Analysis (push) Waiting to run
				
			Security Monitoring & Compliance / Security Compliance Validation (push) Waiting to run
				
			Security Monitoring & Compliance / Security Summary Report (push) Blocked by required conditions
				
			Security Monitoring & Compliance / Security Team Notification (push) Blocked by required conditions
				
			- Add 90+ test files including E2E, unit, and integration tests - Implement Page Object Model (POM) architecture - Add Docker testing environment with comprehensive services - Include modernized test framework with error recovery - Add specialized test suites for master trainer and trainer workflows - Update .gitignore to properly track test infrastructure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			411 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			411 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Base Test Class for HVAC Testing Framework
 | |
|  * 
 | |
|  * Provides standardized test foundation with:
 | |
|  * - WordPress-aware setup and teardown
 | |
|  * - Screenshot and video management
 | |
|  * - Database isolation and cleanup
 | |
|  * - Error handling and recovery
 | |
|  * - Consistent test reporting
 | |
|  * 
 | |
|  * @package HVAC_Community_Events
 | |
|  * @version 2.0.0
 | |
|  * @created 2025-08-27
 | |
|  */
 | |
| 
 | |
| const { test, expect } = require('@playwright/test');
 | |
| const fs = require('fs').promises;
 | |
| const path = require('path');
 | |
| const ConfigManager = require('./ConfigManager');
 | |
| const AuthManager = require('./AuthManager');
 | |
| const WordPressUtils = require('../utils/WordPressUtils');
 | |
| const ScreenshotManager = require('../utils/ScreenshotManager');
 | |
| 
 | |
| class BaseTest {
 | |
|     constructor() {
 | |
|         this.config = ConfigManager;
 | |
|         this.authManager = AuthManager;
 | |
|         this.wpUtils = new WordPressUtils();
 | |
|         this.screenshotManager = new ScreenshotManager();
 | |
|         this.testStartTime = null;
 | |
|         this.testContext = null;
 | |
|         this.testMetadata = {};
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Configure test with metadata and options
 | |
|      */
 | |
|     configure(metadata = {}) {
 | |
|         this.testMetadata = {
 | |
|             category: metadata.category || 'general',
 | |
|             priority: metadata.priority || 'medium',
 | |
|             tags: metadata.tags || [],
 | |
|             requirements: metadata.requirements || [],
 | |
|             author: metadata.author || 'automated',
 | |
|             ...metadata
 | |
|         };
 | |
|         
 | |
|         return this;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Standard test setup - call this in beforeEach
 | |
|      */
 | |
|     async setup(page, testInfo) {
 | |
|         this.testStartTime = Date.now();
 | |
|         this.testContext = {
 | |
|             page,
 | |
|             testInfo,
 | |
|             testId: this.generateTestId(testInfo),
 | |
|             screenshotPath: this.getScreenshotPath(testInfo)
 | |
|         };
 | |
|         
 | |
|         // Set up error handling
 | |
|         this.setupErrorHandling(page);
 | |
|         
 | |
|         // Set up WordPress environment
 | |
|         await this.setupWordPressEnvironment(page);
 | |
|         
 | |
|         // Initialize test data if needed
 | |
|         if (this.config.get('testData.seedData')) {
 | |
|             await this.setupTestData();
 | |
|         }
 | |
|         
 | |
|         // Take initial screenshot if configured
 | |
|         if (this.config.get('media.screenshotMode') === 'all') {
 | |
|             await this.screenshotManager.capture(page, 'test-start', this.testContext.screenshotPath);
 | |
|         }
 | |
|         
 | |
|         console.log(`🧪 Test setup complete: ${testInfo.title}`);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Standard test teardown - call this in afterEach
 | |
|      */
 | |
|     async teardown(page, testInfo) {
 | |
|         const testDuration = Date.now() - this.testStartTime;
 | |
|         const testPassed = testInfo.status === 'passed';
 | |
|         
 | |
|         try {
 | |
|             // Take final screenshot if test failed or if configured for all tests
 | |
|             const screenshotMode = this.config.get('media.screenshotMode');
 | |
|             if (!testPassed || screenshotMode === 'all') {
 | |
|                 await this.screenshotManager.capture(
 | |
|                     page, 
 | |
|                     testPassed ? 'test-end' : 'test-failure', 
 | |
|                     this.testContext.screenshotPath
 | |
|                 );
 | |
|             }
 | |
|             
 | |
|             // Clean up test data if configured
 | |
|             if (this.config.get('testData.cleanupAfterTests')) {
 | |
|                 await this.cleanupTestData();
 | |
|             }
 | |
|             
 | |
|             // Log test completion
 | |
|             const status = testPassed ? '✅ PASSED' : '❌ FAILED';
 | |
|             console.log(`${status} ${testInfo.title} (${testDuration}ms)`);
 | |
|             
 | |
|             // Record test metrics
 | |
|             await this.recordTestMetrics(testInfo, testDuration, testPassed);
 | |
|             
 | |
|         } catch (error) {
 | |
|             console.error('Error during test teardown:', error.message);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Set up error handling for the page
 | |
|      */
 | |
|     setupErrorHandling(page) {
 | |
|         // Handle console errors
 | |
|         page.on('console', msg => {
 | |
|             if (msg.type() === 'error') {
 | |
|                 console.error(`🔴 Console Error: ${msg.text()}`);
 | |
|             }
 | |
|         });
 | |
|         
 | |
|         // Handle page errors
 | |
|         page.on('pageerror', error => {
 | |
|             console.error(`🔴 Page Error: ${error.message}`);
 | |
|         });
 | |
|         
 | |
|         // Handle failed requests (optional)
 | |
|         page.on('requestfailed', request => {
 | |
|             if (request.url().includes(this.config.get('app.baseUrl'))) {
 | |
|                 console.warn(`⚠️ Request Failed: ${request.method()} ${request.url()}`);
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Set up WordPress environment for test
 | |
|      */
 | |
|     async setupWordPressEnvironment(page) {
 | |
|         // Set WordPress-specific headers and context
 | |
|         await page.setExtraHTTPHeaders({
 | |
|             'User-Agent': 'HVAC-Testing-Framework/2.0 Playwright',
 | |
|             'X-Test-Framework': 'HVAC-WordPress-Tests'
 | |
|         });
 | |
|         
 | |
|         // Add WordPress helper scripts
 | |
|         await page.addInitScript(`
 | |
|             window.HVAC_TEST_MODE = true;
 | |
|             window.HVAC_TEST_ID = '${this.testContext.testId}';
 | |
|             window.HVAC_TEST_START = ${this.testStartTime};
 | |
|             
 | |
|             // WordPress-specific test helpers
 | |
|             window.hvacTestHelpers = {
 | |
|                 waitForAjax: function() {
 | |
|                     return new Promise(resolve => {
 | |
|                         if (typeof jQuery !== 'undefined' && jQuery.active === 0) {
 | |
|                             resolve();
 | |
|                         } else if (typeof jQuery !== 'undefined') {
 | |
|                             jQuery(document).ajaxStop(resolve);
 | |
|                         } else {
 | |
|                             // Fallback for non-jQuery AJAX
 | |
|                             setTimeout(resolve, 100);
 | |
|                         }
 | |
|                     });
 | |
|                 },
 | |
|                 
 | |
|                 waitForWordPress: function() {
 | |
|                     return new Promise(resolve => {
 | |
|                         if (document.readyState === 'complete') {
 | |
|                             resolve();
 | |
|                         } else {
 | |
|                             window.addEventListener('load', resolve);
 | |
|                         }
 | |
|                     });
 | |
|                 },
 | |
|                 
 | |
|                 triggerWordPressEvent: function(eventName, data) {
 | |
|                     if (typeof jQuery !== 'undefined') {
 | |
|                         jQuery(document).trigger(eventName, data);
 | |
|                     }
 | |
|                 }
 | |
|             };
 | |
|         `);
 | |
|         
 | |
|         // Flush rewrite rules if configured
 | |
|         if (this.config.get('wordpress.flushRewriteRules')) {
 | |
|             await this.wpUtils.flushRewriteRules();
 | |
|         }
 | |
|         
 | |
|         // Clear cache if configured
 | |
|         if (this.config.get('wordpress.clearCache')) {
 | |
|             await this.wpUtils.clearCache();
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Set up test data for the test
 | |
|      */
 | |
|     async setupTestData() {
 | |
|         // This method can be overridden by specific test classes
 | |
|         // to set up role-specific test data
 | |
|         
 | |
|         console.log('📋 Setting up test data...');
 | |
|         
 | |
|         // Load fixtures if available
 | |
|         const fixtures = this.config.get('testData.fixtures');
 | |
|         if (fixtures && fixtures.users) {
 | |
|             await this.loadUserFixtures(fixtures.users);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Clean up test data after test
 | |
|      */
 | |
|     async cleanupTestData() {
 | |
|         console.log('🧹 Cleaning up test data...');
 | |
|         
 | |
|         // Clean up test-specific data
 | |
|         await this.wpUtils.cleanupTestData(this.testContext.testId);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Load user fixtures for testing
 | |
|      */
 | |
|     async loadUserFixtures(fixturesPath) {
 | |
|         try {
 | |
|             const fixtures = JSON.parse(await fs.readFile(fixturesPath, 'utf8'));
 | |
|             // Process user fixtures as needed
 | |
|             console.log(`📂 Loaded user fixtures: ${Object.keys(fixtures).length} users`);
 | |
|         } catch (error) {
 | |
|             console.warn('Could not load user fixtures:', error.message);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Authenticate as specific user role
 | |
|      */
 | |
|     async authenticateAs(page, role, options = {}) {
 | |
|         console.log(`🔐 Authenticating as: ${role}`);
 | |
|         return await this.authManager.authenticate(page, role, options);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Wait for WordPress-specific conditions
 | |
|      */
 | |
|     async waitForWordPress(page, condition = 'ready') {
 | |
|         switch (condition) {
 | |
|             case 'ready':
 | |
|                 await page.waitForFunction('document.readyState === "complete"');
 | |
|                 break;
 | |
|                 
 | |
|             case 'ajax':
 | |
|                 await page.waitForFunction(`
 | |
|                     window.hvacTestHelpers && window.hvacTestHelpers.waitForAjax()
 | |
|                 `);
 | |
|                 break;
 | |
|                 
 | |
|             case 'dom-ready':
 | |
|                 await page.waitForFunction('document.readyState !== "loading"');
 | |
|                 break;
 | |
|                 
 | |
|             case 'network-idle':
 | |
|                 await page.waitForLoadState('networkidle');
 | |
|                 break;
 | |
|                 
 | |
|             default:
 | |
|                 throw new Error(`Unknown WordPress condition: ${condition}`);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Take screenshot with context
 | |
|      */
 | |
|     async takeScreenshot(page, name = 'screenshot', fullPage = false) {
 | |
|         return await this.screenshotManager.capture(
 | |
|             page, 
 | |
|             name, 
 | |
|             this.testContext.screenshotPath,
 | |
|             { fullPage }
 | |
|         );
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Assert WordPress-specific conditions
 | |
|      */
 | |
|     async assertWordPressState(page, assertions) {
 | |
|         if (assertions.authenticated) {
 | |
|             // Check if user is authenticated
 | |
|             const isLoggedIn = await page.evaluate(() => {
 | |
|                 return document.body.classList.contains('logged-in') ||
 | |
|                        document.querySelector('#wpadminbar') !== null ||
 | |
|                        window.location.pathname.includes('/wp-admin/');
 | |
|             });
 | |
|             expect(isLoggedIn).toBe(true);
 | |
|         }
 | |
|         
 | |
|         if (assertions.role) {
 | |
|             // Verify user has expected role
 | |
|             const hasRole = await page.evaluate((expectedRole) => {
 | |
|                 return document.body.classList.contains(`role-${expectedRole}`) ||
 | |
|                        window.HVAC_AUTH_ROLE === expectedRole;
 | |
|             }, assertions.role);
 | |
|             expect(hasRole).toBe(true);
 | |
|         }
 | |
|         
 | |
|         if (assertions.capability) {
 | |
|             // Check if user has specific capability
 | |
|             await expect(page.locator(`[data-capability="${assertions.capability}"]`)).toBeVisible();
 | |
|         }
 | |
|         
 | |
|         if (assertions.nonce) {
 | |
|             // Verify WordPress nonce is present
 | |
|             const nonceExists = await page.evaluate((nonceName) => {
 | |
|                 return document.querySelector(`[name="_wpnonce"]`) !== null ||
 | |
|                        document.querySelector(`[name="${nonceName}"]`) !== null;
 | |
|             }, assertions.nonce);
 | |
|             expect(nonceExists).toBe(true);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Generate unique test ID
 | |
|      */
 | |
|     generateTestId(testInfo) {
 | |
|         const timestamp = Date.now();
 | |
|         const testName = testInfo.title.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
 | |
|         return `hvac-test-${testName}-${timestamp}`;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get screenshot path for test
 | |
|      */
 | |
|     getScreenshotPath(testInfo) {
 | |
|         const screenshotDir = this.config.get('media.screenshotDir');
 | |
|         const testName = testInfo.title.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
 | |
|         const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
 | |
|         return path.join(screenshotDir, `${testName}-${timestamp}`);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Record test metrics for reporting
 | |
|      */
 | |
|     async recordTestMetrics(testInfo, duration, passed) {
 | |
|         const metrics = {
 | |
|             testId: this.testContext.testId,
 | |
|             title: testInfo.title,
 | |
|             category: this.testMetadata.category,
 | |
|             priority: this.testMetadata.priority,
 | |
|             tags: this.testMetadata.tags,
 | |
|             duration,
 | |
|             passed,
 | |
|             timestamp: new Date().toISOString(),
 | |
|             environment: this.config.getEnvironment(),
 | |
|             metadata: this.testMetadata
 | |
|         };
 | |
|         
 | |
|         // Write metrics to results directory
 | |
|         const resultsDir = this.config.get('reporting.outputDir');
 | |
|         const metricsFile = path.join(resultsDir, 'test-metrics.jsonl');
 | |
|         
 | |
|         try {
 | |
|             await fs.mkdir(resultsDir, { recursive: true });
 | |
|             await fs.appendFile(metricsFile, JSON.stringify(metrics) + '\n');
 | |
|         } catch (error) {
 | |
|             console.warn('Could not record test metrics:', error.message);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Helper method to create WordPress-aware test
 | |
|      */
 | |
|     static create(title, testFn, metadata = {}) {
 | |
|         return test(title, async ({ page }, testInfo) => {
 | |
|             const baseTest = new BaseTest();
 | |
|             baseTest.configure(metadata);
 | |
|             
 | |
|             try {
 | |
|                 await baseTest.setup(page, testInfo);
 | |
|                 await testFn(page, testInfo, baseTest);
 | |
|             } finally {
 | |
|                 await baseTest.teardown(page, testInfo);
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Helper method to create WordPress-aware describe block
 | |
|      */
 | |
|     static describe(title, testSuite, metadata = {}) {
 | |
|         return test.describe(title, () => {
 | |
|             // Set up common hooks for the test suite
 | |
|             test.beforeAll(async () => {
 | |
|                 console.log(`📦 Starting test suite: ${title}`);
 | |
|             });
 | |
|             
 | |
|             test.afterAll(async () => {
 | |
|                 console.log(`📦 Completed test suite: ${title}`);
 | |
|             });
 | |
|             
 | |
|             // Run the test suite
 | |
|             testSuite();
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| module.exports = BaseTest; |