#!/usr/bin/env node /** * Comprehensive E2E Tests for Authentication & Public Access (Agent E) * * Coverage: Authentication flows and public access (8+ pages) * - āœ… training-login/ (already validated) * - trainer-registration/ - New trainer registration * - registration-pending/ - Pending registration status * - trainer-account-pending/ - Account approval workflows * - trainer-account-disabled/ - Disabled account handling * - find-trainer/ - Public trainer directory * - documentation/ - Public help system * - Public page access validation and error handling * * @package HVAC_Community_Events * @version 2.0.0 * @agent Agent E * @created 2025-08-27 */ const BaseTest = require('./tests/framework/base/BaseTest'); const { getBrowserManager } = require('./tests/framework/browser/BrowserManager'); const { getAuthManager } = require('./tests/framework/authentication/AuthManager'); const BasePage = require('./tests/framework/base/BasePage'); class AuthPublicE2ETest extends BaseTest { constructor() { super('Authentication-Public-Access-E2E'); this.baseUrl = process.env.BASE_URL || 'https://upskill-staging.measurequick.com'; // Test accounts from instructions this.testAccounts = { trainer: { username: 'test_trainer', password: 'TestTrainer123!', email: 'test_trainer@example.com', role: 'hvac_trainer' }, master: { username: 'test_master', password: 'TestMaster123!', email: 'test_master@example.com', role: 'master_trainer' }, // Test data for registration scenarios newTrainer: { username: 'new_test_trainer_' + Date.now(), email: 'new_trainer_' + Date.now() + '@example.com', password: 'NewTrainer2025!', firstName: 'Test', lastName: 'Trainer' } }; } /** * Main test execution */ async run() { try { console.log('šŸš€ Starting Authentication & Public Access E2E Tests'); console.log(`šŸ“ Testing against: ${this.baseUrl}`); // Initialize test framework await this.setUp({ headless: process.env.HEADLESS !== 'false', baseUrl: this.baseUrl, timeout: 60000, viewport: { width: 1920, height: 1080 } }); // Run comprehensive authentication and public access tests await this.runTestStep('WordPress Error Check', () => this.checkWordPressErrors()); await this.runTestStep('Test Training Login Page', () => this.testTrainingLoginPage()); await this.runTestStep('Test Trainer Registration Flow', () => this.testTrainerRegistrationFlow()); await this.runTestStep('Test Registration Pending Page', () => this.testRegistrationPendingPage()); await this.runTestStep('Test Account Pending Workflow', () => this.testAccountPendingWorkflow()); await this.runTestStep('Test Account Disabled Handling', () => this.testAccountDisabledHandling()); await this.runTestStep('Test Public Trainer Directory', () => this.testPublicTrainerDirectory()); await this.runTestStep('Test Public Documentation System', () => this.testPublicDocumentationSystem()); await this.runTestStep('Test Authentication Security Boundaries', () => this.testAuthenticationSecurityBoundaries()); await this.runTestStep('Test Password Reset Workflow', () => this.testPasswordResetWorkflow()); await this.runTestStep('Test Account Status Transitions', () => this.testAccountStatusTransitions()); await this.runTestStep('Test Public Access Error Handling', () => this.testPublicAccessErrorHandling()); console.log('\nšŸŽ‰ Authentication & Public Access E2E Tests Completed Successfully!'); this.printTestSummary(); } catch (error) { console.error('\nāŒ Test execution failed:', error.message); if (error.stack) { console.error('Stack trace:', error.stack); } throw error; } finally { await this.tearDown(); } } /** * Check for WordPress errors before starting tests */ async checkWordPressErrors() { const page = this.browserManager.getCurrentPage(); await page.goto(`${this.baseUrl}/trainer/dashboard/`); await page.waitForLoadState('networkidle'); // Check for PHP errors const content = await page.content(); if (content.includes('Fatal error') || content.includes('Parse error') || content.includes('Warning:')) { throw new Error('WordPress PHP errors detected on page'); } // Check for database errors if (content.includes('Error establishing a database connection')) { throw new Error('WordPress database connection error detected'); } console.log('āœ… No WordPress errors detected'); } /** * Test training login page functionality */ async testTrainingLoginPage() { const page = this.browserManager.getCurrentPage(); // Navigate to training login page await page.goto(`${this.baseUrl}/training-login/`); await page.waitForLoadState('networkidle'); // Verify page loads correctly await this.assertElementExists('.login-form, #loginform, form[name="loginform"]', 'Login form should be present'); // Check for required form elements await this.assertElementExists('input[name="log"], #user_login', 'Username field should be present'); await this.assertElementExists('input[name="pwd"], #user_pass', 'Password field should be present'); await this.assertElementExists('input[type="submit"], #wp-submit', 'Submit button should be present'); // Test successful login with valid credentials await page.fill('input[name="log"], #user_login', this.testAccounts.trainer.username); await page.fill('input[name="pwd"], #user_pass', this.testAccounts.trainer.password); await page.click('input[type="submit"], #wp-submit'); // Wait for redirect after successful login await page.waitForURL(url => !url.includes('training-login'), { timeout: 15000 }); // Verify we're logged in (check for trainer dashboard or logged-in indicators) const loggedIn = await page.locator('body.logged-in, #wpadminbar, .trainer-dashboard').isVisible() .catch(() => false); this.assertTrue(loggedIn, 'Should be redirected to authenticated area after login'); // Logout for next tests await this.authManager.logout(); console.log('āœ… Training login page functionality verified'); } /** * Test trainer registration flow */ async testTrainerRegistrationFlow() { const page = this.browserManager.getCurrentPage(); // Navigate to trainer registration page await page.goto(`${this.baseUrl}/trainer-registration/`); await page.waitForLoadState('networkidle'); // Check if registration form exists const hasRegistrationForm = await page.locator('form, .registration-form').isVisible() .catch(() => false); if (hasRegistrationForm) { // Test registration form elements const formFields = [ 'input[name="user_login"], input[name="username"], #user_login', 'input[name="user_email"], input[name="email"], #user_email', 'input[name="user_password"], input[name="password"], #user_pass', 'input[type="submit"], button[type="submit"]' ]; for (const fieldSelector of formFields) { const fieldExists = await page.locator(fieldSelector).isVisible().catch(() => false); if (fieldExists) { console.log(`āœ“ Found registration field: ${fieldSelector}`); break; } } // Test form validation const submitButton = await page.locator('input[type="submit"], button[type="submit"]').first(); if (await submitButton.isVisible()) { await submitButton.click(); // Check for validation messages await page.waitForTimeout(2000); const hasValidation = await page.locator('.error, .notice-error, .required').isVisible() .catch(() => false); console.log(`āœ“ Form validation ${hasValidation ? 'working' : 'not detected'}`); } // Test with valid data (but don't complete to avoid duplicate accounts) await page.fill('input[name="user_login"], input[name="username"], #user_login', this.testAccounts.newTrainer.username); await page.fill('input[name="user_email"], input[name="email"], #user_email', this.testAccounts.newTrainer.email); // Check if password field exists and fill it const passwordField = await page.locator('input[name="user_password"], input[name="password"], #user_pass').first(); if (await passwordField.isVisible()) { await passwordField.fill(this.testAccounts.newTrainer.password); } console.log('āœ… Trainer registration form validation completed'); } else { // Registration might be disabled or require special access const pageContent = await page.textContent('body'); if (pageContent.includes('registration') || pageContent.includes('sign up')) { console.log('āœ… Registration page exists but form is not currently available'); } else { console.log('āš ļø Registration page may not be available or configured differently'); } } } /** * Test registration pending page */ async testRegistrationPendingPage() { const page = this.browserManager.getCurrentPage(); // Navigate to registration pending page await page.goto(`${this.baseUrl}/registration-pending/`); await page.waitForLoadState('networkidle'); // Check for pending registration content const pendingContent = await page.textContent('body'); const hasPendingContent = pendingContent.includes('pending') || pendingContent.includes('approval') || pendingContent.includes('review') || pendingContent.includes('waiting'); if (hasPendingContent) { console.log('āœ… Registration pending page contains appropriate messaging'); // Check for common pending page elements const elements = [ '.pending-message', '.approval-notice', '.registration-status', 'p, div, .content' ]; let foundElement = false; for (const selector of elements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { foundElement = true; console.log(`āœ“ Found pending content element: ${selector}`); break; } } this.assertTrue(foundElement, 'Should have pending registration content elements'); } else { // Check if page is restricted or redirects const currentUrl = page.url(); if (currentUrl.includes('login') || currentUrl.includes('access-denied')) { console.log('āœ… Registration pending page is properly restricted'); } else { console.log('āš ļø Registration pending page may not be configured or accessible'); } } } /** * Test account pending workflow */ async testAccountPendingWorkflow() { const page = this.browserManager.getCurrentPage(); // Navigate to account pending page await page.goto(`${this.baseUrl}/trainer-account-pending/`); await page.waitForLoadState('networkidle'); // Check for account pending specific content const content = await page.textContent('body'); const hasAccountPendingContent = content.includes('account') && (content.includes('pending') || content.includes('approval') || content.includes('administrator')); if (hasAccountPendingContent) { console.log('āœ… Account pending page has appropriate content'); // Look for status information const statusElements = [ '.account-status', '.pending-approval', '.admin-review', '[data-status]' ]; for (const selector of statusElements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { console.log(`āœ“ Found account status element: ${selector}`); } } // Check for contact information or next steps const hasContactInfo = content.includes('contact') || content.includes('email') || content.includes('administrator'); console.log(`āœ“ Contact information ${hasContactInfo ? 'available' : 'not found'}`); } else { console.log('āš ļø Account pending page may be restricted or configured differently'); } // Test with authenticated user to see if message changes await this.authManager.loginAsTrainer(); await page.goto(`${this.baseUrl}/trainer-account-pending/`); await page.waitForLoadState('networkidle'); const authenticatedContent = await page.textContent('body'); const isDifferent = authenticatedContent !== content; console.log(`āœ“ Page content ${isDifferent ? 'changes' : 'remains same'} when authenticated`); await this.authManager.logout(); } /** * Test account disabled handling */ async testAccountDisabledHandling() { const page = this.browserManager.getCurrentPage(); // Navigate to account disabled page await page.goto(`${this.baseUrl}/trainer-account-disabled/`); await page.waitForLoadState('networkidle'); // Check for disabled account messaging const content = await page.textContent('body'); const hasDisabledContent = content.includes('disabled') || content.includes('suspended') || content.includes('deactivated') || content.includes('inactive'); if (hasDisabledContent) { console.log('āœ… Account disabled page has appropriate messaging'); // Look for reactivation or contact information const hasReactivationInfo = content.includes('reactivate') || content.includes('restore') || content.includes('contact') || content.includes('administrator'); console.log(`āœ“ Reactivation information ${hasReactivationInfo ? 'available' : 'not found'}`); // Check for security messaging const hasSecurityInfo = content.includes('security') || content.includes('violation') || content.includes('terms'); console.log(`āœ“ Security information ${hasSecurityInfo ? 'present' : 'not found'}`); } else { // Check if page redirects to login or shows generic message const currentUrl = page.url(); if (currentUrl.includes('login')) { console.log('āœ… Account disabled page redirects to login'); } else { console.log('āš ļø Account disabled page may not be configured'); } } } /** * Test public trainer directory */ async testPublicTrainerDirectory() { const page = this.browserManager.getCurrentPage(); // Navigate to public trainer directory await page.goto(`${this.baseUrl}/find-trainer/`); await page.waitForLoadState('networkidle'); // Check for trainer directory content const content = await page.textContent('body'); const hasDirectoryContent = content.includes('trainer') || content.includes('directory') || content.includes('find') || content.includes('search'); if (hasDirectoryContent) { console.log('āœ… Find trainer page has directory-related content'); // Look for search functionality const searchElements = [ 'input[type="search"]', 'input[name="search"]', '.search-field', '[placeholder*="search"]' ]; let hasSearch = false; for (const selector of searchElements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { hasSearch = true; console.log(`āœ“ Found search element: ${selector}`); // Test search functionality await page.fill(selector, 'test'); await page.press(selector, 'Enter'); await page.waitForTimeout(2000); break; } } // Look for trainer listings const listingElements = [ '.trainer-card', '.trainer-item', '.trainer-listing', 'article', '.post' ]; let hasListings = false; for (const selector of listingElements) { const count = await page.locator(selector).count(); if (count > 0) { hasListings = true; console.log(`āœ“ Found ${count} trainer listing elements: ${selector}`); break; } } // Look for filter or sort options const filterElements = [ 'select', '.filter', '.sort', '[name="category"]', '[name="location"]' ]; for (const selector of filterElements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { console.log(`āœ“ Found filter/sort element: ${selector}`); } } console.log(`āœ“ Public trainer directory - Search: ${hasSearch}, Listings: ${hasListings}`); } else { console.log('āš ļø Find trainer page may not be configured or may have different content structure'); } // Test public access (should work without authentication) const isPubliclyAccessible = !page.url().includes('login'); this.assertTrue(isPubliclyAccessible, 'Find trainer page should be publicly accessible'); } /** * Test public documentation system */ async testPublicDocumentationSystem() { const page = this.browserManager.getCurrentPage(); // Navigate to documentation page await page.goto(`${this.baseUrl}/documentation/`); await page.waitForLoadState('networkidle'); // Check for documentation content const content = await page.textContent('body'); const hasDocContent = content.includes('documentation') || content.includes('help') || content.includes('guide') || content.includes('how to') || content.includes('support'); if (hasDocContent) { console.log('āœ… Documentation page has help-related content'); // Look for navigation or table of contents const navElements = [ '.doc-nav', '.toc', '.documentation-menu', 'nav', '.sidebar' ]; let hasNavigation = false; for (const selector of navElements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { hasNavigation = true; console.log(`āœ“ Found documentation navigation: ${selector}`); break; } } // Look for help articles or sections const articleElements = [ 'article', '.help-article', '.doc-section', '.faq', 'h2, h3' ]; let articleCount = 0; for (const selector of articleElements) { const count = await page.locator(selector).count(); articleCount += count; } console.log(`āœ“ Found ${articleCount} potential help/documentation sections`); // Check for search functionality in documentation const docSearchElements = [ 'input[type="search"]', '.doc-search', '.help-search', '[placeholder*="search"]' ]; let hasDocSearch = false; for (const selector of docSearchElements) { const exists = await page.locator(selector).isVisible().catch(() => false); if (exists) { hasDocSearch = true; console.log(`āœ“ Found documentation search: ${selector}`); break; } } console.log(`āœ“ Documentation system - Navigation: ${hasNavigation}, Articles: ${articleCount > 0}, Search: ${hasDocSearch}`); } else { console.log('āš ļø Documentation page may not be configured or may redirect to other help resources'); } // Verify public accessibility const isPubliclyAccessible = !page.url().includes('login'); this.assertTrue(isPubliclyAccessible, 'Documentation page should be publicly accessible'); // Test a few common help topics if links exist const helpLinks = await page.locator('a[href*="help"], a[href*="guide"], a[href*="how-to"]').all(); if (helpLinks.length > 0) { console.log(`āœ“ Found ${helpLinks.length} potential help topic links`); // Test first help link const firstLink = helpLinks[0]; const href = await firstLink.getAttribute('href'); if (href) { await page.goto(href); await page.waitForLoadState('networkidle'); console.log(`āœ“ Successfully navigated to help topic: ${href}`); } } } /** * Test authentication security boundaries */ async testAuthenticationSecurityBoundaries() { const page = this.browserManager.getCurrentPage(); console.log('šŸ”’ Testing authentication security boundaries...'); // Test 1: Protected pages should redirect to login when not authenticated const protectedPages = [ '/trainer/dashboard/', '/trainer/profile/', '/trainer/events/', '/master-trainer/master-dashboard/', '/master-trainer/trainers/' ]; for (const protectedPage of protectedPages) { await page.goto(`${this.baseUrl}${protectedPage}`); await page.waitForLoadState('networkidle'); const currentUrl = page.url(); const isRedirectedToLogin = currentUrl.includes('training-login') || currentUrl.includes('wp-login') || currentUrl.includes('access-denied'); if (isRedirectedToLogin) { console.log(`āœ“ Protected page ${protectedPage} properly redirects to login`); } else { console.log(`āš ļø Protected page ${protectedPage} may not be properly secured (URL: ${currentUrl})`); } } // Test 2: Role-based access control await this.authManager.loginAsTrainer(); // Trainer should NOT access master trainer pages await page.goto(`${this.baseUrl}/master-trainer/trainers/`); await page.waitForLoadState('networkidle'); const trainerBlockedFromMaster = page.url().includes('access-denied') || page.url().includes('login') || !page.url().includes('master-trainer'); if (trainerBlockedFromMaster) { console.log('āœ“ Role-based access control: Trainer properly blocked from master trainer pages'); } else { console.log('āš ļø Role-based access control may need attention'); } await this.authManager.logout(); // Test 3: Session timeout behavior await this.authManager.loginAsTrainer(); // Navigate to trainer page and verify access await page.goto(`${this.baseUrl}/trainer/dashboard/`); await page.waitForLoadState('networkidle'); const hasTrainerAccess = !page.url().includes('login'); console.log(`āœ“ Trainer session: ${hasTrainerAccess ? 'Active' : 'Expired/Invalid'}`); await this.authManager.logout(); console.log('āœ… Authentication security boundary testing completed'); } /** * Test password reset workflow */ async testPasswordResetWorkflow() { const page = this.browserManager.getCurrentPage(); // Navigate to login page to find password reset link await page.goto(`${this.baseUrl}/training-login/`); await page.waitForLoadState('networkidle'); // Look for password reset link const resetLinks = [ 'a[href*="lostpassword"]', 'a[href*="forgot-password"]', 'a[href*="reset-password"]', 'a:has-text("Forgot password")', 'a:has-text("Lost password")', 'a:has-text("Reset password")' ]; let foundResetLink = false; for (const selector of resetLinks) { const resetLink = await page.locator(selector).first(); if (await resetLink.isVisible()) { foundResetLink = true; console.log(`āœ“ Found password reset link: ${selector}`); // Click the reset link await resetLink.click(); await page.waitForLoadState('networkidle'); // Check for reset form const hasResetForm = await page.locator('form, input[name="user_login"], input[name="user_email"]') .isVisible().catch(() => false); if (hasResetForm) { console.log('āœ“ Password reset form is accessible'); // Test form with test email (don't submit to avoid spam) const emailField = await page.locator('input[name="user_login"], input[name="user_email"]').first(); if (await emailField.isVisible()) { await emailField.fill(this.testAccounts.trainer.email); console.log('āœ“ Password reset form accepts email input'); } } else { console.log('āš ļø Password reset form not found after clicking reset link'); } break; } } if (!foundResetLink) { // Check if WordPress default lost password URL works await page.goto(`${this.baseUrl}/wp-login.php?action=lostpassword`); await page.waitForLoadState('networkidle'); const hasWpResetForm = await page.locator('#lostpasswordform, input[name="user_login"]') .isVisible().catch(() => false); if (hasWpResetForm) { console.log('āœ“ WordPress default password reset form is accessible'); } else { console.log('āš ļø No password reset functionality found'); } } console.log('āœ… Password reset workflow testing completed'); } /** * Test account status transitions */ async testAccountStatusTransitions() { const page = this.browserManager.getCurrentPage(); console.log('šŸ”„ Testing account status transitions...'); // Test various account states by navigating to different status pages const statusPages = [ { path: '/registration-pending/', status: 'pending' }, { path: '/trainer-account-pending/', status: 'account_pending' }, { path: '/trainer-account-disabled/', status: 'disabled' } ]; for (const statusPage of statusPages) { await page.goto(`${this.baseUrl}${statusPage.path}`); await page.waitForLoadState('networkidle'); const content = await page.textContent('body'); const currentUrl = page.url(); // Check if page has appropriate messaging for the status let hasAppropriateContent = false; switch (statusPage.status) { case 'pending': hasAppropriateContent = content.includes('pending') || content.includes('review') || content.includes('approval'); break; case 'account_pending': hasAppropriateContent = content.includes('account') && (content.includes('pending') || content.includes('approval')); break; case 'disabled': hasAppropriateContent = content.includes('disabled') || content.includes('suspended') || content.includes('deactivated'); break; } console.log(`āœ“ Status page ${statusPage.path}: ${hasAppropriateContent ? 'Has appropriate content' : 'May need content review'}`); // Check if authenticated users get different messages await this.authManager.loginAsTrainer(); await page.goto(`${this.baseUrl}${statusPage.path}`); await page.waitForLoadState('networkidle'); const authenticatedContent = await page.textContent('body'); const contentChanged = authenticatedContent !== content; console.log(`āœ“ Status page ${statusPage.path} with auth: ${contentChanged ? 'Different content' : 'Same content'}`); await this.authManager.logout(); } console.log('āœ… Account status transition testing completed'); } /** * Test public access error handling */ async testPublicAccessErrorHandling() { const page = this.browserManager.getCurrentPage(); console.log('šŸ”§ Testing public access error handling...'); // Test 1: Invalid/non-existent public pages const invalidPages = [ '/non-existent-page/', '/trainer/invalid-page/', '/master-trainer/non-existent/', '/documentation/invalid-doc/' ]; for (const invalidPage of invalidPages) { await page.goto(`${this.baseUrl}${invalidPage}`, { waitUntil: 'networkidle', timeout: 15000 }).catch(() => { // Handle navigation errors console.log(`āœ“ Navigation to ${invalidPage} properly handled error`); }); const currentUrl = page.url(); const is404 = currentUrl.includes('404') || await page.locator('h1:has-text("404"), h1:has-text("Not Found")').isVisible().catch(() => false); if (is404) { console.log(`āœ“ Invalid page ${invalidPage} shows proper 404 error`); } else { console.log(`āš ļø Invalid page ${invalidPage} may not have proper error handling`); } } // Test 2: Network error handling simulation // This would require more complex setup, so we'll test basic connectivity // Test 3: Form submission errors on public forms const publicFormsPages = ['/trainer-registration/', '/find-trainer/', '/documentation/']; for (const formPage of publicFormsPages) { await page.goto(`${this.baseUrl}${formPage}`); await page.waitForLoadState('networkidle'); // Look for forms and test basic validation const forms = await page.locator('form').all(); for (let i = 0; i < Math.min(forms.length, 2); i++) { // Test max 2 forms per page const form = forms[i]; const submitButton = await form.locator('input[type="submit"], button[type="submit"]').first(); if (await submitButton.isVisible()) { // Submit empty form to test validation await submitButton.click(); await page.waitForTimeout(2000); // Check for validation messages const hasValidation = await page.locator('.error, .notice-error, .invalid, .required').isVisible() .catch(() => false); console.log(`āœ“ Form ${i + 1} on ${formPage}: ${hasValidation ? 'Has validation' : 'No validation detected'}`); } } } // Test 4: JavaScript error handling await page.goto(`${this.baseUrl}/find-trainer/`); await page.waitForLoadState('networkidle'); // Check for JavaScript errors in console const jsErrors = []; page.on('console', msg => { if (msg.type() === 'error') { jsErrors.push(msg.text()); } }); // Trigger some interactions to check for JS errors const interactiveElements = await page.locator('button, input[type="submit"], a[href="#"]').all(); for (let i = 0; i < Math.min(interactiveElements.length, 3); i++) { try { await interactiveElements[i].click(); await page.waitForTimeout(1000); } catch (error) { // Click failed, which is fine for this test } } if (jsErrors.length > 0) { console.log(`āš ļø Found ${jsErrors.length} JavaScript errors: ${jsErrors.join(', ')}`); } else { console.log('āœ“ No JavaScript errors detected during interaction testing'); } console.log('āœ… Public access error handling testing completed'); } /** * Print test summary */ printTestSummary() { const summary = this.getTestSummary(); console.log('\nšŸ“Š Test Summary:'); console.log(`Total Steps: ${summary.total}`); console.log(`Passed: ${summary.passed}`); console.log(`Failed: ${summary.failed}`); console.log(`Success Rate: ${summary.successRate}%`); if (summary.failed > 0) { console.log('\nāŒ Failed Steps:'); this.testResults .filter(r => r.status === 'failed') .forEach(r => console.log(` - ${r.step}: ${r.error}`)); } console.log(`\nā±ļø Total Duration: ${(Date.now() - this.startTime)}ms`); // Agent E specific summary console.log('\nšŸŽÆ Agent E Authentication & Public Access Coverage:'); console.log(' āœ… Training Login Page'); console.log(' āœ… Trainer Registration Flow'); console.log(' āœ… Registration Pending Page'); console.log(' āœ… Account Pending Workflow'); console.log(' āœ… Account Disabled Handling'); console.log(' āœ… Public Trainer Directory'); console.log(' āœ… Public Documentation System'); console.log(' āœ… Authentication Security Boundaries'); console.log(' āœ… Password Reset Workflow'); console.log(' āœ… Account Status Transitions'); console.log(' āœ… Public Access Error Handling'); } } // Execute tests if run directly if (require.main === module) { const test = new AuthPublicE2ETest(); test.run() .then(() => { console.log('\nšŸŽ‰ All Authentication & Public Access tests completed successfully!'); process.exit(0); }) .catch(error => { console.error('\nšŸ’„ Test execution failed:', error.message); process.exit(1); }); } module.exports = AuthPublicE2ETest;