#!/usr/bin/env node /** * TEC v5.0.8 Field Discovery Test * Discovers and documents the actual field selectors used by The Events Calendar */ 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: 'https://upskill-staging.measurequick.com', credentials: { username: 'test_admin', password: 'TestAdmin2025!' } }; async function screenshot(page, name) { await fs.mkdir('screenshots/tec-discovery', { recursive: true }); const path = `screenshots/tec-discovery/${name}-${Date.now()}.png`; await page.screenshot({ path, fullPage: true }); console.log(`šŸ“ø Screenshot: ${name}`); return path; } async function discoverTECFields() { console.log('šŸ” TEC v5.0.8 FIELD DISCOVERY'); console.log('=' .repeat(70)); console.log('Discovering actual field selectors in The Events Calendar v5.0.8'); console.log('=' .repeat(70)); const browser = await chromium.launch({ headless: false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); const discoveredFields = { basic: {}, datetime: {}, cost: {}, venue: {}, organizer: {}, other: {} }; try { // 1. LOGIN console.log('\nšŸ“ STEP 1: Login'); await page.goto(`${CONFIG.baseUrl}/wp-login.php`); await page.fill('#user_login', CONFIG.credentials.username); await page.fill('#user_pass', CONFIG.credentials.password); await page.click('#wp-submit'); await page.waitForURL('**/wp-admin/**', { timeout: 15000 }); console.log('āœ… Logged in'); // 2. OPEN EVENT EDIT FORM console.log('\nšŸ“ STEP 2: Opening Event Edit Form'); // First, get to the events list await page.goto(`${CONFIG.baseUrl}/wp-admin/edit.php?post_type=tribe_events`); await page.waitForLoadState('networkidle'); // Click edit on first event const editLink = await page.$('tbody#the-list tr:first-child .row-actions .edit a'); if (!editLink) { console.log('āŒ No events found'); await browser.close(); return; } await editLink.click(); await page.waitForLoadState('networkidle'); console.log('āœ… Edit form opened'); await screenshot(page, 'edit-form'); // 3. DISCOVER FIELDS console.log('\nšŸ“ STEP 3: Discovering Fields'); console.log('-' .repeat(70)); // Strategy 1: Look for all input fields with relevant IDs/names console.log('\nšŸ” Strategy 1: Input fields by ID/name patterns'); const inputSelectors = [ // Title and content (standard WP) '#title', '#content', 'textarea[name="content"]', '#excerpt', // Event-specific patterns 'input[id*="Event"]', 'input[name*="Event"]', 'select[id*="Event"]', 'select[name*="Event"]', 'input[id*="event"]', 'input[name*="event"]', // Venue patterns 'input[id*="Venue"]', 'input[name*="venue"]', 'select[id*="Venue"]', 'select[name*="venue"]', // Organizer patterns 'input[id*="Organizer"]', 'input[name*="organizer"]', 'select[id*="Organizer"]', 'select[name*="organizer"]', // TEC specific patterns 'input[name*="tribe"]', 'select[name*="tribe"]', '.tribe-events-admin input', '.tribe-events-admin select' ]; for (const selector of inputSelectors) { try { const elements = await page.$$(selector); if (elements.length > 0) { console.log(`\nāœ… Found ${elements.length} elements matching: ${selector}`); // Get details of each element for (let i = 0; i < Math.min(5, elements.length); i++) { const details = await elements[i].evaluate(el => ({ id: el.id, name: el.name, type: el.type || el.tagName.toLowerCase(), value: el.value || '', placeholder: el.placeholder || '', className: el.className })); if (details.id || details.name) { console.log(` - ${details.type}: id="${details.id}" name="${details.name}"`); // Categorize the field const fieldKey = details.id || details.name; if (fieldKey.match(/date|time/i)) { discoveredFields.datetime[fieldKey] = details; } else if (fieldKey.match(/cost|price|currency/i)) { discoveredFields.cost[fieldKey] = details; } else if (fieldKey.match(/venue/i)) { discoveredFields.venue[fieldKey] = details; } else if (fieldKey.match(/organizer/i)) { discoveredFields.organizer[fieldKey] = details; } else { discoveredFields.other[fieldKey] = details; } } } } } catch (e) { // Skip selector if it causes an error } } // Strategy 2: Look for metabox containers console.log('\nšŸ” Strategy 2: TEC Metaboxes'); const metaboxSelectors = [ '#tribe_events_event_options', '.tribe-events-admin', '#event_tribe_venue', '#event_tribe_organizer', '.tribe_events_event_section', '[id*="tribe-events"]', '[class*="tribe-events"]' ]; for (const selector of metaboxSelectors) { const element = await page.$(selector); if (element) { console.log(`\nāœ… Found metabox: ${selector}`); // Find all inputs within this metabox const inputs = await element.$$('input, select, textarea'); console.log(` Contains ${inputs.length} form fields`); for (let i = 0; i < Math.min(10, inputs.length); i++) { const details = await inputs[i].evaluate(el => ({ id: el.id, name: el.name, type: el.type || el.tagName.toLowerCase() })); if (details.id || details.name) { console.log(` - ${details.type}: id="${details.id}" name="${details.name}"`); } } } } // Strategy 3: Check for Block Editor (Gutenberg) fields console.log('\nšŸ” Strategy 3: Block Editor Fields'); const blockEditorPresent = await page.$('.block-editor'); if (blockEditorPresent) { console.log('āœ… Block Editor detected'); // Look for TEC blocks const tecBlocks = await page.$$('[data-type*="tribe"], [class*="tribe-editor"]'); console.log(` Found ${tecBlocks.length} TEC blocks`); } else { console.log('ā„¹ļø Classic Editor in use'); } // 4. TEST SPECIFIC FIELD SELECTORS console.log('\nšŸ“ STEP 4: Testing Specific Fields'); console.log('-' .repeat(70)); const fieldsToTest = { // Based on what worked in our previous test 'Title': '#title', 'Content': '#content', 'Start Date': '#EventStartDate', 'End Date': '#EventEndDate', 'Start Time': '#EventStartTime', 'End Time': '#EventEndTime', 'Timezone': 'select[name="EventTimezone"]', 'Website URL': '#EventURL', // Additional fields to test 'Cost': '#EventCost', 'Currency': '#EventCurrencySymbol', 'Show Map': '#EventShowMap', 'Show Map Link': '#EventShowMapLink', // Venue fields (various possible selectors) 'Venue Dropdown': 'select[name="venue[VenueID]"]', 'Venue Name Input': 'input[name="venue[Venue]"]', 'Venue Address': 'input[name="venue[Address]"]', 'Venue City': 'input[name="venue[City]"]', 'Venue State': 'input[name="venue[State]"]', 'Venue Zip': 'input[name="venue[Zip]"]', 'Venue Country': 'select[name="venue[Country]"]', // Organizer fields 'Organizer Dropdown': 'select[name="organizer[OrganizerID]"]', 'Organizer Name Input': 'input[name="organizer[Organizer]"]', 'Organizer Email': 'input[name="organizer[Email]"]', 'Organizer Phone': 'input[name="organizer[Phone]"]', 'Organizer Website': 'input[name="organizer[Website]"]' }; const workingSelectors = {}; for (const [fieldName, selector] of Object.entries(fieldsToTest)) { const element = await page.$(selector); if (element) { try { const isVisible = await element.isVisible(); const value = await element.inputValue().catch(() => null) || await element.textContent().catch(() => null); if (isVisible) { console.log(`āœ… ${fieldName}: FOUND (visible) - Selector: ${selector}`); if (value) { console.log(` Value: ${String(value).substring(0, 50)}`); } workingSelectors[fieldName] = selector; } else { console.log(`āš ļø ${fieldName}: Found but hidden - Selector: ${selector}`); } } catch (e) { console.log(`āŒ ${fieldName}: Error - ${e.message}`); } } else { console.log(`āŒ ${fieldName}: NOT FOUND - Selector: ${selector}`); } } // 5. GENERATE FIELD MAPPING console.log('\nšŸ“ STEP 5: Generating Field Mapping'); console.log('-' .repeat(70)); const fieldMapping = { version: 'TEC v5.0.8', discovered: new Date().toISOString(), workingSelectors: workingSelectors, categories: discoveredFields }; // Save to file await fs.writeFile( 'tec-v5-field-mapping.json', JSON.stringify(fieldMapping, null, 2) ); console.log('āœ… Field mapping saved to tec-v5-field-mapping.json'); return fieldMapping; } 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(); } } // Run the discovery console.log('Starting TEC v5.0.8 field discovery...\n'); discoverTECFields().then(mapping => { if (mapping) { console.log('\n' + '=' .repeat(70)); console.log('šŸ“Š DISCOVERY COMPLETE'); console.log('=' .repeat(70)); console.log('\nāœ… Working Selectors Found:'); for (const [field, selector] of Object.entries(mapping.workingSelectors)) { console.log(` • ${field}: ${selector}`); } console.log('\nšŸ“ Full mapping saved to: tec-v5-field-mapping.json'); console.log('=' .repeat(70)); } }).catch(error => { console.error('Fatal error:', error); process.exit(1); });