- Updated LoginPage selectors to use more robust input[name] selectors instead of id-based - Added better error handling and message detection - Enhanced form waiting for better reliability - Created debug test scripts to verify selector functionality - Fixed isLoginFormVisible reference in trainer journey test 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
142 lines
No EOL
4.5 KiB
TypeScript
142 lines
No EOL
4.5 KiB
TypeScript
import { Page, expect } from '@playwright/test';
|
|
import { BasePage } from './BasePage';
|
|
import { PATHS } from '../config/staging-config';
|
|
|
|
/**
|
|
* Page object representing the login page
|
|
*/
|
|
export class LoginPage extends BasePage {
|
|
// Login form elements based on debug analysis
|
|
private readonly usernameInput = 'input[name="log"]';
|
|
private readonly passwordInput = 'input[name="pwd"]';
|
|
private readonly loginButton = 'input[type="submit"]';
|
|
private readonly rememberMeCheckbox = 'input[name="rememberme"]';
|
|
private readonly loginError = '.login-error, .login_error, #login_error, .notice-error, .woocommerce-error, .wp-die-message';
|
|
private readonly forgotPasswordLink = 'a:text("Lost your password?")';
|
|
private readonly loginForm = 'form#hvac_community_loginform';
|
|
|
|
constructor(page: Page) {
|
|
super(page);
|
|
}
|
|
|
|
/**
|
|
* Navigate to the login page
|
|
*/
|
|
async navigate(): Promise<void> {
|
|
this.log('Navigating to login page');
|
|
await this.page.goto(PATHS.login);
|
|
await this.page.waitForLoadState('networkidle');
|
|
|
|
// Make sure the form is visible before proceeding
|
|
await this.page.waitForSelector(this.loginForm, { timeout: 10000 });
|
|
}
|
|
|
|
/**
|
|
* Alternative name for navigate for backward compatibility
|
|
*/
|
|
async navigateToLogin(): Promise<void> {
|
|
await this.navigate();
|
|
}
|
|
|
|
/**
|
|
* Login with provided credentials
|
|
* @param username Username or email
|
|
* @param password Password
|
|
*/
|
|
async login(username: string, password: string): Promise<void> {
|
|
this.log(`Logging in as ${username}`);
|
|
|
|
// Wait for form elements to be ready
|
|
await this.page.waitForSelector(this.usernameInput, { state: 'visible', timeout: 10000 });
|
|
await this.page.waitForSelector(this.passwordInput, { state: 'visible', timeout: 5000 });
|
|
await this.page.waitForSelector(this.loginButton, { state: 'visible', timeout: 5000 });
|
|
|
|
// Fill in the credentials
|
|
await this.page.fill(this.usernameInput, username);
|
|
await this.page.fill(this.passwordInput, password);
|
|
|
|
// Click login and wait for navigation
|
|
await this.page.click(this.loginButton);
|
|
await this.page.waitForLoadState('networkidle');
|
|
|
|
this.log('Login form submitted');
|
|
}
|
|
|
|
/**
|
|
* Check if we're logged in
|
|
*/
|
|
async isLoggedIn(): Promise<boolean> {
|
|
await this.page.waitForLoadState('networkidle');
|
|
const url = await this.getUrl();
|
|
return url.includes('hvac-dashboard');
|
|
}
|
|
|
|
/**
|
|
* Check if username field is visible
|
|
*/
|
|
async isUsernameFieldVisible(): Promise<boolean> {
|
|
try {
|
|
await this.page.waitForSelector(this.usernameInput, { state: 'visible', timeout: 5000 });
|
|
return true;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get error message if login failed
|
|
*/
|
|
async getErrorMessage(): Promise<string | null> {
|
|
// Check all possible error selectors
|
|
const errorSelectors = this.loginError.split(', ');
|
|
|
|
for (const selector of errorSelectors) {
|
|
if (await this.page.isVisible(selector)) {
|
|
return await this.page.textContent(selector);
|
|
}
|
|
}
|
|
|
|
// Check for any text containing common error messages
|
|
const pageContent = await this.page.content();
|
|
if (pageContent.includes('Invalid username') ||
|
|
pageContent.includes('incorrect password') ||
|
|
pageContent.includes('Unknown username') ||
|
|
pageContent.includes('Error:')) {
|
|
|
|
// Try to find error message in the page content
|
|
const errorText = await this.page.evaluate(() => {
|
|
const errorElements = Array.from(document.querySelectorAll('p, div, span'))
|
|
.filter(el => el.textContent &&
|
|
(el.textContent.includes('Invalid') ||
|
|
el.textContent.includes('Error') ||
|
|
el.textContent.includes('incorrect') ||
|
|
el.textContent.includes('Unknown')));
|
|
|
|
return errorElements.length > 0 ? errorElements[0].textContent : null;
|
|
});
|
|
|
|
return errorText;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Click on "forgot password" link
|
|
*/
|
|
async clickForgotPassword(): Promise<void> {
|
|
await this.page.click(this.forgotPasswordLink);
|
|
await this.page.waitForLoadState('networkidle');
|
|
}
|
|
|
|
/**
|
|
* Toggle "remember me" checkbox
|
|
* @param check If true, check the box; if false, uncheck it
|
|
*/
|
|
async setRememberMe(check: boolean): Promise<void> {
|
|
const isChecked = await this.page.isChecked(this.rememberMeCheckbox);
|
|
if (check !== isChecked) {
|
|
await this.page.click(this.rememberMeCheckbox);
|
|
}
|
|
}
|
|
} |