upskill-event-manager/tests/page-objects/public/PublicPages.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

505 lines
No EOL
17 KiB
JavaScript

/**
* Public Pages Object Models
*
* Page objects for public-facing pages including authentication flows
* and public access pages for comprehensive E2E testing.
*
* @package HVAC_Community_Events
* @version 2.0.0
* @created 2025-08-27
*/
const BasePage = require('../../framework/base/BasePage');
/**
* Training Login Page Object
*/
class TrainingLoginPage extends BasePage {
constructor() {
super();
this.url = '/training-login/';
this.selectors = {
loginForm: 'form#loginform, form.login-form, form[name="loginform"]',
usernameField: '#user_login, input[name="log"], input[name="username"]',
passwordField: '#user_pass, input[name="pwd"], input[name="password"]',
submitButton: '#wp-submit, input[type="submit"], button[type="submit"]',
errorMessage: '.login_error, .error, .notice-error, .warning',
forgotPasswordLink: 'a[href*="lostpassword"], a[href*="forgot"], a:has-text("Forgot")',
rememberMe: '#rememberme, input[name="rememberme"]'
};
}
async waitForPageReady() {
await this.waitForElement(this.selectors.loginForm, 30000);
}
async login(username, password) {
await this.fillField(this.selectors.usernameField, username);
await this.fillField(this.selectors.passwordField, password);
await this.clickElement(this.selectors.submitButton);
// Wait for redirect or error
await this.page.waitForTimeout(3000);
}
async getErrorMessage() {
try {
return await this.getElementText(this.selectors.errorMessage);
} catch (error) {
return null;
}
}
async clickForgotPassword() {
await this.clickElement(this.selectors.forgotPasswordLink);
}
async hasRememberMeOption() {
return await this.hasElement(this.selectors.rememberMe);
}
}
/**
* Trainer Registration Page Object
*/
class TrainerRegistrationPage extends BasePage {
constructor() {
super();
this.url = '/trainer-registration/';
this.selectors = {
registrationForm: 'form.registration-form, form#registerform, form[name="registerform"]',
usernameField: '#user_login, input[name="user_login"], input[name="username"]',
emailField: '#user_email, input[name="user_email"], input[name="email"]',
passwordField: '#user_pass, input[name="user_pass"], input[name="password"]',
confirmPasswordField: '#user_pass_confirm, input[name="user_pass_confirm"]',
firstNameField: 'input[name="first_name"], input[name="user_firstname"]',
lastNameField: 'input[name="last_name"], input[name="user_lastname"]',
phoneField: 'input[name="phone"], input[name="user_phone"]',
companyField: 'input[name="company"], input[name="user_company"]',
submitButton: 'input[type="submit"], button[type="submit"]',
errorMessages: '.error, .notice-error, .registration-errors',
successMessage: '.success, .notice-success, .registration-success',
requiredFieldIndicators: '.required, .asterisk, span[title*="required"]'
};
}
async waitForPageReady() {
await this.waitForElement(this.selectors.registrationForm, 15000);
}
async fillRegistrationForm(userData) {
if (await this.hasElement(this.selectors.usernameField)) {
await this.fillField(this.selectors.usernameField, userData.username);
}
if (await this.hasElement(this.selectors.emailField)) {
await this.fillField(this.selectors.emailField, userData.email);
}
if (await this.hasElement(this.selectors.passwordField)) {
await this.fillField(this.selectors.passwordField, userData.password);
}
if (await this.hasElement(this.selectors.confirmPasswordField)) {
await this.fillField(this.selectors.confirmPasswordField, userData.password);
}
if (await this.hasElement(this.selectors.firstNameField)) {
await this.fillField(this.selectors.firstNameField, userData.firstName);
}
if (await this.hasElement(this.selectors.lastNameField)) {
await this.fillField(this.selectors.lastNameField, userData.lastName);
}
if (await this.hasElement(this.selectors.phoneField)) {
await this.fillField(this.selectors.phoneField, userData.phone || '555-0123');
}
if (await this.hasElement(this.selectors.companyField)) {
await this.fillField(this.selectors.companyField, userData.company || 'Test Company');
}
}
async submitRegistration() {
await this.clickElement(this.selectors.submitButton);
await this.page.waitForTimeout(5000); // Wait for submission processing
}
async getRegistrationErrors() {
try {
const errorElements = await this.page.locator(this.selectors.errorMessages).all();
const errors = [];
for (const element of errorElements) {
const text = await element.textContent();
if (text && text.trim()) {
errors.push(text.trim());
}
}
return errors;
} catch (error) {
return [];
}
}
async hasSuccessMessage() {
return await this.hasElement(this.selectors.successMessage);
}
async getRequiredFields() {
const requiredFields = [];
const indicators = await this.page.locator(this.selectors.requiredFieldIndicators).all();
for (const indicator of indicators) {
const parentForm = await indicator.locator('..').first();
const inputField = await parentForm.locator('input').first();
const fieldName = await inputField.getAttribute('name');
if (fieldName) {
requiredFields.push(fieldName);
}
}
return requiredFields;
}
}
/**
* Registration Pending Page Object
*/
class RegistrationPendingPage extends BasePage {
constructor() {
super();
this.url = '/registration-pending/';
this.selectors = {
pendingMessage: '.pending-message, .approval-notice, .registration-pending',
statusIndicator: '.status, [data-status="pending"]',
contactInfo: '.contact-info, .admin-contact',
nextStepsInfo: '.next-steps, .what-happens-next',
timeframeInfo: '.timeframe, .approval-time'
};
}
async getPendingMessage() {
try {
return await this.getElementText(this.selectors.pendingMessage);
} catch (error) {
return await this.getElementText('body'); // Fallback to body content
}
}
async hasContactInformation() {
return await this.hasElement(this.selectors.contactInfo);
}
async hasNextStepsInformation() {
return await this.hasElement(this.selectors.nextStepsInfo);
}
async getApprovalTimeframe() {
try {
return await this.getElementText(this.selectors.timeframeInfo);
} catch (error) {
return null;
}
}
}
/**
* Account Pending Page Object
*/
class AccountPendingPage extends BasePage {
constructor() {
super();
this.url = '/trainer-account-pending/';
this.selectors = {
pendingMessage: '.account-pending, .approval-pending',
statusDisplay: '.account-status, [data-account-status]',
adminContact: '.admin-contact, .contact-administrator',
submittedDate: '.submission-date, .request-date',
accountDetails: '.account-details, .pending-account-info'
};
}
async getAccountStatus() {
try {
return await this.getElementText(this.selectors.statusDisplay);
} catch (error) {
return 'pending'; // Default status
}
}
async getSubmissionDate() {
try {
return await this.getElementText(this.selectors.submittedDate);
} catch (error) {
return null;
}
}
async hasAdminContactInfo() {
return await this.hasElement(this.selectors.adminContact);
}
}
/**
* Account Disabled Page Object
*/
class AccountDisabledPage extends BasePage {
constructor() {
super();
this.url = '/trainer-account-disabled/';
this.selectors = {
disabledMessage: '.account-disabled, .account-suspended, .disabled-notice',
reasonDisplay: '.disable-reason, .suspension-reason',
reactivationInfo: '.reactivation-info, .restore-account',
contactInfo: '.contact-admin, .appeal-info',
disabledDate: '.disabled-date, .suspension-date'
};
}
async getDisabledReason() {
try {
return await this.getElementText(this.selectors.reasonDisplay);
} catch (error) {
return 'Account has been disabled';
}
}
async hasReactivationInstructions() {
return await this.hasElement(this.selectors.reactivationInfo);
}
async getDisabledDate() {
try {
return await this.getElementText(this.selectors.disabledDate);
} catch (error) {
return null;
}
}
async hasAppealProcess() {
return await this.hasElement(this.selectors.contactInfo);
}
}
/**
* Find Trainer Page Object (Public Directory)
*/
class FindTrainerPage extends BasePage {
constructor() {
super();
this.url = '/find-trainer/';
this.selectors = {
searchForm: '.trainer-search, .directory-search, form[role="search"]',
searchField: 'input[type="search"], input[name="search"], .search-input',
searchButton: 'button[type="submit"], input[type="submit"], .search-submit',
trainerCards: '.trainer-card, .trainer-item, .trainer-listing, article.trainer',
trainerName: '.trainer-name, .trainer-title, h3, h2',
trainerLocation: '.trainer-location, .location',
trainerSpecialty: '.trainer-specialty, .specialties',
contactButton: '.contact-trainer, .trainer-contact',
filterOptions: '.filter-options, .directory-filters',
locationFilter: 'select[name="location"], input[name="location"]',
specialtyFilter: 'select[name="specialty"], input[name="specialty"]',
sortOptions: '.sort-options, select[name="sort"]',
resultsCount: '.results-count, .trainer-count',
noResultsMessage: '.no-results, .no-trainers-found',
paginationLinks: '.pagination, .page-numbers'
};
}
async waitForPageReady() {
await this.waitForElement('body', 10000);
// Wait for dynamic content to load
await this.page.waitForTimeout(2000);
}
async searchTrainers(searchTerm) {
if (await this.hasElement(this.selectors.searchField)) {
await this.fillField(this.selectors.searchField, searchTerm);
if (await this.hasElement(this.selectors.searchButton)) {
await this.clickElement(this.selectors.searchButton);
} else {
await this.page.press(this.selectors.searchField, 'Enter');
}
// Wait for search results
await this.page.waitForTimeout(3000);
}
}
async getTrainerCount() {
return await this.page.locator(this.selectors.trainerCards).count();
}
async getTrainerDetails(index = 0) {
const trainerCard = this.page.locator(this.selectors.trainerCards).nth(index);
if (!(await trainerCard.isVisible())) {
return null;
}
const details = {};
try {
details.name = await trainerCard.locator(this.selectors.trainerName).textContent();
} catch (error) {
details.name = 'Unknown';
}
try {
details.location = await trainerCard.locator(this.selectors.trainerLocation).textContent();
} catch (error) {
details.location = '';
}
try {
details.specialty = await trainerCard.locator(this.selectors.trainerSpecialty).textContent();
} catch (error) {
details.specialty = '';
}
return details;
}
async filterByLocation(location) {
if (await this.hasElement(this.selectors.locationFilter)) {
await this.selectOption(this.selectors.locationFilter, location);
await this.page.waitForTimeout(2000);
}
}
async filterBySpecialty(specialty) {
if (await this.hasElement(this.selectors.specialtyFilter)) {
await this.selectOption(this.selectors.specialtyFilter, specialty);
await this.page.waitForTimeout(2000);
}
}
async sortResults(sortOption) {
if (await this.hasElement(this.selectors.sortOptions)) {
await this.selectOption(this.selectors.sortOptions, sortOption);
await this.page.waitForTimeout(2000);
}
}
async hasNoResults() {
return await this.hasElement(this.selectors.noResultsMessage);
}
async getResultsCount() {
try {
const countText = await this.getElementText(this.selectors.resultsCount);
const match = countText.match(/\d+/);
return match ? parseInt(match[0]) : 0;
} catch (error) {
return await this.getTrainerCount();
}
}
}
/**
* Documentation Page Object
*/
class DocumentationPage extends BasePage {
constructor() {
super();
this.url = '/documentation/';
this.selectors = {
docNavigation: '.doc-nav, .documentation-nav, .help-nav, nav',
searchField: '.doc-search, .help-search, input[type="search"]',
searchButton: '.search-submit, button[type="submit"]',
articleLinks: '.doc-link, .help-link, .article-link, a[href*="help"], a[href*="doc"]',
articleTitle: '.doc-title, .help-title, .article-title, h1, h2',
articleContent: '.doc-content, .help-content, .article-content, .content',
breadcrumbs: '.breadcrumbs, .breadcrumb',
categoryLinks: '.category-link, .doc-category',
tableOfContents: '.toc, .table-of-contents',
relatedArticles: '.related-articles, .see-also',
backToTop: '.back-to-top, a[href="#top"]',
printButton: '.print-doc, .print-page',
lastUpdated: '.last-updated, .updated-date'
};
}
async waitForPageReady() {
await this.waitForElement('body', 10000);
await this.page.waitForTimeout(1000);
}
async searchDocumentation(searchTerm) {
if (await this.hasElement(this.selectors.searchField)) {
await this.fillField(this.selectors.searchField, searchTerm);
if (await this.hasElement(this.selectors.searchButton)) {
await this.clickElement(this.selectors.searchButton);
} else {
await this.page.press(this.selectors.searchField, 'Enter');
}
await this.page.waitForTimeout(3000);
}
}
async getArticleLinks() {
const links = await this.page.locator(this.selectors.articleLinks).all();
const linkData = [];
for (const link of links) {
const text = await link.textContent();
const href = await link.getAttribute('href');
if (text && href) {
linkData.push({ text: text.trim(), href });
}
}
return linkData;
}
async navigateToArticle(linkText) {
const link = this.page.locator(this.selectors.articleLinks).filter({ hasText: linkText });
if (await link.isVisible()) {
await link.click();
await this.page.waitForLoadState('networkidle');
}
}
async hasTableOfContents() {
return await this.hasElement(this.selectors.tableOfContents);
}
async getArticleTitle() {
try {
return await this.getElementText(this.selectors.articleTitle);
} catch (error) {
return await this.getPageTitle();
}
}
async hasSearchFunctionality() {
return await this.hasElement(this.selectors.searchField);
}
async getLastUpdatedDate() {
try {
return await this.getElementText(this.selectors.lastUpdated);
} catch (error) {
return null;
}
}
async hasRelatedArticles() {
return await this.hasElement(this.selectors.relatedArticles);
}
}
module.exports = {
TrainingLoginPage,
TrainerRegistrationPage,
RegistrationPendingPage,
AccountPendingPage,
AccountDisabledPage,
FindTrainerPage,
DocumentationPage
};