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