438 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
	
		
			17 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 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);
 | |
|   });
 | |
| });
 |