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>
821 lines
No EOL
28 KiB
JavaScript
821 lines
No EOL
28 KiB
JavaScript
/**
|
|
* Event Editing Page Object Model
|
|
*
|
|
* Handles event editing and modification functionality including:
|
|
* - Event detail editing and updates
|
|
* - Status transitions (Draft → Pending → Published)
|
|
* - Event modification workflows
|
|
* - Version control and change tracking
|
|
*
|
|
* @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 EventEditing extends BasePage {
|
|
constructor(page) {
|
|
super(page);
|
|
|
|
// Event editing form selectors
|
|
this.selectors = {
|
|
// Main form containers
|
|
editEventForm: [
|
|
'[data-testid="edit-event-form"]',
|
|
'.hvac-edit-event-form',
|
|
'.tribe-events-community-form',
|
|
'.event-edit-form',
|
|
'form#edit-event'
|
|
],
|
|
|
|
// Form fields (similar to creation but for editing context)
|
|
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'
|
|
],
|
|
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'
|
|
],
|
|
status: [
|
|
'[data-testid="event-status"]',
|
|
'select[name="post_status"]',
|
|
'select[name="event_status"]',
|
|
'#event-status',
|
|
'.event-status-select'
|
|
]
|
|
},
|
|
|
|
// Event status indicators
|
|
status: {
|
|
draft: [
|
|
'[data-testid="status-draft"]',
|
|
'.status-draft',
|
|
'.event-status-draft',
|
|
'text=Draft'
|
|
],
|
|
pending: [
|
|
'[data-testid="status-pending"]',
|
|
'.status-pending',
|
|
'.event-status-pending',
|
|
'text=Pending Review'
|
|
],
|
|
published: [
|
|
'[data-testid="status-published"]',
|
|
'.status-published',
|
|
'.event-status-published',
|
|
'text=Published'
|
|
],
|
|
cancelled: [
|
|
'[data-testid="status-cancelled"]',
|
|
'.status-cancelled',
|
|
'.event-status-cancelled',
|
|
'text=Cancelled'
|
|
]
|
|
},
|
|
|
|
// Action buttons
|
|
buttons: {
|
|
update: [
|
|
'[data-testid="update-event"]',
|
|
'input[value="Update Event"]',
|
|
'button[name="update"]',
|
|
'#update-event',
|
|
'.update-event-btn'
|
|
],
|
|
publish: [
|
|
'[data-testid="publish-event"]',
|
|
'input[value="Publish"]',
|
|
'button[name="publish"]',
|
|
'#publish-event',
|
|
'.publish-event-btn'
|
|
],
|
|
submitForReview: [
|
|
'[data-testid="submit-for-review"]',
|
|
'input[value="Submit for Review"]',
|
|
'button[name="submit_review"]',
|
|
'#submit-for-review',
|
|
'.submit-review-btn'
|
|
],
|
|
saveDraft: [
|
|
'[data-testid="save-draft"]',
|
|
'input[value="Save Draft"]',
|
|
'button[name="save_draft"]',
|
|
'#save-draft',
|
|
'.save-draft-btn'
|
|
],
|
|
delete: [
|
|
'[data-testid="delete-event"]',
|
|
'button[name="delete"]',
|
|
'#delete-event',
|
|
'.delete-event-btn',
|
|
'text=Delete Event'
|
|
],
|
|
cancel: [
|
|
'[data-testid="cancel-event"]',
|
|
'button[name="cancel_event"]',
|
|
'#cancel-event',
|
|
'.cancel-event-btn',
|
|
'text=Cancel Event'
|
|
],
|
|
preview: [
|
|
'[data-testid="preview-event"]',
|
|
'button[name="preview"]',
|
|
'#preview-event',
|
|
'.preview-event-btn'
|
|
]
|
|
},
|
|
|
|
// Event info display
|
|
eventInfo: {
|
|
id: [
|
|
'[data-testid="event-id"]',
|
|
'.event-id',
|
|
'#event-id'
|
|
],
|
|
createdDate: [
|
|
'[data-testid="created-date"]',
|
|
'.created-date',
|
|
'.event-created'
|
|
],
|
|
lastModified: [
|
|
'[data-testid="last-modified"]',
|
|
'.last-modified',
|
|
'.event-modified'
|
|
],
|
|
author: [
|
|
'[data-testid="event-author"]',
|
|
'.event-author',
|
|
'.post-author'
|
|
]
|
|
},
|
|
|
|
// Attendee management
|
|
attendees: {
|
|
list: [
|
|
'[data-testid="attendees-list"]',
|
|
'.attendees-list',
|
|
'.event-attendees'
|
|
],
|
|
count: [
|
|
'[data-testid="attendee-count"]',
|
|
'.attendee-count',
|
|
'.event-attendee-count'
|
|
],
|
|
addAttendee: [
|
|
'[data-testid="add-attendee"]',
|
|
'.add-attendee-btn',
|
|
'button.add-attendee'
|
|
],
|
|
removeAttendee: [
|
|
'[data-testid="remove-attendee"]',
|
|
'.remove-attendee-btn',
|
|
'button.remove-attendee'
|
|
]
|
|
},
|
|
|
|
// Validation and messages
|
|
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'
|
|
],
|
|
warnings: [
|
|
'[data-testid="form-warnings"]',
|
|
'.form-warnings',
|
|
'.event-warnings',
|
|
'.warning-message'
|
|
]
|
|
},
|
|
|
|
// Version control and history
|
|
versions: {
|
|
history: [
|
|
'[data-testid="version-history"]',
|
|
'.version-history',
|
|
'.event-revisions'
|
|
],
|
|
compareLink: [
|
|
'[data-testid="compare-versions"]',
|
|
'.compare-versions',
|
|
'a.compare-revisions'
|
|
]
|
|
}
|
|
};
|
|
|
|
this.urls = {
|
|
editEvent: '/trainer/edit-event/',
|
|
tecEditEvent: '/trainer/tec-edit-event/',
|
|
customEditEvent: '/edit-event/',
|
|
communityEdit: '/events/community/edit/'
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Navigate to event editing page
|
|
*/
|
|
async navigateToEdit(eventId, pageType = 'standard') {
|
|
let url;
|
|
switch (pageType) {
|
|
case 'tec':
|
|
url = `${this.urls.tecEditEvent}?event_id=${eventId}`;
|
|
break;
|
|
case 'community':
|
|
url = `${this.urls.communityEdit}${eventId}/`;
|
|
break;
|
|
case 'custom':
|
|
url = `${this.urls.customEditEvent}?id=${eventId}`;
|
|
break;
|
|
default:
|
|
url = `${this.urls.editEvent}?event_id=${eventId}`;
|
|
}
|
|
|
|
await this.goto(url);
|
|
await this.waitForEditFormLoad();
|
|
console.log(`✅ Navigated to event editing page: ${pageType} (ID: ${eventId})`);
|
|
}
|
|
|
|
/**
|
|
* Wait for edit form to load completely
|
|
*/
|
|
async waitForEditFormLoad() {
|
|
// Wait for form container
|
|
await this.waitForVisible(this.selectors.editEventForm, { 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 AJAX to complete
|
|
await this.waitForWordPressReady();
|
|
await this.waitForAjax();
|
|
|
|
console.log('✅ Event editing form loaded');
|
|
}
|
|
|
|
/**
|
|
* Get current event information
|
|
*/
|
|
async getEventInfo() {
|
|
const eventInfo = {};
|
|
|
|
// Get event ID if available
|
|
if (await this.isVisible(this.selectors.eventInfo.id)) {
|
|
eventInfo.id = await this.getText(this.selectors.eventInfo.id);
|
|
}
|
|
|
|
// Get current values from form fields
|
|
eventInfo.title = await this.getInputValue(this.selectors.fields.title);
|
|
eventInfo.description = await this.getInputValue(this.selectors.fields.description);
|
|
eventInfo.startDate = await this.getInputValue(this.selectors.fields.startDate);
|
|
eventInfo.endDate = await this.getInputValue(this.selectors.fields.endDate);
|
|
eventInfo.startTime = await this.getInputValue(this.selectors.fields.startTime);
|
|
eventInfo.endTime = await this.getInputValue(this.selectors.fields.endTime);
|
|
eventInfo.capacity = await this.getInputValue(this.selectors.fields.capacity);
|
|
eventInfo.cost = await this.getInputValue(this.selectors.fields.cost);
|
|
|
|
// Get status
|
|
eventInfo.status = await this.getCurrentStatus();
|
|
|
|
// Get created/modified dates if available
|
|
if (await this.isVisible(this.selectors.eventInfo.createdDate)) {
|
|
eventInfo.createdDate = await this.getText(this.selectors.eventInfo.createdDate);
|
|
}
|
|
|
|
if (await this.isVisible(this.selectors.eventInfo.lastModified)) {
|
|
eventInfo.lastModified = await this.getText(this.selectors.eventInfo.lastModified);
|
|
}
|
|
|
|
console.log('📋 Current event info:', eventInfo);
|
|
return eventInfo;
|
|
}
|
|
|
|
/**
|
|
* Update specific event fields
|
|
*/
|
|
async updateEventFields(updates) {
|
|
console.log('✏️ Updating event fields:', Object.keys(updates));
|
|
|
|
for (const [fieldName, newValue] of Object.entries(updates)) {
|
|
const fieldSelectors = this.selectors.fields[fieldName];
|
|
|
|
if (fieldSelectors && await this.isVisible(fieldSelectors)) {
|
|
await this.clear(fieldSelectors);
|
|
await this.fill(fieldSelectors, newValue);
|
|
console.log(` ✓ Updated ${fieldName}: ${newValue}`);
|
|
} else {
|
|
console.warn(` ⚠️ Field '${fieldName}' not found or not visible`);
|
|
}
|
|
}
|
|
|
|
// Wait for any auto-save or validation
|
|
await this.waitForAjax();
|
|
}
|
|
|
|
/**
|
|
* Get current event status
|
|
*/
|
|
async getCurrentStatus() {
|
|
// Check for status indicators
|
|
const statusChecks = [
|
|
{ status: 'published', selectors: this.selectors.status.published },
|
|
{ status: 'pending', selectors: this.selectors.status.pending },
|
|
{ status: 'draft', selectors: this.selectors.status.draft },
|
|
{ status: 'cancelled', selectors: this.selectors.status.cancelled }
|
|
];
|
|
|
|
for (const { status, selectors } of statusChecks) {
|
|
if (await this.isVisible(selectors)) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// Check status dropdown if available
|
|
if (await this.isVisible(this.selectors.fields.status)) {
|
|
return await this.getSelectedValue(this.selectors.fields.status);
|
|
}
|
|
|
|
return 'unknown';
|
|
}
|
|
|
|
/**
|
|
* Change event status
|
|
*/
|
|
async changeStatus(newStatus) {
|
|
console.log(`🔄 Changing event status to: ${newStatus}`);
|
|
|
|
// If status dropdown is available
|
|
if (await this.isVisible(this.selectors.fields.status)) {
|
|
await this.selectByValue(this.selectors.fields.status, newStatus);
|
|
console.log(`✅ Status changed via dropdown: ${newStatus}`);
|
|
return;
|
|
}
|
|
|
|
// Use status-specific buttons
|
|
const statusButtonMap = {
|
|
'draft': this.selectors.buttons.saveDraft,
|
|
'pending': this.selectors.buttons.submitForReview,
|
|
'published': this.selectors.buttons.publish
|
|
};
|
|
|
|
const buttonSelectors = statusButtonMap[newStatus];
|
|
if (buttonSelectors && await this.isVisible(buttonSelectors)) {
|
|
await this.click(buttonSelectors);
|
|
console.log(`✅ Status changed via button: ${newStatus}`);
|
|
} else {
|
|
throw new Error(`Cannot change status to ${newStatus} - no available method found`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update event and save changes
|
|
*/
|
|
async updateEvent(options = {}) {
|
|
const {
|
|
waitForRedirect = true,
|
|
expectedOutcome = 'success',
|
|
timeout = 15000
|
|
} = options;
|
|
|
|
console.log('💾 Updating event');
|
|
|
|
// Take screenshot before update
|
|
await this.takeScreenshot('before-event-update');
|
|
|
|
// Click update button (priority order)
|
|
const updateButtons = [
|
|
this.selectors.buttons.update,
|
|
this.selectors.buttons.saveDraft,
|
|
this.selectors.buttons.publish
|
|
];
|
|
|
|
let updateClicked = false;
|
|
for (const buttonSelectors of updateButtons) {
|
|
if (await this.isVisible(buttonSelectors)) {
|
|
await this.click(buttonSelectors, { timeout });
|
|
updateClicked = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!updateClicked) {
|
|
throw new Error('No update button found on event edit form');
|
|
}
|
|
|
|
if (waitForRedirect) {
|
|
// Wait for form processing
|
|
await this.waitForAjax();
|
|
|
|
// Wait for success/error message or URL change
|
|
if (expectedOutcome === 'success') {
|
|
await Promise.race([
|
|
this.waitForVisible(this.selectors.validation.success, { timeout }),
|
|
this.waitForUrlChange(timeout)
|
|
]);
|
|
console.log('✅ Event updated successfully');
|
|
} else if (expectedOutcome === 'error') {
|
|
await this.waitForVisible(this.selectors.validation.errors, { timeout });
|
|
console.log('⚠️ Event update resulted in expected error');
|
|
}
|
|
}
|
|
|
|
// Take screenshot after update
|
|
await this.takeScreenshot('after-event-update');
|
|
|
|
return await this.getUpdateResult();
|
|
}
|
|
|
|
/**
|
|
* Get update result with status and messages
|
|
*/
|
|
async getUpdateResult() {
|
|
// 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,
|
|
currentUrl: await this.page.url(),
|
|
newStatus: await this.getCurrentStatus()
|
|
};
|
|
}
|
|
|
|
// Check for error messages
|
|
if (await this.isVisible(this.selectors.validation.errors)) {
|
|
const errorMessage = await this.getText(this.selectors.validation.errors);
|
|
return {
|
|
success: false,
|
|
message: errorMessage,
|
|
currentUrl: await this.page.url(),
|
|
currentStatus: await this.getCurrentStatus()
|
|
};
|
|
}
|
|
|
|
// Check for warnings
|
|
if (await this.isVisible(this.selectors.validation.warnings)) {
|
|
const warningMessage = await this.getText(this.selectors.validation.warnings);
|
|
return {
|
|
success: true,
|
|
warning: true,
|
|
message: warningMessage,
|
|
currentUrl: await this.page.url(),
|
|
newStatus: await this.getCurrentStatus()
|
|
};
|
|
}
|
|
|
|
// Default success if no explicit messages
|
|
return {
|
|
success: true,
|
|
message: 'Event updated (no confirmation message displayed)',
|
|
currentUrl: await this.page.url(),
|
|
newStatus: await this.getCurrentStatus()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Delete event (with confirmation handling)
|
|
*/
|
|
async deleteEvent(confirmDelete = true) {
|
|
console.log('🗑️ Attempting to delete event');
|
|
|
|
if (!await this.isVisible(this.selectors.buttons.delete)) {
|
|
throw new Error('Delete button not available');
|
|
}
|
|
|
|
await this.click(this.selectors.buttons.delete);
|
|
|
|
// Handle confirmation dialog
|
|
if (confirmDelete) {
|
|
// Wait for browser confirmation dialog
|
|
this.page.on('dialog', async dialog => {
|
|
console.log(`🔔 Confirmation dialog: ${dialog.message()}`);
|
|
await dialog.accept();
|
|
});
|
|
}
|
|
|
|
await this.waitForAjax();
|
|
const currentUrl = await this.page.url();
|
|
|
|
const result = {
|
|
deleted: !currentUrl.includes('edit-event'),
|
|
currentUrl
|
|
};
|
|
|
|
console.log(result.deleted ? '✅ Event deleted' : '⚠️ Event deletion may have failed');
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Cancel event (change status to cancelled)
|
|
*/
|
|
async cancelEvent(reason = '') {
|
|
console.log('❌ Cancelling event');
|
|
|
|
// If there's a specific cancel button
|
|
if (await this.isVisible(this.selectors.buttons.cancel)) {
|
|
await this.click(this.selectors.buttons.cancel);
|
|
|
|
// Handle reason input if available
|
|
const reasonInput = await this.getVisibleSelector([
|
|
'[data-testid="cancel-reason"]',
|
|
'textarea[name="cancel_reason"]',
|
|
'#cancel-reason'
|
|
]);
|
|
|
|
if (reasonInput && reason) {
|
|
await this.fill(reasonInput, reason);
|
|
}
|
|
|
|
await this.waitForAjax();
|
|
} else {
|
|
// Use status change
|
|
await this.changeStatus('cancelled');
|
|
}
|
|
|
|
return await this.updateEvent();
|
|
}
|
|
|
|
/**
|
|
* Get attendee information
|
|
*/
|
|
async getAttendeeInfo() {
|
|
const attendeeInfo = {
|
|
count: 0,
|
|
attendees: [],
|
|
hasAttendeeManagement: false
|
|
};
|
|
|
|
// Check if attendee management is available
|
|
if (await this.isVisible(this.selectors.attendees.list)) {
|
|
attendeeInfo.hasAttendeeManagement = true;
|
|
|
|
// Get attendee count
|
|
if (await this.isVisible(this.selectors.attendees.count)) {
|
|
const countText = await this.getText(this.selectors.attendees.count);
|
|
attendeeInfo.count = parseInt(countText.match(/\d+/)?.[0] || '0', 10);
|
|
}
|
|
|
|
// Get attendee list
|
|
const attendeeElements = await this.page.locator(`${this.selectors.attendees.list.join(', ')} .attendee`).all();
|
|
for (const attendeeElement of attendeeElements) {
|
|
const attendeeText = await attendeeElement.textContent();
|
|
attendeeInfo.attendees.push(attendeeText.trim());
|
|
}
|
|
}
|
|
|
|
console.log('👥 Attendee info:', attendeeInfo);
|
|
return attendeeInfo;
|
|
}
|
|
|
|
/**
|
|
* Test event status transitions
|
|
*/
|
|
async testStatusTransitions() {
|
|
console.log('🔄 Testing status transitions');
|
|
|
|
const transitions = [];
|
|
const currentStatus = await this.getCurrentStatus();
|
|
transitions.push({ from: null, to: currentStatus, timestamp: Date.now() });
|
|
|
|
// Test possible transitions based on current status
|
|
const transitionMap = {
|
|
'draft': ['pending', 'published'],
|
|
'pending': ['published', 'draft'],
|
|
'published': ['draft'],
|
|
'cancelled': ['draft']
|
|
};
|
|
|
|
const possibleTransitions = transitionMap[currentStatus] || [];
|
|
|
|
for (const targetStatus of possibleTransitions) {
|
|
try {
|
|
await this.changeStatus(targetStatus);
|
|
await this.updateEvent({ waitForRedirect: false });
|
|
|
|
const newStatus = await this.getCurrentStatus();
|
|
transitions.push({
|
|
from: currentStatus,
|
|
to: newStatus,
|
|
expected: targetStatus,
|
|
successful: newStatus === targetStatus,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
console.log(` ${currentStatus} → ${newStatus} ${newStatus === targetStatus ? '✅' : '❌'}`);
|
|
} catch (error) {
|
|
transitions.push({
|
|
from: currentStatus,
|
|
to: targetStatus,
|
|
expected: targetStatus,
|
|
successful: false,
|
|
error: error.message,
|
|
timestamp: Date.now()
|
|
});
|
|
|
|
console.log(` ${currentStatus} → ${targetStatus} ❌ (${error.message})`);
|
|
}
|
|
}
|
|
|
|
return transitions;
|
|
}
|
|
|
|
/**
|
|
* Compare current event data with expected data
|
|
*/
|
|
async compareEventData(expectedData) {
|
|
const currentData = await this.getEventInfo();
|
|
const comparison = {
|
|
matches: {},
|
|
differences: {},
|
|
missing: {}
|
|
};
|
|
|
|
for (const [field, expectedValue] of Object.entries(expectedData)) {
|
|
if (currentData.hasOwnProperty(field)) {
|
|
const matches = currentData[field] === expectedValue;
|
|
if (matches) {
|
|
comparison.matches[field] = expectedValue;
|
|
} else {
|
|
comparison.differences[field] = {
|
|
expected: expectedValue,
|
|
actual: currentData[field]
|
|
};
|
|
}
|
|
} else {
|
|
comparison.missing[field] = expectedValue;
|
|
}
|
|
}
|
|
|
|
const overallMatch = Object.keys(comparison.differences).length === 0 &&
|
|
Object.keys(comparison.missing).length === 0;
|
|
|
|
console.log('🔍 Event data comparison:', { overallMatch, ...comparison });
|
|
return { overallMatch, ...comparison };
|
|
}
|
|
|
|
/**
|
|
* Preview event
|
|
*/
|
|
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;
|
|
}
|
|
console.log('⚠️ Preview not available');
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get version history if available
|
|
*/
|
|
async getVersionHistory() {
|
|
if (!await this.isVisible(this.selectors.versions.history)) {
|
|
return { hasVersioning: false };
|
|
}
|
|
|
|
const versions = [];
|
|
const versionElements = await this.page.locator(`${this.selectors.versions.history.join(', ')} .revision`).all();
|
|
|
|
for (const versionElement of versionElements) {
|
|
const versionText = await versionElement.textContent();
|
|
versions.push(versionText.trim());
|
|
}
|
|
|
|
return {
|
|
hasVersioning: true,
|
|
versions,
|
|
count: versions.length
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Take screenshot of edit form
|
|
*/
|
|
async screenshotEditForm(name = 'event-edit-form') {
|
|
return await this.takeScreenshot(name, { fullPage: true });
|
|
}
|
|
|
|
/**
|
|
* Verify edit form access and functionality
|
|
*/
|
|
async verifyEditAccess() {
|
|
const checks = {
|
|
formVisible: await this.isVisible(this.selectors.editEventForm),
|
|
fieldsEditable: await this.areFieldsEditable(),
|
|
updateButtonAvailable: await this.isVisible(this.selectors.buttons.update),
|
|
canChangeStatus: await this.canChangeStatus(),
|
|
eventInfoDisplayed: await this.isVisible(this.selectors.eventInfo.id)
|
|
};
|
|
|
|
const hasEditAccess = Object.values(checks).every(check => check === true);
|
|
|
|
console.log('🔐 Edit access verification:', { hasEditAccess, ...checks });
|
|
return { hasEditAccess, checks };
|
|
}
|
|
|
|
/**
|
|
* Check if form fields are editable
|
|
*/
|
|
async areFieldsEditable() {
|
|
const fieldsToCheck = ['title', 'description', 'startDate', 'capacity'];
|
|
|
|
for (const fieldName of fieldsToCheck) {
|
|
const fieldSelectors = this.selectors.fields[fieldName];
|
|
if (await this.isVisible(fieldSelectors)) {
|
|
const isDisabled = await this.page.locator(fieldSelectors.join(', ')).first().isDisabled();
|
|
if (isDisabled) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if user can change event status
|
|
*/
|
|
async canChangeStatus() {
|
|
return await this.isVisible(this.selectors.fields.status) ||
|
|
await this.isVisible(this.selectors.buttons.publish) ||
|
|
await this.isVisible(this.selectors.buttons.submitForReview);
|
|
}
|
|
}
|
|
|
|
module.exports = EventEditing; |