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; |