UNPKG

@knowcode/screenshotfetch

Version:

Web application spider with screenshot capture and customer journey documentation. Automate user flow documentation with authentication support.

256 lines (223 loc) 7.6 kB
class LoginHandler { constructor(options = {}) { this.options = { timeout: 10000, waitTime: 2000, ...options }; // Common login form selectors this.usernameSelectors = [ 'input[name="username"]', 'input[name="email"]', 'input[name="user"]', 'input[name="login"]', 'input[type="email"]', 'input[id*="username" i]', 'input[id*="email" i]', 'input[id*="user" i]', 'input[id*="login" i]', 'input[placeholder*="username" i]', 'input[placeholder*="email" i]', 'input[placeholder*="user" i]', 'input[class*="username" i]', 'input[class*="email" i]', 'input[class*="user" i]' ]; this.passwordSelectors = [ 'input[name="password"]', 'input[name="pass"]', 'input[type="password"]', 'input[id*="password" i]', 'input[id*="pass" i]', 'input[placeholder*="password" i]', 'input[class*="password" i]', 'input[class*="pass" i]' ]; this.submitSelectors = [ 'button[type="submit"]', 'input[type="submit"]', 'button[id*="login" i]', 'button[id*="signin" i]', 'button[class*="login" i]', 'button[class*="signin" i]', 'button[class*="submit" i]', '[data-testid*="login" i]', '[data-testid*="signin" i]', '[data-testid*="submit" i]' ]; } async detectLoginForm(page) { console.log('🔍 Detecting login form...'); try { // Look for username/email field const usernameField = await this.findElement(page, this.usernameSelectors); if (!usernameField) { console.log('❌ No username/email field found'); return null; } // Look for password field const passwordField = await this.findElement(page, this.passwordSelectors); if (!passwordField) { console.log('❌ No password field found'); return null; } // Look for submit button const submitButton = await this.findElement(page, this.submitSelectors); if (!submitButton) { console.log('❌ No submit button found'); return null; } console.log('✅ Login form detected'); return { usernameField, passwordField, submitButton }; } catch (error) { console.error('❌ Error detecting login form:', error.message); return null; } } async findElement(page, selectors) { for (const selector of selectors) { try { await page.waitForSelector(selector, { timeout: 1000, visible: true }); return selector; } catch (e) { // Continue to next selector } } return null; } async performLogin(page, username, password) { console.log('🔐 Attempting to log in...'); // Detect login form const form = await this.detectLoginForm(page); if (!form) { throw new Error('Login form not found on page'); } try { // Clear and fill username field await page.click(form.usernameField); await page.keyboard.down('Control'); await page.keyboard.press('KeyA'); await page.keyboard.up('Control'); await page.type(form.usernameField, username); console.log('✅ Username entered'); // Clear and fill password field await page.click(form.passwordField); await page.keyboard.down('Control'); await page.keyboard.press('KeyA'); await page.keyboard.up('Control'); await page.type(form.passwordField, password); console.log('✅ Password entered'); // Submit the form await page.click(form.submitButton); console.log('✅ Login form submitted'); // Wait for navigation or page change await new Promise(resolve => setTimeout(resolve, this.options.waitTime)); // Check if login was successful const isLoggedIn = await this.verifyLogin(page); if (isLoggedIn) { console.log('✅ Login successful'); return true; } else { console.log('❌ Login may have failed - no clear success indicators'); return false; } } catch (error) { console.error('❌ Login failed:', error.message); throw new Error(`Login failed: ${error.message}`); } } async verifyLogin(page) { try { // Common indicators of successful login const successIndicators = [ // Look for logout button/link 'a[href*="logout" i]', 'button[onclick*="logout" i]', '[data-testid*="logout" i]', // Look for user profile elements '.user-profile', '.user-menu', '[class*="user-avatar"]', '[class*="profile"]', // Look for dashboard elements '.dashboard', '[class*="dashboard"]', // Look for navigation that suggests logged in state '[class*="main-nav"]', '[class*="app-nav"]' ]; for (const selector of successIndicators) { try { await page.waitForSelector(selector, { timeout: 2000, visible: true }); console.log(`✅ Login success indicator found: ${selector}`); return true; } catch (e) { // Continue checking other indicators } } // Check if we're still on a login page (negative indicator) const currentUrl = page.url(); const isStillOnLogin = currentUrl.includes('login') || currentUrl.includes('signin') || currentUrl.includes('auth'); if (!isStillOnLogin) { console.log('✅ URL changed from login page, assuming success'); return true; } // Look for error messages (negative indicator) const errorSelectors = [ '.error', '.alert-error', '[class*="error"]', '[class*="invalid"]', '[data-testid*="error"]' ]; for (const selector of errorSelectors) { try { await page.waitForSelector(selector, { timeout: 1000, visible: true }); console.log(`❌ Login error indicator found: ${selector}`); return false; } catch (e) { // Continue checking } } // If no clear indicators either way, assume success if URL changed return !isStillOnLogin; } catch (error) { console.log('⚠️ Could not verify login status:', error.message); return false; } } async isLoginRequired(page) { // Check if current page looks like a login page const currentUrl = page.url().toLowerCase(); const isLoginUrl = currentUrl.includes('login') || currentUrl.includes('signin') || currentUrl.includes('auth') || currentUrl.includes('sign-in'); if (isLoginUrl) { return true; } // Check for login form on current page const form = await this.detectLoginForm(page); return form !== null; } async waitForLoginPage(page, timeout = 10000) { console.log('⏳ Waiting for login page to load...'); const startTime = Date.now(); while (Date.now() - startTime < timeout) { if (await this.isLoginRequired(page)) { console.log('✅ Login page detected'); return true; } await new Promise(resolve => setTimeout(resolve, 500)); } console.log('❌ Login page not detected within timeout'); return false; } } module.exports = LoginHandler;