fix: Update login form selectors to match actual page structure

- 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>
This commit is contained in:
bengizmo 2025-05-21 20:21:16 -03:00
parent 7ac4d4b853
commit d1f1005a5a
4 changed files with 215 additions and 37 deletions

View file

@ -0,0 +1,113 @@
import { test, expect } from '@playwright/test';
import { STAGING_URL } from './config/staging-config';
test('Debug login page', async ({ page }) => {
console.log('Starting login page debug');
// Navigate to the login page
console.log('Navigating to login page...');
await page.goto(`${STAGING_URL}/community-login/`, { waitUntil: 'networkidle' });
console.log(`Current URL: ${page.url()}`);
// Take screenshot of the page
await page.screenshot({ path: 'screenshots/login-page.png' });
console.log('Screenshot saved to screenshots/login-page.png');
// Get page title
const title = await page.title();
console.log(`Page title: ${title}`);
// Check for redirects
if (!page.url().includes('community-login')) {
console.log(`⚠️ Page was redirected to: ${page.url()}`);
}
// Check for iframes
const iframes = await page.$$('iframe');
console.log(`Number of iframes: ${iframes.length}`);
// Dump HTML content
const html = await page.content();
console.log('First 500 chars of HTML:');
console.log(html.substring(0, 500));
// Look for common login form elements with various selectors
const selectors = [
'#user_login',
'#username',
'input[name="log"]',
'input[name="username"]',
'#user_pass',
'#password',
'input[name="pwd"]',
'input[name="password"]',
'#wp-submit',
'input[type="submit"]',
'button[type="submit"]'
];
console.log('\nChecking for form elements:');
for (const selector of selectors) {
const count = await page.$$eval(selector, (elements) => elements.length).catch(() => 0);
if (count > 0) {
console.log(`✅ Found ${count} elements matching selector: ${selector}`);
const element = await page.$(selector);
if (element) {
const tagName = await element.evaluate(el => el.tagName).catch(() => 'Unknown');
const type = await element.evaluate(el => el.getAttribute('type')).catch(() => 'Unknown');
const name = await element.evaluate(el => el.getAttribute('name')).catch(() => 'Unknown');
const id = await element.evaluate(el => el.getAttribute('id')).catch(() => 'Unknown');
console.log(` Tag: ${tagName}, Type: ${type}, Name: ${name}, ID: ${id}`);
}
} else {
console.log(`❌ No elements found for selector: ${selector}`);
}
}
// Check for error containers
const errorSelectors = [
'.login-error',
'#login_error',
'.login_error',
'.notice-error',
'.woocommerce-error',
'.wp-die-message'
];
console.log('\nChecking for error containers:');
for (const selector of errorSelectors) {
const count = await page.$$eval(selector, (elements) => elements.length).catch(() => 0);
console.log(`${selector}: ${count > 0 ? '✅ Found' : '❌ Not found'}`);
}
// Output all forms on the page
const forms = await page.$$('form');
console.log(`\nNumber of forms: ${forms.length}`);
for (let i = 0; i < forms.length; i++) {
const form = forms[i];
const action = await form.evaluate(f => f.getAttribute('action') || 'No action').catch(() => 'Unknown');
const method = await form.evaluate(f => f.getAttribute('method') || 'No method').catch(() => 'Unknown');
const id = await form.evaluate(f => f.getAttribute('id') || 'No id').catch(() => 'Unknown');
const className = await form.evaluate(f => f.getAttribute('class') || 'No class').catch(() => 'Unknown');
console.log(`\nForm #${i+1}:`);
console.log(` ID: ${id}`);
console.log(` Class: ${className}`);
console.log(` Action: ${action}`);
console.log(` Method: ${method}`);
// Get inputs in the form
const inputs = await form.$$('input');
console.log(` Inputs: ${inputs.length}`);
for (const input of inputs) {
const type = await input.evaluate(i => i.getAttribute('type') || 'No type').catch(() => 'Unknown');
const name = await input.evaluate(i => i.getAttribute('name') || 'No name').catch(() => 'Unknown');
const id = await input.evaluate(i => i.getAttribute('id') || 'No id').catch(() => 'Unknown');
console.log(` Input: Type=${type}, Name=${name}, ID=${id}`);
}
}
console.log('\nLogin page debug complete');
});

View file

@ -1,48 +1,63 @@
import { test, expect } from '@playwright/test'; import { test, expect } from '@playwright/test';
import { LoginPage } from './pages/LoginPage'; import { LoginPage } from './pages/LoginPage';
import { DashboardPage } from './pages/DashboardPage';
test.describe('Login Flow', () => { test.describe('Login Flow', () => {
test('should show error on login page', async ({ page }) => { test('should login with valid credentials', async ({ page }) => {
// Constants // Constants
const testTrainerUsername = 'test_trainer'; const testTrainerUsername = 'test_trainer';
const testTrainerPassword = 'Test123!'; const testTrainerPassword = 'Test123!';
// Create a login page instance
const loginPage = new LoginPage(page);
// Navigate to login page // Navigate to login page
await page.goto('/community-login/'); await loginPage.navigate();
await page.waitForLoadState('networkidle');
// Save a screenshot before login // Save a screenshot before login
await page.screenshot({ path: 'login-page-before.png' }); await page.screenshot({ path: 'screenshots/login-page-before.png' });
// Dump the HTML content of the login form // Verify login form is visible
const loginFormHTML = await page.locator('#loginform').innerHTML(); expect(await loginPage.isUsernameFieldVisible()).toBeTruthy();
console.log('Login form HTML:', loginFormHTML);
// Fill login form // Login with valid credentials
await page.fill('#user_login', testTrainerUsername); await loginPage.login(testTrainerUsername, testTrainerPassword);
await page.fill('#user_pass', testTrainerPassword);
await page.click('#wp-submit');
// Wait for navigation // Wait for navigation
await page.waitForTimeout(2000);
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');
// Check if we're logged in by looking for dashboard elements // Check if logged in
await page.screenshot({ path: 'login-result.png' }); const isLoggedIn = await loginPage.isLoggedIn();
console.log(`Is logged in: ${isLoggedIn}`);
// Print current URL and page title for debugging // Take a screenshot of the result
console.log('Current URL after login:', page.url()); await page.screenshot({ path: 'screenshots/login-success.png' });
// Dump any error messages // Print current URL for debugging
const errorHTML = await page.locator('.login-error, .message, #login_error').innerHTML().catch(() => 'No error element found'); const currentUrl = await page.url();
console.log('Login error message:', errorHTML); console.log('Current URL after login:', currentUrl);
// Instead of checking login success, check if an error is displayed // Verify login was successful
const hasError = await page.locator('.login-error, .message, #login_error').isVisible(); expect(isLoggedIn).toBeTruthy();
console.log('Has error displayed:', hasError); });
test('should show error with invalid credentials', async ({ page }) => {
// Create a login page instance
const loginPage = new LoginPage(page);
// Accept any result - we're just debugging // Navigate to login page
expect(true).toBeTruthy(); await loginPage.navigate();
// Login with invalid credentials
await loginPage.login('invalid_user', 'wrong_password');
// Check for error message
const errorMessage = await loginPage.getErrorMessage();
console.log(`Error message: ${errorMessage}`);
// Take a screenshot of the error
await page.screenshot({ path: 'screenshots/login-error.png' });
// Expect an error message
expect(errorMessage).not.toBeNull();
}); });
}); });

View file

@ -6,13 +6,14 @@ import { PATHS } from '../config/staging-config';
* Page object representing the login page * Page object representing the login page
*/ */
export class LoginPage extends BasePage { export class LoginPage extends BasePage {
// Login form elements // Login form elements based on debug analysis
private readonly usernameInput = '#user_login'; private readonly usernameInput = 'input[name="log"]';
private readonly passwordInput = '#user_pass'; private readonly passwordInput = 'input[name="pwd"]';
private readonly loginButton = '#wp-submit'; private readonly loginButton = 'input[type="submit"]';
private readonly rememberMeCheckbox = '#rememberme'; private readonly rememberMeCheckbox = 'input[name="rememberme"]';
private readonly loginError = '.login-error, .login_error'; private readonly loginError = '.login-error, .login_error, #login_error, .notice-error, .woocommerce-error, .wp-die-message';
private readonly forgotPasswordLink = 'a.forgot-password, a:text("Lost your password?")'; private readonly forgotPasswordLink = 'a:text("Lost your password?")';
private readonly loginForm = 'form#hvac_community_loginform';
constructor(page: Page) { constructor(page: Page) {
super(page); super(page);
@ -22,8 +23,12 @@ export class LoginPage extends BasePage {
* Navigate to the login page * Navigate to the login page
*/ */
async navigate(): Promise<void> { async navigate(): Promise<void> {
this.log('Navigating to login page');
await this.page.goto(PATHS.login); await this.page.goto(PATHS.login);
await this.page.waitForSelector(this.usernameInput); await this.page.waitForLoadState('networkidle');
// Make sure the form is visible before proceeding
await this.page.waitForSelector(this.loginForm, { timeout: 10000 });
} }
/** /**
@ -40,16 +45,28 @@ export class LoginPage extends BasePage {
*/ */
async login(username: string, password: string): Promise<void> { async login(username: string, password: string): Promise<void> {
this.log(`Logging in as ${username}`); 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.usernameInput, username);
await this.page.fill(this.passwordInput, password); await this.page.fill(this.passwordInput, password);
// Click login and wait for navigation
await this.page.click(this.loginButton); await this.page.click(this.loginButton);
await this.page.waitForLoadState('networkidle'); await this.page.waitForLoadState('networkidle');
this.log('Login form submitted');
} }
/** /**
* Check if we're logged in * Check if we're logged in
*/ */
async isLoggedIn(): Promise<boolean> { async isLoggedIn(): Promise<boolean> {
await this.page.waitForLoadState('networkidle');
const url = await this.getUrl(); const url = await this.getUrl();
return url.includes('hvac-dashboard'); return url.includes('hvac-dashboard');
} }
@ -58,16 +75,49 @@ export class LoginPage extends BasePage {
* Check if username field is visible * Check if username field is visible
*/ */
async isUsernameFieldVisible(): Promise<boolean> { async isUsernameFieldVisible(): Promise<boolean> {
return await this.page.isVisible(this.usernameInput); try {
await this.page.waitForSelector(this.usernameInput, { state: 'visible', timeout: 5000 });
return true;
} catch (error) {
return false;
}
} }
/** /**
* Get error message if login failed * Get error message if login failed
*/ */
async getErrorMessage(): Promise<string | null> { async getErrorMessage(): Promise<string | null> {
if (await this.page.isVisible(this.loginError)) { // Check all possible error selectors
return await this.page.textContent(this.loginError); 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; return null;
} }

View file

@ -35,7 +35,7 @@ test.describe('Trainer User Journey', () => {
await expect(page).toHaveURL(/.*community-login/); await expect(page).toHaveURL(/.*community-login/);
// Verify login form is visible // Verify login form is visible
expect(await loginPage.isLoginFormVisible()).toBe(true); expect(await loginPage.isUsernameFieldVisible()).toBe(true);
// Login with test trainer credentials // Login with test trainer credentials
await loginPage.login(trainer.username, trainer.password); await loginPage.login(trainer.username, trainer.password);