upskill-event-manager/tests/page-objects/master-trainer/MasterTrainerTrainers.js
Ben 7c9ca65cf2
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
feat: add comprehensive test framework and test files
- 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>
2025-08-29 23:23:26 -03:00

533 lines
No EOL
20 KiB
JavaScript

/**
* Master Trainer Trainers Management Page Object
* Handles interactions with the master trainer trainers management page
*/
const BasePage = require('../base/BasePage');
class MasterTrainerTrainers extends BasePage {
constructor(page = null) {
super(page);
this.url = '/master-trainer/trainers/';
this.title = 'Trainers Management';
this.selectors = {
// Main page elements
pageTitle: 'h1, .page-title, .trainers-title',
trainersContainer: '.trainers-container, .trainers-list, .trainers-table',
// Trainers list and table
trainersTable: 'table.trainers-table, .trainers-grid',
trainerRows: 'tr.trainer-row, .trainer-item',
trainerName: '.trainer-name, .trainer-title',
trainerEmail: '.trainer-email, .email',
trainerPhone: '.trainer-phone, .phone',
trainerStatus: '.trainer-status, .status',
trainerRole: '.trainer-role, .role',
trainerJoinDate: '.trainer-join-date, .join-date',
trainerLastActive: '.trainer-last-active, .last-active',
trainerEventCount: '.trainer-events, .event-count',
// Action buttons
addTrainerBtn: 'a[href*="add"], .add-trainer, .create-trainer',
editTrainerBtn: 'a[href*="edit"], .edit-trainer, .trainer-edit',
deleteTrainerBtn: 'a[href*="delete"], .delete-trainer, .trainer-delete',
viewTrainerBtn: 'a[href*="view"], .view-trainer, .trainer-view',
profileTrainerBtn: 'a[href*="profile"], .trainer-profile, .profile-link',
// Approval workflow buttons
approveTrainerBtn: '.approve-trainer, .approve-btn, .btn-approve',
rejectTrainerBtn: '.reject-trainer, .reject-btn, .btn-reject',
suspendTrainerBtn: '.suspend-trainer, .suspend-btn',
activateTrainerBtn: '.activate-trainer, .activate-btn',
// Bulk actions
bulkActionsSelect: 'select[name="bulk-action"], .bulk-actions select',
bulkApplyBtn: '.bulk-apply, .apply-bulk-action',
selectAllCheckbox: 'input[type="checkbox"].select-all',
trainerCheckboxes: 'input[type="checkbox"].trainer-select',
// Filters and search
searchInput: 'input[type="search"], .search-trainers, #trainer-search',
searchBtn: '.search-btn, .search-submit',
statusFilter: 'select.status-filter, #status-filter',
roleFilter: 'select.role-filter, #role-filter',
locationFilter: 'select.location-filter, #location-filter',
skillFilter: 'select.skill-filter, #skill-filter',
clearFiltersBtn: '.clear-filters, .reset-filters',
// Trainer statistics and overview
trainerStats: '.trainer-stats, .trainers-statistics',
totalTrainers: '.total-trainers, [data-metric="total"]',
activeTrainers: '.active-trainers, [data-metric="active"]',
pendingTrainers: '.pending-trainers, [data-metric="pending"]',
suspendedTrainers: '.suspended-trainers, [data-metric="suspended"]',
newTrainers: '.new-trainers, [data-metric="new"]',
// Performance metrics
performanceSection: '.trainer-performance, .performance-metrics',
averageRating: '.average-rating, [data-metric="rating"]',
eventCompletionRate: '.completion-rate, [data-metric="completion"]',
trainerUtilization: '.trainer-utilization, [data-metric="utilization"]',
// Communication features
messageTrainerBtn: '.message-trainer, .send-message',
emailTrainerBtn: '.email-trainer, .send-email',
bulkMessageBtn: '.bulk-message, .message-all',
// Import/Export features
importTrainersBtn: '.import-trainers, .import-btn',
exportTrainersBtn: '.export-trainers, .export-btn',
// Pagination
pagination: '.pagination, .page-navigation',
prevPageBtn: '.prev-page, .previous',
nextPageBtn: '.next-page, .next',
pageNumbers: '.page-numbers a',
// Loading and status indicators
loadingIndicator: '.loading, .spinner',
errorMessage: '.error-message, .notice-error',
successMessage: '.success-message, .notice-success',
emptyState: '.no-trainers, .trainers-empty',
// Trainer details modal/panel
trainerDetailsModal: '.trainer-details-modal, .trainer-popup',
trainerDetailsPanel: '.trainer-details-panel, .details-panel',
closeDetailsBtn: '.close-details, .modal-close',
// Trainer history and logs
trainerHistory: '.trainer-history, .activity-log',
trainerEvents: '.trainer-events-list, .events-history',
trainerCertificates: '.trainer-certificates, .certificates-list'
};
}
/**
* Wait for trainers page to be fully loaded
*/
async waitForPageReady() {
await this.waitForElement(this.selectors.pageTitle);
await this.waitForAjaxComplete();
// Wait for trainers container or empty state
try {
await Promise.race([
this.waitForElement(this.selectors.trainersContainer, 10000),
this.waitForElement(this.selectors.emptyState, 10000)
]);
} catch (error) {
console.warn('Trainers container or empty state not found, continuing...');
}
}
/**
* Get all trainers from the current page
*/
async getTrainersList() {
const trainers = [];
const trainerRows = await this.page.$$(this.selectors.trainerRows);
for (const row of trainerRows) {
const trainer = {
name: await this.getTextFromElement(row, this.selectors.trainerName),
email: await this.getTextFromElement(row, this.selectors.trainerEmail),
phone: await this.getTextFromElement(row, this.selectors.trainerPhone),
status: await this.getTextFromElement(row, this.selectors.trainerStatus),
role: await this.getTextFromElement(row, this.selectors.trainerRole),
joinDate: await this.getTextFromElement(row, this.selectors.trainerJoinDate),
lastActive: await this.getTextFromElement(row, this.selectors.trainerLastActive),
eventCount: await this.getTextFromElement(row, this.selectors.trainerEventCount)
};
trainers.push(trainer);
}
return trainers;
}
/**
* Get trainer statistics from the page
*/
async getTrainerStatistics() {
const stats = {};
try {
if (await this.hasElement(this.selectors.totalTrainers)) {
const totalText = await this.getElementText(this.selectors.totalTrainers);
stats.total = this.extractNumber(totalText);
}
if (await this.hasElement(this.selectors.activeTrainers)) {
const activeText = await this.getElementText(this.selectors.activeTrainers);
stats.active = this.extractNumber(activeText);
}
if (await this.hasElement(this.selectors.pendingTrainers)) {
const pendingText = await this.getElementText(this.selectors.pendingTrainers);
stats.pending = this.extractNumber(pendingText);
}
if (await this.hasElement(this.selectors.suspendedTrainers)) {
const suspendedText = await this.getElementText(this.selectors.suspendedTrainers);
stats.suspended = this.extractNumber(suspendedText);
}
if (await this.hasElement(this.selectors.newTrainers)) {
const newText = await this.getElementText(this.selectors.newTrainers);
stats.new = this.extractNumber(newText);
}
} catch (error) {
console.warn('Failed to get trainer statistics:', error.message);
}
return stats;
}
/**
* Get performance metrics
*/
async getPerformanceMetrics() {
const metrics = {};
try {
if (await this.hasElement(this.selectors.averageRating)) {
const ratingText = await this.getElementText(this.selectors.averageRating);
metrics.averageRating = this.extractDecimal(ratingText);
}
if (await this.hasElement(this.selectors.eventCompletionRate)) {
const completionText = await this.getElementText(this.selectors.eventCompletionRate);
metrics.completionRate = this.extractNumber(completionText);
}
if (await this.hasElement(this.selectors.trainerUtilization)) {
const utilizationText = await this.getElementText(this.selectors.trainerUtilization);
metrics.utilization = this.extractNumber(utilizationText);
}
} catch (error) {
console.warn('Failed to get performance metrics:', error.message);
}
return metrics;
}
/**
* Search for trainers by term
*/
async searchTrainers(searchTerm) {
if (await this.hasElement(this.selectors.searchInput)) {
await this.fillField(this.selectors.searchInput, searchTerm);
if (await this.hasElement(this.selectors.searchBtn)) {
await this.clickElement(this.selectors.searchBtn);
} else {
await this.page.keyboard.press('Enter');
}
await this.waitForAjaxComplete();
await this.waitForPageReady();
}
}
/**
* Filter trainers by status
*/
async filterByStatus(status) {
if (await this.hasElement(this.selectors.statusFilter)) {
await this.selectOption(this.selectors.statusFilter, status);
await this.waitForAjaxComplete();
await this.waitForPageReady();
}
}
/**
* Filter trainers by role
*/
async filterByRole(role) {
if (await this.hasElement(this.selectors.roleFilter)) {
await this.selectOption(this.selectors.roleFilter, role);
await this.waitForAjaxComplete();
await this.waitForPageReady();
}
}
/**
* Approve a trainer by index or name
*/
async approveTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.approveTrainerBtn);
}
/**
* Reject a trainer by index or name
*/
async rejectTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.rejectTrainerBtn);
}
/**
* Suspend a trainer by index or name
*/
async suspendTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.suspendTrainerBtn);
}
/**
* Activate a trainer by index or name
*/
async activateTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.activateTrainerBtn);
}
/**
* Perform an action on a specific trainer
*/
async performTrainerAction(identifier, actionSelector) {
const trainerRows = await this.page.$$(this.selectors.trainerRows);
if (typeof identifier === 'number' && trainerRows[identifier]) {
// Action by index
const actionBtn = await trainerRows[identifier].$(actionSelector);
if (actionBtn) {
await actionBtn.click();
await this.waitForAjaxComplete();
return true;
}
} else if (typeof identifier === 'string') {
// Action by trainer name
for (const row of trainerRows) {
const nameElement = await row.$(this.selectors.trainerName);
if (nameElement) {
const name = await nameElement.textContent();
if (name.trim().includes(identifier)) {
const actionBtn = await row.$(actionSelector);
if (actionBtn) {
await actionBtn.click();
await this.waitForAjaxComplete();
return true;
}
}
}
}
}
return false;
}
/**
* Navigate to add new trainer page
*/
async addNewTrainer() {
if (await this.hasElement(this.selectors.addTrainerBtn)) {
await this.clickElement(this.selectors.addTrainerBtn);
await this.waitForPageLoad();
return true;
}
return false;
}
/**
* Edit a trainer by identifier
*/
async editTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.editTrainerBtn);
}
/**
* View trainer profile
*/
async viewTrainerProfile(identifier) {
return await this.performTrainerAction(identifier, this.selectors.profileTrainerBtn);
}
/**
* Perform bulk actions on selected trainers
*/
async performBulkAction(action, trainerIndices = []) {
// Select trainers by index
const trainerRows = await this.page.$$(this.selectors.trainerRows);
for (const index of trainerIndices) {
if (trainerRows[index]) {
const checkbox = await trainerRows[index].$(this.selectors.trainerCheckboxes);
if (checkbox) {
await checkbox.click();
}
}
}
// Select bulk action
if (await this.hasElement(this.selectors.bulkActionsSelect)) {
await this.selectOption(this.selectors.bulkActionsSelect, action);
if (await this.hasElement(this.selectors.bulkApplyBtn)) {
await this.clickElement(this.selectors.bulkApplyBtn);
await this.waitForAjaxComplete();
await this.waitForPageReady();
return true;
}
}
return false;
}
/**
* Select all trainers on current page
*/
async selectAllTrainers() {
if (await this.hasElement(this.selectors.selectAllCheckbox)) {
await this.clickElement(this.selectors.selectAllCheckbox);
return true;
}
return false;
}
/**
* Send message to trainer
*/
async messageTrainer(identifier) {
return await this.performTrainerAction(identifier, this.selectors.messageTrainerBtn);
}
/**
* Send bulk message to selected trainers
*/
async sendBulkMessage() {
if (await this.hasElement(this.selectors.bulkMessageBtn)) {
await this.clickElement(this.selectors.bulkMessageBtn);
await this.waitForPageLoad();
return true;
}
return false;
}
/**
* Import trainers data
*/
async importTrainers() {
if (await this.hasElement(this.selectors.importTrainersBtn)) {
await this.clickElement(this.selectors.importTrainersBtn);
await this.waitForPageLoad();
return true;
}
return false;
}
/**
* Export trainers data
*/
async exportTrainers() {
if (await this.hasElement(this.selectors.exportTrainersBtn)) {
await this.clickElement(this.selectors.exportTrainersBtn);
await this.page.waitForTimeout(2000);
return true;
}
return false;
}
/**
* Get trainer details by opening details modal/panel
*/
async getTrainerDetails(identifier) {
if (await this.performTrainerAction(identifier, this.selectors.viewTrainerBtn)) {
// Wait for details to load
try {
await Promise.race([
this.waitForElement(this.selectors.trainerDetailsModal, 5000),
this.waitForElement(this.selectors.trainerDetailsPanel, 5000)
]);
// Extract details from modal/panel
const details = await this.page.evaluate(() => {
const modal = document.querySelector('.trainer-details-modal, .trainer-popup, .trainer-details-panel, .details-panel');
if (modal) {
return {
name: modal.querySelector('.trainer-name, .name')?.textContent || '',
email: modal.querySelector('.trainer-email, .email')?.textContent || '',
phone: modal.querySelector('.trainer-phone, .phone')?.textContent || '',
status: modal.querySelector('.trainer-status, .status')?.textContent || '',
joinDate: modal.querySelector('.join-date, .joined')?.textContent || '',
eventCount: modal.querySelector('.event-count, .events-completed')?.textContent || '',
rating: modal.querySelector('.trainer-rating, .rating')?.textContent || ''
};
}
return null;
});
// Close details
if (await this.hasElement(this.selectors.closeDetailsBtn)) {
await this.clickElement(this.selectors.closeDetailsBtn);
}
return details;
} catch (error) {
console.warn('Failed to get trainer details:', error.message);
return null;
}
}
return null;
}
/**
* Check if trainers page has any trainers
*/
async hasTrainers() {
return await this.hasElement(this.selectors.trainerRows);
}
/**
* Check if page is showing empty state
*/
async isEmptyState() {
return await this.isElementVisible(this.selectors.emptyState);
}
/**
* Get available trainer management features
*/
async getAvailableFeatures() {
return {
canAdd: await this.hasElement(this.selectors.addTrainerBtn),
canSearch: await this.hasElement(this.selectors.searchInput),
canFilter: await this.hasElement(this.selectors.statusFilter),
canApprove: await this.hasElement(this.selectors.approveTrainerBtn),
canBulkAction: await this.hasElement(this.selectors.bulkActionsSelect),
canMessage: await this.hasElement(this.selectors.messageTrainerBtn),
canImport: await this.hasElement(this.selectors.importTrainersBtn),
canExport: await this.hasElement(this.selectors.exportTrainersBtn),
hasStatistics: await this.hasElement(this.selectors.trainerStats),
hasPerformance: await this.hasElement(this.selectors.performanceSection)
};
}
/**
* Helper method to get text from child element
*/
async getTextFromElement(parentElement, selector) {
try {
const element = await parentElement.$(selector);
return element ? await element.textContent() : '';
} catch (error) {
return '';
}
}
/**
* Extract number from text string
*/
extractNumber(text) {
const match = text.match(/\d+/);
return match ? parseInt(match[0], 10) : 0;
}
/**
* Extract decimal number from text string
*/
extractDecimal(text) {
const match = text.match(/\d+\.?\d*/);
return match ? parseFloat(match[0]) : 0;
}
}
module.exports = MasterTrainerTrainers;