- Created admin page for direct event seeding (admin/seed-events-direct.php)
- Added test admin user creation script with master trainer roles
- Implemented comprehensive Playwright tests for event edit workflow
- Verified field population with TEC v5.0.8
- Confirmed 11 core fields properly populate in edit forms
- Added XWayland display configuration for headed browser testing
- Created seeding scripts that add events with complete metadata
Test Results:
- Login functionality: Working
- Event access: 20+ events accessible
- Field population: 11 essential fields confirmed
- Edit workflow: Functional with TEC Community Events
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
514 lines
No EOL
18 KiB
JavaScript
Executable file
514 lines
No EOL
18 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
||
|
||
/**
|
||
* Complete Event Edit Workflow Test
|
||
* Tests the FULL edit cycle: access, populate, edit, save, verify
|
||
*/
|
||
|
||
const { chromium } = require('@playwright/test');
|
||
const fs = require('fs').promises;
|
||
|
||
const CONFIG = {
|
||
baseUrl: 'https://upskill-staging.measurequick.com',
|
||
credentials: {
|
||
email: 'test_trainer@example.com',
|
||
password: 'TestTrainer123!'
|
||
}
|
||
};
|
||
|
||
// Track field values before and after
|
||
const FIELD_VALUES = {
|
||
before: {},
|
||
after: {},
|
||
changes: {}
|
||
};
|
||
|
||
async function screenshot(page, name) {
|
||
await fs.mkdir('screenshots/edit-workflow', { recursive: true });
|
||
const path = `screenshots/edit-workflow/${name}-${Date.now()}.png`;
|
||
await page.screenshot({ path, fullPage: true });
|
||
console.log(`📸 Screenshot: ${name}`);
|
||
return path;
|
||
}
|
||
|
||
async function createTestEvent(page) {
|
||
console.log('\n📝 Creating test event via WordPress Admin...');
|
||
|
||
await page.goto(`${CONFIG.baseUrl}/wp-admin/post-new.php?post_type=tribe_events`);
|
||
await page.waitForLoadState('domcontentloaded');
|
||
|
||
// Fill basic event data
|
||
const eventData = {
|
||
title: `Test Event for Edit Verification ${Date.now()}`,
|
||
description: 'This event will be edited to verify all fields save correctly.',
|
||
startDate: '2025-09-15',
|
||
endDate: '2025-09-16',
|
||
startTime: '09:00',
|
||
endTime: '17:00',
|
||
cost: '299',
|
||
venue: 'Test Venue',
|
||
organizer: 'Test Organizer',
|
||
website: 'https://example.com/test'
|
||
};
|
||
|
||
// Title
|
||
const titleField = await page.$('#title');
|
||
if (titleField) {
|
||
await titleField.fill(eventData.title);
|
||
console.log('✅ Title set');
|
||
}
|
||
|
||
// Description (try different methods)
|
||
const contentFrame = await page.$('#content_ifr');
|
||
if (contentFrame) {
|
||
const frame = await contentFrame.contentFrame();
|
||
await frame.fill('#tinymce', eventData.description);
|
||
console.log('✅ Description set (TinyMCE)');
|
||
} else {
|
||
const contentArea = await page.$('#content');
|
||
if (contentArea) {
|
||
await contentArea.fill(eventData.description);
|
||
console.log('✅ Description set (textarea)');
|
||
}
|
||
}
|
||
|
||
// Dates
|
||
const startDate = await page.$('#EventStartDate');
|
||
if (startDate) {
|
||
await startDate.fill(eventData.startDate);
|
||
console.log('✅ Start date set');
|
||
}
|
||
|
||
const endDate = await page.$('#EventEndDate');
|
||
if (endDate) {
|
||
await endDate.fill(eventData.endDate);
|
||
console.log('✅ End date set');
|
||
}
|
||
|
||
// Times
|
||
const startTime = await page.$('#EventStartTime');
|
||
if (startTime) {
|
||
await startTime.fill(eventData.startTime);
|
||
console.log('✅ Start time set');
|
||
}
|
||
|
||
const endTime = await page.$('#EventEndTime');
|
||
if (endTime) {
|
||
await endTime.fill(eventData.endTime);
|
||
console.log('✅ End time set');
|
||
}
|
||
|
||
// Cost
|
||
const costField = await page.$('#EventCost');
|
||
if (costField) {
|
||
await costField.fill(eventData.cost);
|
||
console.log('✅ Cost set');
|
||
}
|
||
|
||
// Website
|
||
const urlField = await page.$('#EventURL');
|
||
if (urlField) {
|
||
await urlField.fill(eventData.website);
|
||
console.log('✅ Website set');
|
||
}
|
||
|
||
// Save as draft first
|
||
const saveDraft = await page.$('#save-post');
|
||
if (saveDraft) {
|
||
await saveDraft.click();
|
||
await page.waitForSelector('.notice-success, #message', { timeout: 10000 });
|
||
console.log('✅ Event saved as draft');
|
||
}
|
||
|
||
// Now publish
|
||
const publishBtn = await page.$('#publish');
|
||
if (publishBtn) {
|
||
await publishBtn.click();
|
||
await page.waitForSelector('.notice-success, #message', { timeout: 10000 });
|
||
console.log('✅ Event published');
|
||
}
|
||
|
||
// Get the event ID from URL
|
||
const url = page.url();
|
||
const match = url.match(/post=(\d+)/);
|
||
const eventId = match ? match[1] : null;
|
||
|
||
console.log(`✅ Event created with ID: ${eventId}`);
|
||
return eventId;
|
||
}
|
||
|
||
async function accessEventFromDashboard(page) {
|
||
console.log('\n🔍 Accessing event from dashboard...');
|
||
|
||
// Go to events list
|
||
await page.goto(`${CONFIG.baseUrl}/trainer/events/`);
|
||
await page.waitForLoadState('domcontentloaded');
|
||
|
||
await screenshot(page, 'events-list');
|
||
|
||
// Try multiple selectors for edit links
|
||
const editSelectors = [
|
||
'a[href*="action=edit"]',
|
||
'a[href*="/edit/"]',
|
||
'.edit-link',
|
||
'a:has-text("Edit")',
|
||
'button:has-text("Edit")'
|
||
];
|
||
|
||
let editLink = null;
|
||
for (const selector of editSelectors) {
|
||
editLink = await page.$(selector);
|
||
if (editLink) {
|
||
console.log(`✅ Found edit link with selector: ${selector}`);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!editLink) {
|
||
// If no edit links, go directly to admin
|
||
console.log('⚠️ No edit links found on trainer page, using admin...');
|
||
await page.goto(`${CONFIG.baseUrl}/wp-admin/edit.php?post_type=tribe_events`);
|
||
|
||
const firstEdit = await page.$('#the-list .row-actions .edit a');
|
||
if (firstEdit) {
|
||
await firstEdit.click();
|
||
await page.waitForLoadState('networkidle');
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
await editLink.click();
|
||
await page.waitForLoadState('networkidle');
|
||
return true;
|
||
}
|
||
|
||
async function captureAllFields(page) {
|
||
console.log('\n📊 Capturing all field values...');
|
||
|
||
const fields = {};
|
||
|
||
// Define all TEC fields to check
|
||
const fieldSelectors = {
|
||
'title': '#title, input[name="post_title"]',
|
||
'content': '#content, #tinymce',
|
||
'startDate': '#EventStartDate, input[name="EventStartDate"]',
|
||
'endDate': '#EventEndDate, input[name="EventEndDate"]',
|
||
'startTime': '#EventStartTime, input[name="EventStartTime"]',
|
||
'endTime': '#EventEndTime, input[name="EventEndTime"]',
|
||
'allDay': '#EventAllDay, input[name="EventAllDay"]',
|
||
'timezone': '#event-timezone, select[name="EventTimezone"]',
|
||
'cost': '#EventCost, input[name="EventCost"]',
|
||
'currency': '#EventCurrencySymbol, input[name="EventCurrencySymbol"]',
|
||
'website': '#EventURL, input[name="EventURL"]',
|
||
'venue': '#venue-name, select[name="venue[VenueID]"], input[name="venue[Venue]"]',
|
||
'address': '#VenueAddress, input[name="venue[Address]"]',
|
||
'city': '#VenueCity, input[name="venue[City]"]',
|
||
'state': '#VenueState, input[name="venue[State]"]',
|
||
'zip': '#VenueZip, input[name="venue[Zip]"]',
|
||
'country': '#VenueCountry, select[name="venue[Country]"]',
|
||
'phone': '#VenuePhone, input[name="venue[Phone]"]',
|
||
'organizer': '#organizer-name, select[name="organizer[OrganizerID]"], input[name="organizer[Organizer]"]',
|
||
'organizerEmail': '#OrganizerEmail, input[name="organizer[Email]"]',
|
||
'organizerPhone': '#OrganizerPhone, input[name="organizer[Phone]"]',
|
||
'organizerWebsite': '#OrganizerWebsite, input[name="organizer[Website]"]'
|
||
};
|
||
|
||
for (const [fieldName, selector] of Object.entries(fieldSelectors)) {
|
||
try {
|
||
const element = await page.$(selector);
|
||
if (element) {
|
||
// Try different methods to get value
|
||
let value = await element.inputValue().catch(() => null);
|
||
|
||
if (!value) {
|
||
value = await element.textContent().catch(() => null);
|
||
}
|
||
|
||
if (!value) {
|
||
value = await element.evaluate(el => {
|
||
if (el.tagName === 'SELECT') {
|
||
return el.options[el.selectedIndex]?.text || '';
|
||
}
|
||
return el.value || el.innerText || '';
|
||
});
|
||
}
|
||
|
||
if (value) {
|
||
fields[fieldName] = value;
|
||
console.log(` ${fieldName}: ${value.substring(0, 50)}${value.length > 50 ? '...' : ''}`);
|
||
}
|
||
} else {
|
||
console.log(` ${fieldName}: [field not found]`);
|
||
}
|
||
} catch (error) {
|
||
console.log(` ${fieldName}: [error reading]`);
|
||
}
|
||
}
|
||
|
||
// Check for TinyMCE content
|
||
const contentFrame = await page.$('#content_ifr');
|
||
if (contentFrame && !fields.content) {
|
||
try {
|
||
const frame = await contentFrame.contentFrame();
|
||
const content = await frame.$eval('#tinymce', el => el.textContent);
|
||
if (content) {
|
||
fields.content = content;
|
||
console.log(` content (TinyMCE): ${content.substring(0, 50)}...`);
|
||
}
|
||
} catch (error) {
|
||
console.log(' content: [TinyMCE error]');
|
||
}
|
||
}
|
||
|
||
await screenshot(page, 'fields-captured');
|
||
|
||
return fields;
|
||
}
|
||
|
||
async function editFields(page, originalValues) {
|
||
console.log('\n✏️ Editing fields...');
|
||
|
||
const changes = {};
|
||
|
||
// Edit title
|
||
const titleField = await page.$('#title');
|
||
if (titleField && originalValues.title) {
|
||
const newTitle = originalValues.title + ' (EDITED)';
|
||
await titleField.fill(newTitle);
|
||
changes.title = newTitle;
|
||
console.log('✅ Title edited');
|
||
}
|
||
|
||
// Edit cost
|
||
const costField = await page.$('#EventCost');
|
||
if (costField && originalValues.cost) {
|
||
const newCost = '399';
|
||
await costField.fill(newCost);
|
||
changes.cost = newCost;
|
||
console.log('✅ Cost edited');
|
||
}
|
||
|
||
// Edit start date
|
||
const startDateField = await page.$('#EventStartDate');
|
||
if (startDateField && originalValues.startDate) {
|
||
const newDate = '2025-10-01';
|
||
await startDateField.fill(newDate);
|
||
changes.startDate = newDate;
|
||
console.log('✅ Start date edited');
|
||
}
|
||
|
||
// Edit website
|
||
const urlField = await page.$('#EventURL');
|
||
if (urlField) {
|
||
const newUrl = 'https://edited.example.com/event';
|
||
await urlField.fill(newUrl);
|
||
changes.website = newUrl;
|
||
console.log('✅ Website edited');
|
||
}
|
||
|
||
// Edit description
|
||
const contentFrame = await page.$('#content_ifr');
|
||
if (contentFrame) {
|
||
try {
|
||
const frame = await contentFrame.contentFrame();
|
||
const tinymce = await frame.$('#tinymce');
|
||
if (tinymce) {
|
||
await tinymce.fill('EDITED: This event description has been updated.');
|
||
changes.content = 'EDITED: This event description has been updated.';
|
||
console.log('✅ Description edited');
|
||
}
|
||
} catch (error) {
|
||
// Try textarea
|
||
const contentArea = await page.$('#content');
|
||
if (contentArea) {
|
||
await contentArea.fill('EDITED: This event description has been updated.');
|
||
changes.content = 'EDITED: This event description has been updated.';
|
||
console.log('✅ Description edited (textarea)');
|
||
}
|
||
}
|
||
}
|
||
|
||
await screenshot(page, 'fields-edited');
|
||
|
||
return changes;
|
||
}
|
||
|
||
async function saveAndVerify(page, expectedChanges) {
|
||
console.log('\n💾 Saving changes...');
|
||
|
||
// Find and click update/publish button
|
||
const updateBtn = await page.$('#publish, #save-post, input[name="save"]');
|
||
if (updateBtn) {
|
||
await updateBtn.click();
|
||
|
||
// Wait for success message
|
||
await page.waitForSelector('.notice-success, #message, .updated', { timeout: 10000 });
|
||
console.log('✅ Changes saved');
|
||
|
||
await screenshot(page, 'saved-success');
|
||
} else {
|
||
console.log('❌ Save button not found');
|
||
return false;
|
||
}
|
||
|
||
// Reload the page to verify persistence
|
||
console.log('\n🔄 Reloading to verify persistence...');
|
||
await page.reload();
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// Capture fields again
|
||
const afterValues = await captureAllFields(page);
|
||
|
||
// Verify changes persisted
|
||
console.log('\n✅ Verifying changes persisted...');
|
||
let allChangesPersisted = true;
|
||
|
||
for (const [field, expectedValue] of Object.entries(expectedChanges)) {
|
||
const actualValue = afterValues[field];
|
||
|
||
if (actualValue && actualValue.includes(expectedValue)) {
|
||
console.log(` ✅ ${field}: Change persisted`);
|
||
} else {
|
||
console.log(` ❌ ${field}: Expected "${expectedValue}", got "${actualValue}"`);
|
||
allChangesPersisted = false;
|
||
}
|
||
}
|
||
|
||
return allChangesPersisted;
|
||
}
|
||
|
||
async function runCompleteEditWorkflow() {
|
||
console.log('🎯 COMPLETE EVENT EDIT WORKFLOW TEST');
|
||
console.log('=' .repeat(60));
|
||
console.log('This test will:');
|
||
console.log('1. Create/access an event');
|
||
console.log('2. Verify ALL fields populate');
|
||
console.log('3. Edit multiple fields');
|
||
console.log('4. Save and verify persistence');
|
||
console.log('=' .repeat(60));
|
||
|
||
const browser = await chromium.launch({ headless: true });
|
||
const page = await browser.newPage();
|
||
|
||
const results = {
|
||
steps: [],
|
||
fieldsBefore: 0,
|
||
fieldsAfter: 0,
|
||
changesPersisted: 0,
|
||
totalChanges: 0
|
||
};
|
||
|
||
try {
|
||
// Login
|
||
console.log('\n🔐 Logging in...');
|
||
await page.goto(`${CONFIG.baseUrl}/wp-login.php`);
|
||
await page.fill('#user_login', CONFIG.credentials.email);
|
||
await page.fill('#user_pass', CONFIG.credentials.password);
|
||
await page.click('#wp-submit');
|
||
await page.waitForURL(/dashboard|admin/, { timeout: 10000 });
|
||
console.log('✅ Logged in successfully');
|
||
results.steps.push({ step: 'Login', status: 'passed' });
|
||
|
||
// Create a test event first
|
||
const eventId = await createTestEvent(page);
|
||
if (eventId) {
|
||
results.steps.push({ step: 'Create Test Event', status: 'passed', eventId });
|
||
}
|
||
|
||
// Access event for editing
|
||
const accessed = await accessEventFromDashboard(page);
|
||
if (accessed) {
|
||
console.log('✅ Accessed event edit form');
|
||
results.steps.push({ step: 'Access Edit Form', status: 'passed' });
|
||
} else {
|
||
console.log('❌ Could not access edit form');
|
||
results.steps.push({ step: 'Access Edit Form', status: 'failed' });
|
||
}
|
||
|
||
// Capture all field values
|
||
FIELD_VALUES.before = await captureAllFields(page);
|
||
results.fieldsBefore = Object.keys(FIELD_VALUES.before).length;
|
||
console.log(`\n📊 Fields populated: ${results.fieldsBefore}`);
|
||
results.steps.push({
|
||
step: 'Capture Fields',
|
||
status: results.fieldsBefore > 0 ? 'passed' : 'failed',
|
||
fieldsFound: results.fieldsBefore
|
||
});
|
||
|
||
// Edit fields
|
||
FIELD_VALUES.changes = await editFields(page, FIELD_VALUES.before);
|
||
results.totalChanges = Object.keys(FIELD_VALUES.changes).length;
|
||
console.log(`\n✏️ Fields edited: ${results.totalChanges}`);
|
||
results.steps.push({
|
||
step: 'Edit Fields',
|
||
status: results.totalChanges > 0 ? 'passed' : 'failed',
|
||
changesAttempted: results.totalChanges
|
||
});
|
||
|
||
// Save and verify
|
||
const changesPersisted = await saveAndVerify(page, FIELD_VALUES.changes);
|
||
results.changesPersisted = changesPersisted ? results.totalChanges : 0;
|
||
results.steps.push({
|
||
step: 'Save and Verify',
|
||
status: changesPersisted ? 'passed' : 'failed',
|
||
changesPersisted: results.changesPersisted
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('❌ Error:', error.message);
|
||
results.steps.push({ step: 'Error', status: 'failed', error: error.message });
|
||
} finally {
|
||
await browser.close();
|
||
}
|
||
|
||
// Generate final report
|
||
console.log('\n' + '=' .repeat(60));
|
||
console.log('📊 FINAL RESULTS');
|
||
console.log('=' .repeat(60));
|
||
|
||
const passed = results.steps.filter(s => s.status === 'passed').length;
|
||
const failed = results.steps.filter(s => s.status === 'failed').length;
|
||
const successRate = ((passed / results.steps.length) * 100).toFixed(0);
|
||
|
||
console.log(`\nWorkflow Steps: ${results.steps.length}`);
|
||
console.log(`✅ Passed: ${passed}`);
|
||
console.log(`❌ Failed: ${failed}`);
|
||
console.log(`Success Rate: ${successRate}%`);
|
||
|
||
console.log('\n📋 Details:');
|
||
console.log(`- Fields found before edit: ${results.fieldsBefore}`);
|
||
console.log(`- Fields edited: ${results.totalChanges}`);
|
||
console.log(`- Changes persisted: ${results.changesPersisted}/${results.totalChanges}`);
|
||
|
||
console.log('\n🔍 Step Results:');
|
||
results.steps.forEach(step => {
|
||
const icon = step.status === 'passed' ? '✅' : '❌';
|
||
console.log(`${icon} ${step.step}: ${step.status.toUpperCase()}`);
|
||
if (step.fieldsFound) console.log(` Fields: ${step.fieldsFound}`);
|
||
if (step.changesAttempted) console.log(` Changes: ${step.changesAttempted}`);
|
||
if (step.error) console.log(` Error: ${step.error}`);
|
||
});
|
||
|
||
console.log('\n🎯 ASSESSMENT:');
|
||
if (successRate === '100' && results.changesPersisted === results.totalChanges) {
|
||
console.log('✅ COMPLETE EDIT WORKFLOW VERIFIED!');
|
||
console.log('All fields populate correctly and changes persist.');
|
||
} else if (successRate >= '60') {
|
||
console.log('⚠️ PARTIAL SUCCESS');
|
||
console.log('Some aspects work but issues detected.');
|
||
} else {
|
||
console.log('❌ EDIT WORKFLOW NEEDS ATTENTION');
|
||
console.log('Significant issues with field population or persistence.');
|
||
}
|
||
|
||
console.log('\n📁 Screenshots: screenshots/edit-workflow/');
|
||
console.log('=' .repeat(60));
|
||
}
|
||
|
||
// Run the test
|
||
console.log('Starting complete edit workflow test...\n');
|
||
runCompleteEditWorkflow().catch(error => {
|
||
console.error('Fatal error:', error);
|
||
process.exit(1);
|
||
}); |