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>
866 lines
No EOL
32 KiB
JavaScript
866 lines
No EOL
32 KiB
JavaScript
/**
|
||
* HVAC Trainer Events E2E Test Suite
|
||
*
|
||
* This comprehensive test suite validates the complete trainer event creation and editing workflow
|
||
* using The Events Calendar (TEC) Community Events integration.
|
||
*
|
||
* Test Coverage:
|
||
* - Trainer authentication and access control
|
||
* - Event creation through TEC integration
|
||
* - Event editing and updates
|
||
* - Event management navigation
|
||
* - Data persistence and validation
|
||
* - Form field validation and error handling
|
||
* - Dashboard integration
|
||
* - Complete trainer workflow
|
||
*/
|
||
|
||
const { chromium } = require('playwright');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// Test configuration
|
||
const CONFIG = {
|
||
baseUrl: 'https://upskill-staging.measurequick.com',
|
||
credentials: {
|
||
username: 'test_trainer',
|
||
password: 'TestTrainer123!'
|
||
},
|
||
timeouts: {
|
||
navigation: 30000,
|
||
element: 10000,
|
||
wait: 3000
|
||
},
|
||
headless: true,
|
||
screenshots: true
|
||
};
|
||
|
||
// Test data for event creation
|
||
const TEST_EVENT_DATA = {
|
||
title: `Test HVAC Training Event ${Date.now()}`,
|
||
description: 'This is a comprehensive test event for HVAC training with detailed description covering all aspects of the training program.',
|
||
excerpt: 'Brief summary of the test HVAC training event',
|
||
startDate: '2025-09-01',
|
||
startTime: '09:00',
|
||
endDate: '2025-09-01',
|
||
endTime: '17:00',
|
||
venue: 'Test Training Center',
|
||
venueAddress: '123 Training Street, Test City, Test State 12345',
|
||
category: 'Installation Training',
|
||
tags: 'hvac, installation, test',
|
||
cost: '299.00',
|
||
maxAttendees: '25'
|
||
};
|
||
|
||
// Test results tracking
|
||
const testResults = {
|
||
passed: 0,
|
||
failed: 0,
|
||
total: 0,
|
||
details: []
|
||
};
|
||
|
||
/**
|
||
* Utility function to log test results
|
||
*/
|
||
function logTest(testName, passed, details = '', screenshot = null) {
|
||
const status = passed ? '✅ PASS' : '❌ FAIL';
|
||
const timestamp = new Date().toISOString();
|
||
|
||
console.log(`[${timestamp}] ${status} ${testName}`);
|
||
if (details) {
|
||
console.log(` ${details}`);
|
||
}
|
||
|
||
testResults.total++;
|
||
if (passed) {
|
||
testResults.passed++;
|
||
} else {
|
||
testResults.failed++;
|
||
}
|
||
|
||
testResults.details.push({
|
||
name: testName,
|
||
passed,
|
||
details,
|
||
screenshot,
|
||
timestamp
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Utility function to take screenshots for debugging
|
||
*/
|
||
async function takeScreenshot(page, filename, description = '') {
|
||
if (!CONFIG.screenshots) return null;
|
||
|
||
try {
|
||
const screenshotPath = path.join(__dirname, '../../screenshots', `${filename}-${Date.now()}.png`);
|
||
|
||
// Ensure screenshots directory exists
|
||
const screenshotsDir = path.dirname(screenshotPath);
|
||
if (!fs.existsSync(screenshotsDir)) {
|
||
fs.mkdirSync(screenshotsDir, { recursive: true });
|
||
}
|
||
|
||
await page.screenshot({
|
||
path: screenshotPath,
|
||
fullPage: true
|
||
});
|
||
|
||
console.log(` 📸 Screenshot saved: ${path.basename(screenshotPath)}`);
|
||
if (description) {
|
||
console.log(` 📝 ${description}`);
|
||
}
|
||
|
||
return screenshotPath;
|
||
} catch (error) {
|
||
console.log(` ⚠️ Screenshot failed: ${error.message}`);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Wait for element with better error handling
|
||
*/
|
||
async function waitForElement(page, selector, timeout = CONFIG.timeouts.element) {
|
||
try {
|
||
await page.waitForSelector(selector, { timeout, state: 'visible' });
|
||
return true;
|
||
} catch (error) {
|
||
console.log(` ⚠️ Element not found: ${selector}`);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Login as trainer with comprehensive validation
|
||
*/
|
||
async function loginAsTrainer(page) {
|
||
console.log('\n🔐 AUTHENTICATION TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
// Navigate to login page
|
||
await page.goto(`${CONFIG.baseUrl}/trainer/training-login/`, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const loginPageLoaded = await page.locator('form').isVisible();
|
||
logTest('Login page loads', loginPageLoaded, page.url());
|
||
|
||
if (!loginPageLoaded) {
|
||
await takeScreenshot(page, 'login-page-failed', 'Login page failed to load');
|
||
return false;
|
||
}
|
||
|
||
// Fill login credentials
|
||
await page.fill('#username, #user_login, input[name="log"]', CONFIG.credentials.username);
|
||
await page.fill('#password, #user_pass, input[name="pwd"]', CONFIG.credentials.password);
|
||
|
||
// Submit login form
|
||
await page.click('input[type="submit"], button[type="submit"]');
|
||
await page.waitForTimeout(CONFIG.timeouts.wait);
|
||
|
||
// Verify login success
|
||
const currentUrl = page.url();
|
||
const isLoggedIn = currentUrl.includes('/trainer/') && !currentUrl.includes('login');
|
||
|
||
logTest('Login authentication', isLoggedIn, `Final URL: ${currentUrl}`);
|
||
|
||
if (isLoggedIn) {
|
||
await takeScreenshot(page, 'login-success', 'Successfully logged in as trainer');
|
||
return true;
|
||
} else {
|
||
await takeScreenshot(page, 'login-failed', 'Login failed - not redirected to trainer area');
|
||
return false;
|
||
}
|
||
|
||
} catch (error) {
|
||
logTest('Login process', false, `Login error: ${error.message}`);
|
||
await takeScreenshot(page, 'login-error', 'Login process encountered an error');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test trainer dashboard access and navigation
|
||
*/
|
||
async function testTrainerDashboard(page) {
|
||
console.log('\n📊 TRAINER DASHBOARD TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
await page.goto(`${CONFIG.baseUrl}/trainer/dashboard/`, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
// Check dashboard loads correctly
|
||
const dashboardTitle = await page.title();
|
||
const hasDashboardContent = await page.locator('h1, h2').first().isVisible();
|
||
|
||
logTest('Dashboard page loads', hasDashboardContent, `Title: ${dashboardTitle}`);
|
||
|
||
// Check navigation menu - use first() to avoid strict mode violation
|
||
const hasNavigation = await page.locator('.hvac-trainer-nav, .hvac-trainer-menu').first().isVisible();
|
||
logTest('Dashboard navigation menu', hasNavigation);
|
||
|
||
// Check for event management links
|
||
const hasEventManageLink = await page.locator('a[href*="event/manage"], a[href*="events"]').first().isVisible();
|
||
logTest('Event management link present', hasEventManageLink);
|
||
|
||
await takeScreenshot(page, 'dashboard-loaded', 'Trainer dashboard successfully loaded');
|
||
return true;
|
||
|
||
} catch (error) {
|
||
logTest('Dashboard access', false, `Dashboard error: ${error.message}`);
|
||
await takeScreenshot(page, 'dashboard-error', 'Dashboard access failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test event management page access
|
||
*/
|
||
async function testEventManagePage(page) {
|
||
console.log('\n📋 EVENT MANAGEMENT PAGE TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
await page.goto(`${CONFIG.baseUrl}/trainer/event/manage/`, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
// Check page loads
|
||
const pageTitle = await page.title();
|
||
const hasContent = await page.locator('h1, h2, .hvac-event-manage').first().isVisible();
|
||
|
||
logTest('Event management page loads', hasContent, `Title: ${pageTitle}`);
|
||
|
||
// Check for create event button/link
|
||
const hasCreateButton = await page.locator('a[href*="add"], a[href*="create"], button:has-text("Create")').first().isVisible();
|
||
logTest('Create event button present', hasCreateButton);
|
||
|
||
// Check for my events link
|
||
const hasMyEventsLink = await page.locator('a[href*="events"], a:has-text("My Events")').first().isVisible();
|
||
logTest('My Events link present', hasMyEventsLink);
|
||
|
||
await takeScreenshot(page, 'event-manage-page', 'Event management page loaded');
|
||
return true;
|
||
|
||
} catch (error) {
|
||
logTest('Event management page', false, `Event management error: ${error.message}`);
|
||
await takeScreenshot(page, 'event-manage-error', 'Event management page failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test event creation workflow
|
||
*/
|
||
async function testEventCreation(page) {
|
||
console.log('\n➕ EVENT CREATION TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
// Navigate to event creation page (TEC Community Events)
|
||
const createUrls = [
|
||
`${CONFIG.baseUrl}/events/network/add/`,
|
||
`${CONFIG.baseUrl}/trainer/event/create/`,
|
||
`${CONFIG.baseUrl}/events/community/add/`
|
||
];
|
||
|
||
let createPageLoaded = false;
|
||
let workingUrl = '';
|
||
|
||
// Try different URLs for event creation
|
||
for (const url of createUrls) {
|
||
try {
|
||
await page.goto(url, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const hasForm = await page.locator('form').first().isVisible({ timeout: 5000 });
|
||
if (hasForm) {
|
||
createPageLoaded = true;
|
||
workingUrl = url;
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
console.log(` ⚠️ URL not accessible: ${url}`);
|
||
}
|
||
}
|
||
|
||
logTest('Event creation page access', createPageLoaded, `Working URL: ${workingUrl}`);
|
||
|
||
if (!createPageLoaded) {
|
||
await takeScreenshot(page, 'create-event-no-access', 'Cannot access event creation page');
|
||
return null;
|
||
}
|
||
|
||
await takeScreenshot(page, 'create-event-form', 'Event creation form loaded');
|
||
|
||
// Fill out event creation form - using exact TEC field selectors from form analysis
|
||
const formFields = {
|
||
title: '#post_title',
|
||
startDate: '#EventStartDate',
|
||
startTime: '#EventStartTime',
|
||
endDate: '#EventEndDate',
|
||
endTime: '#EventEndTime',
|
||
venue: '#saved_tribe_venue',
|
||
organizer: '#saved_tribe_organizer'
|
||
};
|
||
|
||
const filledFields = {};
|
||
|
||
// Fill form fields with better error handling - use .first() to avoid strict mode violations
|
||
for (const [fieldName, selector] of Object.entries(formFields)) {
|
||
try {
|
||
const element = page.locator(selector).first();
|
||
const isVisible = await element.isVisible({ timeout: 3000 });
|
||
|
||
if (isVisible) {
|
||
let value;
|
||
switch (fieldName) {
|
||
case 'title':
|
||
value = TEST_EVENT_DATA.title;
|
||
break;
|
||
case 'startDate':
|
||
value = TEST_EVENT_DATA.startDate;
|
||
break;
|
||
case 'startTime':
|
||
value = TEST_EVENT_DATA.startTime;
|
||
break;
|
||
case 'endDate':
|
||
value = TEST_EVENT_DATA.endDate;
|
||
break;
|
||
case 'endTime':
|
||
value = TEST_EVENT_DATA.endTime;
|
||
break;
|
||
case 'venue':
|
||
// For select fields, select by index if it's a dropdown
|
||
try {
|
||
await element.selectOption({ index: 1 });
|
||
filledFields[fieldName] = true;
|
||
console.log(` ✓ ${fieldName} selected`);
|
||
continue;
|
||
} catch (e) {
|
||
value = 'Test Venue';
|
||
}
|
||
break;
|
||
case 'organizer':
|
||
// For select fields, select by index if it's a dropdown
|
||
try {
|
||
await element.selectOption({ index: 1 });
|
||
filledFields[fieldName] = true;
|
||
console.log(` ✓ ${fieldName} selected`);
|
||
continue;
|
||
} catch (e) {
|
||
value = 'Test Organizer';
|
||
}
|
||
break;
|
||
default:
|
||
value = TEST_EVENT_DATA[fieldName] || 'Test Value';
|
||
}
|
||
|
||
await element.fill(value);
|
||
filledFields[fieldName] = true;
|
||
console.log(` ✓ ${fieldName} filled: ${value}`);
|
||
} else {
|
||
console.log(` ⚠️ Field not visible: ${fieldName} (${selector})`);
|
||
}
|
||
} catch (error) {
|
||
console.log(` ⚠️ Could not fill field: ${fieldName} - ${error.message}`);
|
||
}
|
||
}
|
||
|
||
// Try to fill the description field using WordPress rich text editor
|
||
try {
|
||
// Try to interact with TinyMCE editor
|
||
await page.evaluate(() => {
|
||
// Try to set content via TinyMCE if available
|
||
if (typeof tinymce !== 'undefined' && tinymce.get('tcepostcontent')) {
|
||
tinymce.get('tcepostcontent').setContent('Test event description for automated testing');
|
||
return true;
|
||
}
|
||
// Otherwise try to set via textarea directly
|
||
const textarea = document.getElementById('tcepostcontent');
|
||
if (textarea) {
|
||
textarea.value = 'Test event description for automated testing';
|
||
textarea.dispatchEvent(new Event('input', { bubbles: true }));
|
||
return true;
|
||
}
|
||
return false;
|
||
});
|
||
|
||
filledFields.description = true;
|
||
console.log(' ✓ Description filled via editor');
|
||
} catch (error) {
|
||
console.log(' ⚠️ Could not fill description field');
|
||
}
|
||
|
||
const fieldsFilledCount = Object.keys(filledFields).length;
|
||
logTest('Form fields populated', fieldsFilledCount > 0, `${fieldsFilledCount} fields filled`);
|
||
|
||
// Handle special fields (dropdowns, etc.)
|
||
try {
|
||
// Try to set event category
|
||
const categorySelect = page.locator('select[name*="category"], #tribe_events_cat');
|
||
if (await categorySelect.isVisible({ timeout: 2000 })) {
|
||
await categorySelect.selectOption({ index: 1 });
|
||
console.log(' ✓ Category selected');
|
||
}
|
||
} catch (error) {
|
||
console.log(' ⚠️ Category field not available');
|
||
}
|
||
|
||
await takeScreenshot(page, 'create-event-filled', 'Event creation form filled out');
|
||
|
||
// Submit the form
|
||
const submitButton = page.locator('input[type="submit"], button[type="submit"], button:has-text("Create"), button:has-text("Publish")');
|
||
const hasSubmitButton = await submitButton.first().isVisible();
|
||
|
||
logTest('Submit button present', hasSubmitButton);
|
||
|
||
if (hasSubmitButton) {
|
||
await submitButton.first().click();
|
||
await page.waitForTimeout(CONFIG.timeouts.wait);
|
||
|
||
// Check for success or errors - use first() to avoid strict mode violations
|
||
const currentUrl = page.url();
|
||
const hasErrorMessage = await page.locator('.tribe-community-notice-error').first().isVisible({ timeout: 3000 });
|
||
const hasSuccessMessage = await page.locator('.tribe-community-notice-success, .success').first().isVisible({ timeout: 3000 });
|
||
|
||
const submissionSuccessful = !hasErrorMessage && (hasSuccessMessage || currentUrl !== workingUrl);
|
||
|
||
logTest('Event creation submission', submissionSuccessful,
|
||
hasErrorMessage ? 'Form has errors' : `Redirected to: ${currentUrl}`);
|
||
|
||
await takeScreenshot(page, 'create-event-submitted', 'Event creation form submitted');
|
||
|
||
// Extract event ID or URL for editing test
|
||
if (submissionSuccessful) {
|
||
const eventId = currentUrl.match(/event\/(\d+)/)?.[1] ||
|
||
currentUrl.match(/post=(\d+)/)?.[1] ||
|
||
Date.now().toString();
|
||
return eventId;
|
||
}
|
||
}
|
||
|
||
return null;
|
||
|
||
} catch (error) {
|
||
logTest('Event creation process', false, `Creation error: ${error.message}`);
|
||
await takeScreenshot(page, 'create-event-error', 'Event creation process failed');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test event editing workflow
|
||
*/
|
||
async function testEventEditing(page, eventId) {
|
||
console.log('\n✏️ EVENT EDITING TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
if (!eventId) {
|
||
logTest('Event editing setup', false, 'No event ID available for editing');
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
// Navigate to edit event page
|
||
const editUrls = [
|
||
`${CONFIG.baseUrl}/events/network/edit/?event_id=${eventId}`,
|
||
`${CONFIG.baseUrl}/events/community/edit/${eventId}/`,
|
||
`${CONFIG.baseUrl}/wp-admin/post.php?post=${eventId}&action=edit`
|
||
];
|
||
|
||
let editPageLoaded = false;
|
||
let workingUrl = '';
|
||
|
||
for (const url of editUrls) {
|
||
try {
|
||
await page.goto(url, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const hasEditForm = await page.locator('form, #post').first().isVisible({ timeout: 5000 });
|
||
if (hasEditForm) {
|
||
editPageLoaded = true;
|
||
workingUrl = url;
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
console.log(` ⚠️ Edit URL not accessible: ${url}`);
|
||
}
|
||
}
|
||
|
||
logTest('Event edit page access', editPageLoaded, `Working URL: ${workingUrl}`);
|
||
|
||
if (!editPageLoaded) {
|
||
await takeScreenshot(page, 'edit-event-no-access', 'Cannot access event edit page');
|
||
return false;
|
||
}
|
||
|
||
await takeScreenshot(page, 'edit-event-form', 'Event edit form loaded');
|
||
|
||
// Modify event data
|
||
const updatedTitle = `${TEST_EVENT_DATA.title} - EDITED`;
|
||
const titleField = page.locator('#post_title, input[name="post_title"], #title');
|
||
|
||
if (await titleField.isVisible({ timeout: 3000 })) {
|
||
await titleField.fill(updatedTitle);
|
||
console.log(' ✓ Event title updated');
|
||
}
|
||
|
||
// Update description if available
|
||
const descriptionField = page.locator('#post_content, textarea[name="post_content"]');
|
||
if (await descriptionField.isVisible({ timeout: 3000 })) {
|
||
await descriptionField.fill(TEST_EVENT_DATA.description + ' - This event has been edited.');
|
||
console.log(' ✓ Event description updated');
|
||
}
|
||
|
||
await takeScreenshot(page, 'edit-event-modified', 'Event edit form modified');
|
||
|
||
// Submit changes
|
||
const updateButton = page.locator('input[type="submit"], button[type="submit"], button:has-text("Update"), button:has-text("Save")');
|
||
const hasUpdateButton = await updateButton.first().isVisible();
|
||
|
||
logTest('Update button present', hasUpdateButton);
|
||
|
||
if (hasUpdateButton) {
|
||
await updateButton.first().click();
|
||
await page.waitForTimeout(CONFIG.timeouts.wait);
|
||
|
||
const currentUrl = page.url();
|
||
const hasErrorMessage = await page.locator('.tribe-community-notice-error').first().isVisible({ timeout: 3000 });
|
||
const updateSuccessful = !hasErrorMessage;
|
||
|
||
logTest('Event update submission', updateSuccessful,
|
||
hasErrorMessage ? 'Update has errors' : 'Update completed');
|
||
|
||
await takeScreenshot(page, 'edit-event-submitted', 'Event edit form submitted');
|
||
return updateSuccessful;
|
||
}
|
||
|
||
return false;
|
||
|
||
} catch (error) {
|
||
logTest('Event editing process', false, `Editing error: ${error.message}`);
|
||
await takeScreenshot(page, 'edit-event-error', 'Event editing process failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test My Events page functionality
|
||
*/
|
||
async function testMyEventsPage(page) {
|
||
console.log('\n📅 MY EVENTS PAGE TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
const myEventsUrls = [
|
||
`${CONFIG.baseUrl}/events/network/`,
|
||
`${CONFIG.baseUrl}/trainer/events/`,
|
||
`${CONFIG.baseUrl}/events/community/my-events/`
|
||
];
|
||
|
||
let myEventsPageLoaded = false;
|
||
let workingUrl = '';
|
||
|
||
for (const url of myEventsUrls) {
|
||
try {
|
||
await page.goto(url, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const pageTitle = await page.title();
|
||
const hasEventsList = await page.locator('.tribe-events-list, .events-list, table').first().isVisible({ timeout: 5000 });
|
||
|
||
if (pageTitle.toLowerCase().includes('events') || hasEventsList) {
|
||
myEventsPageLoaded = true;
|
||
workingUrl = url;
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
console.log(` ⚠️ My Events URL not accessible: ${url}`);
|
||
}
|
||
}
|
||
|
||
logTest('My Events page access', myEventsPageLoaded, `Working URL: ${workingUrl}`);
|
||
|
||
if (myEventsPageLoaded) {
|
||
// Count events displayed
|
||
const eventCount = await page.locator('.tribe-events-list-event, .event-row, tr').count();
|
||
logTest('Events list displayed', eventCount >= 0, `${eventCount} events found`);
|
||
|
||
// Check for event management actions
|
||
const hasEditLinks = await page.locator('a:has-text("Edit"), a[href*="edit"]').first().isVisible({ timeout: 3000 });
|
||
logTest('Event edit links present', hasEditLinks);
|
||
|
||
await takeScreenshot(page, 'my-events-page', 'My Events page loaded');
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
|
||
} catch (error) {
|
||
logTest('My Events page', false, `My Events error: ${error.message}`);
|
||
await takeScreenshot(page, 'my-events-error', 'My Events page failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test navigation between event pages
|
||
*/
|
||
async function testEventNavigation(page) {
|
||
console.log('\n🧭 EVENT NAVIGATION TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
// Test navigation from dashboard to event management
|
||
await page.goto(`${CONFIG.baseUrl}/trainer/dashboard/`, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const eventManagementLink = page.locator('a[href*="event"], a:has-text("Event"), a:has-text("Manage")').first();
|
||
const hasEventManagementLink = await eventManagementLink.isVisible({ timeout: 5000 });
|
||
|
||
logTest('Dashboard to Event Management navigation', hasEventManagementLink);
|
||
|
||
if (hasEventManagementLink) {
|
||
await eventManagementLink.click();
|
||
await page.waitForTimeout(CONFIG.timeouts.wait);
|
||
|
||
const navigationSuccessful = page.url().includes('event') || page.url().includes('manage');
|
||
logTest('Event Management navigation successful', navigationSuccessful, page.url());
|
||
}
|
||
|
||
// Test breadcrumb navigation if available
|
||
const hasBreadcrumbs = await page.locator('.breadcrumb, .hvac-breadcrumb, nav[aria-label*="breadcrumb"]').isVisible({ timeout: 3000 });
|
||
logTest('Breadcrumb navigation present', hasBreadcrumbs);
|
||
|
||
await takeScreenshot(page, 'event-navigation', 'Event navigation testing');
|
||
return true;
|
||
|
||
} catch (error) {
|
||
logTest('Event navigation', false, `Navigation error: ${error.message}`);
|
||
await takeScreenshot(page, 'navigation-error', 'Event navigation failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Test form validation and error handling
|
||
*/
|
||
async function testFormValidation(page) {
|
||
console.log('\n🔍 FORM VALIDATION TEST');
|
||
console.log('─'.repeat(50));
|
||
|
||
try {
|
||
// Navigate to event creation page
|
||
await page.goto(`${CONFIG.baseUrl}/events/network/add/`, {
|
||
waitUntil: 'networkidle',
|
||
timeout: CONFIG.timeouts.navigation
|
||
});
|
||
|
||
const hasForm = await page.locator('form').first().isVisible({ timeout: 5000 });
|
||
|
||
if (hasForm) {
|
||
// Test empty form submission
|
||
const submitButton = page.locator('input[type="submit"], button[type="submit"]').first();
|
||
if (await submitButton.isVisible()) {
|
||
await submitButton.click();
|
||
await page.waitForTimeout(CONFIG.timeouts.wait);
|
||
|
||
// Check for validation errors - be more specific to avoid strict mode violations
|
||
const hasValidationErrors = await page.locator('.tribe-community-notice-error, .required').first().isVisible({ timeout: 3000 });
|
||
logTest('Form validation on empty submission', hasValidationErrors, 'Required field validation');
|
||
|
||
// Test field-specific validation
|
||
const titleField = page.locator('#post_title, input[name="post_title"]').first();
|
||
if (await titleField.isVisible()) {
|
||
await titleField.fill('Test');
|
||
await titleField.fill(''); // Clear field
|
||
await titleField.blur();
|
||
|
||
const hasFieldError = await page.locator('.error, .invalid').first().isVisible({ timeout: 2000 });
|
||
logTest('Individual field validation', hasFieldError, 'Field-level validation working');
|
||
}
|
||
}
|
||
}
|
||
|
||
await takeScreenshot(page, 'form-validation', 'Form validation testing');
|
||
return hasForm;
|
||
|
||
} catch (error) {
|
||
logTest('Form validation', false, `Validation error: ${error.message}`);
|
||
await takeScreenshot(page, 'validation-error', 'Form validation failed');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Main test execution function
|
||
*/
|
||
async function runHVACTrainerEventsTest() {
|
||
console.log('🚀 HVAC TRAINER EVENTS E2E TEST SUITE');
|
||
console.log('═'.repeat(70));
|
||
console.log(`📅 Test Started: ${new Date().toLocaleString()}`);
|
||
console.log(`🌐 Base URL: ${CONFIG.baseUrl}`);
|
||
console.log(`👤 Test User: ${CONFIG.credentials.username}`);
|
||
console.log('═'.repeat(70));
|
||
|
||
// Browser setup
|
||
const browser = await chromium.launch({
|
||
headless: CONFIG.headless,
|
||
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
|
||
});
|
||
|
||
const context = await browser.newContext({
|
||
ignoreHTTPSErrors: true,
|
||
viewport: { width: 1920, height: 1080 },
|
||
userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||
});
|
||
|
||
const page = await context.newPage();
|
||
|
||
// Enable request/response logging for debugging
|
||
page.on('requestfailed', request => {
|
||
console.log(` ⚠️ Request failed: ${request.url()}`);
|
||
});
|
||
|
||
let eventId = null;
|
||
|
||
try {
|
||
// Test Sequence
|
||
const loginSuccessful = await loginAsTrainer(page);
|
||
|
||
if (loginSuccessful) {
|
||
await testTrainerDashboard(page);
|
||
await testEventManagePage(page);
|
||
|
||
// Event creation and editing workflow
|
||
eventId = await testEventCreation(page);
|
||
if (eventId) {
|
||
await testEventEditing(page, eventId);
|
||
}
|
||
|
||
await testMyEventsPage(page);
|
||
await testEventNavigation(page);
|
||
await testFormValidation(page);
|
||
} else {
|
||
console.log('⚠️ Skipping remaining tests due to authentication failure');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ Test suite error:', error.message);
|
||
await takeScreenshot(page, 'test-suite-error', 'Critical test suite error');
|
||
logTest('Test suite execution', false, `Critical error: ${error.message}`);
|
||
} finally {
|
||
await browser.close();
|
||
}
|
||
|
||
// Generate comprehensive test report
|
||
console.log('\n' + '═'.repeat(70));
|
||
console.log('📊 COMPREHENSIVE TEST REPORT');
|
||
console.log('═'.repeat(70));
|
||
console.log(`📅 Test Completed: ${new Date().toLocaleString()}`);
|
||
console.log(`⏱️ Total Tests: ${testResults.total}`);
|
||
console.log(`✅ Passed: ${testResults.passed}`);
|
||
console.log(`❌ Failed: ${testResults.failed}`);
|
||
|
||
if (testResults.total > 0) {
|
||
const successRate = Math.round((testResults.passed / testResults.total) * 100);
|
||
console.log(`📈 Success Rate: ${successRate}%`);
|
||
|
||
if (successRate >= 80) {
|
||
console.log('🎉 TEST SUITE: EXCELLENT PERFORMANCE');
|
||
} else if (successRate >= 60) {
|
||
console.log('⚠️ TEST SUITE: NEEDS ATTENTION');
|
||
} else {
|
||
console.log('🚨 TEST SUITE: CRITICAL ISSUES');
|
||
}
|
||
}
|
||
|
||
// Detailed results
|
||
if (testResults.failed > 0) {
|
||
console.log('\n❌ FAILED TESTS:');
|
||
console.log('─'.repeat(50));
|
||
testResults.details.filter(t => !t.passed).forEach((test, index) => {
|
||
console.log(`${index + 1}. ${test.name}`);
|
||
if (test.details) console.log(` Details: ${test.details}`);
|
||
if (test.screenshot) console.log(` Screenshot: ${path.basename(test.screenshot)}`);
|
||
});
|
||
}
|
||
|
||
if (testResults.passed > 0) {
|
||
console.log('\n✅ PASSED TESTS:');
|
||
console.log('─'.repeat(50));
|
||
testResults.details.filter(t => t.passed).forEach((test, index) => {
|
||
console.log(`${index + 1}. ${test.name}`);
|
||
if (test.details) console.log(` Details: ${test.details}`);
|
||
});
|
||
}
|
||
|
||
// Recommendations
|
||
console.log('\n🔍 RECOMMENDATIONS:');
|
||
console.log('─'.repeat(50));
|
||
|
||
const authTest = testResults.details.find(t => t.name.includes('Login authentication'));
|
||
const createTest = testResults.details.find(t => t.name.includes('Event creation'));
|
||
const editTest = testResults.details.find(t => t.name.includes('Event update'));
|
||
|
||
if (!authTest?.passed) {
|
||
console.log('• Fix trainer authentication system');
|
||
console.log('• Verify test user credentials and permissions');
|
||
}
|
||
|
||
if (!createTest?.passed) {
|
||
console.log('• Debug TEC Community Events integration');
|
||
console.log('• Check event creation form accessibility');
|
||
console.log('• Verify required plugins are active');
|
||
}
|
||
|
||
if (eventId && !editTest?.passed) {
|
||
console.log('• Fix event editing permissions');
|
||
console.log('• Debug event update workflow');
|
||
}
|
||
|
||
if (testResults.failed === 0) {
|
||
console.log('• All tests passed! System is working correctly');
|
||
console.log('• Consider adding more edge case tests');
|
||
console.log('• Monitor performance and user experience');
|
||
}
|
||
|
||
console.log('\n' + '═'.repeat(70));
|
||
console.log('🏁 TEST SUITE COMPLETE');
|
||
console.log('═'.repeat(70));
|
||
|
||
// Return exit code based on results
|
||
process.exit(testResults.failed > 0 ? 1 : 0);
|
||
}
|
||
|
||
// Execute the test suite
|
||
if (require.main === module) {
|
||
runHVACTrainerEventsTest().catch(error => {
|
||
console.error('Fatal test error:', error);
|
||
process.exit(1);
|
||
});
|
||
}
|
||
|
||
module.exports = {
|
||
runHVACTrainerEventsTest,
|
||
CONFIG,
|
||
TEST_EVENT_DATA
|
||
}; |