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:
parent
7ac4d4b853
commit
d1f1005a5a
4 changed files with 215 additions and 37 deletions
113
wordpress-dev/tests/e2e/debug-login-page.spec.ts
Normal file
113
wordpress-dev/tests/e2e/debug-login-page.spec.ts
Normal 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');
|
||||
});
|
||||
|
|
@ -1,48 +1,63 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
|
||||
test.describe('Login Flow', () => {
|
||||
test('should show error on login page', async ({ page }) => {
|
||||
test('should login with valid credentials', async ({ page }) => {
|
||||
// Constants
|
||||
const testTrainerUsername = 'test_trainer';
|
||||
const testTrainerPassword = 'Test123!';
|
||||
|
||||
// Create a login page instance
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
// Navigate to login page
|
||||
await page.goto('/community-login/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.navigate();
|
||||
|
||||
// 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
|
||||
const loginFormHTML = await page.locator('#loginform').innerHTML();
|
||||
console.log('Login form HTML:', loginFormHTML);
|
||||
// Verify login form is visible
|
||||
expect(await loginPage.isUsernameFieldVisible()).toBeTruthy();
|
||||
|
||||
// Fill login form
|
||||
await page.fill('#user_login', testTrainerUsername);
|
||||
await page.fill('#user_pass', testTrainerPassword);
|
||||
await page.click('#wp-submit');
|
||||
// Login with valid credentials
|
||||
await loginPage.login(testTrainerUsername, testTrainerPassword);
|
||||
|
||||
// Wait for navigation
|
||||
await page.waitForTimeout(2000);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// Check if we're logged in by looking for dashboard elements
|
||||
await page.screenshot({ path: 'login-result.png' });
|
||||
// Check if logged in
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
console.log(`Is logged in: ${isLoggedIn}`);
|
||||
|
||||
// Print current URL and page title for debugging
|
||||
console.log('Current URL after login:', page.url());
|
||||
// Take a screenshot of the result
|
||||
await page.screenshot({ path: 'screenshots/login-success.png' });
|
||||
|
||||
// Dump any error messages
|
||||
const errorHTML = await page.locator('.login-error, .message, #login_error').innerHTML().catch(() => 'No error element found');
|
||||
console.log('Login error message:', errorHTML);
|
||||
// Print current URL for debugging
|
||||
const currentUrl = await page.url();
|
||||
console.log('Current URL after login:', currentUrl);
|
||||
|
||||
// Instead of checking login success, check if an error is displayed
|
||||
const hasError = await page.locator('.login-error, .message, #login_error').isVisible();
|
||||
console.log('Has error displayed:', hasError);
|
||||
// Verify login was successful
|
||||
expect(isLoggedIn).toBeTruthy();
|
||||
});
|
||||
|
||||
// Accept any result - we're just debugging
|
||||
expect(true).toBeTruthy();
|
||||
test('should show error with invalid credentials', async ({ page }) => {
|
||||
// Create a login page instance
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
// Navigate to login page
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
|
@ -6,13 +6,14 @@ import { PATHS } from '../config/staging-config';
|
|||
* Page object representing the login page
|
||||
*/
|
||||
export class LoginPage extends BasePage {
|
||||
// Login form elements
|
||||
private readonly usernameInput = '#user_login';
|
||||
private readonly passwordInput = '#user_pass';
|
||||
private readonly loginButton = '#wp-submit';
|
||||
private readonly rememberMeCheckbox = '#rememberme';
|
||||
private readonly loginError = '.login-error, .login_error';
|
||||
private readonly forgotPasswordLink = 'a.forgot-password, a:text("Lost your password?")';
|
||||
// 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);
|
||||
|
|
@ -22,8 +23,12 @@ export class LoginPage extends BasePage {
|
|||
* Navigate to the login page
|
||||
*/
|
||||
async navigate(): Promise<void> {
|
||||
this.log('Navigating to login page');
|
||||
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> {
|
||||
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');
|
||||
}
|
||||
|
|
@ -58,16 +75,49 @@ export class LoginPage extends BasePage {
|
|||
* Check if username field is visible
|
||||
*/
|
||||
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
|
||||
*/
|
||||
async getErrorMessage(): Promise<string | null> {
|
||||
if (await this.page.isVisible(this.loginError)) {
|
||||
return await this.page.textContent(this.loginError);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ test.describe('Trainer User Journey', () => {
|
|||
await expect(page).toHaveURL(/.*community-login/);
|
||||
|
||||
// Verify login form is visible
|
||||
expect(await loginPage.isLoginFormVisible()).toBe(true);
|
||||
expect(await loginPage.isUsernameFieldVisible()).toBe(true);
|
||||
|
||||
// Login with test trainer credentials
|
||||
await loginPage.login(trainer.username, trainer.password);
|
||||
|
|
|
|||
Loading…
Reference in a new issue