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; |