// @ts-check
const { test, expect } = require('@playwright/test');
/**
* E2E Tests for HVAC Trainer Announcements System
*
* Tests the complete user journey for:
* - Master trainers creating and managing announcements
* - Regular trainers viewing announcements
* - Email notifications
* - Security and permissions
*/
// Test configuration
const BASE_URL = process.env.UPSKILL_STAGING_URL || 'https://upskill-staging.measurequick.com';
const ADMIN_USER = process.env.STAGING_ADMIN_USER || 'benr';
const ADMIN_PASS = process.env.STAGING_ADMIN_PASS || 'Xy8$kP#mN2@Lq9Z!';
// Test users
const MASTER_TRAINER = {
username: 'JoeMedosch@gmail.com',
password: 'JoeTrainer2025@',
displayName: 'Joe Medosch'
};
const REGULAR_TRAINER = {
username: 'test_trainer',
password: 'TestTrainer123!',
displayName: 'Test Trainer'
};
// Helper function to login
async function loginAsUser(page, username, password) {
await page.goto(`${BASE_URL}/wp-login.php`);
await page.fill('#user_login', username);
await page.fill('#user_pass', password);
await page.click('#wp-submit');
await page.waitForURL(/\/wp-admin|\/trainer\//);
}
// Helper function to navigate to trainer page
async function navigateToTrainerPage(page, path) {
await page.goto(`${BASE_URL}/trainer/${path}/`);
await page.waitForLoadState('networkidle');
}
test.describe('HVAC Announcements - Master Trainer Features', () => {
test.beforeEach(async ({ page }) => {
await loginAsUser(page, MASTER_TRAINER.username, MASTER_TRAINER.password);
});
test('Master trainer can access announcements management page', async ({ page }) => {
// Navigate to master announcements page
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Verify page loaded
await expect(page.locator('h1')).toContainText('Manage Announcements');
// Verify admin interface elements
await expect(page.locator('#announcements-admin-container')).toBeVisible();
await expect(page.locator('.announcement-header')).toBeVisible();
await expect(page.locator('#announcement-list')).toBeVisible();
// Verify action buttons
await expect(page.locator('#new-announcement-btn')).toBeVisible();
await expect(page.locator('#new-announcement-btn')).toHaveText('New Announcement');
});
test('Master trainer can create a new announcement', async ({ page }) => {
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Click new announcement button
await page.click('#new-announcement-btn');
// Wait for form to appear
await expect(page.locator('#announcement-form')).toBeVisible();
// Fill in announcement details
const testTitle = `E2E Test Announcement ${Date.now()}`;
const testContent = 'This is a test announcement created by Playwright E2E tests.';
const testExcerpt = 'Test excerpt for E2E';
await page.fill('#announcement-title', testTitle);
// Handle rich text editor (TinyMCE or plain textarea)
const editorFrame = page.frameLocator('#announcement-content_ifr');
const hasIframe = await editorFrame.locator('body').count() > 0;
if (hasIframe) {
// TinyMCE editor
await editorFrame.locator('body').fill(testContent);
} else {
// Plain textarea
await page.fill('#announcement-content', testContent);
}
await page.fill('#announcement-excerpt', testExcerpt);
// Select status
await page.selectOption('#announcement-status', 'draft');
// Add categories (if available)
const categoryCheckboxes = await page.locator('.category-checkbox').count();
if (categoryCheckboxes > 0) {
await page.locator('.category-checkbox').first().check();
}
// Add tags
await page.fill('#announcement-tags', 'e2e-test, playwright, automated');
// Save announcement
await page.click('#save-announcement-btn');
// Wait for success message
await expect(page.locator('.notice-success')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.notice-success')).toContainText(/created|saved|success/i);
// Verify announcement appears in list
await expect(page.locator('#announcement-list')).toContainText(testTitle);
});
test('Master trainer can edit an existing announcement', async ({ page }) => {
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Wait for announcements to load
await page.waitForSelector('.announcement-item', { timeout: 10000 });
// Click edit on first announcement
const firstAnnouncement = page.locator('.announcement-item').first();
await firstAnnouncement.locator('.edit-announcement').click();
// Wait for form to load with data
await expect(page.locator('#announcement-form')).toBeVisible();
await expect(page.locator('#announcement-title')).not.toBeEmpty();
// Edit the title
const currentTitle = await page.locator('#announcement-title').inputValue();
const updatedTitle = currentTitle + ' (Updated)';
await page.fill('#announcement-title', updatedTitle);
// Update status to publish
await page.selectOption('#announcement-status', 'publish');
// Save changes
await page.click('#save-announcement-btn');
// Wait for success message
await expect(page.locator('.notice-success')).toBeVisible({ timeout: 10000 });
// Verify updated title in list
await expect(page.locator('#announcement-list')).toContainText(updatedTitle);
});
test('Master trainer can delete an announcement', async ({ page }) => {
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Wait for announcements to load
await page.waitForSelector('.announcement-item', { timeout: 10000 });
// Count initial announcements
const initialCount = await page.locator('.announcement-item').count();
// Find an announcement with "E2E Test" in title (if exists)
const testAnnouncements = page.locator('.announcement-item:has-text("E2E Test")');
const hasTestAnnouncement = await testAnnouncements.count() > 0;
if (hasTestAnnouncement) {
// Click delete on test announcement
await testAnnouncements.first().locator('.delete-announcement').click();
// Confirm deletion
page.on('dialog', dialog => dialog.accept());
// Wait for announcement to be removed
await expect(page.locator('.notice-success')).toBeVisible({ timeout: 10000 });
// Verify count decreased
const newCount = await page.locator('.announcement-item').count();
expect(newCount).toBeLessThan(initialCount);
}
});
test('Master trainer can filter announcements by status', async ({ page }) => {
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Wait for filter controls
await expect(page.locator('#announcement-status-filter')).toBeVisible();
// Filter by published
await page.selectOption('#announcement-status-filter', 'publish');
await page.waitForTimeout(1000); // Wait for filter to apply
// Verify only published announcements shown
const statusBadges = await page.locator('.status-badge').allTextContents();
statusBadges.forEach(status => {
expect(status.toLowerCase()).toContain('publish');
});
// Filter by draft
await page.selectOption('#announcement-status-filter', 'draft');
await page.waitForTimeout(1000);
// Verify only draft announcements shown (if any)
const draftBadges = await page.locator('.status-badge').allTextContents();
if (draftBadges.length > 0) {
draftBadges.forEach(status => {
expect(status.toLowerCase()).toContain('draft');
});
}
});
test('Master trainer can search announcements', async ({ page }) => {
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Wait for search box
await expect(page.locator('#announcement-search')).toBeVisible();
// Search for specific term
await page.fill('#announcement-search', 'training');
await page.press('#announcement-search', 'Enter');
// Wait for search results
await page.waitForTimeout(1000);
// Verify search results contain search term (if any results)
const searchResults = await page.locator('.announcement-item').count();
if (searchResults > 0) {
const titles = await page.locator('.announcement-title').allTextContents();
const contents = await page.locator('.announcement-excerpt').allTextContents();
const allText = [...titles, ...contents].join(' ').toLowerCase();
expect(allText).toContain('training');
}
});
});
test.describe('HVAC Announcements - Regular Trainer Features', () => {
test.beforeEach(async ({ page }) => {
await loginAsUser(page, REGULAR_TRAINER.username, REGULAR_TRAINER.password);
});
test('Regular trainer can view announcements on resources page', async ({ page }) => {
// Navigate to resources page
await navigateToTrainerPage(page, 'resources');
// Verify page loaded
await expect(page.locator('h1')).toContainText('Training Resources');
// Verify announcements timeline is visible
await expect(page.locator('.hvac-announcements-timeline')).toBeVisible();
// Verify at least one announcement is displayed (if any published)
const announcementCount = await page.locator('.timeline-item').count();
if (announcementCount > 0) {
// Verify announcement structure
await expect(page.locator('.timeline-item').first()).toBeVisible();
await expect(page.locator('.timeline-title').first()).toBeVisible();
await expect(page.locator('.timeline-date').first()).toBeVisible();
await expect(page.locator('.timeline-excerpt').first()).toBeVisible();
}
});
test('Regular trainer can click to view announcement details', async ({ page }) => {
await navigateToTrainerPage(page, 'resources');
// Wait for announcements to load
const announcements = await page.locator('.announcement-link').count();
if (announcements > 0) {
// Click first announcement
await page.locator('.announcement-link').first().click();
// Wait for modal to appear
await expect(page.locator('#announcement-modal')).toBeVisible();
await expect(page.locator('.modal-content')).toBeVisible();
// Verify modal content
await expect(page.locator('.announcement-full')).toBeVisible();
await expect(page.locator('.announcement-header h2')).toBeVisible();
await expect(page.locator('.announcement-content')).toBeVisible();
// Close modal
await page.locator('.modal-close').click();
await expect(page.locator('#announcement-modal')).not.toBeVisible();
}
});
test('Regular trainer cannot access management page', async ({ page }) => {
// Try to access master announcements page
const response = await page.goto(`${BASE_URL}/trainer/master-trainer/manage-announcements/`);
// Should be redirected or show error
expect(response.status()).not.toBe(200);
// Verify error message or redirect
const url = page.url();
expect(url).not.toContain('/manage-announcements');
});
test('Regular trainer can use load more functionality', async ({ page }) => {
await navigateToTrainerPage(page, 'resources');
// Check if load more button exists
const hasLoadMore = await page.locator('.load-more-announcements').count() > 0;
if (hasLoadMore) {
// Count initial announcements
const initialCount = await page.locator('.timeline-item').count();
// Click load more
await page.click('.load-more-announcements');
// Wait for new announcements to load
await page.waitForTimeout(2000);
// Verify more announcements loaded
const newCount = await page.locator('.timeline-item').count();
expect(newCount).toBeGreaterThan(initialCount);
}
});
test('Regular trainer can view Google Drive resources', async ({ page }) => {
await navigateToTrainerPage(page, 'resources');
// Check if Google Drive embed exists
const hasDriveEmbed = await page.locator('.hvac-google-drive-embed').count() > 0;
if (hasDriveEmbed) {
// Verify iframe is present
await expect(page.locator('.hvac-google-drive-embed iframe')).toBeVisible();
// Verify iframe has correct attributes
const iframe = page.locator('.hvac-google-drive-embed iframe');
await expect(iframe).toHaveAttribute('src', /drive\.google\.com/);
}
});
});
test.describe('HVAC Announcements - Security and Permissions', () => {
test('Unauthenticated user cannot access announcements', async ({ page }) => {
// Try to access resources page without login
await page.goto(`${BASE_URL}/trainer/resources/`);
// Should be redirected to login
await expect(page).toHaveURL(/wp-login\.php/);
});
test('Subscriber cannot view announcements', async ({ page }) => {
// Create or use a subscriber account
// This would need a subscriber test account to be set up
// For now, we'll test that non-trainer roles are blocked
// Attempt to access resources page directly
const response = await page.goto(`${BASE_URL}/trainer/resources/`);
// Should not have access
if (response.ok()) {
// If page loads, verify no announcements shown
const hasPermissionError = await page.locator(':text("You do not have permission")').count() > 0;
expect(hasPermissionError).toBeTruthy();
}
});
test('XSS prevention in announcement content', async ({ page }) => {
await loginAsUser(page, MASTER_TRAINER.username, MASTER_TRAINER.password);
await navigateToTrainerPage(page, 'master-trainer/manage-announcements');
// Create announcement with potential XSS
await page.click('#new-announcement-btn');
await page.fill('#announcement-title', 'XSS Test ');
await page.fill('#announcement-content', '
Test content');
await page.fill('#announcement-excerpt', 'Excerpt');
await page.selectOption('#announcement-status', 'publish');
await page.click('#save-announcement-btn');
// Wait for save
await page.waitForTimeout(2000);
// Now view as regular trainer
await loginAsUser(page, REGULAR_TRAINER.username, REGULAR_TRAINER.password);
await navigateToTrainerPage(page, 'resources');
// Verify no script execution
page.on('dialog', dialog => {
// Fail test if any alert appears
expect(dialog.message()).not.toContain('XSS');
dialog.dismiss();
});
// Check that dangerous content is escaped
const pageContent = await page.content();
expect(pageContent).not.toContain('