import { test, expect } from '@playwright/test'; import fs from 'fs'; // --- GLOBAL NETWORK LOGGING --- const allNetworkLogs = []; test.beforeEach(async ({ page }) => { page.on('request', request => { allNetworkLogs.push({ type: 'request', url: request.url(), method: request.method(), headers: request.headers(), postData: request.postData(), timestamp: Date.now() }); }); page.on('response', async response => { let body = ''; try { body = await response.text(); } catch (e) { body = '[unavailable]'; } allNetworkLogs.push({ type: 'response', url: response.url(), status: response.status(), statusText: response.statusText(), headers: response.headers(), body: body, timestamp: Date.now() }); }); }); test.afterEach(async () => { fs.writeFileSync( 'test-results/all-network-traffic.json', JSON.stringify(allNetworkLogs, null, 2), { encoding: 'utf8' } ); }); test.describe('Create Event', () => { test.beforeEach(async ({ page }) => { page.on('console', msg => { if (msg.type() === 'error') { fs.appendFileSync('test-results/browser-console-errors.log', `[${new Date().toISOString()}] ${msg.text()}\n`); } }); page.on('pageerror', error => { fs.appendFileSync('test-results/browser-console-errors.log', `[${new Date().toISOString()}] PAGE ERROR: ${error.message}\n`); }); }); test('should allow a logged-in trainer to create a new event', async ({ page }) => { // --- Login Steps (reused from login.test.ts) --- // Navigate to the community login page await page.goto('/community-login/'); // Check if the login form fields are visible and enabled const usernameField = page.locator('#user_login'); const passwordField = page.locator('#user_pass'); const loginButton = page.locator('#wp-submit'); await expect(usernameField).toBeVisible(); await expect(usernameField).toBeEnabled(); await expect(passwordField).toBeVisible(); await expect(passwordField).toBeEnabled(); await expect(loginButton).toBeVisible(); await expect(loginButton).toBeEnabled(); // Fill in login credentials const username = 'test_trainer'; const password = 'Test123!'; await usernameField.fill(username); console.log(`Filled in username field with: ${username}`); await passwordField.fill(password); console.log('Filled in password field.'); // Click the login button await loginButton.click(); console.log('Clicked login button.'); // Assert successful login (redirection to dashboard) await page.waitForURL('/hvac-dashboard/', { timeout: 15000 }); console.log('Successfully logged in and redirected to dashboard.'); // --- End Login Steps --- // --- Create Event Steps --- // Navigate to the dashboard after login await page.goto('/hvac-dashboard/'); console.log('Navigated to Dashboard:', page.url()); await page.screenshot({ path: 'dashboard-before-create-event.png', fullPage: true }); // Look for a link or button to create a new event on the dashboard // Look for and click the "Create Event" link on the dashboard const createEventLink = page.locator('a', { hasText: 'Create Event' }).first(); await expect(createEventLink).toBeVisible(); console.log('Found "Create Event" link.'); // Click the link to navigate to the event creation page await createEventLink.click(); console.log('Clicked "Create Event" link.'); // Wait for navigation to the event creation page (/manage-event/) await page.waitForURL('/manage-event/', { timeout: 20000 }); // Increase timeout slightly console.log('Navigated to event creation page. Current URL:', page.url()); await page.screenshot({ path: 'create-event-page.png', fullPage: true }); // Wait for network to be idle after navigation await page.waitForLoadState('networkidle'); console.log('Network idle on event creation page.'); // --- Force CREATE mode: Remove hidden post_ID input if present --- // Log current URL and title before waiting for elements console.log('Current URL before element checks:', page.url()); console.log('Current Page Title before element checks:', await page.title()); // --- Select Venue and Organizer, and populate all other fields --- // Select the first available Venue const venueDropdown = page.locator('#saved_tribe_venue'); await expect(venueDropdown).toBeVisible({ timeout: 10000 }); const venueOptions = await venueDropdown.locator('option').all(); if (venueOptions.length > 1) { // Skip the first option ("Create or Find a Venue"), select the next const firstVenueValue = await venueOptions[1].getAttribute('value'); await venueDropdown.selectOption(firstVenueValue || ''); console.log('Selected Venue:', await venueOptions[1].textContent()); } // Select the first available Organizer const organizerDropdown = page.locator('#saved_tribe_organizer'); await expect(organizerDropdown).toBeVisible({ timeout: 10000 }); const organizerOptions = await organizerDropdown.locator('option').all(); if (organizerOptions.length > 1) { // Skip the first option ("Create or Find an Organizer"), select the next const firstOrganizerValue = await organizerOptions[1].getAttribute('value'); await organizerDropdown.selectOption(firstOrganizerValue || ''); console.log('Selected Organizer:', await organizerOptions[1].textContent()); } // Fill Event Website (optional) const eventWebsiteField = page.locator('#EventURL'); if (await eventWebsiteField.count()) { await eventWebsiteField.fill('https://example.com'); console.log('Filled Event Website'); } // Set Event Status to "Scheduled" if dropdown exists const statusDropdown = page.locator('#tribe-events-status-status'); if (await statusDropdown.count()) { await statusDropdown.selectOption('scheduled'); console.log('Set Event Status to Scheduled'); } // Optionally, select the first category and tag if present const categoryDropdown = page.locator('select[name="tax_input[tribe_events_cat][]"]'); if (await categoryDropdown.count()) { const catOptions = await categoryDropdown.locator('option').all(); if (catOptions.length > 0) { const firstCatValue = await catOptions[0].getAttribute('value'); if (firstCatValue && firstCatValue !== '') { await categoryDropdown.selectOption(firstCatValue); console.log('Selected first Event Category'); } } } const tagDropdown = page.locator('select[name="tax_input[post_tag][]"]'); if (await tagDropdown.count()) { const tagOptions = await tagDropdown.locator('option').all(); if (tagOptions.length > 0) { const firstTagValue = await tagOptions[0].getAttribute('value'); if (firstTagValue && firstTagValue !== '') { await tagDropdown.selectOption(firstTagValue); console.log('Selected first Event Tag'); } } } // Optionally, configure virtual event fields if present // Optionally check Virtual Event if interactable (skip if covered) const virtualCheckbox = page.locator('#tribe-events-virtual-setup'); if (await virtualCheckbox.count() && !(await virtualCheckbox.isChecked())) { try { await virtualCheckbox.check({ trial: true, force: false, timeout: 1000 }); await virtualCheckbox.check(); console.log('Checked Virtual Event'); } catch (e) { console.log('Virtual Event checkbox not interactable, skipping.'); } } // End of additional field population // Identify and fill in event form fields (selectors based on typical TEC Community Events forms) // Enhanced selectors with fallbacks const eventTitleField = page.locator('#post_title, [name="post_title"]'); const startDateField = page.locator('input[name="EventStartDate"], [data-testid="event-start-date"]'); const endDateField = page.locator('input[name="EventEndDate"], [data-testid="event-end-date"]'); const publishButton = page.locator('#post, .events-community-submit'); // Time selectors will be defined after interacting with date fields await expect(eventTitleField).toBeVisible({ timeout: 15000 }); // Keep increased timeout // Wait for the rich text editor iframe and its content to be visible // Click the "Code" tab for the event description to switch to text mode const descriptionCodeTab = page.locator('button', { hasText: 'Code' }); await expect(descriptionCodeTab).toBeVisible(); await descriptionCodeTab.click(); console.log('Switched to Code view for event description.'); // Now the description field should be a visible textarea with ID tcepostcontent const eventDescriptionField = page.locator('#tcepostcontent'); // Correct selector for the textarea in Code mode await expect(eventDescriptionField).toBeVisible({ timeout: 15000 }); // Wait for the textarea to be visible console.log('Event description textarea is visible.'); await expect(eventTitleField).toBeVisible({ timeout: 15000 }); await expect(startDateField).toBeVisible({ timeout: 15000 }); // Interact with date field first to trigger time fields to load await startDateField.click(); await startDateField.fill('04/30/2025'); // Wait for time fields to be loaded - try multiple selector patterns let startTimeSelect = page.locator('.tribe-timepicker-hour, .tribe-timepicker-minute, .tribe-timepicker-meridian, [data-name="EventStartTime"] input, [name*="EventStartTime"]').first(); await expect(startTimeSelect).toBeVisible({ timeout: 20000 }); await expect(endDateField).toBeVisible({ timeout: 15000 }); await endDateField.click(); await endDateField.fill('04/30/2025'); let endTimeSelect = page.locator('.tribe-timepicker-hour, .tribe-timepicker-minute, .tribe-timepicker-meridian, [data-name="EventEndTime"] input, [name*="EventEndTime"]').first(); await expect(endTimeSelect).toBeVisible({ timeout: 20000 }); // --- Enhanced DEBUG: Log page state before looking for publish button --- const fs = require('fs'); const debugDir = 'test-results'; if (!fs.existsSync(debugDir)) { fs.mkdirSync(debugDir, { recursive: true }); } // Capture page HTML and console logs const pageHtml = await page.content(); const consoleLogs: string[] = []; page.on('console', msg => { if (msg.text()) { consoleLogs.push(msg.text()); } }); // Enhanced debug info const debugInfo = { timestamp: new Date().toISOString(), url: page.url(), title: await page.title(), buttons: await page.locator('button').allTextContents(), inputs: await page.locator('input').allTextContents(), links: await page.locator('a').allTextContents(), publishButtonExists: await page.locator('#post, .events-community-submit').count() > 0, consoleErrors: consoleLogs.filter(log => log.includes('Error')), pageHtml: pageHtml }; // Write debug files fs.writeFileSync( `${debugDir}/debug-before-publish-button.json`, JSON.stringify(debugInfo, null, 2), { encoding: 'utf8' } ); await page.screenshot({ path: `${debugDir}/debug-before-publish-button.png`, fullPage: true }); console.log('DEBUG: Saved detailed debug information to test-results/'); // --- END DEBUG --- await expect(publishButton).toBeVisible({ timeout: 15000 }); const eventTitle = `Test Event ${Date.now()}`; const eventDescription = 'This is a test event created by Playwright.'; const eventDate = '2025-12-31'; // Example date in the future const eventTime = '10:00'; // Example time await eventTitleField.fill(eventTitle); console.log(`Filled in event title: ${eventTitle}`); // Fill in the event description in the textarea await eventDescriptionField.fill(eventDescription); console.log('Filled in event description.'); await startDateField.fill(eventDate); console.log(`Filled in start date: ${eventDate}`); await startTimeSelect.fill(eventTime); // Fill time input directly console.log(`Filled in start time: ${eventTime}`); await endDateField.fill(eventDate); // Assuming same end date console.log(`Filled in end date: ${eventDate}`); await endTimeSelect.fill('12:00'); // Example end time console.log('Filled in end time.'); await publishButton.click(); console.log('Clicked Publish button.'); // --- Enhanced Diagnostics after Submission --- // 1. Wait for network to be idle await page.waitForLoadState('networkidle', { timeout: 15000 }); // 2. Log current URL after clicking publish const postPublishUrl = page.url(); console.log('URL after clicking Publish:', postPublishUrl); // 3. Capture error and notice messages const errorNotice = page.locator('.tribe-community-notice.tribe-error, .notice-error, .event-error, .tribe-error, .notice, .tribe-community-notice'); const successNotice = page.locator('.tribe-community-notice, .notice-success, .event-success, .updated, .tribe-message'); let errorMessages: string[] = []; let successMessages: string[] = []; let errorVisible = false; let successVisible = false; try { await expect(errorNotice).toBeVisible({ timeout: 5000 }); errorVisible = true; errorMessages = await errorNotice.allTextContents(); console.log('Error notice(s) visible after publishing:', errorMessages); } catch (e) { console.log('No visible error notices after publishing.'); } try { await expect(successNotice).toBeVisible({ timeout: 10000 }); successVisible = true; successMessages = await successNotice.allTextContents(); console.log('Success notice(s) visible after publishing:', successMessages); } catch (e) { console.log('No visible success notice after publishing.'); } // 4. Capture page HTML after submission const postSubmitHtml = await page.content(); const postSubmitDebug = { timestamp: new Date().toISOString(), url: postPublishUrl, errorVisible, errorMessages, successVisible, successMessages, html: postSubmitHtml }; fs.writeFileSync( 'test-results/debug-after-publish.json', JSON.stringify(postSubmitDebug, null, 2), { encoding: 'utf8' } ); await page.screenshot({ path: 'test-results/event-created-after-submit.png', fullPage: true }); // 5. Capture browser console logs // (consoleLogs array is already being filled above) fs.writeFileSync( 'test-results/console-logs-after-publish.json', JSON.stringify(consoleLogs, null, 2), { encoding: 'utf8' } ); // 6. Capture network response for the form submission (added for diagnosis) const networkLogs = []; const networkListener = async (response) => { try { const req = response.request(); if ( req.method() === 'POST' && req.url().includes('/manage-event') ) { const body = await response.text(); networkLogs.push({ url: req.url(), status: response.status(), statusText: response.statusText(), requestHeaders: req.headers(), postData: req.postData(), responseHeaders: response.headers(), body: body, }); } } catch (err) { networkLogs.push({ error: err.message }); } }; page.on('response', networkListener); // --- DEBUG LOGGING ADDED FOR DIAGNOSIS --- // Log the actual post-submission URL console.log('DEBUG: postPublishUrl:', postPublishUrl); // Log the value of successVisible console.log('DEBUG: successVisible:', successVisible); // Log the DOM content after submission const domContent = await page.content(); require('fs').writeFileSync( 'test-results/dom-content-after-publish.html', domContent, { encoding: 'utf8' } ); console.log('DEBUG: DOM content after publish written to test-results/dom-content-after-publish.html'); // Log any visible notices (success/error) const notices = await page.$$eval( '.tribe-community-notice, .notice-success, .event-success, .updated, .tribe-message, .tribe-community-notice.tribe-error, .notice-error, .event-error, .tribe-error, .notice', els => els.map(el => el.textContent) ); console.log('DEBUG: Notices found after publish:', notices); // Assert: Either redirected to event page or success notice is visible // --- New: After submission, follow "View Your Submitted Events" and check for the new event --- // Try to find and click the "View Your Submitted Events" link const submittedEventsLink = page.locator('a', { hasText: 'View Your Submitted Events' }); await expect(submittedEventsLink).toBeVisible({ timeout: 10000 }); await submittedEventsLink.click(); // Wait for navigation to the submitted events list await page.waitForLoadState('networkidle'); // Assert the new event appears in the list await expect(page.locator('body')).toContainText(eventTitle); // --- End Create Event Steps --- // Write network logs after test fs.writeFileSync( 'test-results/event-form-network-response.json', JSON.stringify(networkLogs, null, 2), { encoding: 'utf8' } ); page.off('response', networkListener); }); });