upskill-event-manager/tests/page-objects/event-management/EventCreation.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

694 lines
No EOL
23 KiB
JavaScript

/**
* Event Creation Page Object Model
*
* Handles event creation functionality including:
* - Standard HVAC event creation
* - TEC (The Events Calendar) integration
* - Form validation and error handling
* - Event data population and submission
*
* @package HVAC_Community_Events
* @version 2.0.0
* @created 2025-08-27
* @author Agent-B-Event-Management
*/
const BasePage = require('../base/BasePage');
const { expect } = require('@playwright/test');
class EventCreation extends BasePage {
constructor(page) {
super(page);
// Event creation form selectors
this.selectors = {
// Main form containers
createEventForm: [
'[data-testid="create-event-form"]',
'.hvac-create-event-form',
'.tribe-events-community-form',
'.event-creation-form',
'form#create-event'
],
// Form fields - Standard HVAC
fields: {
title: [
'[data-testid="event-title"]',
'input[name="event_title"]',
'input[name="post_title"]',
'#event-title',
'.event-title-input'
],
description: [
'[data-testid="event-description"]',
'textarea[name="event_description"]',
'textarea[name="post_content"]',
'#event-description',
'.event-description-textarea'
],
startDate: [
'[data-testid="event-start-date"]',
'input[name="event_start_date"]',
'input[name="EventStartDate"]',
'#event-start-date',
'.event-start-date'
],
endDate: [
'[data-testid="event-end-date"]',
'input[name="event_end_date"]',
'input[name="EventEndDate"]',
'#event-end-date',
'.event-end-date'
],
startTime: [
'[data-testid="event-start-time"]',
'input[name="event_start_time"]',
'input[name="EventStartTime"]',
'#event-start-time',
'.event-start-time'
],
endTime: [
'[data-testid="event-end-time"]',
'input[name="event_end_time"]',
'input[name="EventEndTime"]',
'#event-end-time',
'.event-end-time'
],
venue: [
'[data-testid="event-venue"]',
'select[name="venue"]',
'select[name="event_venue"]',
'#event-venue',
'.venue-select'
],
organizer: [
'[data-testid="event-organizer"]',
'select[name="organizer"]',
'select[name="event_organizer"]',
'#event-organizer',
'.organizer-select'
],
category: [
'[data-testid="event-category"]',
'select[name="event_category"]',
'select[name="tax_input[tribe_events_cat][]"]',
'#event-category',
'.event-category-select'
],
capacity: [
'[data-testid="event-capacity"]',
'input[name="event_capacity"]',
'input[name="_EventCapacity"]',
'#event-capacity',
'.event-capacity'
],
cost: [
'[data-testid="event-cost"]',
'input[name="event_cost"]',
'input[name="_EventCost"]',
'#event-cost',
'.event-cost'
],
website: [
'[data-testid="event-website"]',
'input[name="event_website"]',
'input[name="_EventURL"]',
'#event-website',
'.event-website'
]
},
// TEC specific fields
tecFields: {
allDayEvent: [
'[data-testid="all-day-event"]',
'input[name="_EventAllDay"]',
'#allDayCheckbox',
'.all-day-checkbox'
],
timezone: [
'[data-testid="event-timezone"]',
'select[name="_EventTimezone"]',
'#event-timezone',
'.timezone-select'
],
showMapLink: [
'[data-testid="show-map-link"]',
'input[name="_EventShowMapLink"]',
'#show-map-link',
'.show-map-link'
],
showMap: [
'[data-testid="show-map"]',
'input[name="_EventShowMap"]',
'#show-map',
'.show-map'
]
},
// Action buttons
buttons: {
submit: [
'[data-testid="submit-event"]',
'input[type="submit"]',
'button[type="submit"]',
'#submit-event',
'.submit-event-btn',
'.tribe-community-event-submit'
],
preview: [
'[data-testid="preview-event"]',
'button[name="preview"]',
'#preview-event',
'.preview-event-btn'
],
saveDraft: [
'[data-testid="save-draft"]',
'button[name="save_draft"]',
'#save-draft',
'.save-draft-btn'
],
cancel: [
'[data-testid="cancel-event"]',
'button[name="cancel"]',
'#cancel-event',
'.cancel-btn'
]
},
// Validation and error elements
validation: {
errors: [
'[data-testid="form-errors"]',
'.form-errors',
'.tribe-community-notice-error',
'.event-validation-errors',
'.error-message'
],
success: [
'[data-testid="form-success"]',
'.form-success',
'.tribe-community-notice-success',
'.event-success-message',
'.success-message'
],
fieldError: [
'.field-error',
'.input-error',
'.validation-error'
]
},
// Loading states
loading: [
'[data-testid="form-loading"]',
'.form-loading',
'.tribe-community-loading',
'.loading-spinner'
]
};
this.urls = {
createEvent: '/trainer/create-event/',
tecCreateEvent: '/trainer/tec-create-event/',
customCreateEvent: '/create-event/',
communityCreate: '/events/community/add/'
};
// Test data templates
this.testEventData = {
basic: {
title: 'Test HVAC Training Event',
description: 'Comprehensive HVAC training covering heat pump installation and maintenance.',
startDate: this.getFormattedDate(7), // 7 days from now
endDate: this.getFormattedDate(7),
startTime: '09:00',
endTime: '17:00',
capacity: '25',
cost: '299.00'
},
advanced: {
title: 'Advanced Heat Pump Diagnostics Workshop',
description: 'Deep dive into advanced heat pump diagnostic techniques using measureQuick tools and protocols.',
startDate: this.getFormattedDate(14),
endDate: this.getFormattedDate(15),
startTime: '08:30',
endTime: '16:30',
capacity: '15',
cost: '499.00',
website: 'https://upskill-staging.measurequick.com/advanced-diagnostics'
},
multiDay: {
title: 'HVAC Certification Bootcamp',
description: 'Intensive 3-day certification program covering all aspects of HVAC systems.',
startDate: this.getFormattedDate(21),
endDate: this.getFormattedDate(23),
startTime: '09:00',
endTime: '17:00',
capacity: '20',
cost: '799.00'
}
};
}
/**
* Navigate to event creation page
*/
async navigate(pageType = 'standard') {
let url;
switch (pageType) {
case 'tec':
url = this.urls.tecCreateEvent;
break;
case 'community':
url = this.urls.communityCreate;
break;
case 'custom':
url = this.urls.customCreateEvent;
break;
default:
url = this.urls.createEvent;
}
await this.goto(url);
await this.waitForFormLoad();
console.log(`✅ Navigated to event creation page: ${pageType}`);
}
/**
* Wait for event creation form to load completely
*/
async waitForFormLoad() {
// Wait for form container
await this.waitForVisible(this.selectors.createEventForm, { timeout: 10000 });
// Wait for essential fields
await this.waitForVisible(this.selectors.fields.title, { timeout: 5000 });
await this.waitForVisible(this.selectors.fields.description, { timeout: 5000 });
// Wait for WordPress and any AJAX to complete
await this.waitForWordPressReady();
await this.waitForAjax();
// Wait for loading indicators to disappear
await this.waitForHidden(this.selectors.loading, { timeout: 3000 });
console.log('✅ Event creation form loaded');
}
/**
* Fill event creation form with provided data
*/
async fillEventForm(eventData) {
console.log('📝 Filling event form with data:', eventData.title);
// Fill basic event information
if (eventData.title) {
await this.fill(this.selectors.fields.title, eventData.title);
}
if (eventData.description) {
await this.fill(this.selectors.fields.description, eventData.description);
}
// Fill date and time information
if (eventData.startDate) {
await this.fill(this.selectors.fields.startDate, eventData.startDate);
}
if (eventData.endDate) {
await this.fill(this.selectors.fields.endDate, eventData.endDate);
}
if (eventData.startTime) {
await this.fill(this.selectors.fields.startTime, eventData.startTime);
}
if (eventData.endTime) {
await this.fill(this.selectors.fields.endTime, eventData.endTime);
}
// Fill additional event details
if (eventData.capacity) {
await this.fill(this.selectors.fields.capacity, eventData.capacity);
}
if (eventData.cost) {
await this.fill(this.selectors.fields.cost, eventData.cost);
}
if (eventData.website) {
await this.fill(this.selectors.fields.website, eventData.website);
}
// Handle venue selection if provided
if (eventData.venue) {
await this.selectVenue(eventData.venue);
}
// Handle organizer selection if provided
if (eventData.organizer) {
await this.selectOrganizer(eventData.organizer);
}
// Handle category selection if provided
if (eventData.category) {
await this.selectCategory(eventData.category);
}
// Wait for form validation
await this.waitForAjax();
console.log('✅ Event form filled successfully');
}
/**
* Submit event creation form
*/
async submitEvent(options = {}) {
const {
waitForRedirect = true,
expectedOutcome = 'success',
timeout = 15000
} = options;
console.log('🚀 Submitting event creation form');
// Take screenshot before submission
await this.takeScreenshot('before-event-submission');
// Click submit button
await this.click(this.selectors.buttons.submit, { timeout });
if (waitForRedirect) {
// Wait for form processing
await this.waitForAjax();
// Wait for either success or error message
if (expectedOutcome === 'success') {
await Promise.race([
this.waitForVisible(this.selectors.validation.success, { timeout }),
this.waitForUrlChange(timeout)
]);
console.log('✅ Event submitted successfully');
} else if (expectedOutcome === 'error') {
await this.waitForVisible(this.selectors.validation.errors, { timeout });
console.log('⚠️ Event submission resulted in expected error');
}
}
// Take screenshot after submission
await this.takeScreenshot('after-event-submission');
return await this.getSubmissionResult();
}
/**
* Get form submission result
*/
async getSubmissionResult() {
// Check for success messages
if (await this.isVisible(this.selectors.validation.success)) {
const successMessage = await this.getText(this.selectors.validation.success);
return {
success: true,
message: successMessage,
redirectUrl: await this.page.url()
};
}
// Check for error messages
if (await this.isVisible(this.selectors.validation.errors)) {
const errorMessage = await this.getText(this.selectors.validation.errors);
const fieldErrors = await this.getFieldErrors();
return {
success: false,
message: errorMessage,
fieldErrors,
currentUrl: await this.page.url()
};
}
// Check if URL changed (successful redirect)
const currentUrl = await this.page.url();
const isRedirected = !currentUrl.includes('create-event');
return {
success: isRedirected,
redirected: isRedirected,
currentUrl
};
}
/**
* Get field-specific validation errors
*/
async getFieldErrors() {
const fieldErrors = {};
const errorElements = await this.page.locator(this.selectors.validation.fieldError.join(', ')).all();
for (const errorElement of errorElements) {
const fieldName = await errorElement.getAttribute('data-field') ||
await errorElement.getAttribute('for') ||
'unknown';
const errorText = await errorElement.textContent();
fieldErrors[fieldName] = errorText.trim();
}
return fieldErrors;
}
/**
* Select venue from dropdown
*/
async selectVenue(venueName) {
if (await this.isVisible(this.selectors.fields.venue)) {
// If venue is a dropdown
await this.selectByText(this.selectors.fields.venue, venueName);
} else {
// If venue needs to be created or searched
const venueInput = await this.getVisibleSelector([
'input[name="venue_name"]',
'#venue-search',
'.venue-input'
]);
if (venueInput) {
await this.fill(venueInput, venueName);
await this.waitForAjax();
}
}
console.log(`📍 Selected venue: ${venueName}`);
}
/**
* Select organizer from dropdown
*/
async selectOrganizer(organizerName) {
if (await this.isVisible(this.selectors.fields.organizer)) {
await this.selectByText(this.selectors.fields.organizer, organizerName);
} else {
// Handle organizer creation if needed
const organizerInput = await this.getVisibleSelector([
'input[name="organizer_name"]',
'#organizer-search',
'.organizer-input'
]);
if (organizerInput) {
await this.fill(organizerInput, organizerName);
await this.waitForAjax();
}
}
console.log(`👤 Selected organizer: ${organizerName}`);
}
/**
* Select event category
*/
async selectCategory(categoryName) {
if (await this.isVisible(this.selectors.fields.category)) {
await this.selectByText(this.selectors.fields.category, categoryName);
console.log(`🏷️ Selected category: ${categoryName}`);
}
}
/**
* Create event with test data
*/
async createTestEvent(dataType = 'basic', options = {}) {
const eventData = { ...this.testEventData[dataType], ...options.data };
console.log(`🧪 Creating test event: ${dataType}`);
await this.fillEventForm(eventData);
const result = await this.submitEvent(options);
return {
...result,
eventData,
testType: dataType
};
}
/**
* Validate form fields
*/
async validateForm() {
const validationResults = {
requiredFields: [],
validationErrors: [],
fieldsPresent: {}
};
// Check required fields
const requiredFields = ['title', 'description', 'startDate', 'startTime'];
for (const fieldName of requiredFields) {
const fieldSelectors = this.selectors.fields[fieldName];
const isPresent = await this.isVisible(fieldSelectors);
validationResults.fieldsPresent[fieldName] = isPresent;
if (!isPresent) {
validationResults.requiredFields.push(fieldName);
}
}
// Check for existing validation errors
if (await this.isVisible(this.selectors.validation.errors)) {
const errors = await this.getText(this.selectors.validation.errors);
validationResults.validationErrors.push(errors);
}
console.log('🔍 Form validation results:', validationResults);
return validationResults;
}
/**
* Test form validation by submitting empty form
*/
async testValidation() {
console.log('🧪 Testing form validation');
// Clear any existing data
await this.clearForm();
// Attempt to submit empty form
await this.click(this.selectors.buttons.submit);
// Wait for validation errors
await this.waitForVisible(this.selectors.validation.errors, { timeout: 5000 });
return await this.getFieldErrors();
}
/**
* Clear all form fields
*/
async clearForm() {
const fieldsToCheck = ['title', 'description', 'startDate', 'endDate', 'startTime', 'endTime', 'capacity', 'cost'];
for (const fieldName of fieldsToCheck) {
const fieldSelectors = this.selectors.fields[fieldName];
if (await this.isVisible(fieldSelectors)) {
await this.clear(fieldSelectors);
}
}
console.log('🧹 Form cleared');
}
/**
* Handle TEC-specific functionality
*/
async configureTECOptions(options = {}) {
if (options.allDay) {
await this.check(this.selectors.tecFields.allDayEvent);
console.log('☑️ Set as all-day event');
}
if (options.timezone) {
await this.selectByText(this.selectors.tecFields.timezone, options.timezone);
console.log(`🌍 Set timezone: ${options.timezone}`);
}
if (options.showMap !== undefined) {
if (options.showMap) {
await this.check(this.selectors.tecFields.showMap);
} else {
await this.uncheck(this.selectors.tecFields.showMap);
}
console.log(`🗺️ Show map: ${options.showMap}`);
}
}
/**
* Preview event before submission
*/
async previewEvent() {
if (await this.isVisible(this.selectors.buttons.preview)) {
await this.click(this.selectors.buttons.preview);
await this.waitForAjax();
console.log('👁️ Event preview opened');
return true;
}
return false;
}
/**
* Save event as draft
*/
async saveDraft() {
if (await this.isVisible(this.selectors.buttons.saveDraft)) {
await this.click(this.selectors.buttons.saveDraft);
await this.waitForAjax();
const result = await this.getSubmissionResult();
console.log('📝 Event saved as draft');
return result;
}
throw new Error('Save draft button not available');
}
/**
* Get formatted date for form input
*/
getFormattedDate(daysFromNow = 0) {
const date = new Date();
date.setDate(date.getDate() + daysFromNow);
return date.toISOString().split('T')[0]; // YYYY-MM-DD format
}
/**
* Take screenshot of form state
*/
async screenshotForm(name = 'event-creation-form') {
return await this.takeScreenshot(name, { fullPage: true });
}
/**
* Verify form is accessible and ready for input
*/
async verifyFormAccessibility() {
const checks = {
formVisible: await this.isVisible(this.selectors.createEventForm),
titleFieldAccessible: await this.isVisible(this.selectors.fields.title),
descriptionFieldAccessible: await this.isVisible(this.selectors.fields.description),
submitButtonAccessible: await this.isVisible(this.selectors.buttons.submit),
hasNoLoadingIndicators: !await this.isVisible(this.selectors.loading)
};
const isAccessible = Object.values(checks).every(check => check === true);
console.log('♿ Form accessibility check:', { isAccessible, ...checks });
return { isAccessible, checks };
}
}
module.exports = EventCreation;