diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 1cae27dc..f5e55065 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -96,7 +96,14 @@ "mcp__zen-mcp__codereview", "mcp__zen-mcp__consensus", "Bash(DISPLAY=:0 node test-custom-edit-with-login.js)", - "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-custom-edit-with-login.js)" + "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-custom-edit-with-login.js)", + "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-template-debug.js)", + "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-login-and-edit.js)", + "Bash(export DISPLAY=:0)", + "Bash(export XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3)", + "Bash(UPSKILL_STAGING_URL=\"https://upskill-staging.measurequick.com\" wp --url=$UPSKILL_STAGING_URL --ssh=root@upskill-staging.measurequick.com post get 6177 --field=post_name,post_parent,post_type)", + "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-direct-access.js)", + "Bash(DISPLAY=:0 XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 node test-create-and-edit-event.js)" ], "deny": [] }, diff --git a/assets/css/hvac-event-edit-custom.css b/assets/css/hvac-event-edit-custom.css new file mode 100644 index 00000000..83ec95bd --- /dev/null +++ b/assets/css/hvac-event-edit-custom.css @@ -0,0 +1,348 @@ +/** + * HVAC Custom Event Edit Form Styles + * Matches the styling patterns of other trainer pages + */ + +/* CSS Custom Properties / Variables */ +:root { + /* Spacing */ + --hvac-spacing-1: 0.25rem; + --hvac-spacing-2: 0.5rem; + --hvac-spacing-3: 0.75rem; + --hvac-spacing-4: 1rem; + --hvac-spacing-5: 1.5rem; + --hvac-spacing-6: 2rem; + --hvac-spacing-8: 3rem; + + /* Colors */ + --hvac-primary: #0073aa; + --hvac-primary-dark: #005a87; + --hvac-text: #333333; + --hvac-border: #dddddd; + --hvac-border-light: #eeeeee; + + /* Shadows */ + --hvac-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + --hvac-shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +/* Fix navigation bar z-index issue */ +.hvac-event-edit-page .hvac-trainer-menu-wrapper { + position: relative; + z-index: 100; + background: #fff; + margin-bottom: 0; +} + +/* Ensure breadcrumbs appear correctly */ +.hvac-event-edit-page .hvac-breadcrumbs { + position: relative; + z-index: 90; + background: #fff; + padding: 15px 0; +} + +/* Main container - match other trainer pages width */ +.hvac-event-edit-page .container { + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; +} + +/* Event edit wrapper - increase width to match dashboard */ +.hvac-event-edit-wrapper { + max-width: 100%; + margin: 0 auto; + padding: 30px 0; +} + +/* Form sections */ +.hvac-form-section { + background: #fff; + border: 1px solid var(--hvac-border-light); + border-radius: 8px; + padding: 25px; + margin-bottom: 25px; + box-shadow: var(--hvac-shadow); +} + +.hvac-form-section h2 { + margin: 0 0 20px 0; + padding-bottom: 15px; + border-bottom: 2px solid var(--hvac-border-light); + font-size: 20px; + color: var(--hvac-text); + font-weight: 600; +} + +/* Form rows */ +.hvac-form-row { + margin-bottom: 20px; +} + +.hvac-form-row label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: #555; + font-size: 14px; +} + +.hvac-form-row label.required::after { + content: ' *'; + color: #d63638; +} + +/* Form inputs */ +.hvac-form-row input[type="text"], +.hvac-form-row input[type="email"], +.hvac-form-row input[type="url"], +.hvac-form-row input[type="tel"], +.hvac-form-row input[type="date"], +.hvac-form-row input[type="time"], +.hvac-form-row textarea, +.hvac-form-row select { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--hvac-border); + border-radius: 4px; + font-size: 14px; + transition: border-color 0.2s; +} + +.hvac-form-row input:focus, +.hvac-form-row textarea:focus, +.hvac-form-row select:focus { + outline: none; + border-color: var(--hvac-primary); + box-shadow: 0 0 0 2px rgba(0, 115, 170, 0.1); +} + +/* Two column layout */ +.hvac-row-half { + display: flex; + gap: 20px; +} + +.hvac-row-half .hvac-col { + flex: 1; +} + +/* Checkboxes */ +.hvac-checkbox-label { + display: inline-block; + margin-right: 20px; + margin-bottom: 10px; + font-weight: normal; + cursor: pointer; + font-size: 14px; +} + +.hvac-checkbox-label input[type="checkbox"] { + margin-right: 6px; + vertical-align: middle; +} + +/* Input group (for currency) */ +.hvac-input-group { + display: flex; + align-items: center; +} + +.hvac-input-prefix { + padding: 10px 12px; + background: #f5f5f5; + border: 1px solid var(--hvac-border); + border-right: none; + border-radius: 4px 0 0 4px; + font-weight: 600; +} + +.hvac-input-group input { + border-radius: 0 4px 4px 0 !important; +} + +/* Form actions */ +.hvac-form-actions { + display: flex; + gap: 15px; + padding-top: 30px; + border-top: 2px solid var(--hvac-border-light); + margin-top: 30px; +} + +/* Buttons */ +.hvac-button { + padding: 12px 24px; + border: none; + border-radius: 4px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + text-decoration: none; + display: inline-block; + transition: all 0.2s; +} + +.hvac-button-primary { + background: var(--hvac-primary); + color: white; +} + +.hvac-button-primary:hover { + background: var(--hvac-primary-dark); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +.hvac-button-secondary { + background: #f0f0f1; + color: var(--hvac-text); +} + +.hvac-button-secondary:hover { + background: #e0e0e1; + color: var(--hvac-text); +} + +/* Success notice */ +.hvac-notice { + padding: 15px 20px; + margin-bottom: 25px; + border-left: 4px solid; + background: #fff; + border-radius: 4px; +} + +.hvac-notice-success { + border-color: #46b450; + background: #ecf7ed; +} + +.hvac-notice p { + margin: 0; + font-weight: 500; +} + +/* WordPress editor overrides */ +.hvac-form-row .wp-editor-wrap { + border: 1px solid var(--hvac-border); + border-radius: 4px; + overflow: hidden; +} + +.hvac-form-row .wp-editor-container { + border: none; +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .hvac-event-edit-page .container { + padding: 0 15px; + } + + .hvac-form-section { + padding: 20px; + margin-bottom: 20px; + } + + .hvac-row-half { + flex-direction: column; + gap: 0; + } + + .hvac-checkbox-label { + display: block; + margin-bottom: 12px; + } + + .hvac-form-actions { + flex-direction: column; + } + + .hvac-button { + width: 100%; + text-align: center; + } +} + +@media (max-width: 480px) { + .hvac-form-section { + padding: 15px; + border-radius: 0; + border-left: 0; + border-right: 0; + } + + .hvac-form-section h2 { + font-size: 18px; + } +} + +/* Ensure proper stacking order */ +.hvac-event-edit-page { + position: relative; +} + +/* Fix for overlapping headers */ +.site-header, +.ast-main-header-wrap, +.ast-above-header-wrap, +.ast-below-header-wrap { + position: relative; + z-index: 50; +} + +/* Venue and Organizer field groups */ +.hvac-venue-fields, +.hvac-organizer-fields { + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid var(--hvac-border-light); +} + +/* Category checkboxes grid */ +.hvac-checkbox-group { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 10px; +} + +@media (max-width: 768px) { + .hvac-checkbox-group { + grid-template-columns: 1fr; + } +} + +/* Page title styling */ +.hvac-event-edit-wrapper h1.entry-title { + font-size: 28px; + color: var(--hvac-text); + margin-bottom: 25px; + font-weight: 600; +} + +/* Ensure full width on larger screens */ +@media (min-width: 1400px) { + .hvac-event-edit-page .container { + max-width: 1320px; + } +} + +/* Fix z-index layering with site navigation */ +.hvac-event-edit-page > * { + position: relative; +} + +.hvac-event-edit-page .hvac-trainer-menu-wrapper { + margin-top: 0; + padding-top: 20px; +} + +/* Ensure navigation doesn't get hidden */ +@media (max-width: 921px) { + .hvac-event-edit-page .hvac-trainer-menu-wrapper { + position: relative; + z-index: 1000; + } +} \ No newline at end of file diff --git a/includes/class-hvac-access-control.php b/includes/class-hvac-access-control.php index 3b45a06d..8df6c585 100644 --- a/includes/class-hvac-access-control.php +++ b/includes/class-hvac-access-control.php @@ -38,6 +38,7 @@ class HVAC_Access_Control { private static $trainer_pages = array( 'trainer/dashboard', 'trainer/event/manage', + 'trainer/event/edit', 'trainer/generate-certificates', 'trainer/certificate-reports', 'trainer/event-summary', diff --git a/includes/class-hvac-community-events.php b/includes/class-hvac-community-events.php index 9de13b94..51562573 100644 --- a/includes/class-hvac-community-events.php +++ b/includes/class-hvac-community-events.php @@ -149,11 +149,14 @@ class HVAC_Community_Events { add_action('init', array($this, 'init')); // Template loading for custom pages - add_filter('template_include', array($this, 'load_custom_templates')); + add_filter('template_include', array($this, 'load_custom_templates'), 999); // Force correct content on master dashboard add_filter('the_content', array($this, 'force_master_dashboard_content'), 1); + // Force content on edit event page + add_filter('the_content', array($this, 'force_edit_event_content'), 1); + // Cache invalidation hooks for master dashboard performance add_action('save_post', array($this, 'clear_master_dashboard_cache'), 10, 1); add_action('delete_post', array($this, 'clear_master_dashboard_cache'), 10, 1); @@ -817,6 +820,14 @@ class HVAC_Community_Events { * Include custom templates for plugin pages */ public function load_custom_templates($template) { + // Debug logging for edit page + if (strpos($_SERVER['REQUEST_URI'], '/trainer/event/edit') !== false) { + error_log("HVAC Debug: load_custom_templates called for edit page"); + error_log("HVAC Debug: Original template: " . $template); + error_log("HVAC Debug: Current page ID: " . get_the_ID()); + error_log("HVAC Debug: is_page(): " . (is_page() ? 'YES' : 'NO')); + } + $custom_template = null; @@ -862,12 +873,8 @@ class HVAC_Community_Events { HVAC_Logger::info("Loading edit-event template", 'Template Loader'); } - // Check for new custom edit event page (hierarchical URL) - // Using multiple detection methods for reliability - if (is_page(6177) || is_page('trainer/event/edit') || (is_page() && strpos($_SERVER['REQUEST_URI'], '/trainer/event/edit') !== false)) { - $custom_template = HVAC_PLUGIN_DIR . 'templates/page-edit-event-custom.php'; - HVAC_Logger::info("Loading custom edit-event template", 'Template Loader'); - } + // NOTE: Custom edit event page is now handled by HVAC_Custom_Event_Edit class + // to avoid duplicate template loading logic // Check for event-summary page if (is_page('trainer/event/summary')) { @@ -951,6 +958,37 @@ class HVAC_Community_Events { return $content; } + + /** + * Force edit event content on edit event page + */ + public function force_edit_event_content($content) { + // Check if we're on the edit event page + if ((is_page(6177) || strpos($_SERVER['REQUEST_URI'], '/trainer/event/edit') !== false) && in_the_loop() && is_main_query()) { + // Check if user is logged in + if (!is_user_logged_in()) { + wp_safe_redirect(home_url('/training-login/?redirect=' . urlencode($_SERVER['REQUEST_URI']))); + exit; + } + + // Get event ID from URL + $event_id = isset($_GET['event_id']) ? (int) $_GET['event_id'] : 0; + + // Load and return the custom form + ob_start(); + ?> + +
+

Edit Event

+

Event ID:

+

This is a test to confirm the content injection is working.

+

If you see this, the template loading mechanism is working but needs the full form implementation.

+
+ $value) { + yield $key => $value; + } return; } @@ -161,6 +184,30 @@ final class HVAC_Custom_Event_Edit { // Taxonomies yield 'categories' => wp_get_post_terms($eventId, 'tribe_events_cat', ['fields' => 'ids']); yield 'tags' => wp_get_post_terms($eventId, 'post_tag', ['fields' => 'ids']); + + // Cache the data for future use (convert generator to array) + // Note: This is done after yielding to maintain generator efficiency + // The next call will use the cached version + $dataToCache = []; + $dataToCache['id'] = $eventId; + $dataToCache['title'] = $event->post_title; + $dataToCache['content'] = $event->post_content; + $dataToCache['excerpt'] = $event->post_excerpt; + $dataToCache['status'] = $event->post_status; + $dataToCache['author'] = $event->post_author; + foreach ($this->getEventMetaKeys() as $key) { + $dataToCache[$key] = get_post_meta($eventId, $key, true); + } + if ($venueId) { + $dataToCache['venue'] = $this->getVenueData((int) $venueId); + } + if ($organizerId) { + $dataToCache['organizer'] = $this->getOrganizerData((int) $organizerId); + } + $dataToCache['categories'] = wp_get_post_terms($eventId, 'tribe_events_cat', ['fields' => 'ids']); + $dataToCache['tags'] = wp_get_post_terms($eventId, 'post_tag', ['fields' => 'ids']); + + wp_cache_set($cacheKey, $dataToCache, self::CACHE_GROUP, self::CACHE_TTL); } /** @@ -465,20 +512,43 @@ final class HVAC_Custom_Event_Edit { return false; } - // New event + $user = wp_get_current_user(); + $userId = $user->ID; + + // New event - check if user has the capability to create events if ($eventId === 0) { - return current_user_can('edit_posts'); + // Check for The Events Calendar capabilities or trainer role + return current_user_can('edit_tribe_events') || + current_user_can('publish_tribe_events') || + in_array('hvac_trainer', $user->roles) || + in_array('hvac_master_trainer', $user->roles) || + in_array('administrator', $user->roles); } - // Existing event + // Existing event - validate ownership and permissions $event = get_post($eventId); if (!$event || $event->post_type !== 'tribe_events') { return false; } - // Check ownership or admin capability - $userId = get_current_user_id(); - return $event->post_author === $userId || current_user_can('edit_others_posts'); + // Check ownership FIRST - owners can always edit their own events + if ($event->post_author == $userId) { + return true; + } + + // Administrators can edit any event + if (in_array('administrator', $user->roles)) { + return true; + } + + // Master trainers can edit any event + if (in_array('hvac_master_trainer', $user->roles)) { + return true; + } + + // Non-owners need special permissions to edit others' events + return current_user_can('edit_others_tribe_events') || + current_user_can('edit_others_posts'); } /** @@ -489,7 +559,7 @@ final class HVAC_Custom_Event_Edit { public function getVenuesForDropdown(): Generator { $venues = get_posts([ 'post_type' => 'tribe_venue', - 'posts_per_page' => -1, + 'posts_per_page' => 100, // Limit to prevent memory issues 'orderby' => 'title', 'order' => 'ASC', 'author' => get_current_user_id(), @@ -508,7 +578,7 @@ final class HVAC_Custom_Event_Edit { public function getOrganizersForDropdown(): Generator { $organizers = get_posts([ 'post_type' => 'tribe_organizer', - 'posts_per_page' => -1, + 'posts_per_page' => 100, // Limit to prevent memory issues 'orderby' => 'title', 'order' => 'ASC', 'author' => get_current_user_id(), diff --git a/templates/page-edit-event-custom.php b/templates/page-edit-event-custom.php index a28e47ea..d927a475 100644 --- a/templates/page-edit-event-custom.php +++ b/templates/page-edit-event-custom.php @@ -12,13 +12,20 @@ if (!defined('ABSPATH')) { // Define constant for page identification define('HVAC_IN_PAGE_TEMPLATE', true); +// Check if user is logged in first +if (!is_user_logged_in()) { + // Redirect to training login page + wp_safe_redirect(home_url('/training-login/?redirect=' . urlencode($_SERVER['REQUEST_URI']))); + exit; +} + // Get event ID from URL $event_id = isset($_GET['event_id']) ? (int) $_GET['event_id'] : 0; // Initialize form handler $form_handler = HVAC_Custom_Event_Edit::instance(); -// Check permissions +// Check permissions (after login check) if (!$form_handler->canUserEditEvent($event_id)) { wp_die('You do not have permission to edit this event.'); } @@ -79,6 +86,7 @@ if (!empty($event_data['_EventEndDate'])) { get_header(); ?> +
- + { + const currentUrl = window.location.href; + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + const hasTrainerNav = document.querySelector('.hvac-navigation-wrapper') !== null; + const hasEventForm = document.querySelector('.hvac-event-form, .tribe-community-events') !== null; + const title = document.title; + + return { + currentUrl, + hasLoginForm, + hasTrainerNav, + hasEventForm, + title + }; + }); + + console.log(` Final URL: ${result.currentUrl}`); + console.log(` Title: ${result.title}`); + console.log(` Redirected to login: ${result.hasLoginForm}`); + console.log(` Has trainer navigation: ${result.hasTrainerNav}`); + console.log(` Has event form: ${result.hasEventForm}`); + + if (result.hasLoginForm) { + console.log(' ❌ ACCESS DENIED - redirected to login'); + } else if (result.hasTrainerNav) { + console.log(' ✅ ACCESS GRANTED - showing trainer content'); + } else { + console.log(' ⚠️ UNKNOWN - page loaded but content unclear'); + } + } + + // Step 3: Check authentication status + console.log('\\n3️⃣ Checking authentication status...'); + + await page.goto(`${baseUrl}/trainer/dashboard/`); + await page.waitForLoadState('networkidle'); + + const authStatus = await page.evaluate(() => { + // Check for user info in the page + const body = document.body.innerHTML; + const hasLogout = body.includes('logout') || body.includes('Logout'); + const hasWelcome = body.includes('Welcome') || body.includes('welcome'); + const hasUserName = body.includes('test_trainer') || body.includes('Test Trainer'); + + // Check for WordPress authentication + const hasAdminBar = document.querySelector('#wpadminbar') !== null; + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + + return { + hasLogout, + hasWelcome, + hasUserName, + hasAdminBar, + hasLoginForm, + currentUrl: window.location.href + }; + }); + + console.log(' Current URL:', authStatus.currentUrl); + console.log(' Has logout link:', authStatus.hasLogout); + console.log(' Has welcome message:', authStatus.hasWelcome); + console.log(' Has username:', authStatus.hasUserName); + console.log(' Has admin bar:', authStatus.hasAdminBar); + console.log(' Has login form:', authStatus.hasLoginForm); + + if (authStatus.hasLoginForm) { + console.log('\\n❌ AUTHENTICATION FAILED - user is not logged in'); + } else if (authStatus.hasLogout || authStatus.hasAdminBar) { + console.log('\\n✅ AUTHENTICATION SUCCESSFUL - user is logged in'); + } else { + console.log('\\n⚠️ AUTHENTICATION UNCLEAR - mixed signals'); + } + + // Take final screenshot + await page.screenshot({ + path: `auth-access-${Date.now()}.png`, + fullPage: true + }); + console.log('\\n📸 Screenshot saved'); + + } catch (error) { + console.error('\\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-auth-access-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testAuthAccess() + .then(() => { + console.log('\\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-create-and-edit-event.js b/test-create-and-edit-event.js new file mode 100644 index 00000000..bec84472 --- /dev/null +++ b/test-create-and-edit-event.js @@ -0,0 +1,164 @@ +/** + * Test creating an event then editing it as the same trainer + */ + +const { chromium } = require('playwright'); + +async function testCreateAndEditEvent() { + console.log('🔍 Testing Create and Edit Event Workflow...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login as test_trainer + console.log('1️⃣ Logging in as test_trainer...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Step 2: Create a new event + console.log('\n2️⃣ Creating a new test event...'); + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + // Fill in event details + const eventTitle = `Test Event ${Date.now()}`; + await page.fill('input[name="post_title"]', eventTitle); + await page.fill('textarea[name="post_excerpt"]', 'This is a test event created for permission testing'); + + // Set dates (today) + const today = new Date().toISOString().split('T')[0]; + await page.fill('input[name="EventStartDate"]', today); + await page.fill('input[name="EventEndDate"]', today); + + // Fill venue info + await page.fill('input[name="venue_name"]', 'Test Venue'); + await page.fill('input[name="venue_city"]', 'Test City'); + await page.fill('input[name="venue_state"]', 'TX'); + + // Submit the form + console.log('Submitting event creation form...'); + await page.click('button[type="submit"]'); + + // Wait for redirect with event_id + await page.waitForURL('**/trainer/event/edit/**', { timeout: 10000 }); + + // Extract the event ID from URL + const url = page.url(); + const eventIdMatch = url.match(/event_id=(\d+)/); + const eventId = eventIdMatch ? eventIdMatch[1] : null; + + if (eventId) { + console.log(`✅ Event created successfully! ID: ${eventId}`); + console.log(`Event title: ${eventTitle}`); + + // Step 3: Now try to edit the same event + console.log('\n3️⃣ Testing edit of the newly created event...'); + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=${eventId}`); + await page.waitForLoadState('networkidle'); + + const editCheck = await page.evaluate(() => { + const bodyText = document.body.innerText; + const hasForm = document.querySelector('input[name="post_title"]') !== null; + const hasPermissionError = bodyText.includes('permission') || bodyText.includes('Permission'); + const eventTitle = document.querySelector('input[name="post_title"]')?.value || ''; + + return { + hasForm, + hasPermissionError, + eventTitle, + canEdit: hasForm && !hasPermissionError + }; + }); + + console.log('Edit own event check:'); + console.log(' - Has form:', editCheck.hasForm); + console.log(' - Has permission error:', editCheck.hasPermissionError); + console.log(' - Event title loaded:', editCheck.eventTitle); + console.log(' - Can edit own event:', editCheck.canEdit ? '✅ YES' : '❌ NO (BUG!)'); + + // Step 4: Try to make a change and save + if (editCheck.canEdit) { + console.log('\n4️⃣ Testing save after edit...'); + const updatedTitle = eventTitle + ' - Updated'; + await page.fill('input[name="post_title"]', updatedTitle); + await page.click('button[type="submit"]'); + + // Wait for save to complete + await page.waitForLoadState('networkidle'); + + // Check if save was successful + const saveCheck = await page.evaluate(() => { + const bodyText = document.body.innerText; + const hasSuccessMessage = bodyText.includes('successfully') || bodyText.includes('saved'); + const currentTitle = document.querySelector('input[name="post_title"]')?.value || ''; + return { hasSuccessMessage, currentTitle }; + }); + + console.log('Save check:'); + console.log(' - Has success message:', saveCheck.hasSuccessMessage); + console.log(' - Updated title:', saveCheck.currentTitle); + console.log(' - Save successful:', saveCheck.hasSuccessMessage ? '✅ YES' : '❌ NO'); + } + + // Summary + console.log('\n📋 CREATE AND EDIT TEST SUMMARY:'); + console.log('================================'); + console.log(`✅ Event created: ID ${eventId}`); + console.log(`✅ Can edit own event: ${editCheck.canEdit ? 'YES' : 'NO (BUG!)'}`) + + if (!editCheck.canEdit) { + console.log('\n⚠️ WARNING: Trainer cannot edit their own event!'); + console.log('This is a permission bug that needs to be fixed.'); + } + } else { + console.log('❌ Failed to extract event ID from redirect URL'); + } + + // Take screenshot + await page.screenshot({ + path: `create-edit-test-${Date.now()}.png`, + fullPage: true + }); + console.log('\n📸 Screenshot saved'); + + } catch (error) { + console.error('\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-create-edit-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testCreateAndEditEvent() + .then(() => { + console.log('\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-detailed-debug.js b/test-detailed-debug.js new file mode 100644 index 00000000..5e2766e1 --- /dev/null +++ b/test-detailed-debug.js @@ -0,0 +1,199 @@ +/** + * Detailed debugging to understand the redirect behavior + */ + +const { chromium } = require('playwright'); + +async function testDetailedDebug() { + console.log('🔍 Testing Detailed Debug...\\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + // Track all requests and responses + const requests = []; + const responses = []; + + page.on('request', request => { + requests.push({ + url: request.url(), + method: request.method(), + headers: request.headers() + }); + console.log(`📤 REQUEST: ${request.method()} ${request.url()}`); + }); + + page.on('response', response => { + responses.push({ + url: response.url(), + status: response.status(), + headers: response.headers() + }); + console.log(`📥 RESPONSE: ${response.status()} ${response.url()}`); + if (response.status() >= 300 && response.status() < 400) { + console.log(`🔄 REDIRECT: ${response.status()} - Location: ${response.headers()['location'] || 'Not specified'}`); + } + }); + + try { + // Step 1: Login first + console.log('1️⃣ Logging in...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + // Wait for form to be visible + await page.waitForSelector('input[name="log"]', { timeout: 10000 }); + + console.log(' Filling login form...'); + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + + // Try different submission methods + const submitButton = await page.$('input[type="submit"]'); + const form = await page.$('form#loginform, form.wp-form'); + + console.log(' Submit button found:', !!submitButton); + console.log(' Form found:', !!form); + + if (submitButton) { + console.log(' Clicking submit button...'); + await Promise.all([ + page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }), + submitButton.click() + ]); + } else if (form) { + console.log(' Submitting form...'); + await Promise.all([ + page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }), + form.evaluate(form => form.submit()) + ]); + } else { + console.log(' Trying Enter key...'); + await page.press('input[name="pwd"]', 'Enter'); + await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }); + } + + const afterLoginUrl = page.url(); + console.log('✅ Logged in - Current URL:', afterLoginUrl); + + // Check login status + console.log('\\n2️⃣ Checking login status...'); + const loginStatus = await page.evaluate(() => { + // Check for login indicators + const body = document.body.innerHTML; + const hasLogoutLink = body.includes('logout') || body.includes('Logout'); + const hasLoginForm = body.includes('name="log"') && body.includes('name="pwd"'); + + return { + hasLogoutLink, + hasLoginForm, + bodySnippet: body.substring(0, 500) + }; + }); + + console.log(' Has logout link:', loginStatus.hasLogoutLink); + console.log(' Has login form:', loginStatus.hasLoginForm); + + if (loginStatus.hasLoginForm) { + console.log('❌ Still on login page - authentication failed'); + await page.screenshot({ path: `auth-failed-${Date.now()}.png` }); + return; + } + + // Wait a moment for any auth cookies to settle + await page.waitForTimeout(2000); + + // Step 3: Navigate to edit page and track redirects + console.log('\\n3️⃣ Navigating to edit page...'); + const targetUrl = `${baseUrl}/trainer/event/edit/?event_id=6161`; + console.log(' Target URL:', targetUrl); + + // Clear request/response arrays + requests.length = 0; + responses.length = 0; + + await page.goto(targetUrl, { waitUntil: 'networkidle' }); + + const finalUrl = page.url(); + console.log(' Final URL:', finalUrl); + + // Analyze the redirect chain + console.log('\\n4️⃣ Redirect Analysis:'); + const redirectChain = responses.filter(r => r.status >= 300 && r.status < 400); + + if (redirectChain.length > 0) { + console.log(` Found ${redirectChain.length} redirects:`); + redirectChain.forEach((redirect, index) => { + console.log(` ${index + 1}. ${redirect.status} - ${redirect.url}`); + if (redirect.headers['location']) { + console.log(` → Location: ${redirect.headers['location']}`); + } + }); + } else { + console.log(' No redirects detected'); + } + + // Check final page content + console.log('\\n5️⃣ Final Page Analysis:'); + const pageAnalysis = await page.evaluate(() => { + const title = document.title; + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + const hasEventForm = document.querySelector('.hvac-event-form') !== null; + const hasCustomTemplate = document.body.innerHTML.includes('Custom Event Edit Template Loaded Successfully'); + const mainContent = document.querySelector('#main, .site-main, [role="main"]'); + + return { + title, + hasLoginForm, + hasEventForm, + hasCustomTemplate, + mainContentSnippet: mainContent ? mainContent.innerHTML.substring(0, 300) : 'Main content not found' + }; + }); + + console.log(' Page title:', pageAnalysis.title); + console.log(' Has login form:', pageAnalysis.hasLoginForm); + console.log(' Has event form:', pageAnalysis.hasEventForm); + console.log(' Has custom template marker:', pageAnalysis.hasCustomTemplate); + console.log(' Main content snippet:', pageAnalysis.mainContentSnippet); + + // Take screenshot + await page.screenshot({ + path: `detailed-debug-${Date.now()}.png`, + fullPage: true + }); + console.log('\\n📸 Screenshot saved'); + + } catch (error) { + console.error('\\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-detailed-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testDetailedDebug() + .then(() => { + console.log('\\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-direct-access.js b/test-direct-access.js new file mode 100644 index 00000000..050047ce --- /dev/null +++ b/test-direct-access.js @@ -0,0 +1,113 @@ +/** + * Test direct access to see what content is returned + */ + +const { chromium } = require('playwright'); + +async function testDirectAccess() { + console.log('🔍 Testing Direct Access to Edit Page...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login first + console.log('1️⃣ Logging in...'); + await page.goto(`${baseUrl}/training-login/`); + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + + const submitButton = await page.$('input[type="submit"]'); + if (submitButton) { + await Promise.all([ + page.waitForNavigation({ waitUntil: 'networkidle' }), + submitButton.click() + ]); + } + + console.log('✅ Logged in\n'); + + // Step 2: Go directly to edit page + console.log('2️⃣ Navigating to edit page...'); + const targetUrl = `${baseUrl}/trainer/event/edit/?event_id=6161`; + console.log(' Target URL:', targetUrl); + await page.goto(targetUrl); + await page.waitForLoadState('networkidle'); + + const finalUrl = page.url(); + console.log(' Final URL:', finalUrl); + if (finalUrl !== targetUrl) { + console.log(' ⚠️ URL redirect detected!'); + } + + // Step 3: Analyze page content + console.log('3️⃣ Analyzing page content...\n'); + + // Get page HTML + const html = await page.content(); + + // Check for various markers + const checks = [ + { marker: '', name: 'Custom template marker' }, + { marker: 'hvac-event-edit-wrapper', name: 'Event edit wrapper class' }, + { marker: 'hvac-event-form', name: 'Event form class' }, + { marker: 'HVAC_Custom_Event_Edit', name: 'PHP class reference' }, + { marker: 'hvac_event_nonce', name: 'Event nonce field' }, + { marker: 'EventStartDate', name: 'Event start date field' }, + { marker: 'post_title', name: 'Post title field' }, + { marker: 'tribe-community-events', name: 'TEC form (should NOT be present)' } + ]; + + for (const check of checks) { + const found = html.includes(check.marker); + console.log(` ${found ? '✅' : '❌'} ${check.name}`); + } + + // Check what's in the main content area + console.log('\n4️⃣ Main content area:'); + const mainContent = await page.$eval('#main, .site-main, [role="main"], .ast-container', + el => el ? el.innerHTML.substring(0, 500) : 'Main content not found' + ).catch(() => 'Could not get main content'); + + console.log(mainContent); + + // Take screenshot + await page.screenshot({ + path: `direct-access-${Date.now()}.png`, + fullPage: true + }); + console.log('\n📸 Screenshot saved'); + + } catch (error) { + console.error('\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-direct-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testDirectAccess() + .then(() => { + console.log('\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-error-details.js b/test-error-details.js new file mode 100644 index 00000000..a84497bc --- /dev/null +++ b/test-error-details.js @@ -0,0 +1,167 @@ +/** + * Test to get detailed error information from the edit page + */ + +const { chromium } = require('playwright'); + +async function testErrorDetails() { + console.log('🔍 Testing Error Details...\\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login first + console.log('1️⃣ Logging in...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + // Wait for redirect + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Step 2: Go to edit page and capture detailed error + console.log('\\n2️⃣ Accessing edit page and capturing error details...'); + + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + const errorDetails = await page.evaluate(() => { + const title = document.title; + const bodyContent = document.body.innerText; + const htmlContent = document.body.innerHTML; + + // Look for specific error patterns + const has404 = bodyContent.includes('404') || bodyContent.includes('not found') || + bodyContent.includes('Not Found') || title.includes('404'); + + const hasError = bodyContent.includes('error') || bodyContent.includes('Error') || + title.includes('Error'); + + const hasDebug = bodyContent.includes('PHP') || bodyContent.includes('Fatal') || + bodyContent.includes('Warning') || bodyContent.includes('Notice'); + + // Extract first 1000 characters of body content for analysis + const bodySnippet = bodyContent.substring(0, 1000); + + // Look for specific WordPress error messages + const hasTemplateError = bodyContent.includes('template') || bodyContent.includes('Template'); + const hasPermissionError = bodyContent.includes('permission') || bodyContent.includes('Permission'); + + return { + title, + has404, + hasError, + hasDebug, + hasTemplateError, + hasPermissionError, + bodySnippet, + htmlSnippet: htmlContent.substring(0, 1500) + }; + }); + + console.log(' Page title:', errorDetails.title); + console.log(' Has 404 error:', errorDetails.has404); + console.log(' Has error message:', errorDetails.hasError); + console.log(' Has PHP debug info:', errorDetails.hasDebug); + console.log(' Has template error:', errorDetails.hasTemplateError); + console.log(' Has permission error:', errorDetails.hasPermissionError); + + console.log('\\n Body content snippet:'); + console.log(' ' + errorDetails.bodySnippet.replace(/\\n/g, '\\n ')); + + console.log('\\n HTML content snippet:'); + console.log(' ' + errorDetails.htmlSnippet.replace(/\\n/g, '\\n ')); + + // Step 3: Test with event_id parameter + console.log('\\n3️⃣ Testing with event_id parameter...'); + + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=6161`); + await page.waitForLoadState('networkidle'); + + const withParamDetails = await page.evaluate(() => { + const title = document.title; + const bodyContent = document.body.innerText; + const hasCustomTemplate = bodyContent.includes('Custom Event Edit Template') || + document.body.innerHTML.includes('Custom Event Edit Template'); + + return { + title, + bodySnippet: bodyContent.substring(0, 500), + hasCustomTemplate + }; + }); + + console.log(' With event_id - Title:', withParamDetails.title); + console.log(' With event_id - Has custom template:', withParamDetails.hasCustomTemplate); + console.log(' With event_id - Body snippet:'); + console.log(' ' + withParamDetails.bodySnippet.replace(/\\n/g, '\\n ')); + + // Step 4: Check URL variations + console.log('\\n4️⃣ Testing URL variations...'); + + const urlVariations = [ + '/trainer/event/edit', + '/trainer/event/edit/', + '/trainer/event/edit/?event_id=123', + '/trainer/events/edit/', + '/trainer/edit-event/' + ]; + + for (const url of urlVariations) { + await page.goto(`${baseUrl}${url}`); + await page.waitForLoadState('networkidle', { timeout: 5000 }); + + const result = await page.evaluate(() => ({ + title: document.title, + url: window.location.href, + hasError: document.title.includes('Error') || document.body.innerText.includes('404') + })); + + console.log(` ${url} -> ${result.hasError ? '❌' : '✅'} ${result.title}`); + } + + // Take screenshot + await page.screenshot({ + path: `error-details-${Date.now()}.png`, + fullPage: true + }); + console.log('\\n📸 Screenshot saved'); + + } catch (error) { + console.error('\\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-test-failed-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testErrorDetails() + .then(() => { + console.log('\\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-final-verification.js b/test-final-verification.js new file mode 100644 index 00000000..a7ca082c --- /dev/null +++ b/test-final-verification.js @@ -0,0 +1,161 @@ +/** + * Final verification test for the original target URL + */ + +const { chromium } = require('playwright'); + +async function testFinalVerification() { + console.log('🔍 Final Verification Test...\\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login with proper handling + console.log('1️⃣ Logging in...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + // Wait for redirect to dashboard + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Step 2: Test the original failing URL + console.log('\\n2️⃣ Testing original target URL...'); + const targetUrl = `${baseUrl}/trainer/event/edit/?event_id=6161`; + console.log(' Target URL:', targetUrl); + + await page.goto(targetUrl); + await page.waitForLoadState('networkidle'); + + const result = await page.evaluate(() => { + const currentUrl = window.location.href; + const title = document.title; + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + const hasError = title.includes('Error') || document.body.innerText.includes('permission'); + const hasEventForm = document.querySelector('.hvac-event-form, input[name="post_title"]') !== null; + const hasCustomTemplate = document.body.innerHTML.includes('Custom Event Edit Template'); + + // Get form fields for verification + const titleField = document.querySelector('input[name="post_title"]'); + const descriptionField = document.querySelector('textarea[name="post_content"]'); + const startDateField = document.querySelector('input[name="EventStartDate"]'); + + return { + currentUrl, + title, + hasLoginForm, + hasError, + hasEventForm, + hasCustomTemplate, + hasFormFields: !!(titleField && descriptionField && startDateField), + bodySnippet: document.body.innerText.substring(0, 500) + }; + }); + + console.log(' Final URL:', result.currentUrl); + console.log(' Page title:', result.title); + console.log(' Redirected to login:', result.hasLoginForm); + console.log(' Has error:', result.hasError); + console.log(' Has event form:', result.hasEventForm); + console.log(' Has custom template marker:', result.hasCustomTemplate); + console.log(' Has form fields (title, description, date):', result.hasFormFields); + + if (result.hasLoginForm) { + console.log('\\n❌ FAILED - Still redirected to login'); + } else if (result.hasError) { + console.log('\\n❌ FAILED - Permission or other error'); + console.log(' Error content:', result.bodySnippet); + } else if (result.hasFormFields) { + console.log('\\n✅ SUCCESS - Custom event edit form is working!'); + } else { + console.log('\\n⚠️ PARTIAL - Page loads but form may not be complete'); + console.log(' Content preview:', result.bodySnippet); + } + + // Step 3: Test without event_id (new event) + console.log('\\n3️⃣ Testing new event creation (no event_id)...'); + + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + const newEventResult = await page.evaluate(() => { + const title = document.title; + const hasError = title.includes('Error'); + const hasEventForm = document.querySelector('input[name="post_title"]') !== null; + const bodySnippet = document.body.innerText.substring(0, 300); + + return { title, hasError, hasEventForm, bodySnippet }; + }); + + console.log(' Title:', newEventResult.title); + console.log(' Has error:', newEventResult.hasError); + console.log(' Has event form:', newEventResult.hasEventForm); + + if (newEventResult.hasError) { + console.log(' ❌ New event creation failed'); + } else if (newEventResult.hasEventForm) { + console.log(' ✅ New event creation works'); + } else { + console.log(' ⚠️ New event - unclear result'); + } + + // Take final screenshot + await page.screenshot({ + path: `final-verification-${Date.now()}.png`, + fullPage: true + }); + console.log('\\n📸 Screenshot saved'); + + // Summary + console.log('\\n📋 FINAL SUMMARY:'); + if (!result.hasLoginForm && !result.hasError && result.hasFormFields) { + console.log('🎉 SUCCESS: Custom event edit template is working correctly!'); + console.log(' - Authentication works'); + console.log(' - Page loads without errors'); + console.log(' - Custom form fields are present'); + console.log(' - Template loading is working'); + } else { + console.log('❌ Issues remain:'); + if (result.hasLoginForm) console.log(' - Still redirected to login'); + if (result.hasError) console.log(' - Permission or template errors'); + if (!result.hasFormFields) console.log(' - Form fields missing'); + } + + } catch (error) { + console.error('\\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-final-verification-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testFinalVerification() + .then(() => { + console.log('\\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-login-and-edit.js b/test-login-and-edit.js new file mode 100644 index 00000000..2b09fb8c --- /dev/null +++ b/test-login-and-edit.js @@ -0,0 +1,186 @@ +/** + * Test Custom Event Edit with Better Login Debugging + */ + +const { chromium } = require('playwright'); + +async function testLoginAndEdit() { + console.log('🔄 Testing Login and Custom Event Edit Form...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = process.env.UPSKILL_STAGING_URL || 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Navigate to login page + console.log('1️⃣ Navigating to login page...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + // Check if we see the login form + const loginForm = await page.$('form#hvac_community_loginform'); + if (loginForm) { + console.log('✅ Login form found'); + } else { + console.log('❌ Login form not found'); + const forms = await page.$$('form'); + console.log(` Found ${forms.length} form(s) on page`); + } + + // Step 2: Fill and submit login + console.log('\n2️⃣ Filling login credentials...'); + + // Try different selectors for username field + const usernameField = await page.$('input[name="log"]') || + await page.$('input#user_login') || + await page.$('input[name="username"]'); + + if (usernameField) { + await usernameField.fill('test_trainer'); + console.log(' ✅ Username filled'); + } else { + console.log(' ❌ Username field not found'); + } + + // Try different selectors for password field + const passwordField = await page.$('input[name="pwd"]') || + await page.$('input#user_pass') || + await page.$('input[name="password"]'); + + if (passwordField) { + await passwordField.fill('TestTrainer123!'); + console.log(' ✅ Password filled'); + } else { + console.log(' ❌ Password field not found'); + } + + // Take screenshot before submitting + await page.screenshot({ + path: 'login-form-filled.png', + fullPage: false + }); + console.log(' 📸 Screenshot saved: login-form-filled.png'); + + // Submit the form + console.log('\n3️⃣ Submitting login form...'); + + // Try multiple submit methods + const submitButton = await page.$('input[type="submit"]') || + await page.$('button[type="submit"]') || + await page.$('#wp-submit'); + + if (submitButton) { + await Promise.all([ + page.waitForNavigation({ waitUntil: 'networkidle' }), + submitButton.click() + ]); + console.log(' ✅ Form submitted and navigation completed'); + } else { + console.log(' ❌ Submit button not found'); + } + + // Step 4: Check where we ended up + console.log('\n4️⃣ Checking authentication result...'); + const currentUrl = page.url(); + const currentTitle = await page.title(); + + console.log(` Current URL: ${currentUrl}`); + console.log(` Page Title: ${currentTitle}`); + + // Check for login errors + const loginError = await page.$('.login-error') || + await page.$('.hvac-login-error') || + await page.$('#login_error'); + + if (loginError) { + const errorText = await loginError.textContent(); + console.log(` ❌ Login error: ${errorText}`); + } + + // Check if we're on the dashboard + if (currentUrl.includes('/dashboard/')) { + console.log(' ✅ Successfully redirected to dashboard!'); + } else if (currentUrl.includes('/training-login/')) { + console.log(' ⚠️ Still on login page - authentication may have failed'); + } + + // Step 5: Try navigating to edit page + console.log('\n5️⃣ Navigating to event edit page...'); + const editUrl = `${baseUrl}/trainer/event/edit/?event_id=6161`; + await page.goto(editUrl); + await page.waitForLoadState('networkidle'); + + const editPageUrl = page.url(); + const editPageTitle = await page.title(); + + console.log(` Current URL: ${editPageUrl}`); + console.log(` Page Title: ${editPageTitle}`); + + // Check if we were redirected back to login + if (editPageUrl.includes('/training-login/')) { + console.log(' ❌ Redirected to login - not authenticated'); + } else if (editPageUrl.includes('/trainer/event/edit/')) { + console.log(' ✅ On edit page!'); + + // Check for custom template marker + const pageContent = await page.content(); + if (pageContent.includes('Custom Event Edit Template Loaded Successfully')) { + console.log(' ✅ Custom template is loading!'); + } else { + console.log(' ⚠️ Custom template marker not found'); + } + + // Check for form wrapper + const formWrapper = await page.$('.hvac-event-edit-wrapper'); + if (formWrapper) { + console.log(' ✅ Custom form wrapper found'); + } else { + console.log(' ❌ Custom form wrapper NOT found'); + } + } + + // Take final screenshot + await page.screenshot({ + path: `final-page-${Date.now()}.png`, + fullPage: true + }); + console.log('\n📸 Final screenshot saved'); + + console.log('\n✅ Test Complete!'); + + } catch (error) { + console.error('\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-${Date.now()}.png`, + fullPage: true + }); + console.log('📸 Error screenshot saved'); + + throw error; + } finally { + // Keep browser open for manual inspection + console.log('\n⏸️ Keeping browser open for 10 seconds...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testLoginAndEdit() + .then(() => { + console.log('\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-page-exists.js b/test-page-exists.js new file mode 100644 index 00000000..dcdaef23 --- /dev/null +++ b/test-page-exists.js @@ -0,0 +1,170 @@ +/** + * Test to check if the edit page actually exists and what its settings are + */ + +const { chromium } = require('playwright'); + +async function testPageExists() { + console.log('🔍 Testing Page Existence...\\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login as admin to check WordPress admin + console.log('1️⃣ Logging in as admin...'); + await page.goto(`${baseUrl}/wp-admin/`); + await page.waitForLoadState('networkidle'); + + // Check if already logged in or need to login + const hasLoginForm = await page.$('input[name="log"]') !== null; + + if (hasLoginForm) { + console.log(' Admin login required - checking page via direct URL instead...'); + + // Step 2: Try to access the page directly and see what happens + console.log('\\n2️⃣ Checking page directly...'); + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + const currentUrl = page.url(); + console.log(' Current URL:', currentUrl); + + // Check what type of page we get + const pageInfo = await page.evaluate(() => { + const title = document.title; + const bodyClasses = document.body.className; + const hasNotFound = document.body.innerHTML.includes('404') || + document.body.innerHTML.includes('not found') || + document.body.innerHTML.includes('Not Found'); + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + + return { + title, + bodyClasses, + hasNotFound, + hasLoginForm + }; + }); + + console.log(' Page title:', pageInfo.title); + console.log(' Body classes:', pageInfo.bodyClasses); + console.log(' Has 404 content:', pageInfo.hasNotFound); + console.log(' Has login form:', pageInfo.hasLoginForm); + + if (pageInfo.hasNotFound) { + console.log('\\n❌ Page returns 404 - it does not exist!'); + } else if (pageInfo.hasLoginForm) { + console.log('\\n🔄 Page redirects to login - access control issue'); + } else { + console.log('\\n✅ Page exists and loads content'); + } + + } else { + console.log('\\n2️⃣ Already logged in to admin - checking pages...'); + + // Go to pages list + await page.goto(`${baseUrl}/wp-admin/edit.php?post_type=page`); + await page.waitForLoadState('networkidle'); + + // Search for edit page + const searchBox = await page.$('input[name="s"]'); + if (searchBox) { + await searchBox.fill('Edit Event'); + await page.press('input[name="s"]', 'Enter'); + await page.waitForLoadState('networkidle'); + + // Check results + const pageExists = await page.evaluate(() => { + const rows = document.querySelectorAll('.wp-list-table tbody tr'); + for (const row of rows) { + const titleCell = row.querySelector('.column-title'); + if (titleCell && titleCell.textContent.includes('Edit Event')) { + const link = titleCell.querySelector('a'); + const id = link ? link.href.match(/post=(\\d+)/)?.[1] : null; + return { + exists: true, + title: titleCell.textContent.trim(), + id: id + }; + } + } + return { exists: false }; + }); + + if (pageExists.exists) { + console.log(`\\n✅ Page exists: "${pageExists.title}" (ID: ${pageExists.id})`); + } else { + console.log('\\n❌ Page "Edit Event" not found in WordPress admin'); + } + } + } + + // Step 3: Test URL patterns + console.log('\\n3️⃣ Testing URL patterns...'); + const testUrls = [ + '/trainer/event/edit/', + '/trainer/event/edit', + '/trainer/event/manage/', + '/trainer/dashboard/' + ]; + + for (const testUrl of testUrls) { + console.log(`\\n Testing: ${testUrl}`); + await page.goto(`${baseUrl}${testUrl}`); + await page.waitForLoadState('networkidle', { timeout: 10000 }); + + const result = await page.evaluate(() => { + const currentUrl = window.location.href; + const hasLoginForm = document.querySelector('input[name="log"]') !== null; + const hasNotFound = document.body.innerHTML.includes('404') || + document.body.innerHTML.includes('not found'); + + return { currentUrl, hasLoginForm, hasNotFound }; + }); + + console.log(` Final URL: ${result.currentUrl}`); + console.log(` Redirected to login: ${result.hasLoginForm}`); + console.log(` 404 error: ${result.hasNotFound}`); + } + + // Take screenshot + await page.screenshot({ + path: `page-exists-${Date.now()}.png`, + fullPage: true + }); + console.log('\\n📸 Screenshot saved'); + + } catch (error) { + console.error('\\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-page-exists-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testPageExists() + .then(() => { + console.log('\\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-security-fix-verification.js b/test-security-fix-verification.js new file mode 100644 index 00000000..c440a0e5 --- /dev/null +++ b/test-security-fix-verification.js @@ -0,0 +1,170 @@ +/** + * Test to verify the security fixes are working correctly + */ + +const { chromium } = require('playwright'); + +async function testSecurityFixes() { + console.log('🔍 Security Fix Verification Test...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login as test_trainer + console.log('1️⃣ Logging in as test_trainer...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Step 2: Check that debug output is removed + console.log('\n2️⃣ Checking for debug output removal...'); + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + const pageSource = await page.content(); + const hasDebugComments = pageSource.includes('HVAC_Custom_Event_Edit::') || + pageSource.includes('')) { + console.log(' ✅ Template comment marker found'); + } + + console.log(''); + } + + // Step 3: Check page structure + console.log('3️⃣ Checking page structure at /trainer/event/edit/?event_id=6161...\n'); + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=6161`); + await page.waitForLoadState('networkidle'); + + // Check body classes + const bodyClasses = await page.evaluate(() => document.body.className); + console.log(`Body classes: ${bodyClasses}`); + + // Check for WordPress admin bar + const adminBar = await page.$('#wpadminbar'); + console.log(`Admin bar: ${adminBar ? 'Present' : 'Not present'}`); + + // Check page ID + const pageId = await page.evaluate(() => { + const bodyClass = document.body.className; + const match = bodyClass.match(/page-id-(\d+)/); + return match ? match[1] : null; + }); + console.log(`Page ID from body class: ${pageId || 'Not found'}`); + + // Check template + const templateClass = await page.evaluate(() => { + const bodyClass = document.body.className; + const match = bodyClass.match(/page-template-([^ ]+)/); + return match ? match[1] : null; + }); + console.log(`Template class: ${templateClass || 'Not found'}`); + + // Step 4: Check page content structure + console.log('\n4️⃣ Analyzing page content structure...\n'); + + // Check for main content area + const mainContent = await page.$('#main') || await page.$('.site-main') || await page.$('[role="main"]'); + if (mainContent) { + console.log('✅ Main content area found'); + + // Get the HTML of main content + const mainHtml = await mainContent.evaluate(el => el.innerHTML.substring(0, 500)); + console.log('Main content preview:'); + console.log(mainHtml); + } else { + console.log('❌ Main content area not found'); + } + + // Step 5: Check for form elements + console.log('\n5️⃣ Checking for form elements...\n'); + + const formSelectors = { + 'Custom wrapper': '.hvac-event-edit-wrapper', + 'Form element': 'form.hvac-event-form', + 'Title field': '#post_title', + 'Start date': '#EventStartDate', + 'Submit button': 'button[type="submit"]', + 'Nonce field': 'input[name="hvac_event_nonce"]' + }; + + for (const [name, selector] of Object.entries(formSelectors)) { + const element = await page.$(selector); + console.log(`${name}: ${element ? '✅ Found' : '❌ Not found'}`); + } + + // Step 6: Take screenshot for visual inspection + console.log('\n6️⃣ Taking screenshot for inspection...\n'); + const timestamp = Date.now(); + await page.screenshot({ + path: `template-debug-${timestamp}.png`, + fullPage: true + }); + console.log(`📸 Screenshot saved as template-debug-${timestamp}.png`); + + // Step 7: Check console errors + console.log('\n7️⃣ Checking for console errors...\n'); + page.on('console', msg => { + if (msg.type() === 'error') { + console.log(`Console error: ${msg.text()}`); + } + }); + + // Reload to catch any console errors + await page.reload(); + await page.waitForTimeout(2000); + + console.log('\n✅ Debug test complete!'); + + } catch (error) { + console.error('\n❌ Debug test failed:', error.message); + + await page.screenshot({ + path: `template-debug-error-${Date.now()}.png`, + fullPage: true + }); + console.log('📸 Error screenshot saved'); + + throw error; + } finally { + await browser.close(); + } +} + +// Run debug test +debugTemplateLoading() + .then(() => { + console.log('\n✨ Debug test completed successfully!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Debug test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-trainer-event-permissions.js b/test-trainer-event-permissions.js new file mode 100644 index 00000000..8c366993 --- /dev/null +++ b/test-trainer-event-permissions.js @@ -0,0 +1,170 @@ +/** + * Test trainer event editing permissions + */ + +const { chromium } = require('playwright'); + +async function testTrainerEventPermissions() { + console.log('🔍 Testing Trainer Event Permissions...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Step 1: Login as test_trainer + console.log('1️⃣ Logging in as test_trainer...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Step 2: Go to event manage page to find an event + console.log('\n2️⃣ Looking for trainer\'s events...'); + await page.goto(`${baseUrl}/trainer/event/manage/`); + await page.waitForLoadState('networkidle'); + + // Check if there are any events listed + const eventLinks = await page.$$eval('.hvac-event-table a[href*="event_id="]', links => + links.map(link => { + const href = link.getAttribute('href'); + const match = href.match(/event_id=(\d+)/); + return { + id: match ? match[1] : null, + text: link.textContent.trim(), + href: href + }; + }) + ); + + console.log(`Found ${eventLinks.length} events:`, eventLinks); + + // Step 3: Try to create a new event + console.log('\n3️⃣ Testing new event creation...'); + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + const newEventCheck = await page.evaluate(() => { + const bodyText = document.body.innerText; + const hasForm = document.querySelector('input[name="post_title"]') !== null; + const hasPermissionError = bodyText.includes('permission') || bodyText.includes('Permission'); + const pageTitle = document.querySelector('h1')?.innerText || ''; + + return { + hasForm, + hasPermissionError, + pageTitle, + canCreate: hasForm && !hasPermissionError + }; + }); + + console.log('New event creation check:'); + console.log(' - Has form:', newEventCheck.hasForm); + console.log(' - Has permission error:', newEventCheck.hasPermissionError); + console.log(' - Page title:', newEventCheck.pageTitle); + console.log(' - Can create:', newEventCheck.canCreate ? '✅ YES' : '❌ NO'); + + // Step 4: If there are events, try to edit the first one + if (eventLinks.length > 0 && eventLinks[0].id) { + const eventId = eventLinks[0].id; + console.log(`\n4️⃣ Testing edit of event ID ${eventId}...`); + + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=${eventId}`); + await page.waitForLoadState('networkidle'); + + const editCheck = await page.evaluate(() => { + const bodyText = document.body.innerText; + const hasForm = document.querySelector('input[name="post_title"]') !== null; + const hasPermissionError = bodyText.includes('permission') || bodyText.includes('Permission'); + const eventTitle = document.querySelector('input[name="post_title"]')?.value || ''; + + return { + hasForm, + hasPermissionError, + eventTitle, + canEdit: hasForm && !hasPermissionError + }; + }); + + console.log('Edit event check:'); + console.log(' - Has form:', editCheck.hasForm); + console.log(' - Has permission error:', editCheck.hasPermissionError); + console.log(' - Event title:', editCheck.eventTitle); + console.log(' - Can edit:', editCheck.canEdit ? '✅ YES' : '❌ NO'); + } + + // Step 5: Try to edit a random event (likely not owned) + console.log('\n5️⃣ Testing edit of event not owned by trainer (ID 6161)...'); + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=6161`); + await page.waitForLoadState('networkidle'); + + const otherEventCheck = await page.evaluate(() => { + const bodyText = document.body.innerText; + const hasForm = document.querySelector('input[name="post_title"]') !== null; + const hasPermissionError = bodyText.includes('permission') || bodyText.includes('Permission'); + + return { + hasForm, + hasPermissionError, + canEdit: hasForm && !hasPermissionError + }; + }); + + console.log('Other event check:'); + console.log(' - Has form:', otherEventCheck.hasForm); + console.log(' - Has permission error:', otherEventCheck.hasPermissionError); + console.log(' - Can edit:', otherEventCheck.canEdit ? '✅ YES (BUG!)' : '❌ NO (Correct)'); + + // Summary + console.log('\n📋 PERMISSION TEST SUMMARY:'); + console.log('================================'); + console.log(`✅ Can create new events: ${newEventCheck.canCreate ? 'YES' : 'NO'}`); + if (eventLinks.length > 0) { + console.log(`✅ Can edit own events: Needs verification`); + } + console.log(`✅ Cannot edit others' events: ${!otherEventCheck.canEdit ? 'YES (Secure)' : 'NO (Security Issue)'}`); + + // Take screenshot + await page.screenshot({ + path: `trainer-permissions-${Date.now()}.png`, + fullPage: true + }); + console.log('\n📸 Screenshot saved'); + + } catch (error) { + console.error('\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-permissions-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testTrainerEventPermissions() + .then(() => { + console.log('\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file diff --git a/test-trainer-events.js b/test-trainer-events.js new file mode 100644 index 00000000..6e6be382 --- /dev/null +++ b/test-trainer-events.js @@ -0,0 +1,175 @@ +/** + * Test to find events owned by test_trainer + */ + +const { chromium } = require('playwright'); + +async function testTrainerEvents() { + console.log('🔍 Finding test_trainer events...\n'); + + const browser = await chromium.launch({ + headless: false, + args: ['--disable-dev-shm-usage', '--no-sandbox'] + }); + + const context = await browser.newContext({ + viewport: { width: 1280, height: 720 } + }); + + const page = await context.newPage(); + const baseUrl = 'https://upskill-staging.measurequick.com'; + + try { + // Login as test_trainer + console.log('1️⃣ Logging in...'); + await page.goto(`${baseUrl}/training-login/`); + await page.waitForLoadState('networkidle'); + + await page.fill('input[name="log"]', 'test_trainer'); + await page.fill('input[name="pwd"]', 'TestTrainer123!'); + await page.press('input[name="pwd"]', 'Enter'); + + await page.waitForURL('**/trainer/dashboard/**', { timeout: 10000 }); + console.log('✅ Login successful'); + + // Go to event manage page to see what events are available + console.log('\n2️⃣ Checking trainer events...'); + await page.goto(`${baseUrl}/trainer/event/manage/`); + await page.waitForLoadState('networkidle'); + + const eventsInfo = await page.evaluate(() => { + // Look for event links or IDs in the page + const links = Array.from(document.querySelectorAll('a')).filter(link => + link.href.includes('event') && (link.href.includes('edit') || link.href.includes('id=')) + ); + + const eventIds = []; + links.forEach(link => { + const match = link.href.match(/[\?&]event_id=(\d+)/); + if (match) { + eventIds.push({ + id: match[1], + text: link.textContent.trim(), + href: link.href + }); + } + }); + + // Also check the page content for any event information + const pageText = document.body.innerText; + const hasEvents = pageText.includes('event') || pageText.includes('Event'); + + return { + eventIds, + hasEvents, + pageSnippet: pageText.substring(0, 500) + }; + }); + + console.log(' Found event IDs:', eventsInfo.eventIds); + console.log(' Page has event content:', eventsInfo.hasEvents); + console.log(' Page content preview:'); + console.log(' ' + eventsInfo.pageSnippet.replace(/\n/g, '\n ')); + + // Test a specific valid event ID if found + if (eventsInfo.eventIds.length > 0) { + const firstEventId = eventsInfo.eventIds[0].id; + console.log(`\n3️⃣ Testing edit access for event ${firstEventId}...`); + + await page.goto(`${baseUrl}/trainer/event/edit/?event_id=${firstEventId}`); + await page.waitForLoadState('networkidle'); + + const editResult = await page.evaluate(() => { + const title = document.title; + const hasError = title.includes('Error') || document.body.innerText.includes('permission'); + const hasForm = document.querySelector('input[name="post_title"]') !== null; + + return { title, hasError, hasForm }; + }); + + console.log(` Title: ${editResult.title}`); + console.log(` Has error: ${editResult.hasError}`); + console.log(` Has edit form: ${editResult.hasForm}`); + + if (editResult.hasForm) { + console.log(' ✅ Can edit this event'); + } else if (editResult.hasError) { + console.log(' ❌ Permission denied for this event'); + } + } else { + console.log('\n❌ No events found for test_trainer'); + } + + // Test creating a new event first, then editing it + console.log('\n4️⃣ Testing create-then-edit workflow...'); + await page.goto(`${baseUrl}/trainer/event/edit/`); + await page.waitForLoadState('networkidle'); + + // Fill out a minimal event form + await page.fill('input[name="post_title"]', 'Test Event for Edit'); + await page.fill('textarea[name="post_content"]', 'This is a test event'); + + // Submit the form + await page.click('input[type="submit"], button[type="submit"]'); + await page.waitForLoadState('networkidle'); + + const createResult = await page.evaluate(() => { + const currentUrl = window.location.href; + const eventIdMatch = currentUrl.match(/[\?&]event_id=(\d+)/); + return { + currentUrl, + eventId: eventIdMatch ? eventIdMatch[1] : null + }; + }); + + if (createResult.eventId) { + console.log(` ✅ Created new event: ${createResult.eventId}`); + console.log(` Now testing edit access...`); + + // The page should already be showing the edit form for the new event + const finalEditTest = await page.evaluate(() => { + const hasForm = document.querySelector('input[name="post_title"]') !== null; + const title = document.title; + return { hasForm, title }; + }); + + console.log(` Edit form available: ${finalEditTest.hasForm}`); + console.log(` Page title: ${finalEditTest.title}`); + + if (finalEditTest.hasForm) { + console.log(' ✅ SUCCESS: Create-then-edit workflow works!'); + } + } else { + console.log(' ❌ Event creation failed'); + } + + await page.screenshot({ + path: `trainer-events-${Date.now()}.png`, + fullPage: true + }); + console.log('\n📸 Screenshot saved'); + + } catch (error) { + console.error('\n❌ Test failed:', error.message); + + await page.screenshot({ + path: `error-trainer-events-${Date.now()}.png`, + fullPage: true + }); + } finally { + console.log('\n⏸️ Keeping browser open for inspection...'); + await page.waitForTimeout(10000); + await browser.close(); + } +} + +// Run test +testTrainerEvents() + .then(() => { + console.log('\n✨ Test completed!'); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Test failed:', error); + process.exit(1); + }); \ No newline at end of file