upskill-event-manager/wordpress-dev/tests/e2e/communication-templates-validation.test.ts
bengizmo a0d47b3b3e feat: Implement configurable Communication Schedule system
This commit implements Phase 1 of the Communication Schedule system, providing:

Core Infrastructure:
- HVAC_Communication_Scheduler: Main controller with cron integration and AJAX handlers
- HVAC_Communication_Schedule_Manager: CRUD operations and database interactions
- HVAC_Communication_Trigger_Engine: Automation logic and recipient management
- HVAC_Communication_Logger: Execution logging and performance tracking
- HVAC_Communication_Installer: Database table creation and management

Features:
- Event-based triggers (before/after event, on registration)
- Custom date scheduling with recurring options
- Flexible recipient targeting (all attendees, confirmed, custom lists)
- Template integration with placeholder replacement
- WordPress cron integration for automated execution
- Comprehensive AJAX API for schedule management
- Template quickstart options for common scenarios

UI Components:
- Communication Schedules page with full management interface
- Form-based schedule creation with validation
- Schedule listing with filtering and status management
- Modal recipient preview functionality
- Pre-configured schedule templates for quick setup

Database Design:
- hvac_communication_schedules: Schedule configurations
- hvac_communication_logs: Execution history and statistics
- hvac_event_communication_tracking: Individual email tracking

The system integrates with existing email templates and provides a foundation
for automated communication workflows for HVAC trainers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-14 00:07:30 -03:00

556 lines
No EOL
21 KiB
TypeScript

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