import { test, expect } from './fixtures/auth'; import { CommonActions } from './utils/common-actions'; /** * Communication Templates Validation E2E Tests * * Comprehensive tests for CRUD operations, AJAX functionality, * and template management system */ test.describe('Communication Templates Full Validation', () => { test('Navigate to templates page and verify scripts load correctly', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Monitor console for JavaScript errors const jsErrors: string[] = []; page.on('console', (msg) => { if (msg.type() === 'error') { jsErrors.push(msg.text()); } }); // Navigate to communication templates page (critical for script loading) await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('templates-page-initial-load'); // Verify page loaded correctly await expect(page.locator('h1')).toContainText('Communication Templates'); await expect(page.locator('.hvac-templates-wrapper')).toBeVisible(); // Check that hvacTemplates object is initialized with ajaxUrl const hvacTemplatesConfig = await page.evaluate(() => { if (typeof window.hvacTemplates !== 'undefined') { return { exists: true, hasAjaxUrl: !!window.hvacTemplates.ajaxUrl, ajaxUrl: window.hvacTemplates.ajaxUrl }; } return { exists: false }; }); expect(hvacTemplatesConfig.exists).toBe(true); expect(hvacTemplatesConfig.hasAjaxUrl).toBe(true); console.log(`✓ hvacTemplates object loaded with AJAX URL: ${hvacTemplatesConfig.ajaxUrl}`); // Verify no critical JavaScript errors const criticalErrors = jsErrors.filter(error => error.includes('hvacTemplates') || error.includes('communication-templates') || error.includes('Uncaught') ); expect(criticalErrors.length).toBe(0); }); test('Create new template - Full CRUD operation', async ({ authenticatedPage: page }) => { test.setTimeout(45000); const actions = new CommonActions(page); // Navigate to templates page first (required for scripts) await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('templates-create-start'); // Generate unique test data const testData = actions.generateTestData('Template'); const templateData = { title: testData.title, content: `Dear {attendee_name},\n\nThank you for registering for {event_title}.\n\nEvent Details:\n- Date: {event_date}\n- Time: {event_time}\n- Location: {venue_name}\n\nBest regards,\n{trainer_name}`, category: 'registration' }; // Check if we need to install default templates first const gettingStarted = page.locator('.hvac-getting-started'); if (await gettingStarted.isVisible()) { const installButton = page.locator('a:has-text("Install Default Templates")'); if (await installButton.isVisible()) { await installButton.click(); await actions.waitForComplexAjax(); await page.waitForTimeout(2000); // Wait for templates to load await actions.screenshot('default-templates-installed'); } } // Click create new template button const createButton = page.locator('button:has-text("Create New Template")'); await expect(createButton).toBeVisible(); await createButton.click(); await page.waitForTimeout(1000); await actions.screenshot('template-form-opened'); // Fill in template form await page.fill('#hvac_template_title', templateData.title); // Try to fill content (handle TinyMCE or textarea) const contentField = page.locator('#hvac_template_content'); if (await contentField.isVisible()) { await contentField.fill(templateData.content); } else { // Try TinyMCE await actions.fillTinyMCE('#hvac_template_content', templateData.content); } // Select category await page.selectOption('#hvac_template_category', templateData.category); await actions.screenshot('template-form-filled'); // Test placeholder insertion const placeholderItems = page.locator('.hvac-placeholder-item'); if (await placeholderItems.count() > 0) { // Click a placeholder to test insertion await placeholderItems.filter({ hasText: '{venue_address}' }).click(); await page.waitForTimeout(500); console.log('✓ Placeholder insertion tested'); } // Save the template const saveButton = page.locator('button:has-text("Save Template")'); await expect(saveButton).toBeVisible(); await saveButton.click(); // Wait for AJAX save operation await actions.waitForComplexAjax(); await actions.screenshot('template-saved'); // Verify template was created await page.waitForTimeout(2000); // Allow UI to update const templateCard = page.locator('.hvac-template-card').filter({ hasText: testData.title }); await expect(templateCard).toBeVisible(); console.log(`✓ Template created successfully: ${testData.title}`); return testData; // Return for use in other tests }); test('Edit existing template', async ({ authenticatedPage: page }) => { test.setTimeout(45000); const actions = new CommonActions(page); // Navigate to templates page await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('templates-edit-start'); // Find a template to edit const templateCards = page.locator('.hvac-template-card'); const cardCount = await templateCards.count(); if (cardCount > 0) { // Click edit on the first template const firstCard = templateCards.first(); const editButton = firstCard.locator('button:has-text("Edit")'); if (await editButton.isVisible()) { // Get original title for comparison const originalTitle = await firstCard.locator('.hvac-template-card-title').textContent(); await editButton.click(); await page.waitForTimeout(1000); await actions.screenshot('template-edit-form-opened'); // Modify the template const titleField = page.locator('#hvac_template_title'); await expect(titleField).toBeVisible(); const updatedTitle = `Updated - ${originalTitle} - ${Date.now()}`; await titleField.fill(updatedTitle); // Update content const contentField = page.locator('#hvac_template_content'); if (await contentField.isVisible()) { const currentContent = await contentField.inputValue(); await contentField.fill(currentContent + '\n\nUpdated on: ' + new Date().toISOString()); } await actions.screenshot('template-edit-form-modified'); // Save changes const saveButton = page.locator('button:has-text("Save Template")'); await saveButton.click(); await actions.waitForComplexAjax(); await actions.screenshot('template-edit-saved'); // Verify update await page.waitForTimeout(2000); const updatedCard = page.locator('.hvac-template-card').filter({ hasText: updatedTitle }); await expect(updatedCard).toBeVisible(); console.log('✓ Template edited successfully'); } else { console.log('⚠ No edit button found on template cards'); } } else { console.log('⚠ No templates available to edit'); } }); test('Delete template', async ({ authenticatedPage: page }) => { test.setTimeout(45000); const actions = new CommonActions(page); // Navigate to templates page await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('templates-delete-start'); // Find a template to delete (preferably a test template) const templateCards = page.locator('.hvac-template-card'); const testTemplateCard = templateCards.filter({ hasText: /Template \d+/ }).first(); if (await testTemplateCard.isVisible()) { const templateTitle = await testTemplateCard.locator('.hvac-template-card-title').textContent(); const deleteButton = testTemplateCard.locator('button:has-text("Delete")'); if (await deleteButton.isVisible()) { // Set up dialog handler for confirmation page.once('dialog', async dialog => { console.log(`Dialog message: ${dialog.message()}`); await dialog.accept(); }); await deleteButton.click(); await actions.waitForComplexAjax(); await actions.screenshot('template-deleted'); // Verify template was removed await page.waitForTimeout(2000); const deletedCard = page.locator('.hvac-template-card').filter({ hasText: templateTitle }); await expect(deletedCard).not.toBeVisible(); console.log(`✓ Template deleted successfully: ${templateTitle}`); } else { console.log('⚠ No delete button found on test template'); } } else { console.log('⚠ No test templates available to delete'); } }); test('Load saved templates in email attendees page', async ({ authenticatedPage: page }) => { test.setTimeout(45000); const actions = new CommonActions(page); // First navigate to dashboard to find an event await actions.navigateAndWait('/hvac-dashboard/'); // Look for events with email functionality const emailLinks = page.locator('a[href*="email-attendees"]'); const linkCount = await emailLinks.count(); if (linkCount > 0) { // Navigate to email attendees page await emailLinks.first().click(); await page.waitForLoadState('networkidle'); await actions.screenshot('email-attendees-page'); // Check for template widget const templateToggle = page.locator('.hvac-template-toggle'); if (await templateToggle.isVisible()) { await templateToggle.click(); await page.waitForTimeout(1000); await actions.screenshot('template-widget-opened'); // Check if templates are loaded const templateSelect = page.locator('#hvac_template_select, select[name="template_select"]'); if (await templateSelect.isVisible()) { const options = await templateSelect.locator('option').count(); expect(options).toBeGreaterThan(1); // Should have more than just default option // Select a template await templateSelect.selectOption({ index: 1 }); await page.waitForTimeout(1000); // Click load template button const loadButton = page.locator('button:has-text("Load Template")'); if (await loadButton.isVisible()) { await loadButton.click(); await actions.waitForAjax(); await actions.screenshot('template-loaded'); // Verify content was loaded into email form const emailContent = page.locator('#email_message, textarea[name="email_message"]'); const content = await emailContent.inputValue(); expect(content.length).toBeGreaterThan(0); console.log('✓ Template loaded successfully in email form'); } } else { console.log('⚠ Template select dropdown not found'); } } else { console.log('⚠ Template widget not found on email attendees page'); } } else { console.log('⚠ No events with email functionality found'); } }); test('Test placeholder functionality in templates', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Navigate to templates page await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('placeholder-test-start'); // Open create template form const createButton = page.locator('button:has-text("Create New Template")'); if (await createButton.isVisible()) { await createButton.click(); await page.waitForTimeout(1000); // Check placeholder helper is visible const placeholderHelper = page.locator('.hvac-placeholder-helper'); await expect(placeholderHelper).toBeVisible(); // Test all available placeholders const placeholders = [ '{attendee_name}', '{attendee_email}', '{event_title}', '{event_date}', '{event_time}', '{venue_name}', '{venue_address}', '{trainer_name}', '{trainer_email}', '{certificate_link}' ]; // Verify placeholders are displayed for (const placeholder of placeholders) { const placeholderItem = page.locator('.hvac-placeholder-item').filter({ hasText: placeholder }); if (await placeholderItem.isVisible()) { console.log(`✓ Placeholder ${placeholder} is available`); } } // Test clicking placeholders to insert const contentField = page.locator('#hvac_template_content'); await contentField.clear(); // Click multiple placeholders await page.locator('.hvac-placeholder-item').filter({ hasText: '{attendee_name}' }).click(); await page.waitForTimeout(300); await page.locator('.hvac-placeholder-item').filter({ hasText: '{event_title}' }).click(); await page.waitForTimeout(300); // Verify placeholders were inserted const content = await contentField.inputValue(); expect(content).toContain('{attendee_name}'); expect(content).toContain('{event_title}'); await actions.screenshot('placeholders-inserted'); console.log('✓ Placeholder insertion functionality working'); } }); test('Verify AJAX operations work correctly', async ({ authenticatedPage: page }) => { test.setTimeout(45000); const actions = new CommonActions(page); // Monitor network requests const ajaxRequests: string[] = []; page.on('request', request => { if (request.url().includes('admin-ajax.php')) { ajaxRequests.push(request.postData() || 'GET request'); } }); // Navigate to templates page await actions.navigateAndWait('/communication-templates/'); // Test various AJAX operations // 1. Test loading templates (should happen on page load) await page.waitForTimeout(2000); const loadRequests = ajaxRequests.filter(req => req.includes('hvac_get_templates')); expect(loadRequests.length).toBeGreaterThan(0); console.log(`✓ Template loading AJAX requests: ${loadRequests.length}`); // 2. Test creating a template via AJAX const createButton = page.locator('button:has-text("Create New Template")'); if (await createButton.isVisible()) { await createButton.click(); await page.waitForTimeout(1000); // Fill minimal data await page.fill('#hvac_template_title', `AJAX Test ${Date.now()}`); await page.fill('#hvac_template_content', 'Testing AJAX save functionality'); await page.selectOption('#hvac_template_category', 'general'); // Clear previous requests ajaxRequests.length = 0; // Save template const saveButton = page.locator('button:has-text("Save Template")'); await saveButton.click(); await actions.waitForComplexAjax(); // Check for save AJAX request const saveRequests = ajaxRequests.filter(req => req.includes('hvac_save_template')); expect(saveRequests.length).toBeGreaterThan(0); console.log('✓ Template save AJAX request successful'); } // 3. Test loading template in email form (if available) await actions.navigateAndWait('/hvac-dashboard/'); const emailLink = page.locator('a[href*="email-attendees"]').first(); if (await emailLink.isVisible()) { await emailLink.click(); await page.waitForLoadState('networkidle'); const templateToggle = page.locator('.hvac-template-toggle'); if (await templateToggle.isVisible()) { await templateToggle.click(); await page.waitForTimeout(1000); // Clear requests ajaxRequests.length = 0; // Load a template const templateSelect = page.locator('#hvac_template_select, select[name="template_select"]'); if (await templateSelect.isVisible() && await templateSelect.locator('option').count() > 1) { await templateSelect.selectOption({ index: 1 }); const loadButton = page.locator('button:has-text("Load Template")'); if (await loadButton.isVisible()) { await loadButton.click(); await actions.waitForAjax(); // Check for load template AJAX request const loadTemplateRequests = ajaxRequests.filter(req => req.includes('hvac_load_template') || req.includes('hvac_get_template') ); expect(loadTemplateRequests.length).toBeGreaterThan(0); console.log('✓ Template load in email form AJAX request successful'); } } } } await actions.screenshot('ajax-operations-complete'); console.log(`✓ Total AJAX requests captured: ${ajaxRequests.length}`); }); test('Verify template categories work correctly', async ({ authenticatedPage: page }) => { test.setTimeout(30000); const actions = new CommonActions(page); // Navigate to templates page await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('categories-test-start'); // Check if category filter exists const categoryFilter = page.locator('.hvac-category-filter, select[name="category_filter"]'); if (await categoryFilter.isVisible()) { // Get available categories const options = await categoryFilter.locator('option').allTextContents(); console.log(`Available categories: ${options.join(', ')}`); // Test filtering by each category for (let i = 1; i < Math.min(options.length, 4); i++) { // Test up to 3 categories await categoryFilter.selectOption({ index: i }); await actions.waitForAjax(); await page.waitForTimeout(1000); // Check if templates are filtered const visibleCards = await page.locator('.hvac-template-card:visible').count(); console.log(`✓ Category "${options[i]}" shows ${visibleCards} templates`); await actions.screenshot(`category-filter-${options[i].toLowerCase()}`); } // Reset to all categories await categoryFilter.selectOption({ index: 0 }); await actions.waitForAjax(); } else { console.log('⚠ Category filter not found - may not have enough templates'); } }); test('Full end-to-end template workflow', async ({ authenticatedPage: page }) => { test.setTimeout(60000); const actions = new CommonActions(page); console.log('Starting full end-to-end template workflow test...'); // Step 1: Navigate to templates page await actions.navigateAndWait('/communication-templates/'); await actions.screenshot('e2e-workflow-start'); // Step 2: Create a new template const testData = actions.generateTestData('E2E Template'); const createButton = page.locator('button:has-text("Create New Template")'); if (await createButton.isVisible()) { await createButton.click(); await page.waitForTimeout(1000); // Fill template with placeholders await page.fill('#hvac_template_title', testData.title); await page.fill('#hvac_template_content', `Hello {attendee_name},\n\nThis is a test template for {event_title}.\n\nSee you at {venue_name}!\n\n{trainer_name}` ); await page.selectOption('#hvac_template_category', 'reminder'); // Save template await page.locator('button:has-text("Save Template")').click(); await actions.waitForComplexAjax(); console.log('✓ Template created'); } // Step 3: Navigate to email attendees and use the template await actions.navigateAndWait('/hvac-dashboard/'); const emailLink = page.locator('a[href*="email-attendees"]').first(); if (await emailLink.isVisible()) { await emailLink.click(); await page.waitForLoadState('networkidle'); // Open template widget const templateToggle = page.locator('.hvac-template-toggle'); if (await templateToggle.isVisible()) { await templateToggle.click(); await page.waitForTimeout(1000); // Select our created template const templateSelect = page.locator('#hvac_template_select, select[name="template_select"]'); if (await templateSelect.isVisible()) { // Find our template in the dropdown const optionWithText = page.locator(`option:has-text("${testData.title}")`); if (await optionWithText.count() > 0) { const optionValue = await optionWithText.getAttribute('value'); await templateSelect.selectOption(optionValue); // Load the template await page.locator('button:has-text("Load Template")').click(); await actions.waitForAjax(); // Verify content loaded const emailContent = page.locator('#email_message, textarea[name="email_message"]'); const loadedContent = await emailContent.inputValue(); expect(loadedContent).toContain('{attendee_name}'); expect(loadedContent).toContain('{event_title}'); console.log('✓ Template loaded and ready for use'); await actions.screenshot('e2e-workflow-complete'); } } } } console.log('✓ Full end-to-end workflow completed successfully'); }); });