upskill-event-manager/wordpress-dev/tests/e2e/create-event.test.ts

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);
});
});