#!/usr/bin/env node /** * TEC v5.0.8 VALIDATED FIELD TEST * Uses the correct field selectors from our field mapping documentation * Tests complete event edit workflow with proper TEC v5.0.8 selectors */ const { chromium } = require('@playwright/test'); const fs = require('fs').promises; const { execSync } = require('child_process'); // Configure XWayland display process.env.DISPLAY = ':0'; try { const xauthFile = execSync('ls /run/user/1000/.mutter-Xwaylandauth.* 2>/dev/null | head -n1', { encoding: 'utf8' }).trim(); if (xauthFile) { process.env.XAUTHORITY = xauthFile; } } catch (e) { // Continue without XAUTHORITY } const CONFIG = { baseUrl: process.env.UPSKILL_STAGING_URL || 'https://upskill-staging.measurequick.com', credentials: { username: 'test_admin', password: 'TestAdmin2025!' } }; /** * TEC v5.0.8 FIELD MAPPING * Based on our documented field mapping (docs/TEC-V5-FIELD-MAPPING.md) */ const TEC_FIELDS = { // Core Event Fields basic: { 'Event Title': { selector: '#title', metaKey: 'post_title', type: 'text' }, 'Event Content': { selector: '#content', metaKey: 'post_content', type: 'wysiwyg' }, 'Event Excerpt': { selector: '#excerpt', metaKey: 'post_excerpt', type: 'textarea' } }, // Date & Time Fields datetime: { 'Start Date': { selector: '#EventStartDate', metaKey: '_EventStartDate', type: 'date', format: 'YYYY-MM-DD' }, 'End Date': { selector: '#EventEndDate', metaKey: '_EventEndDate', type: 'date', format: 'YYYY-MM-DD' }, 'Start Time': { selector: '#EventStartTime', metaKey: null, // Combined with date type: 'time', format: 'HH:MM am/pm' }, 'End Time': { selector: '#EventEndTime', metaKey: null, // Combined with date type: 'time', format: 'HH:MM am/pm' }, 'All Day Event': { selector: '#EventAllDay', metaKey: '_EventAllDay', type: 'checkbox' }, 'Timezone': { selector: 'select[name="EventTimezone"]', metaKey: '_EventTimezone', type: 'select' } }, // Cost & Currency Fields cost: { 'Event Cost': { selector: '#EventCost', metaKey: '_EventCost', type: 'text' }, 'Currency Symbol': { selector: '#EventCurrencySymbol', metaKey: '_EventCurrencySymbol', type: 'text', default: '$' }, 'Currency Code': { selector: '#EventCurrencyCode', metaKey: '_EventCurrencyCode', type: 'text', default: 'USD' }, 'Currency Position': { selector: 'select[name="EventCurrencyPosition"]', metaKey: null, type: 'select' } }, // Event Details details: { 'Event URL': { selector: '#EventURL', metaKey: '_EventURL', type: 'url' }, 'Show Map': { selector: '#EventShowMap', metaKey: '_EventShowMap', type: 'checkbox' }, 'Show Map Link': { selector: '#EventShowMapLink', metaKey: '_EventShowMapLink', type: 'checkbox' } }, // Venue Fields venue: { 'Venue ID': { selector: 'select[name="venue[VenueID]"]', metaKey: '_EventVenueID', type: 'select' }, 'Venue Name': { selector: 'input[name="venue[Venue]"]', metaKey: '_VenueVenue', type: 'text' }, 'Venue Address': { selector: 'input[name="venue[Address]"]', metaKey: '_VenueAddress', type: 'text' }, 'Venue City': { selector: 'input[name="venue[City]"]', metaKey: '_VenueCity', type: 'text' }, 'Venue State': { selector: 'input[name="venue[State]"]', metaKey: '_VenueStateProvince', type: 'text' }, 'Venue Zip': { selector: 'input[name="venue[Zip]"]', metaKey: '_VenueZip', type: 'text' }, 'Venue Country': { selector: 'select[name="venue[Country]"]', metaKey: '_VenueCountry', type: 'select' }, 'Venue Phone': { selector: 'input[name="venue[Phone]"]', metaKey: '_VenuePhone', type: 'tel' }, 'Venue Website': { selector: 'input[name="venue[URL]"]', metaKey: '_VenueURL', type: 'url' } }, // Organizer Fields organizer: { 'Organizer ID': { selector: 'select[name="organizer[OrganizerID]"]', metaKey: '_EventOrganizerID', type: 'select' }, 'Organizer Name': { selector: 'input[name="organizer[Organizer]"]', metaKey: '_OrganizerOrganizer', type: 'text' }, 'Organizer Email': { selector: 'input[name="organizer[Email]"]', metaKey: '_OrganizerEmail', type: 'email' }, 'Organizer Phone': { selector: 'input[name="organizer[Phone]"]', metaKey: '_OrganizerPhone', type: 'tel' }, 'Organizer Website': { selector: 'input[name="organizer[Website]"]', metaKey: '_OrganizerWebsite', type: 'url' } } }; async function screenshot(page, name) { await fs.mkdir('screenshots/tec-v5-validated', { recursive: true }); const path = `screenshots/tec-v5-validated/${name}-${Date.now()}.png`; await page.screenshot({ path, fullPage: true }); console.log(`šŸ“ø Screenshot: ${name}`); return path; } async function getFieldValue(page, fieldDef) { try { const element = await page.$(fieldDef.selector); if (!element) return null; switch (fieldDef.type) { case 'checkbox': return await element.isChecked(); case 'select': return await element.inputValue(); case 'wysiwyg': case 'textarea': // Try to get from the actual textarea or content div const textValue = await element.inputValue().catch(() => null); if (textValue) return textValue; return await element.textContent(); default: return await element.inputValue(); } } catch (e) { return null; } } async function setFieldValue(page, fieldDef, value) { try { const element = await page.$(fieldDef.selector); if (!element) return false; switch (fieldDef.type) { case 'checkbox': if (value) { await element.check(); } else { await element.uncheck(); } return true; case 'select': await element.selectOption(value); return true; case 'wysiwyg': case 'textarea': case 'text': case 'date': case 'time': case 'url': case 'email': case 'tel': default: await element.fill(value.toString()); return true; } } catch (e) { console.log(` Error setting field: ${e.message}`); return false; } } async function runTECv5ValidatedTest() { console.log('šŸŽÆ TEC v5.0.8 VALIDATED FIELD TEST'); console.log('=' .repeat(70)); console.log('Using correct field selectors from TEC v5.0.8 documentation'); console.log('=' .repeat(70)); const browser = await chromium.launch({ headless: false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); const results = { loginSuccess: false, eventsFound: 0, fieldsFound: {}, fieldsPopulated: {}, fieldsTested: 0, fieldsWorking: 0, editTestResults: {} }; try { // 1. LOGIN console.log('\nšŸ“ STEP 1: Login'); console.log('-' .repeat(50)); await page.goto(`${CONFIG.baseUrl}/training-login`); await page.waitForLoadState('networkidle'); // Use the training login form selectors await page.fill('input[name="log"]', CONFIG.credentials.username); await page.fill('input[name="pwd"]', CONFIG.credentials.password); await page.click('input[type="submit"]'); // Wait for redirect to dashboard await page.waitForURL('**/dashboard/**', { timeout: 15000 }); results.loginSuccess = true; console.log('āœ… Logged in successfully'); // 2. ACCESS EVENT EDIT FORM console.log('\nšŸ“ STEP 2: Opening Event Edit Form'); console.log('-' .repeat(50)); // From dashboard, go to manage events page await page.goto(`${CONFIG.baseUrl}/trainer/event/manage/`); await page.waitForLoadState('networkidle'); await screenshot(page, 'manage-events-page'); // Look for event list on the manage page const eventLinks = await page.$$('.hvac-events-list a[href*="edit"], .event-actions a:has-text("Edit")'); results.eventsFound = eventLinks.length; console.log(`āœ… Found ${results.eventsFound} events on manage page`); if (results.eventsFound === 0) { // Try alternate approach - go directly to wp-admin console.log('āš ļø No events on manage page, trying wp-admin...'); await page.goto(`${CONFIG.baseUrl}/wp-admin/edit.php?post_type=tribe_events`); await page.waitForLoadState('networkidle'); const eventRows = await page.$$('tbody#the-list tr'); results.eventsFound = eventRows.length; console.log(` Found ${results.eventsFound} events in wp-admin`); if (results.eventsFound === 0) { console.log('āŒ No events found - running seed script...'); execSync('bash bin/create-test-admin-and-seed.sh', { stdio: 'inherit' }); await page.reload(); await page.waitForLoadState('networkidle'); } // Click edit on first event from wp-admin const editLink = await page.$('tbody#the-list tr:first-child .row-actions .edit a'); if (editLink) { await editLink.click(); } } else { // Click first edit link from manage page await eventLinks[0].click(); } await page.waitForLoadState('networkidle'); console.log('āœ… Edit form opened'); await screenshot(page, 'edit-form'); // 3. TEST FIELD DISCOVERY WITH CORRECT SELECTORS console.log('\nšŸ“ STEP 3: Testing TEC v5.0.8 Field Selectors'); console.log('-' .repeat(50)); for (const [category, fields] of Object.entries(TEC_FIELDS)) { console.log(`\nšŸ” Testing ${category.toUpperCase()} fields:`); for (const [fieldName, fieldDef] of Object.entries(fields)) { results.fieldsTested++; const element = await page.$(fieldDef.selector); if (element) { const isVisible = await element.isVisible().catch(() => false); const value = await getFieldValue(page, fieldDef); results.fieldsFound[fieldName] = { found: true, visible: isVisible, value: value, selector: fieldDef.selector, metaKey: fieldDef.metaKey }; if (isVisible) { results.fieldsWorking++; if (value !== null && value !== '' && value !== false) { results.fieldsPopulated[fieldName] = value; console.log(` āœ… ${fieldName}: POPULATED - "${String(value).substring(0, 50)}"`); } else { console.log(` āœ… ${fieldName}: FOUND (empty)`); } } else { console.log(` āš ļø ${fieldName}: Hidden`); } } else { results.fieldsFound[fieldName] = { found: false, selector: fieldDef.selector }; console.log(` āŒ ${fieldName}: NOT FOUND`); } } } // 4. EDIT TEST WITH CORRECT SELECTORS console.log('\nšŸ“ STEP 4: Testing Field Updates'); console.log('-' .repeat(50)); const testEdits = { 'Event Title': 'TEC v5.0.8 Test Event - EDITED', 'Start Date': '2025-12-25', 'End Date': '2025-12-26', 'Event Cost': '999', 'Event URL': 'https://tec-v5-test.example.com' }; for (const [fieldName, newValue] of Object.entries(testEdits)) { // Find the field definition let fieldDef = null; for (const category of Object.values(TEC_FIELDS)) { if (category[fieldName]) { fieldDef = category[fieldName]; break; } } if (fieldDef) { const success = await setFieldValue(page, fieldDef, newValue); if (success) { console.log(` āœ… ${fieldName} updated to: ${newValue}`); results.editTestResults[fieldName] = 'updated'; } else { console.log(` āŒ ${fieldName} update failed`); results.editTestResults[fieldName] = 'failed'; } } } await screenshot(page, 'after-edits'); // 5. SAVE AND VERIFY console.log('\nšŸ“ STEP 5: Saving and Verifying'); console.log('-' .repeat(50)); const publishButton = await page.$('#publish'); if (publishButton) { await publishButton.click(); console.log(' Saving changes...'); try { await page.waitForSelector('.notice-success, #message', { timeout: 10000 }); console.log('āœ… Changes saved'); } catch (e) { console.log('āš ļø Save confirmation not detected'); } // Reload and verify await page.reload(); await page.waitForLoadState('networkidle'); console.log('\n Verifying persistence:'); for (const [fieldName, expectedValue] of Object.entries(testEdits)) { let fieldDef = null; for (const category of Object.values(TEC_FIELDS)) { if (category[fieldName]) { fieldDef = category[fieldName]; break; } } if (fieldDef) { const currentValue = await getFieldValue(page, fieldDef); if (currentValue === expectedValue || (currentValue && currentValue.includes && currentValue.includes(expectedValue))) { console.log(` āœ… ${fieldName}: PERSISTED`); results.editTestResults[fieldName] = 'persisted'; } else { console.log(` āŒ ${fieldName}: NOT persisted (current: ${currentValue})`); results.editTestResults[fieldName] = 'not_persisted'; } } } } } catch (error) { console.error('\nāŒ Error:', error.message); await screenshot(page, 'error'); } finally { console.log('\nā±ļø Keeping browser open for 5 seconds...'); await page.waitForTimeout(5000); await browser.close(); } return results; } // Run the test console.log('Starting TEC v5.0.8 validated field test...\n'); runTECv5ValidatedTest().then(results => { console.log('\n' + '=' .repeat(70)); console.log('šŸ“Š TEC v5.0.8 VALIDATION REPORT'); console.log('=' .repeat(70)); console.log('\nāœ… Field Discovery Results:'); console.log(` • Total fields tested: ${results.fieldsTested}`); console.log(` • Fields found & visible: ${results.fieldsWorking}`); console.log(` • Fields populated: ${Object.keys(results.fieldsPopulated).length}`); console.log('\nāœ… Working Field Selectors:'); for (const [field, data] of Object.entries(results.fieldsFound)) { if (data.found && data.visible) { console.log(` • ${field}: ${data.selector}`); } } console.log('\nāš ļø Missing or Hidden Fields:'); for (const [field, data] of Object.entries(results.fieldsFound)) { if (!data.found || !data.visible) { console.log(` • ${field}: ${data.found ? 'Hidden' : 'Not Found'} (${data.selector})`); } } console.log('\nāœ… Edit Test Results:'); const persistedCount = Object.values(results.editTestResults) .filter(r => r === 'persisted').length; console.log(` • Changes persisted: ${persistedCount}/${Object.keys(results.editTestResults).length}`); for (const [field, result] of Object.entries(results.editTestResults)) { const icon = result === 'persisted' ? 'āœ…' : 'āŒ'; console.log(` ${icon} ${field}: ${result}`); } console.log('\n' + '=' .repeat(70)); console.log('šŸ“Œ SUMMARY'); console.log('-' .repeat(70)); if (results.fieldsWorking >= 20 && persistedCount >= 3) { console.log('\nāœ…āœ…āœ… TEC v5.0.8 VALIDATION SUCCESSFUL! āœ…āœ…āœ…'); console.log('Field selectors are correct and working!'); console.log('Event editing with TEC v5.0.8 is fully functional!'); } else { console.log('\nāš ļø PARTIAL SUCCESS'); console.log('Some field selectors may need adjustment.'); console.log('Review the missing/hidden fields above.'); } console.log('\nšŸ“ Screenshots saved to: screenshots/tec-v5-validated/'); console.log('šŸ“„ Field mapping documented in: docs/TEC-V5-FIELD-MAPPING.md'); console.log('=' .repeat(70)); }).catch(error => { console.error('Fatal error:', error); process.exit(1); });