import { test, expect } from './fixtures/auth'; import { CommonActions } from './utils/common-actions'; /** * Complete Trainer Journey Test Suite * * Tests the full workflow of a trainer: * 1. Login and dashboard access * 2. Create a new event using TEC Community Events * 3. View event in dashboard * 4. Edit the event * 5. Generate certificates (if attendees exist) * 6. View trainer profile */ test.describe('Complete Trainer Journey', () => { let eventTitle: string; let eventId: string; test.beforeEach(async ({ authenticatedPage: page }) => { const actions = new CommonActions(page); // Verify we're logged in and on dashboard await expect(page).toHaveURL(/hvac-dashboard/); await actions.screenshot('journey-start-dashboard'); }); test('1. Dashboard Overview', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Check dashboard elements await expect(page.locator('h1:has-text("Trainer Dashboard")')).toBeVisible(); // Verify navigation elements const navElements = { createEvent: page.locator('a:has-text("Create Event")').first(), myEvents: page.locator('a:has-text("My Events")').first(), certificateReports: page.locator('a:has-text("Certificate Reports")').first(), generateCertificates: page.locator('a:has-text("Generate Certificates")').first(), profile: page.locator('a:has-text("View Profile")').first() }; for (const [name, element] of Object.entries(navElements)) { await expect(element).toBeVisible({ timeout: 5000 }); console.log(`✓ ${name} link is visible`); } // Check statistics const stats = await page.locator('.hvac-stat-card').count(); console.log(`Dashboard shows ${stats} statistics cards`); // Check events table const eventsTable = page.locator('.hvac-events-table, table').first(); await expect(eventsTable).toBeVisible(); await actions.screenshot('journey-dashboard-verified'); }); test('2. Create New Event', async ({ authenticatedPage: page }) => { test.setTimeout(90000); const actions = new CommonActions(page); // Navigate to Create Event await page.click('a:has-text("Create Event")'); await page.waitForLoadState('networkidle'); await actions.screenshot('journey-create-event-page'); // Generate unique event data const testData = actions.generateTestData('Training'); eventTitle = `${testData.title} - Trainer Journey Test`; const eventDetails = { title: eventTitle, description: `${testData.description} This comprehensive HVAC training covers: - System diagnostics and troubleshooting - Energy efficiency optimization - Latest industry standards and regulations - Hands-on practical exercises Prerequisites: Basic HVAC knowledge Duration: Full day workshop Certification: Completion certificate provided`, venue: 'HVAC Excellence Training Center', address: '456 Training Blvd, Suite 100', city: 'Dallas', state: 'TX', zip: '75201' }; // Wait for form to be ready await page.waitForTimeout(2000); // Try different form selectors based on what TEC might use const titleSelectors = [ 'input[name="post_title"]', 'input#post_title', 'input[name="event_title"]', '#event_title', 'input[placeholder*="Event Title"]' ]; let titleFilled = false; for (const selector of titleSelectors) { const field = page.locator(selector).first(); if (await field.count() > 0 && await field.isVisible()) { await field.fill(eventDetails.title); titleFilled = true; console.log(`✓ Filled title using selector: ${selector}`); break; } } if (!titleFilled) { throw new Error('Could not find event title field'); } // Fill description - try different approaches const contentFilled = await fillEventDescription(page, eventDetails.description); if (!contentFilled) { console.log('Warning: Could not fill event description'); } // Set event dates (1 week from now) const eventDate = new Date(); eventDate.setDate(eventDate.getDate() + 7); const dateStr = eventDate.toISOString().split('T')[0]; const dateSelectors = [ { field: 'EventStartDate', value: dateStr }, { field: 'EventEndDate', value: dateStr }, { field: 'event_start_date', value: dateStr }, { field: 'event_end_date', value: dateStr } ]; for (const { field, value } of dateSelectors) { const input = page.locator(`input[name="${field}"], input#${field}`).first(); if (await input.count() > 0 && await input.isVisible()) { await input.fill(value); console.log(`✓ Filled ${field}`); } } // Set times if separate fields exist const timeFields = [ { field: 'EventStartTime', value: '09:00' }, { field: 'EventEndTime', value: '17:00' }, { field: 'event_start_time', value: '09:00 AM' }, { field: 'event_end_time', value: '05:00 PM' } ]; for (const { field, value } of timeFields) { const input = page.locator(`input[name="${field}"], input#${field}`).first(); if (await input.count() > 0 && await input.isVisible()) { await input.fill(value); } } // Fill venue information const venueFields = [ { field: 'venue[Venue]', value: eventDetails.venue }, { field: 'venue[Address]', value: eventDetails.address }, { field: 'venue[City]', value: eventDetails.city }, { field: 'venue[State]', value: eventDetails.state }, { field: 'venue[Zip]', value: eventDetails.zip } ]; for (const { field, value } of venueFields) { const input = page.locator(`input[name="${field}"], input#venue-${field.toLowerCase()}`).first(); if (await input.count() > 0 && await input.isVisible()) { await input.fill(value); console.log(`✓ Filled venue ${field}`); } } await actions.screenshot('journey-event-form-filled'); // Find and click submit button const submitSelectors = [ 'input[type="submit"][value*="Submit"]', 'button[type="submit"]', 'input[type="submit"].button-primary', '#submitButton', '.tribe-submit-button' ]; let submitted = false; for (const selector of submitSelectors) { const button = page.locator(selector).first(); if (await button.count() > 0 && await button.isVisible()) { await Promise.all([ page.waitForNavigation({ timeout: 30000, waitUntil: 'networkidle' }).catch(() => {}), button.click() ]); submitted = true; console.log(`✓ Clicked submit using selector: ${selector}`); break; } } if (!submitted) { throw new Error('Could not find submit button'); } await actions.screenshot('journey-after-event-submit'); // Check for success const successSelectors = [ '.tribe-success', '.notice-success', '.updated', 'text=successfully', 'text=pending review' ]; let successFound = false; for (const selector of successSelectors) { const element = page.locator(selector).first(); if (await element.count() > 0) { successFound = true; console.log(`✓ Success indicator found: ${selector}`); console.log(`Message: ${await element.textContent()}`); break; } } expect(successFound).toBe(true); }); test('3. Verify Event in Dashboard', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Navigate to dashboard await actions.navigateAndWait('/hvac-dashboard/'); // Look for our event const eventRow = page.locator(`tr:has-text("${eventTitle}")`).first(); const eventFound = await eventRow.count() > 0; if (eventFound) { console.log('✓ Event found in dashboard!'); // Get event details const status = await eventRow.locator('td').first().textContent(); console.log(`Event status: ${status}`); // Find edit link to get event ID const editLink = eventRow.locator('a:has-text("Edit")').first(); if (await editLink.count() > 0) { const href = await editLink.getAttribute('href'); const match = href?.match(/event_id=(\d+)/); if (match) { eventId = match[1]; console.log(`Event ID: ${eventId}`); } } await actions.screenshot('journey-event-in-dashboard'); } else { console.log('Event not immediately visible (may be pending approval)'); // Try different filters const filters = ['pending', 'draft', 'all']; for (const filter of filters) { const filterLink = page.locator(`a:has-text("${filter}")`).first(); if (await filterLink.count() > 0) { await filterLink.click(); await page.waitForTimeout(1000); const found = await page.locator(`tr:has-text("${eventTitle}")`).count() > 0; if (found) { console.log(`✓ Event found under ${filter} filter`); break; } } } } }); test('4. Edit Event', async ({ authenticatedPage: page }) => { test.setTimeout(60000); const actions = new CommonActions(page); // Start from dashboard await actions.navigateAndWait('/hvac-dashboard/'); // Find our event or any event to edit let editLink = page.locator(`tr:has-text("${eventTitle}") a:has-text("Edit")`).first(); if (await editLink.count() === 0) { // If our specific event isn't found, edit any available event editLink = page.locator('a:has-text("Edit")').first(); } const hasEditableEvent = await editLink.count() > 0; if (!hasEditableEvent) { console.log('No editable events found, skipping edit test'); test.skip(); return; } // Click edit await editLink.click(); await page.waitForLoadState('networkidle'); await actions.screenshot('journey-edit-event-page'); // Update title const titleField = page.locator('input[name="post_title"], input#post_title').first(); await expect(titleField).toBeVisible(); const currentTitle = await titleField.inputValue(); const updatedTitle = `${currentTitle} (Updated ${new Date().toLocaleTimeString()})`; await titleField.fill(updatedTitle); // Add to description const updateNote = `\n\n[Update ${new Date().toLocaleString()}]: Schedule confirmed, materials updated.`; const contentUpdated = await fillEventDescription(page, updateNote, true); await actions.screenshot('journey-edit-form-updated'); // Submit changes const submitButton = page.locator('input[type="submit"], button[type="submit"]').first(); await expect(submitButton).toBeVisible(); await Promise.all([ page.waitForNavigation({ timeout: 30000 }).catch(() => {}), submitButton.click() ]); await actions.screenshot('journey-after-edit-submit'); // Verify update success const successMessage = page.locator('.tribe-success, .notice-success, .updated').first(); const hasSuccess = await successMessage.count() > 0; if (hasSuccess) { console.log('✓ Event updated successfully'); } }); test('5. View Trainer Profile', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Navigate to profile await page.click('a:has-text("View Profile")'); await page.waitForLoadState('networkidle'); await actions.screenshot('journey-trainer-profile'); // Verify profile elements await expect(page.locator('h1, h2').filter({ hasText: /profile/i }).first()).toBeVisible(); // Check for trainer information const profileSections = [ 'contact information', 'bio', 'experience', 'certifications' ]; for (const section of profileSections) { const element = page.locator(`text=/${section}/i`).first(); if (await element.count() > 0) { console.log(`✓ Profile shows ${section}`); } } // Check for edit profile option const editProfileLink = page.locator('a:has-text("Edit Profile")').first(); if (await editProfileLink.count() > 0) { console.log('✓ Edit profile option available'); } }); test('6. Certificate Management', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Navigate to certificate reports await actions.navigateAndWait('/certificate-reports/'); await actions.screenshot('journey-certificate-reports'); // Check page loaded await expect(page.locator('h1, h2').filter({ hasText: /certificate/i }).first()).toBeVisible(); // Check for statistics const stats = await page.locator('.stat-value, .metric-value').count(); console.log(`Certificate reports shows ${stats} statistics`); // Navigate to generate certificates await page.click('a:has-text("Generate Certificates")'); await page.waitForLoadState('networkidle'); await actions.screenshot('journey-generate-certificates'); // Check if any events are available for certificate generation const eventDropdown = page.locator('select[name="event_id"], #event_id').first(); if (await eventDropdown.count() > 0) { const options = await eventDropdown.locator('option').count(); console.log(`Found ${options - 1} events available for certificate generation`); if (options > 1) { // Select first real event (skip the placeholder option) await eventDropdown.selectOption({ index: 1 }); await page.waitForTimeout(1000); // Check if attendees loaded const attendeeCheckboxes = page.locator('input[type="checkbox"][name="attendee_ids[]"]'); const attendeeCount = await attendeeCheckboxes.count(); console.log(`Found ${attendeeCount} attendees for selected event`); } } }); }); /** * Helper function to fill event description in various editor types */ async function fillEventDescription(page: any, content: string, append: boolean = false): Promise { // Try TinyMCE first const tinyMCEFrames = [ '#tcepostcontent_ifr', '#content_ifr', 'iframe[id*="content"]' ]; for (const frameSelector of tinyMCEFrames) { const frame = page.frameLocator(frameSelector).first(); const body = frame.locator('body'); if (await body.count() > 0) { try { if (append) { const current = await body.textContent(); await body.fill(current + content); } else { await body.fill(content); } console.log('✓ Filled content in TinyMCE editor'); return true; } catch (e) { continue; } } } // Try regular textarea const textareaSelectors = [ 'textarea[name="post_content"]', 'textarea#tcepostcontent', 'textarea[name="event_description"]', '#event_description' ]; for (const selector of textareaSelectors) { const textarea = page.locator(selector).first(); if (await textarea.count() > 0 && await textarea.isVisible()) { if (append) { const current = await textarea.inputValue(); await textarea.fill(current + content); } else { await textarea.fill(content); } console.log(`✓ Filled content in textarea: ${selector}`); return true; } } return false; }