upskill-event-manager/test-tec-field-discovery.js
Ben 25d5c9ac7d feat: implement TEC v5.0.8 field mapping and best practices
- Created comprehensive field mapping documentation for TEC v5.0.8
- Documented all meta keys, input selectors, and field types
- Built validation tests using correct TEC v5.0.8 selectors
- Verified working selectors through staging environment testing
- Added best practices guide with implementation patterns
- Included JavaScript and PHP code examples
- Documented common issues and solutions
- Added debugging tips and performance optimizations

Test results show successful field discovery and persistence:
- Title, Start Date, End Date, and URL fields verified working
- Cost field may be hidden when Events Tickets is active
- All date/time fields use expected selectors
- Venue and organizer fields use array notation in names

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-18 13:29:20 -03:00

338 lines
No EOL
13 KiB
JavaScript
Executable file
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);
});