Some checks are pending
		
		
	
	HVAC Plugin CI/CD Pipeline / Security Analysis (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Unit Tests (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Integration Tests (push) Waiting to run
				
			HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Blocked by required conditions
				
			HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
				
			HVAC Plugin CI/CD Pipeline / Notification (push) Blocked by required conditions
				
			Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Waiting to run
				
			Security Monitoring & Compliance / Secrets & Credential Scan (push) Waiting to run
				
			Security Monitoring & Compliance / WordPress Security Analysis (push) Waiting to run
				
			Security Monitoring & Compliance / Static Code Security Analysis (push) Waiting to run
				
			Security Monitoring & Compliance / Security Compliance Validation (push) Waiting to run
				
			Security Monitoring & Compliance / Security Summary Report (push) Blocked by required conditions
				
			Security Monitoring & Compliance / Security Team Notification (push) Blocked by required conditions
				
			- Add 90+ test files including E2E, unit, and integration tests - Implement Page Object Model (POM) architecture - Add Docker testing environment with comprehensive services - Include modernized test framework with error recovery - Add specialized test suites for master trainer and trainer workflows - Update .gitignore to properly track test infrastructure 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			449 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			No EOL
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Trainer Dashboard Page Object Model
 | |
|  * 
 | |
|  * Handles trainer dashboard functionality with WordPress integration:
 | |
|  * - Navigation and menu interactions
 | |
|  * - Dashboard widgets and statistics
 | |
|  * - Quick actions and forms
 | |
|  * - Event management shortcuts
 | |
|  * 
 | |
|  * @package HVAC_Community_Events
 | |
|  * @version 2.0.0
 | |
|  * @created 2025-08-27
 | |
|  */
 | |
| 
 | |
| const BasePage = require('./BasePage');
 | |
| const { expect } = require('@playwright/test');
 | |
| 
 | |
| class TrainerDashboard extends BasePage {
 | |
|     constructor(page) {
 | |
|         super(page);
 | |
|         
 | |
|         // Dashboard-specific selectors
 | |
|         this.selectors = {
 | |
|             // Main dashboard elements
 | |
|             dashboard: [
 | |
|                 'trainer-dashboard',
 | |
|                 '.hvac-trainer-dashboard',
 | |
|                 '.trainer-dashboard',
 | |
|                 '[data-page="trainer-dashboard"]'
 | |
|             ],
 | |
|             
 | |
|             // Navigation elements
 | |
|             navigation: [
 | |
|                 'trainer-nav',
 | |
|                 '.hvac-trainer-nav',
 | |
|                 '.trainer-navigation',
 | |
|                 'nav.trainer-nav'
 | |
|             ],
 | |
|             
 | |
|             // Dashboard widgets
 | |
|             widgets: {
 | |
|                 eventCount: [
 | |
|                     'event-count-widget',
 | |
|                     '.event-count',
 | |
|                     '[data-widget="event-count"]',
 | |
|                     '.dashboard-stat.events'
 | |
|                 ],
 | |
|                 upcomingEvents: [
 | |
|                     'upcoming-events',
 | |
|                     '.upcoming-events-widget',
 | |
|                     '[data-widget="upcoming-events"]'
 | |
|                 ],
 | |
|                 recentActivity: [
 | |
|                     'recent-activity',
 | |
|                     '.recent-activity-widget',
 | |
|                     '[data-widget="recent-activity"]'
 | |
|                 ],
 | |
|                 statistics: [
 | |
|                     'trainer-stats',
 | |
|                     '.trainer-statistics',
 | |
|                     '.dashboard-statistics'
 | |
|                 ]
 | |
|             },
 | |
|             
 | |
|             // Quick action buttons
 | |
|             actions: {
 | |
|                 createEvent: [
 | |
|                     'create-event-btn',
 | |
|                     'a[href*="/create-event"]',
 | |
|                     '.create-event-button',
 | |
|                     'text=Create Event'
 | |
|                 ],
 | |
|                 manageEvents: [
 | |
|                     'manage-events-btn',
 | |
|                     'a[href*="/manage-events"]',
 | |
|                     '.manage-events-button',
 | |
|                     'text=Manage Events'
 | |
|                 ],
 | |
|                 editProfile: [
 | |
|                     'edit-profile-btn',
 | |
|                     'a[href*="/profile/edit"]',
 | |
|                     '.edit-profile-button',
 | |
|                     'text=Edit Profile'
 | |
|                 ],
 | |
|                 viewProfile: [
 | |
|                     'view-profile-btn',
 | |
|                     'a[href*="/profile"]',
 | |
|                     '.view-profile-button',
 | |
|                     'text=View Profile'
 | |
|                 ]
 | |
|             },
 | |
|             
 | |
|             // Breadcrumb navigation
 | |
|             breadcrumb: [
 | |
|                 '.hvac-breadcrumb',
 | |
|                 '.breadcrumb',
 | |
|                 '[data-testid="breadcrumb"]'
 | |
|             ],
 | |
|             
 | |
|             // Welcome message
 | |
|             welcomeMessage: [
 | |
|                 'welcome-message',
 | |
|                 '.welcome-message',
 | |
|                 '.trainer-welcome'
 | |
|             ],
 | |
|             
 | |
|             // Menu items
 | |
|             menuItems: {
 | |
|                 dashboard: 'a[href*="/trainer/dashboard"]',
 | |
|                 events: 'a[href*="/trainer/events"], text=My Events',
 | |
|                 createEvent: 'a[href*="/create-event"], text=Create Event',
 | |
|                 profile: 'a[href*="/profile"], text=Profile',
 | |
|                 venues: 'a[href*="/venues"], text=Venues',
 | |
|                 organizers: 'a[href*="/organizers"], text=Organizers',
 | |
|                 trainingLeads: 'a[href*="/training-leads"], text=Training Leads',
 | |
|                 resources: 'a[href*="/resources"], text=Resources',
 | |
|                 documentation: 'a[href*="/documentation"], text=Documentation',
 | |
|                 announcements: 'a[href*="/announcements"], text=Announcements'
 | |
|             }
 | |
|         };
 | |
|         
 | |
|         this.urls = {
 | |
|             dashboard: '/trainer/dashboard/',
 | |
|             events: '/trainer/events/',
 | |
|             createEvent: '/trainer/create-event/',
 | |
|             profile: '/trainer/profile/',
 | |
|             editProfile: '/trainer/profile/edit/',
 | |
|             venues: '/trainer/venues/',
 | |
|             organizers: '/trainer/organizers/',
 | |
|             trainingLeads: '/trainer/training-leads/',
 | |
|             documentation: '/trainer/documentation/'
 | |
|         };
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Navigate to trainer dashboard
 | |
|      */
 | |
|     async navigate() {
 | |
|         await this.goto(this.urls.dashboard);
 | |
|         await this.waitForDashboardLoad();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Wait for dashboard to fully load
 | |
|      */
 | |
|     async waitForDashboardLoad() {
 | |
|         // Wait for main dashboard container
 | |
|         await this.waitForVisible(this.selectors.dashboard);
 | |
|         
 | |
|         // Wait for navigation to be present
 | |
|         await this.waitForVisible(this.selectors.navigation);
 | |
|         
 | |
|         // Wait for WordPress and AJAX to complete
 | |
|         await this.waitForWordPressReady();
 | |
|         await this.waitForAjax();
 | |
|         
 | |
|         console.log('✅ Trainer dashboard loaded');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Verify dashboard is displayed correctly
 | |
|      */
 | |
|     async verifyDashboard() {
 | |
|         // Check main dashboard container
 | |
|         await expect(this.locator(this.selectors.dashboard)).toBeVisible();
 | |
|         
 | |
|         // Check breadcrumb
 | |
|         if (await this.isVisible(this.selectors.breadcrumb)) {
 | |
|             await this.verifyBreadcrumbs(['Dashboard']);
 | |
|         }
 | |
|         
 | |
|         // Check navigation menu
 | |
|         await expect(this.locator(this.selectors.navigation)).toBeVisible();
 | |
|         
 | |
|         // Check welcome message if present
 | |
|         if (await this.isVisible(this.selectors.welcomeMessage)) {
 | |
|             console.log('✅ Welcome message displayed');
 | |
|         }
 | |
|         
 | |
|         console.log('✅ Dashboard verification complete');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get dashboard statistics
 | |
|      */
 | |
|     async getDashboardStats() {
 | |
|         const stats = {};
 | |
|         
 | |
|         // Get event count if visible
 | |
|         if (await this.isVisible(this.selectors.widgets.eventCount)) {
 | |
|             const eventCountText = await this.getText(this.selectors.widgets.eventCount);
 | |
|             stats.eventCount = this.extractNumber(eventCountText);
 | |
|         }
 | |
|         
 | |
|         // Get upcoming events count
 | |
|         if (await this.isVisible(this.selectors.widgets.upcomingEvents)) {
 | |
|             const upcomingEventsCount = await this.page
 | |
|                 .locator(`${this.selectors.widgets.upcomingEvents.join(', ')} .event-item`)
 | |
|                 .count();
 | |
|             stats.upcomingEvents = upcomingEventsCount;
 | |
|         }
 | |
|         
 | |
|         console.log('📊 Dashboard stats:', stats);
 | |
|         return stats;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Navigate to create event page
 | |
|      */
 | |
|     async goToCreateEvent() {
 | |
|         await this.click(this.selectors.actions.createEvent, { waitForNavigation: true });
 | |
|         await this.waitForUrl('**/create-event**');
 | |
|         console.log('🔗 Navigated to Create Event page');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Navigate to manage events page
 | |
|      */
 | |
|     async goToManageEvents() {
 | |
|         await this.click(this.selectors.actions.manageEvents, { waitForNavigation: true });
 | |
|         await this.waitForUrl('**/events**');
 | |
|         console.log('🔗 Navigated to Manage Events page');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Navigate to profile page
 | |
|      */
 | |
|     async goToProfile() {
 | |
|         await this.click(this.selectors.actions.viewProfile, { waitForNavigation: true });
 | |
|         await this.waitForUrl('**/profile**');
 | |
|         console.log('🔗 Navigated to Profile page');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Navigate to edit profile page
 | |
|      */
 | |
|     async goToEditProfile() {
 | |
|         await this.click(this.selectors.actions.editProfile, { waitForNavigation: true });
 | |
|         await this.waitForUrl('**/profile/edit**');
 | |
|         console.log('🔗 Navigated to Edit Profile page');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Use navigation menu to go to specific section
 | |
|      */
 | |
|     async navigateToSection(section) {
 | |
|         const menuItem = this.selectors.menuItems[section];
 | |
|         if (!menuItem) {
 | |
|             throw new Error(`Unknown navigation section: ${section}`);
 | |
|         }
 | |
|         
 | |
|         // Check if navigation menu is visible
 | |
|         if (await this.isVisible('.hvac-menu-toggle, .mobile-menu-toggle')) {
 | |
|             // Mobile menu - open it first
 | |
|             await this.click('.hvac-menu-toggle, .mobile-menu-toggle');
 | |
|             await this.waitForVisible('.hvac-menu.open, .mobile-menu.open');
 | |
|         }
 | |
|         
 | |
|         // Click menu item
 | |
|         await this.click(menuItem, { waitForNavigation: true });
 | |
|         
 | |
|         // Wait for page to load
 | |
|         const expectedUrl = this.urls[section];
 | |
|         if (expectedUrl) {
 | |
|             await this.waitForUrl(`**${expectedUrl}**`);
 | |
|         }
 | |
|         
 | |
|         console.log(`🔗 Navigated to: ${section}`);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Check for announcements
 | |
|      */
 | |
|     async checkAnnouncements() {
 | |
|         const announcementSelectors = [
 | |
|             '.hvac-announcements',
 | |
|             '.trainer-announcements',
 | |
|             '[data-widget="announcements"]',
 | |
|             '.dashboard-announcements'
 | |
|         ];
 | |
|         
 | |
|         for (const selector of announcementSelectors) {
 | |
|             if (await this.isVisible(selector)) {
 | |
|                 const announcementCount = await this.page.locator(`${selector} .announcement`).count();
 | |
|                 console.log(`📢 Found ${announcementCount} announcements`);
 | |
|                 return announcementCount;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         return 0;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get recent activity items
 | |
|      */
 | |
|     async getRecentActivity() {
 | |
|         if (!await this.isVisible(this.selectors.widgets.recentActivity)) {
 | |
|             return [];
 | |
|         }
 | |
|         
 | |
|         const activityItems = await this.page
 | |
|             .locator(`${this.selectors.widgets.recentActivity.join(', ')} .activity-item`)
 | |
|             .allTextContents();
 | |
|         
 | |
|         console.log(`📋 Found ${activityItems.length} recent activity items`);
 | |
|         return activityItems;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Search functionality (if available)
 | |
|      */
 | |
|     async search(query) {
 | |
|         const searchSelectors = [
 | |
|             '[data-testid="search-input"]',
 | |
|             '.dashboard-search input',
 | |
|             'input[name="search"]',
 | |
|             '.search-box input'
 | |
|         ];
 | |
|         
 | |
|         for (const selector of searchSelectors) {
 | |
|             if (await this.isVisible(selector)) {
 | |
|                 await this.fill(selector, query);
 | |
|                 await this.page.keyboard.press('Enter');
 | |
|                 await this.waitForAjax();
 | |
|                 console.log(`🔍 Searched for: ${query}`);
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         console.log('⚠️ Search functionality not found');
 | |
|         return false;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Quick actions on dashboard
 | |
|      */
 | |
|     async performQuickAction(action) {
 | |
|         const actionHandlers = {
 | |
|             'create-event': () => this.goToCreateEvent(),
 | |
|             'manage-events': () => this.goToManageEvents(),
 | |
|             'edit-profile': () => this.goToEditProfile(),
 | |
|             'view-profile': () => this.goToProfile(),
 | |
|             'view-venues': () => this.navigateToSection('venues'),
 | |
|             'view-organizers': () => this.navigateToSection('organizers'),
 | |
|             'view-training-leads': () => this.navigateToSection('trainingLeads')
 | |
|         };
 | |
|         
 | |
|         const handler = actionHandlers[action];
 | |
|         if (!handler) {
 | |
|             throw new Error(`Unknown quick action: ${action}`);
 | |
|         }
 | |
|         
 | |
|         await handler();
 | |
|         console.log(`⚡ Performed quick action: ${action}`);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Verify trainer has access to expected dashboard elements
 | |
|      */
 | |
|     async verifyTrainerAccess() {
 | |
|         // Check that we're authenticated as trainer
 | |
|         await this.verifyUserRole('trainer');
 | |
|         
 | |
|         // Check essential navigation items
 | |
|         const essentialItems = ['dashboard', 'events', 'profile'];
 | |
|         for (const item of essentialItems) {
 | |
|             const menuItem = this.selectors.menuItems[item];
 | |
|             await expect(this.locator(menuItem)).toBeVisible();
 | |
|         }
 | |
|         
 | |
|         // Check that we can't see admin-only elements
 | |
|         const adminOnlySelectors = [
 | |
|             '.master-trainer-only',
 | |
|             '.admin-only',
 | |
|             'a[href*="/master-trainer/"]',
 | |
|             'a[href*="/wp-admin/"]'
 | |
|         ];
 | |
|         
 | |
|         for (const selector of adminOnlySelectors) {
 | |
|             await expect(this.locator(selector)).not.toBeVisible();
 | |
|         }
 | |
|         
 | |
|         console.log('✅ Trainer access verification complete');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get dashboard page title
 | |
|      */
 | |
|     async getPageTitle() {
 | |
|         return await this.page.title();
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Check if dashboard has mobile responsive layout
 | |
|      */
 | |
|     async checkMobileResponsiveness() {
 | |
|         // Set mobile viewport
 | |
|         await this.page.setViewportSize({ width: 375, height: 667 });
 | |
|         
 | |
|         // Check if mobile menu toggle is visible
 | |
|         const hasMobileMenu = await this.isVisible('.hvac-menu-toggle, .mobile-menu-toggle');
 | |
|         
 | |
|         // Check if navigation is adapted for mobile
 | |
|         const hasMobileNav = await this.isVisible('.hvac-nav-mobile, .mobile-navigation');
 | |
|         
 | |
|         // Reset to desktop viewport
 | |
|         await this.page.setViewportSize({ 
 | |
|             width: this.config.get('browser.viewport.width'),
 | |
|             height: this.config.get('browser.viewport.height')
 | |
|         });
 | |
|         
 | |
|         return {
 | |
|             hasMobileMenu,
 | |
|             hasMobileNav,
 | |
|             isMobileResponsive: hasMobileMenu || hasMobileNav
 | |
|         };
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Extract number from text (helper method)
 | |
|      */
 | |
|     extractNumber(text) {
 | |
|         const match = text.match(/\d+/);
 | |
|         return match ? parseInt(match[0], 10) : 0;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Wait for specific widget to load
 | |
|      */
 | |
|     async waitForWidget(widgetName) {
 | |
|         const widget = this.selectors.widgets[widgetName];
 | |
|         if (!widget) {
 | |
|             throw new Error(`Unknown widget: ${widgetName}`);
 | |
|         }
 | |
|         
 | |
|         await this.waitForVisible(widget);
 | |
|         await this.waitForAjax();
 | |
|         console.log(`📊 Widget loaded: ${widgetName}`);
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Take screenshot of full dashboard
 | |
|      */
 | |
|     async screenshotDashboard() {
 | |
|         return await this.takeScreenshot('trainer-dashboard', { fullPage: true });
 | |
|     }
 | |
| }
 | |
| 
 | |
| module.exports = TrainerDashboard; |