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>
755 lines
No EOL
31 KiB
JavaScript
755 lines
No EOL
31 KiB
JavaScript
/**
|
|
* Advanced E2E Test Suite for HVAC Trainer Events System
|
|
*
|
|
* This comprehensive test suite goes beyond basic functionality to test:
|
|
* - Edge cases and validation
|
|
* - Performance metrics
|
|
* - Cross-browser compatibility
|
|
* - Accessibility compliance
|
|
* - Error recovery
|
|
* - Mobile responsiveness
|
|
* - Concurrent editing
|
|
* - Data persistence
|
|
*
|
|
* Test Environment: https://upskill-staging.measurequick.com
|
|
* Test User: test_trainer / TestTrainer123!
|
|
*/
|
|
|
|
const { test, expect } = require('@playwright/test');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Test configuration
|
|
const STAGING_URL = 'https://upskill-staging.measurequick.com';
|
|
const TEST_USER = 'test_trainer';
|
|
const TEST_PASSWORD = 'TestTrainer123!';
|
|
const SCREENSHOT_DIR = path.join(__dirname, '../../reports/screenshots');
|
|
const REPORT_FILE = path.join(__dirname, '../../reports/advanced-test-report.json');
|
|
|
|
// Ensure directories exist
|
|
if (!fs.existsSync(path.dirname(SCREENSHOT_DIR))) {
|
|
fs.mkdirSync(path.dirname(SCREENSHOT_DIR), { recursive: true });
|
|
}
|
|
if (!fs.existsSync(path.dirname(REPORT_FILE))) {
|
|
fs.mkdirSync(path.dirname(REPORT_FILE), { recursive: true });
|
|
}
|
|
|
|
// Test results tracking
|
|
const testResults = {
|
|
timestamp: new Date().toISOString(),
|
|
environment: 'staging',
|
|
totalTests: 0,
|
|
passedTests: 0,
|
|
failedTests: 0,
|
|
performanceMetrics: {},
|
|
accessibilityIssues: [],
|
|
errors: [],
|
|
recommendations: [],
|
|
testMatrix: {}
|
|
};
|
|
|
|
// Utility functions
|
|
async function login(page) {
|
|
await page.goto(`${STAGING_URL}/wp-admin`);
|
|
await page.fill('#user_login', TEST_USER);
|
|
await page.fill('#user_pass', TEST_PASSWORD);
|
|
await page.click('#wp-submit');
|
|
await page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
async function capturePerformanceMetrics(page, testName) {
|
|
const performanceEntries = await page.evaluate(() => {
|
|
return JSON.stringify({
|
|
navigation: performance.getEntriesByType('navigation')[0],
|
|
paint: performance.getEntriesByType('paint'),
|
|
resource: performance.getEntriesByType('resource').length,
|
|
memory: performance.memory || null
|
|
});
|
|
});
|
|
|
|
testResults.performanceMetrics[testName] = JSON.parse(performanceEntries);
|
|
}
|
|
|
|
async function checkAccessibility(page, testName) {
|
|
try {
|
|
// Inject axe-core for accessibility testing
|
|
await page.addScriptTag({ url: 'https://unpkg.com/axe-core@4.7.0/axe.min.js' });
|
|
|
|
const accessibilityResults = await page.evaluate(() => {
|
|
return new Promise((resolve) => {
|
|
axe.run((err, results) => {
|
|
if (err) resolve({ error: err.message });
|
|
resolve(results);
|
|
});
|
|
});
|
|
});
|
|
|
|
if (accessibilityResults.violations && accessibilityResults.violations.length > 0) {
|
|
testResults.accessibilityIssues.push({
|
|
test: testName,
|
|
violations: accessibilityResults.violations.map(v => ({
|
|
id: v.id,
|
|
impact: v.impact,
|
|
description: v.description,
|
|
help: v.help,
|
|
nodes: v.nodes.length
|
|
}))
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.log(`Accessibility check failed for ${testName}: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function recordTestResult(testName, passed, error = null, screenshot = null) {
|
|
testResults.totalTests++;
|
|
if (passed) {
|
|
testResults.passedTests++;
|
|
} else {
|
|
testResults.failedTests++;
|
|
if (error) {
|
|
testResults.errors.push({
|
|
test: testName,
|
|
error: error.message || error.toString(),
|
|
screenshot
|
|
});
|
|
}
|
|
}
|
|
testResults.testMatrix[testName] = { passed, error: error?.message, screenshot };
|
|
}
|
|
|
|
// Test suite begins here
|
|
test.describe('Advanced HVAC Trainer Events System Tests', () => {
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await login(page);
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
// Generate comprehensive test report
|
|
const reportData = {
|
|
...testResults,
|
|
successRate: (testResults.passedTests / testResults.totalTests * 100).toFixed(2) + '%',
|
|
duration: new Date().toISOString()
|
|
};
|
|
|
|
fs.writeFileSync(REPORT_FILE, JSON.stringify(reportData, null, 2));
|
|
console.log('Advanced test report generated:', REPORT_FILE);
|
|
});
|
|
|
|
test('Event Creation - Minimal Required Fields', async ({ page }) => {
|
|
const testName = 'Event Creation - Minimal Required Fields';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Fill only required fields
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Minimal Test Event ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
// Submit form
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Verify success
|
|
const successMessage = await page.locator('.notice-success, .tribe-events-admin-message--success').count();
|
|
expect(successMessage).toBeGreaterThan(0);
|
|
|
|
passed = true;
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Event Creation - All Optional Fields', async ({ page }) => {
|
|
const testName = 'Event Creation - All Optional Fields';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Complete Test Event ${timestamp}`);
|
|
await page.fill('#content', 'This is a comprehensive test event with all fields filled out. It includes detailed descriptions, venue information, and all optional metadata.');
|
|
await page.fill('#EventStartDate', '2025-12-15');
|
|
await page.fill('#EventStartTime', '2:00 PM');
|
|
await page.fill('#EventEndDate', '2025-12-15');
|
|
await page.fill('#EventEndTime', '5:00 PM');
|
|
|
|
// Fill venue if available
|
|
const venueField = page.locator('#venue');
|
|
if (await venueField.count() > 0) {
|
|
await venueField.selectOption({ index: 1 });
|
|
}
|
|
|
|
// Fill organizer if available
|
|
const organizerField = page.locator('#organizer');
|
|
if (await organizerField.count() > 0) {
|
|
await organizerField.selectOption({ index: 1 });
|
|
}
|
|
|
|
// Fill cost if available
|
|
const costField = page.locator('#EventCost, input[name*="cost"]');
|
|
if (await costField.count() > 0) {
|
|
await costField.fill('150.00');
|
|
}
|
|
|
|
// Fill URL if available
|
|
const urlField = page.locator('#EventURL, input[name*="url"]');
|
|
if (await urlField.count() > 0) {
|
|
await urlField.fill('https://example.com/event');
|
|
}
|
|
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const successMessage = await page.locator('.notice-success, .tribe-events-admin-message--success').count();
|
|
expect(successMessage).toBeGreaterThan(0);
|
|
|
|
passed = true;
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Date/Time Validation - Past Dates', async ({ page }) => {
|
|
const testName = 'Date/Time Validation - Past Dates';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Past Date Test ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2020-01-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2020-01-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Look for validation error message
|
|
const errorMessage = await page.locator('.error, .notice-error, .tribe-events-admin-message--error').count();
|
|
if (errorMessage > 0) {
|
|
passed = true; // Validation working correctly
|
|
} else {
|
|
// Check if event was created despite past date (may be acceptable)
|
|
const successMessage = await page.locator('.notice-success').count();
|
|
if (successMessage > 0) {
|
|
testResults.recommendations.push('Consider adding validation for past event dates');
|
|
passed = true; // Not necessarily a failure
|
|
}
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Form Validation Messages', async ({ page }) => {
|
|
const testName = 'Form Validation Messages';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Try to submit without required fields
|
|
await page.click('text=Publish');
|
|
await page.waitForTimeout(2000); // Wait for validation
|
|
|
|
// Check for validation messages
|
|
const validationMessages = await page.locator('.error, .notice-error, input:invalid').count();
|
|
const requiredFields = await page.locator('input[required], textarea[required]').count();
|
|
|
|
if (validationMessages > 0 || requiredFields > 0) {
|
|
passed = true;
|
|
} else {
|
|
testResults.recommendations.push('Consider adding client-side form validation');
|
|
passed = false;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Event Duplication Functionality', async ({ page }) => {
|
|
const testName = 'Event Duplication Functionality';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Look for existing events or create one first
|
|
const eventLinks = await page.locator('a[href*="event"], a[href*="post.php"]').count();
|
|
|
|
if (eventLinks > 0) {
|
|
// Try to find duplicate/clone functionality
|
|
const duplicateButton = await page.locator('text=Duplicate, text=Clone, .duplicate').count();
|
|
if (duplicateButton > 0) {
|
|
await page.click('text=Duplicate, text=Clone, .duplicate').first();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if duplication worked
|
|
const titleField = await page.locator('#post_title').inputValue();
|
|
if (titleField.includes('Copy') || titleField.includes('Duplicate')) {
|
|
passed = true;
|
|
}
|
|
} else {
|
|
testResults.recommendations.push('Consider adding event duplication functionality');
|
|
passed = false;
|
|
}
|
|
} else {
|
|
testResults.recommendations.push('No events available to test duplication');
|
|
passed = false;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Event Preview Functionality', async ({ page }) => {
|
|
const testName = 'Event Preview Functionality';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Preview Test Event ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
// Look for preview button
|
|
const previewButton = await page.locator('text=Preview, #post-preview').count();
|
|
if (previewButton > 0) {
|
|
const [newPage] = await Promise.all([
|
|
page.context().waitForEvent('page'),
|
|
page.click('text=Preview, #post-preview').first()
|
|
]);
|
|
|
|
await newPage.waitForLoadState('networkidle');
|
|
const eventTitle = await newPage.locator(`text=${timestamp}`).count();
|
|
|
|
if (eventTitle > 0) {
|
|
passed = true;
|
|
}
|
|
|
|
await newPage.close();
|
|
} else {
|
|
testResults.recommendations.push('Preview functionality not found');
|
|
passed = false;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Save as Draft vs Publish', async ({ page }) => {
|
|
const testName = 'Save as Draft vs Publish';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Draft Test Event ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
// Try to save as draft first
|
|
const draftButton = await page.locator('text=Save Draft, #save-post').count();
|
|
if (draftButton > 0) {
|
|
await page.click('text=Save Draft, #save-post').first();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if saved as draft
|
|
const draftStatus = await page.locator('text=Draft').count();
|
|
if (draftStatus > 0) {
|
|
// Now try to publish
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const publishStatus = await page.locator('text=Published').count();
|
|
if (publishStatus > 0) {
|
|
passed = true;
|
|
}
|
|
}
|
|
} else {
|
|
testResults.recommendations.push('Draft functionality not found');
|
|
passed = false;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Mobile Responsiveness', async ({ page }) => {
|
|
const testName = 'Mobile Responsiveness';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
// Test different viewport sizes
|
|
const viewports = [
|
|
{ width: 375, height: 667, name: 'iPhone' },
|
|
{ width: 768, height: 1024, name: 'iPad' },
|
|
{ width: 1920, height: 1080, name: 'Desktop' }
|
|
];
|
|
|
|
let responsiveTests = 0;
|
|
|
|
for (const viewport of viewports) {
|
|
await page.setViewportSize(viewport);
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if navigation is accessible
|
|
const navElements = await page.locator('nav, .menu, .navigation').count();
|
|
if (navElements > 0) {
|
|
responsiveTests++;
|
|
}
|
|
|
|
// Check if form elements are visible and usable
|
|
if (await page.locator('text=Create Event').count() > 0) {
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const formVisible = await page.locator('#post_title').isVisible();
|
|
if (formVisible) {
|
|
responsiveTests++;
|
|
}
|
|
}
|
|
|
|
// Take screenshot for this viewport
|
|
const viewportScreenshot = path.join(SCREENSHOT_DIR, `responsive_${viewport.name}.png`);
|
|
await page.screenshot({ path: viewportScreenshot });
|
|
}
|
|
|
|
passed = responsiveTests >= (viewports.length * 2) * 0.8; // 80% success rate
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Data Persistence After Browser Refresh', async ({ page }) => {
|
|
const testName = 'Data Persistence After Browser Refresh';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
const testTitle = `Persistence Test ${timestamp}`;
|
|
await page.fill('#post_title', testTitle);
|
|
await page.fill('#content', 'This is test content for persistence testing.');
|
|
|
|
// Refresh the page
|
|
await page.reload();
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if data is still there (depends on auto-save functionality)
|
|
const titleValue = await page.locator('#post_title').inputValue();
|
|
const contentValue = await page.locator('#content').inputValue();
|
|
|
|
if (titleValue === testTitle || contentValue.includes('persistence testing')) {
|
|
passed = true;
|
|
} else {
|
|
testResults.recommendations.push('Consider implementing auto-save functionality');
|
|
passed = false;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Special Characters in Event Data', async ({ page }) => {
|
|
const testName = 'Special Characters in Event Data';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const specialTitle = `Spëcial Çharacters & Émojis 🎉 Test ${Date.now()}`;
|
|
const specialContent = 'This content contains special characters: àáâãäåæçèéêë & symbols: @#$%^&*()[]{}|;:"\',.<>?/~`';
|
|
|
|
await page.fill('#post_title', specialTitle);
|
|
await page.fill('#content', specialContent);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Check if event was created successfully
|
|
const successMessage = await page.locator('.notice-success').count();
|
|
if (successMessage > 0) {
|
|
passed = true;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Long Text Handling', async ({ page }) => {
|
|
const testName = 'Long Text Handling';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const longText = 'Lorem ipsum '.repeat(500); // Very long text
|
|
const timestamp = Date.now();
|
|
|
|
await page.fill('#post_title', `Long Text Test ${timestamp}`);
|
|
await page.fill('#content', longText);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const successMessage = await page.locator('.notice-success').count();
|
|
if (successMessage > 0) {
|
|
passed = true;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Performance Under Slow Network', async ({ page }) => {
|
|
const testName = 'Performance Under Slow Network';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
// Simulate slow 3G network
|
|
await page.context().setOffline(false);
|
|
await page.route('**', (route) => {
|
|
// Add artificial delay
|
|
setTimeout(() => route.continue(), 500);
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
const loadTime = Date.now() - startTime;
|
|
|
|
testResults.performanceMetrics[testName] = { loadTime };
|
|
|
|
// Test should pass if page loads within reasonable time (30 seconds)
|
|
if (loadTime < 30000) {
|
|
passed = true;
|
|
} else {
|
|
testResults.recommendations.push('Page load time exceeds 30 seconds under slow network');
|
|
}
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Accessibility Compliance', async ({ page }) => {
|
|
const testName = 'Accessibility Compliance';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Test keyboard navigation
|
|
await page.keyboard.press('Tab');
|
|
const focusedElement = await page.evaluate(() => document.activeElement.tagName);
|
|
|
|
if (focusedElement) {
|
|
// Run accessibility check
|
|
await checkAccessibility(page, testName);
|
|
|
|
// Check for basic accessibility features
|
|
const altTexts = await page.locator('img[alt]').count();
|
|
const labels = await page.locator('label').count();
|
|
const headings = await page.locator('h1, h2, h3, h4, h5, h6').count();
|
|
const ariaLabels = await page.locator('[aria-label]').count();
|
|
|
|
const accessibilityScore = altTexts + labels + headings + ariaLabels;
|
|
if (accessibilityScore >= 5) {
|
|
passed = true;
|
|
} else {
|
|
testResults.recommendations.push('Improve accessibility with more alt texts, labels, and ARIA attributes');
|
|
}
|
|
}
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
test('Error Recovery - Network Failure', async ({ page }) => {
|
|
const testName = 'Error Recovery - Network Failure';
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `Network Failure Test ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
// Simulate network failure
|
|
await page.context().setOffline(true);
|
|
await page.click('text=Publish');
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Check for error handling
|
|
const errorMessage = await page.locator('.error, .notice-error').count();
|
|
const networkError = await page.locator('text=network, text=offline, text=connection').count();
|
|
|
|
// Restore network
|
|
await page.context().setOffline(false);
|
|
|
|
if (errorMessage > 0 || networkError > 0) {
|
|
passed = true; // Good error handling
|
|
} else {
|
|
testResults.recommendations.push('Add network failure error handling');
|
|
passed = false;
|
|
}
|
|
} catch (testError) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
await recordTestResult(testName, passed, testError, screenshotPath);
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
// Cross-browser testing
|
|
['chromium', 'firefox', 'webkit'].forEach(browserName => {
|
|
test.describe(`Cross-browser Testing - ${browserName}`, () => {
|
|
test(`Event Creation in ${browserName}`, async ({ page, browser }) => {
|
|
const testName = `Event Creation in ${browserName}`;
|
|
let passed = false;
|
|
let screenshotPath = null;
|
|
|
|
try {
|
|
await login(page);
|
|
await page.goto(`${STAGING_URL}/trainer/dashboard/`);
|
|
await page.click('text=Create Event');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const timestamp = Date.now();
|
|
await page.fill('#post_title', `${browserName} Test Event ${timestamp}`);
|
|
await page.fill('#EventStartDate', '2025-12-01');
|
|
await page.fill('#EventStartTime', '10:00 AM');
|
|
await page.fill('#EventEndDate', '2025-12-01');
|
|
await page.fill('#EventEndTime', '11:00 AM');
|
|
|
|
await page.click('text=Publish');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const successMessage = await page.locator('.notice-success').count();
|
|
if (successMessage > 0) {
|
|
passed = true;
|
|
}
|
|
|
|
await capturePerformanceMetrics(page, testName);
|
|
} catch (error) {
|
|
screenshotPath = path.join(SCREENSHOT_DIR, `${testName.replace(/[^a-zA-Z0-9]/g, '_')}.png`);
|
|
await page.screenshot({ path: screenshotPath });
|
|
} finally {
|
|
await recordTestResult(testName, passed, error, screenshotPath);
|
|
}
|
|
});
|
|
});
|
|
}); |