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; |