# Instructions - Following Playwright test failed. - Explain why, be concise, respect Playwright best practices. - Provide a snippet of code with the fix, if possible. # Test info - Name: 1-login.spec.ts >> Login Workflow >> should maintain session across page reloads - Location: e2e/workflows/1-login.spec.ts:105:7 # Error details ``` Test timeout of 30000ms exceeded. ``` ``` Error: page.fill: Test timeout of 30000ms exceeded. Call log: - waiting for locator('[data-testid="local-username-input"]') ``` # Page snapshot ```yaml - generic [active] [ref=e1]: - generic [ref=e3]: - generic [ref=e4]: - img [ref=e6] - heading "Identity Check" [level=2] [ref=e9] - paragraph [ref=e10]: Select operator profile or use direct login - generic [ref=e11]: - button "newuser user" [ref=e12] [cursor=pointer]: - generic [ref=e13]: - img [ref=e15] - generic [ref=e18]: - paragraph [ref=e19]: newuser - paragraph [ref=e20]: user - img [ref=e21] - generic [ref=e23]: - button "Enterprise" [ref=e24] [cursor=pointer]: - img [ref=e25] - text: Enterprise - button "Manual Login" [ref=e28] [cursor=pointer]: - img [ref=e29] - text: Manual Login - button "Open Next.js Dev Tools" [ref=e37] [cursor=pointer]: - img [ref=e38] - alert [ref=e41] ``` # Test source ```ts 1 | import { Page, BrowserContext } from '@playwright/test'; 2 | 3 | /** 4 | * Authentication fixture for E2E tests 5 | * Handles login flows and session management 6 | */ 7 | 8 | export interface LoginCredentials { 9 | username: string; 10 | password: string; 11 | } 12 | 13 | export interface AuthSession { 14 | token: string; 15 | userId: string; 16 | email: string; 17 | isAdmin: boolean; 18 | } 19 | 20 | /** 21 | * Perform LDAP authentication 22 | */ 23 | export async function loginWithLdap( 24 | page: Page, 25 | credentials: LoginCredentials, 26 | baseUrl: string = 'http://localhost' 27 | ): Promise { 28 | await page.goto(`${baseUrl}`); 29 | 30 | // Wait for login form to appear 31 | await page.waitForSelector('[data-testid="login-form"]', { timeout: 5000 }); 32 | 33 | // Fill in username 34 | await page.fill('[data-testid="username-input"]', credentials.username); 35 | 36 | // Fill in password 37 | await page.fill('[data-testid="password-input"]', credentials.password); 38 | 39 | // Click login button 40 | await page.click('[data-testid="login-submit"]'); 41 | 42 | // Wait for navigation to inventory page 43 | await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' }); 44 | } 45 | 46 | /** 47 | * Perform local user authentication 48 | */ 49 | export async function loginWithLocalUser( 50 | page: Page, 51 | credentials: LoginCredentials, 52 | baseUrl: string = 'http://localhost' 53 | ): Promise { 54 | await page.goto(`${baseUrl}`); 55 | 56 | // Wait for identity check overlay 57 | await page.waitForSelector('[data-testid="identity-check-overlay"]', { timeout: 5000 }); 58 | 59 | // Click "Local User" tab if it exists 60 | const localUserTab = page.locator('[data-testid="local-user-tab"]'); 61 | if (await localUserTab.isVisible()) { 62 | await localUserTab.click(); 63 | } 64 | 65 | // Fill in username > 66 | await page.fill('[data-testid="local-username-input"]', credentials.username); | ^ Error: page.fill: Test timeout of 30000ms exceeded. 67 | 68 | // Fill in password 69 | await page.fill('[data-testid="local-password-input"]', credentials.password); 70 | 71 | // Click login button 72 | await page.click('[data-testid="local-login-submit"]'); 73 | 74 | // Wait for navigation to inventory page 75 | await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' }); 76 | } 77 | 78 | /** 79 | * Logout from the application 80 | */ 81 | export async function logout(page: Page, baseUrl: string = 'http://localhost'): Promise { 82 | // Click logout button or menu 83 | const logoutButton = page.locator('[data-testid="logout-button"]'); 84 | if (await logoutButton.isVisible()) { 85 | await logoutButton.click(); 86 | } else { 87 | // Try finding it in menu 88 | const menu = page.locator('[data-testid="user-menu"]'); 89 | if (await menu.isVisible()) { 90 | await menu.click(); 91 | await page.click('[data-testid="logout-menu-item"]'); 92 | } 93 | } 94 | 95 | // Wait for redirect to login 96 | await page.waitForURL(`${baseUrl}/*`, { timeout: 5000 }); 97 | } 98 | 99 | /** 100 | * Get authentication token from local storage 101 | */ 102 | export async function getAuthToken(page: Page): Promise { 103 | const token = await page.evaluate(() => { 104 | return localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token'); 105 | }); 106 | return token; 107 | } 108 | 109 | /** 110 | * Set authentication token in local storage 111 | */ 112 | export async function setAuthToken(page: Page, token: string): Promise { 113 | await page.evaluate((t) => { 114 | localStorage.setItem('auth_token', t); 115 | }, token); 116 | } 117 | 118 | /** 119 | * Clear authentication data 120 | */ 121 | export async function clearAuth(page: Page): Promise { 122 | await page.evaluate(() => { 123 | localStorage.removeItem('auth_token'); 124 | sessionStorage.removeItem('auth_token'); 125 | localStorage.removeItem('user_data'); 126 | sessionStorage.removeItem('user_data'); 127 | }); 128 | } 129 | 130 | /** 131 | * Check if user is authenticated 132 | */ 133 | export async function isAuthenticated(page: Page): Promise { 134 | const token = await getAuthToken(page); 135 | return !!token; 136 | } 137 | 138 | /** 139 | * Get current user info from page 140 | */ 141 | export async function getCurrentUser(page: Page): Promise<{ username: string; email: string } | null> { 142 | try { 143 | const userData = await page.evaluate(() => { 144 | const stored = localStorage.getItem('user_data') || sessionStorage.getItem('user_data'); 145 | return stored ? JSON.parse(stored) : null; 146 | }); 147 | return userData; 148 | } catch { 149 | return null; 150 | } 151 | } 152 | 153 | /** 154 | * Wait for authentication to complete 155 | */ 156 | export async function waitForAuth(page: Page, timeout: number = 5000): Promise { 157 | // Wait for either: 158 | // 1. Token to be in storage 159 | // 2. Navigation to happen 160 | // 3. Auth overlay to disappear 161 | await page.waitForFunction( 162 | () => { 163 | const token = 164 | typeof window !== 'undefined' 165 | ? localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token') 166 | : null; ```