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>
431 lines
No EOL
12 KiB
JavaScript
431 lines
No EOL
12 KiB
JavaScript
/**
|
||
* Authentication Manager - Centralized authentication handling
|
||
* Manages all user login/logout scenarios and role-based authentication
|
||
*/
|
||
|
||
const { getBrowserManager } = require('../browser/BrowserManager');
|
||
const BasePage = require('../base/BasePage');
|
||
|
||
class AuthManager {
|
||
constructor() {
|
||
this.currentUser = null;
|
||
this.isLoggedIn = false;
|
||
this.browserManager = null;
|
||
this.loginPage = null;
|
||
this.config = null;
|
||
}
|
||
|
||
/**
|
||
* Initialize authentication manager
|
||
* @param {Object} config - Configuration object
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async initialize(config = {}) {
|
||
this.config = {
|
||
baseUrl: 'https://upskill-staging.measurequick.com',
|
||
loginPath: '/community-login/',
|
||
logoutPath: '/wp-login.php?action=logout',
|
||
...config
|
||
};
|
||
|
||
this.browserManager = getBrowserManager();
|
||
this.loginPage = new LoginPage();
|
||
}
|
||
|
||
/**
|
||
* Login with user credentials
|
||
* @param {Object} user - User credentials
|
||
* @param {Object} options - Login options
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async login(user, options = {}) {
|
||
const loginOptions = {
|
||
verifySuccess: true,
|
||
takeScreenshot: false,
|
||
...options
|
||
};
|
||
|
||
console.log(`🔐 Logging in as ${user.username} (${user.role || 'unknown role'})`);
|
||
|
||
try {
|
||
// Navigate to login page
|
||
await this.loginPage.navigate();
|
||
|
||
// Fill login form
|
||
await this.loginPage.fillLoginForm(user.username, user.password);
|
||
|
||
// Submit form
|
||
await this.loginPage.submitLogin();
|
||
|
||
// Wait for redirect and verify login success
|
||
if (loginOptions.verifySuccess) {
|
||
await this.verifyLoginSuccess(user);
|
||
}
|
||
|
||
this.currentUser = user;
|
||
this.isLoggedIn = true;
|
||
|
||
if (loginOptions.takeScreenshot) {
|
||
await this.browserManager.takeScreenshot(`login-success-${user.username}.png`);
|
||
}
|
||
|
||
console.log(` ✅ Successfully logged in as ${user.username}`);
|
||
|
||
} catch (error) {
|
||
console.error(` ❌ Login failed for ${user.username}: ${error.message}`);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Logout current user
|
||
* @param {Object} options - Logout options
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async logout(options = {}) {
|
||
const logoutOptions = {
|
||
verifySuccess: true,
|
||
...options
|
||
};
|
||
|
||
if (!this.isLoggedIn) {
|
||
console.log(' ℹ️ No user logged in');
|
||
return;
|
||
}
|
||
|
||
console.log(`🔓 Logging out ${this.currentUser?.username || 'current user'}`);
|
||
|
||
try {
|
||
const page = this.browserManager.getCurrentPage();
|
||
|
||
// Try to find and click logout link
|
||
const logoutSelectors = [
|
||
'a[href*="wp-login.php?action=logout"]',
|
||
'a[href*="logout"]',
|
||
'.logout-link',
|
||
'#wp-admin-bar-logout a'
|
||
];
|
||
|
||
let loggedOut = false;
|
||
for (const selector of logoutSelectors) {
|
||
try {
|
||
await page.waitForSelector(selector, { timeout: 5000 });
|
||
await page.click(selector);
|
||
loggedOut = true;
|
||
break;
|
||
} catch (error) {
|
||
// Try next selector
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Fallback: navigate to logout URL directly
|
||
if (!loggedOut) {
|
||
const logoutUrl = this.config.baseUrl + this.config.logoutPath;
|
||
await page.goto(logoutUrl);
|
||
}
|
||
|
||
// Wait for logout confirmation
|
||
if (logoutOptions.verifySuccess) {
|
||
await this.verifyLogoutSuccess();
|
||
}
|
||
|
||
this.currentUser = null;
|
||
this.isLoggedIn = false;
|
||
|
||
console.log(' ✅ Successfully logged out');
|
||
|
||
} catch (error) {
|
||
console.error(` ❌ Logout failed: ${error.message}`);
|
||
this.currentUser = null;
|
||
this.isLoggedIn = false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Quick login for master trainer role
|
||
* @param {Object} options - Login options
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async loginAsMasterTrainer(options = {}) {
|
||
const masterUser = this.getMasterTrainerAccount();
|
||
await this.login(masterUser, options);
|
||
}
|
||
|
||
/**
|
||
* Quick login for regular trainer role
|
||
* @param {Object} options - Login options
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async loginAsTrainer(options = {}) {
|
||
const trainerUser = this.getTrainerAccount();
|
||
await this.login(trainerUser, options);
|
||
}
|
||
|
||
/**
|
||
* Verify that login was successful
|
||
* @param {Object} user - User that attempted login
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async verifyLoginSuccess(user) {
|
||
const page = this.browserManager.getCurrentPage();
|
||
|
||
// Wait for redirect away from login page
|
||
try {
|
||
await page.waitForURL(url => !url.includes('community-login'), { timeout: 15000 });
|
||
} catch (error) {
|
||
throw new Error('Login failed - still on login page');
|
||
}
|
||
|
||
// Look for logged-in indicators
|
||
const loggedInSelectors = [
|
||
'body.logged-in',
|
||
'#wpadminbar',
|
||
'.dashboard-navigation',
|
||
'[data-user-role]'
|
||
];
|
||
|
||
let loginConfirmed = false;
|
||
for (const selector of loggedInSelectors) {
|
||
try {
|
||
await page.waitForSelector(selector, { timeout: 10000 });
|
||
loginConfirmed = true;
|
||
break;
|
||
} catch (error) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (!loginConfirmed) {
|
||
throw new Error('Login success could not be verified');
|
||
}
|
||
|
||
// Verify user role if specified
|
||
if (user.role) {
|
||
await this.verifyUserRole(user.role);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Verify that logout was successful
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async verifyLogoutSuccess() {
|
||
const page = this.browserManager.getCurrentPage();
|
||
|
||
try {
|
||
// Wait for logout indicators
|
||
await Promise.race([
|
||
page.waitForURL(url => url.includes('community-login')),
|
||
page.waitForURL(url => url.includes('wp-login')),
|
||
page.waitForSelector('body:not(.logged-in)', { timeout: 10000 })
|
||
]);
|
||
} catch (error) {
|
||
throw new Error('Logout success could not be verified');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Verify user has expected role
|
||
* @param {string} expectedRole - Expected user role
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async verifyUserRole(expectedRole) {
|
||
const page = this.browserManager.getCurrentPage();
|
||
|
||
try {
|
||
// Check for role-specific elements or attributes
|
||
const roleSelectors = {
|
||
'master_trainer': [
|
||
'[data-user-role*="master"]',
|
||
'.master-trainer-dashboard',
|
||
'a[href*="master-trainer"]'
|
||
],
|
||
'hvac_trainer': [
|
||
'[data-user-role*="trainer"]',
|
||
'.trainer-dashboard',
|
||
'a[href*="trainer/dashboard"]'
|
||
]
|
||
};
|
||
|
||
const selectors = roleSelectors[expectedRole] || [];
|
||
let roleConfirmed = false;
|
||
|
||
for (const selector of selectors) {
|
||
try {
|
||
await page.waitForSelector(selector, { timeout: 5000 });
|
||
roleConfirmed = true;
|
||
break;
|
||
} catch (error) {
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (!roleConfirmed) {
|
||
console.warn(`Could not verify user role: ${expectedRole}`);
|
||
}
|
||
} catch (error) {
|
||
console.warn(`Role verification failed: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Check if user is currently logged in
|
||
* @returns {Promise<boolean>}
|
||
*/
|
||
async isUserLoggedIn() {
|
||
try {
|
||
const page = this.browserManager.getCurrentPage();
|
||
const loggedIn = await page.$('body.logged-in, #wpadminbar') !== null;
|
||
this.isLoggedIn = loggedIn;
|
||
return loggedIn;
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get current user information
|
||
* @returns {Object|null}
|
||
*/
|
||
getCurrentUser() {
|
||
return this.currentUser;
|
||
}
|
||
|
||
/**
|
||
* Get master trainer account credentials
|
||
* @returns {Object}
|
||
*/
|
||
getMasterTrainerAccount() {
|
||
return {
|
||
username: 'test_master',
|
||
password: 'TestMaster123!',
|
||
email: 'test_master@example.com',
|
||
role: 'master_trainer'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Get alternative master trainer account
|
||
* @returns {Object}
|
||
*/
|
||
getAlternateMasterTrainerAccount() {
|
||
return {
|
||
username: 'JoeMedosch@gmail.com',
|
||
password: 'JoeTrainer2025@',
|
||
email: 'JoeMedosch@gmail.com',
|
||
role: 'master_trainer'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Get regular trainer account credentials
|
||
* @returns {Object}
|
||
*/
|
||
getTrainerAccount() {
|
||
return {
|
||
username: 'test_trainer',
|
||
password: 'TestTrainer123!',
|
||
email: 'test_trainer@example.com',
|
||
role: 'hvac_trainer'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Clean up authentication state
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async cleanup() {
|
||
if (this.isLoggedIn) {
|
||
await this.logout({ verifySuccess: false });
|
||
}
|
||
this.currentUser = null;
|
||
this.isLoggedIn = false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Login Page Object - Handles login page interactions
|
||
*/
|
||
class LoginPage extends BasePage {
|
||
constructor() {
|
||
super();
|
||
this.url = '';
|
||
this.selectors = {
|
||
usernameField: '#user_login, [name="log"], input[type="text"]',
|
||
passwordField: '#user_pass, [name="pwd"], input[type="password"]',
|
||
loginButton: '#wp-submit, [name="wp-submit"], input[type="submit"], button[type="submit"]',
|
||
loginForm: '#loginform, .login-form, form[name="loginform"]',
|
||
errorMessage: '.login_error, .error, .notice-error'
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Set login page URL based on environment
|
||
* @param {string} baseUrl - Base URL
|
||
* @param {string} loginPath - Login path
|
||
*/
|
||
setLoginUrl(baseUrl, loginPath) {
|
||
this.url = baseUrl + loginPath;
|
||
}
|
||
|
||
/**
|
||
* Fill login form with credentials
|
||
* @param {string} username - Username
|
||
* @param {string} password - Password
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async fillLoginForm(username, password) {
|
||
await this.waitForElement(this.selectors.usernameField);
|
||
await this.fillField(this.selectors.usernameField, username);
|
||
await this.fillField(this.selectors.passwordField, password);
|
||
}
|
||
|
||
/**
|
||
* Submit login form
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async submitLogin() {
|
||
await this.clickElement(this.selectors.loginButton);
|
||
}
|
||
|
||
/**
|
||
* Check for login errors
|
||
* @returns {Promise<string|null>}
|
||
*/
|
||
async getLoginError() {
|
||
try {
|
||
const errorElement = await this.page.$(this.selectors.errorMessage);
|
||
if (errorElement) {
|
||
return await errorElement.textContent();
|
||
}
|
||
} catch (error) {
|
||
// No error message found
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Set base URL for navigation
|
||
* @param {string} baseUrl - Base URL
|
||
*/
|
||
setBaseUrl(baseUrl) {
|
||
this.url = baseUrl + '/community-login/';
|
||
}
|
||
}
|
||
|
||
// Singleton instance
|
||
let authManager = null;
|
||
|
||
/**
|
||
* Get singleton AuthManager instance
|
||
* @returns {AuthManager}
|
||
*/
|
||
function getAuthManager() {
|
||
if (!authManager) {
|
||
authManager = new AuthManager();
|
||
}
|
||
return authManager;
|
||
}
|
||
|
||
module.exports = { AuthManager, getAuthManager, LoginPage }; |