diff --git a/wordpress-dev/tests/e2e/certificate-core.test.ts b/wordpress-dev/tests/e2e/certificate-core.test.ts
new file mode 100644
index 00000000..c9f59ab3
--- /dev/null
+++ b/wordpress-dev/tests/e2e/certificate-core.test.ts
@@ -0,0 +1,133 @@
+import { test, expect } from './fixtures/auth';
+import { CommonActions } from './utils/common-actions';
+import { STAGING_URL } from './config/staging-config';
+
+/**
+ * Core certificate functionality tests
+ * Tests: generation, viewing, and basic functionality
+ * @tag @certificates @core
+ */
+
+test.describe('Certificate Core Functionality', () => {
+ test('Certificate generation and viewing flow', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Navigate to Generate Certificates page
+ await actions.navigateAndWait('/generate-certificates/');
+ await actions.screenshot('certificate-generation-page');
+
+ // Verify page loaded correctly
+ await expect(page.locator('h1, h2').filter({ hasText: /generate certificates/i })).toBeVisible();
+
+ // Test AJAX functionality - get events
+ const eventSelect = page.locator('select[name="event_id"]');
+ await expect(eventSelect).toBeVisible();
+
+ // Check if events are available
+ const eventOptions = await eventSelect.locator('option').count();
+ if (eventOptions > 1) {
+ // Select first available event
+ await eventSelect.selectOption({ index: 1 });
+ await actions.waitForAjax();
+
+ // Wait for attendees to load
+ await page.waitForSelector('input[name="attendee_ids[]"]', { timeout: 10000 });
+
+ // Verify attendees loaded
+ const attendeeCheckboxes = page.locator('input[name="attendee_ids[]"]');
+ const attendeeCount = await attendeeCheckboxes.count();
+
+ if (attendeeCount > 0) {
+ console.log(`Found ${attendeeCount} attendees for certificate generation`);
+
+ // Select first attendee
+ await attendeeCheckboxes.first().check();
+ await actions.screenshot('attendee-selected');
+
+ // Generate certificate
+ await page.click('button[type="submit"], input[type="submit"]');
+ await actions.waitForAjax();
+
+ // Verify success message or download
+ const successIndicators = [
+ page.locator('text=Certificate generated'),
+ page.locator('text=Download'),
+ page.locator('a[href*=".pdf"]'),
+ page.locator('.success'),
+ page.locator('.notice-success')
+ ];
+
+ let foundSuccess = false;
+ for (const indicator of successIndicators) {
+ if (await indicator.count() > 0) {
+ foundSuccess = true;
+ break;
+ }
+ }
+
+ expect(foundSuccess).toBeTruthy();
+ await actions.screenshot('certificate-generated');
+ }
+ }
+ });
+
+ test('Certificate Reports page functionality', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Navigate to Certificate Reports
+ await actions.navigateAndWait('/certificate-reports/');
+ await actions.screenshot('certificate-reports-page');
+
+ // Verify page loaded
+ await expect(page.locator('h1, h2').filter({ hasText: /certificate reports/i })).toBeVisible();
+
+ // Verify navigation
+ await actions.verifyNavigation();
+
+ // Check for statistics
+ const statElements = page.locator('.stat-value, .stat-number, .dashboard-stat');
+ const statCount = await statElements.count();
+
+ if (statCount > 0) {
+ console.log(`Found ${statCount} certificate statistics`);
+
+ // Verify statistics are numbers
+ for (let i = 0; i < Math.min(statCount, 4); i++) {
+ const statText = await statElements.nth(i).textContent();
+ const statNumber = parseInt(statText?.replace(/[^\d]/g, '') || '0');
+ expect(statNumber).toBeGreaterThanOrEqual(0);
+ }
+ }
+
+ await actions.screenshot('certificate-reports-verified');
+ });
+
+ test('Certificate system navigation and integration', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Test navigation between certificate pages
+ const certificatePages = [
+ { path: '/certificate-reports/', name: 'Certificate Reports' },
+ { path: '/generate-certificates/', name: 'Generate Certificates' }
+ ];
+
+ for (const certPage of certificatePages) {
+ await actions.navigateAndWait(certPage.path);
+
+ // Verify page loaded
+ await expect(page.locator('h1, h2').filter({
+ hasText: new RegExp(certPage.name, 'i')
+ })).toBeVisible();
+
+ // Verify navigation buttons work
+ await actions.verifyNavigation();
+
+ await actions.screenshot(`${certPage.name.toLowerCase().replace(/\s+/g, '-')}-navigation`);
+ }
+
+ // Test return to dashboard
+ await page.click('text=Dashboard');
+ await actions.waitForAjax();
+ await expect(page).toHaveURL(/hvac-dashboard/);
+ });
+});
\ No newline at end of file
diff --git a/wordpress-dev/tests/e2e/certificate-edge-cases.test.ts b/wordpress-dev/tests/e2e/certificate-edge-cases.test.ts
new file mode 100644
index 00000000..2e7d88d3
--- /dev/null
+++ b/wordpress-dev/tests/e2e/certificate-edge-cases.test.ts
@@ -0,0 +1,241 @@
+import { test, expect } from './fixtures/auth';
+import { CommonActions } from './utils/common-actions';
+
+/**
+ * Certificate edge cases and error handling tests
+ * Tests: validation, error scenarios, boundary conditions
+ * @tag @certificates @edge-cases
+ */
+
+test.describe('Certificate Edge Cases', () => {
+ test('Error handling for invalid certificate generation', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Navigate to Generate Certificates page
+ await actions.navigateAndWait('/generate-certificates/');
+
+ // Test submitting without selecting an event
+ const submitButton = page.locator('button[type="submit"], input[type="submit"]');
+ if (await submitButton.count() > 0) {
+ await submitButton.click();
+ await actions.waitForAjax();
+
+ // Look for error messages
+ const errorSelectors = [
+ '.error',
+ '.notice-error',
+ 'div:has-text(/error/i)',
+ 'span:has-text(/required/i)',
+ 'p:has-text(/select/i)'
+ ];
+
+ let foundError = false;
+ for (const selector of errorSelectors) {
+ if (await page.locator(selector).count() > 0) {
+ foundError = true;
+ console.log('Found appropriate error message for invalid submission');
+ break;
+ }
+ }
+
+ // Either error message shown or no action taken (both are valid)
+ await actions.screenshot('invalid-submission-handled');
+ }
+ });
+
+ test('Certificate generation with no attendees', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ await actions.navigateAndWait('/generate-certificates/');
+
+ const eventSelect = page.locator('select[name="event_id"]');
+ const eventOptions = await eventSelect.locator('option').count();
+
+ if (eventOptions > 1) {
+ // Try to find an event with no attendees by testing each option
+ for (let i = 1; i < Math.min(eventOptions, 5); i++) {
+ await eventSelect.selectOption({ index: i });
+ await actions.waitForAjax();
+
+ // Wait a moment for AJAX to complete
+ await page.waitForTimeout(2000);
+
+ const attendeeCheckboxes = page.locator('input[name="attendee_ids[]"]');
+ const attendeeCount = await attendeeCheckboxes.count();
+
+ if (attendeeCount === 0) {
+ console.log(`Found event with no attendees at index ${i}`);
+
+ // Try to submit with no attendees
+ const submitButton = page.locator('button[type="submit"], input[type="submit"]');
+ if (await submitButton.count() > 0) {
+ await submitButton.click();
+ await actions.waitForAjax();
+
+ // Should show appropriate message
+ const noAttendeesMessages = [
+ page.locator('text=No attendees'),
+ page.locator('text=No participants'),
+ page.locator('div:has-text(/no.*attendees/i)'),
+ page.locator('p:has-text(/select.*attendees/i)')
+ ];
+
+ let foundMessage = false;
+ for (const msg of noAttendeesMessages) {
+ if (await msg.count() > 0) {
+ foundMessage = true;
+ console.log('Found appropriate no-attendees message');
+ break;
+ }
+ }
+
+ await actions.screenshot('no-attendees-handled');
+ }
+ break;
+ }
+ }
+ }
+ });
+
+ test('Certificate page accessibility and performance', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Test page load performance
+ const startTime = Date.now();
+ await actions.navigateAndWait('/generate-certificates/');
+ const loadTime = Date.now() - startTime;
+
+ console.log(`Certificate generation page loaded in ${loadTime}ms`);
+ expect(loadTime).toBeLessThan(10000); // Should load within 10 seconds
+
+ // Test basic accessibility
+ const accessibilityChecks = [
+ { selector: 'h1, h2', name: 'Page has heading' },
+ { selector: 'label', name: 'Form has labels' },
+ { selector: 'button, input[type="submit"]', name: 'Page has interactive elements' }
+ ];
+
+ for (const check of accessibilityChecks) {
+ const elements = page.locator(check.selector);
+ const count = await elements.count();
+ expect(count).toBeGreaterThan(0);
+ console.log(`✓ ${check.name}: Found ${count} elements`);
+ }
+
+ await actions.screenshot('accessibility-verified');
+ });
+
+ test('Certificate system under load simulation', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ // Simulate rapid navigation between certificate pages
+ const pages = [
+ '/certificate-reports/',
+ '/generate-certificates/',
+ '/certificate-reports/',
+ '/generate-certificates/'
+ ];
+
+ for (let i = 0; i < pages.length; i++) {
+ const startTime = Date.now();
+ await actions.navigateAndWait(pages[i]);
+ const loadTime = Date.now() - startTime;
+
+ console.log(`Page ${i + 1} loaded in ${loadTime}ms`);
+
+ // Verify page loaded correctly
+ const hasContent = await page.locator('h1, h2, .content, main').count() > 0;
+ expect(hasContent).toBeTruthy();
+
+ // Short pause to simulate user behavior
+ await page.waitForTimeout(500);
+ }
+
+ await actions.screenshot('load-simulation-completed');
+ });
+
+ test('Certificate data validation and sanitization', async ({ authenticatedPage: page }) => {
+ const actions = new CommonActions(page);
+
+ await actions.navigateAndWait('/generate-certificates/');
+
+ // Test XSS prevention in any input fields
+ const inputFields = page.locator('input[type="text"], input[type="search"], textarea');
+ const inputCount = await inputFields.count();
+
+ if (inputCount > 0) {
+ const testInput = '';
+ await inputFields.first().fill(testInput);
+
+ // Verify the input is sanitized or escaped
+ const value = await inputFields.first().inputValue();
+ expect(value).not.toContain('