upskill-event-manager/tests/framework/base/BaseTest.js
Ben 7c9ca65cf2
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
feat: add comprehensive test framework and test files
- 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>
2025-08-29 23:23:26 -03:00

358 lines
No EOL
11 KiB
JavaScript

/**
* Base Test Class - Foundation for all test classes
* Provides common test functionality, setup, and teardown
*/
const { getBrowserManager } = require('../browser/BrowserManager');
const { getAuthManager } = require('../authentication/AuthManager');
const path = require('path');
const fs = require('fs').promises;
class BaseTest {
constructor(testName = 'UnnamedTest') {
this.testName = testName;
this.browserManager = getBrowserManager();
this.authManager = getAuthManager();
this.config = null;
this.testResults = [];
this.startTime = null;
this.evidenceDir = path.join(process.cwd(), 'tests/evidence');
}
/**
* Set up test environment
* @param {Object} config - Test configuration
* @returns {Promise<void>}
*/
async setUp(config = {}) {
this.config = {
environment: 'staging',
headless: true,
slowMo: 0,
timeout: 30000,
screenshotOnFailure: true,
...config
};
this.startTime = Date.now();
// Create evidence directories
await this.createEvidenceDirectories();
// Initialize browser
await this.browserManager.initialize(this.config);
// Set up auth manager
await this.authManager.initialize(this.config);
console.log(`🧪 Starting test: ${this.testName}`);
}
/**
* Clean up test environment
* @returns {Promise<void>}
*/
async tearDown() {
const endTime = Date.now();
const duration = endTime - this.startTime;
try {
// Generate test report
await this.generateTestReport(duration);
// Clean up authentication
await this.authManager.cleanup();
// Clean up browser
await this.browserManager.cleanup();
console.log(`✅ Completed test: ${this.testName} (${duration}ms)`);
} catch (error) {
console.error(`❌ Error during teardown: ${error.message}`);
}
}
/**
* Run a test step with error handling and reporting
* @param {string} stepName - Name of the test step
* @param {Function} testFunction - Test function to execute
* @param {Object} options - Test options
* @returns {Promise<*>} Test result
*/
async runTestStep(stepName, testFunction, options = {}) {
const stepStartTime = Date.now();
const stepConfig = {
screenshotOnFailure: true,
continueOnFailure: false,
...options
};
console.log(` 📋 ${stepName}`);
try {
const result = await testFunction();
const stepEndTime = Date.now();
const stepDuration = stepEndTime - stepStartTime;
this.testResults.push({
step: stepName,
status: 'passed',
duration: stepDuration,
result: result
});
console.log(` ✅ Passed (${stepDuration}ms)`);
return result;
} catch (error) {
const stepEndTime = Date.now();
const stepDuration = stepEndTime - stepStartTime;
// Take screenshot on failure if enabled
if (stepConfig.screenshotOnFailure) {
await this.takeFailureScreenshot(stepName, error);
}
this.testResults.push({
step: stepName,
status: 'failed',
duration: stepDuration,
error: error.message,
stack: error.stack
});
console.error(` ❌ Failed (${stepDuration}ms): ${error.message}`);
if (!stepConfig.continueOnFailure) {
throw error;
}
return null;
}
}
/**
* Take screenshot on test failure
* @param {string} stepName - Name of failed step
* @param {Error} error - Error that occurred
* @returns {Promise<string|null>} Path to screenshot or null
*/
async takeFailureScreenshot(stepName, error) {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `${this.testName}-${stepName}-failure-${timestamp}.png`;
const screenshotPath = await this.browserManager.takeScreenshot(filename);
console.log(` 📸 Screenshot saved: ${filename}`);
return screenshotPath;
} catch (screenshotError) {
console.warn(`Failed to take failure screenshot: ${screenshotError.message}`);
return null;
}
}
/**
* Assert condition with custom message
* @param {boolean} condition - Condition to assert
* @param {string} message - Error message if assertion fails
* @param {*} expected - Expected value
* @param {*} actual - Actual value
* @returns {void}
*/
assert(condition, message, expected = null, actual = null) {
if (!condition) {
let errorMessage = message;
if (expected !== null && actual !== null) {
errorMessage += ` Expected: ${expected}, Actual: ${actual}`;
}
throw new Error(errorMessage);
}
}
/**
* Assert that two values are equal
* @param {*} actual - Actual value
* @param {*} expected - Expected value
* @param {string} message - Custom error message
* @returns {void}
*/
assertEqual(actual, expected, message = '') {
const defaultMessage = `Values are not equal.`;
this.assert(
actual === expected,
message || defaultMessage,
expected,
actual
);
}
/**
* Assert that value is truthy
* @param {*} value - Value to check
* @param {string} message - Custom error message
* @returns {void}
*/
assertTrue(value, message = '') {
const defaultMessage = `Value is not truthy.`;
this.assert(!!value, message || defaultMessage, true, !!value);
}
/**
* Assert that value is falsy
* @param {*} value - Value to check
* @param {string} message - Custom error message
* @returns {void}
*/
assertFalse(value, message = '') {
const defaultMessage = `Value is not falsy.`;
this.assert(!value, message || defaultMessage, false, !!value);
}
/**
* Assert that element exists on page
* @param {string} selector - CSS selector
* @param {string} message - Custom error message
* @returns {Promise<void>}
*/
async assertElementExists(selector, message = '') {
const page = this.browserManager.getCurrentPage();
const exists = await page.$(selector) !== null;
const defaultMessage = `Element not found: ${selector}`;
this.assert(exists, message || defaultMessage);
}
/**
* Assert that element is visible
* @param {string} selector - CSS selector
* @param {string} message - Custom error message
* @returns {Promise<void>}
*/
async assertElementVisible(selector, message = '') {
const page = this.browserManager.getCurrentPage();
const visible = await page.isVisible(selector);
const defaultMessage = `Element not visible: ${selector}`;
this.assert(visible, message || defaultMessage);
}
/**
* Assert that element contains text
* @param {string} selector - CSS selector
* @param {string} expectedText - Expected text
* @param {string} message - Custom error message
* @returns {Promise<void>}
*/
async assertElementText(selector, expectedText, message = '') {
const page = this.browserManager.getCurrentPage();
const actualText = await page.textContent(selector);
const defaultMessage = `Element text mismatch for ${selector}`;
this.assert(
actualText && actualText.includes(expectedText),
message || defaultMessage,
expectedText,
actualText
);
}
/**
* Assert that URL matches pattern
* @param {RegExp|string} pattern - URL pattern
* @param {string} message - Custom error message
* @returns {void}
*/
assertUrlMatches(pattern, message = '') {
const page = this.browserManager.getCurrentPage();
const currentUrl = page.url();
const matches = pattern instanceof RegExp
? pattern.test(currentUrl)
: currentUrl.includes(pattern);
const defaultMessage = `URL does not match pattern. Pattern: ${pattern}, URL: ${currentUrl}`;
this.assert(matches, message || defaultMessage, pattern, currentUrl);
}
/**
* Wait with timeout
* @param {number} ms - Milliseconds to wait
* @returns {Promise<void>}
*/
async wait(ms) {
await new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Create evidence directories
* @returns {Promise<void>}
* @private
*/
async createEvidenceDirectories() {
const dirs = [
path.join(this.evidenceDir, 'screenshots'),
path.join(this.evidenceDir, 'videos'),
path.join(this.evidenceDir, 'reports'),
path.join(this.evidenceDir, 'logs')
];
for (const dir of dirs) {
try {
await fs.mkdir(dir, { recursive: true });
} catch (error) {
console.warn(`Failed to create directory ${dir}: ${error.message}`);
}
}
}
/**
* Generate test report
* @param {number} totalDuration - Total test duration
* @returns {Promise<void>}
* @private
*/
async generateTestReport(totalDuration) {
const report = {
testName: this.testName,
startTime: this.startTime,
endTime: Date.now(),
totalDuration: totalDuration,
config: this.config,
results: this.testResults,
summary: this.getTestSummary()
};
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const reportPath = path.join(
this.evidenceDir,
'reports',
`${this.testName}-${timestamp}.json`
);
try {
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
console.log(`📊 Test report saved: ${reportPath}`);
} catch (error) {
console.warn(`Failed to save test report: ${error.message}`);
}
}
/**
* Get test summary statistics
* @returns {Object} Test summary
* @private
*/
getTestSummary() {
const passed = this.testResults.filter(r => r.status === 'passed').length;
const failed = this.testResults.filter(r => r.status === 'failed').length;
const total = this.testResults.length;
return {
total: total,
passed: passed,
failed: failed,
successRate: total > 0 ? ((passed / total) * 100).toFixed(2) : '0'
};
}
}
module.exports = BaseTest;