# 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 handle network errors gracefully - Location: e2e/workflows/1-login.spec.ts:181:7 # Error details ``` Test timeout of 30000ms exceeded. ``` ``` Error: locator.fill: Test timeout of 30000ms exceeded. Call log: - waiting for locator('[data-testid="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 90 | await assertions.assertUserAuthenticated(page, BASE_URL); 91 | }); 92 | 93 | test('should show user identity after successful login', async ({ page }) => { 94 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL); 95 | 96 | // Check that user info is displayed 97 | const userDisplay = page.locator('[data-testid="user-display"]'); 98 | await expect(userDisplay).toBeVisible(); 99 | 100 | // Verify username is shown 101 | const username = await userDisplay.textContent(); 102 | expect(username).toContain(LOCAL_USERS.admin.username); 103 | }); 104 | 105 | test('should maintain session across page reloads', async ({ page }) => { 106 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL); 107 | await assertions.assertUserAuthenticated(page, BASE_URL); 108 | 109 | // Reload page 110 | await page.reload(); 111 | 112 | // Should still be authenticated 113 | const token = await auth.getAuthToken(page); 114 | expect(token).toBeTruthy(); 115 | 116 | // Should not be on login page 117 | await expect(page).not.toHaveURL(BASE_URL); 118 | }); 119 | 120 | test('should support logout functionality', async ({ page }) => { 121 | // Login first 122 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL); 123 | await assertions.assertUserAuthenticated(page, BASE_URL); 124 | 125 | // Logout 126 | await auth.logout(page, BASE_URL); 127 | 128 | // Should be back on login page 129 | const identityOverlay = page.locator('[data-testid="identity-check-overlay"]'); 130 | await expect(identityOverlay).toBeVisible({ timeout: 5000 }); 131 | 132 | // Token should be cleared 133 | const token = await auth.getAuthToken(page); 134 | expect(token).toBeFalsy(); 135 | }); 136 | 137 | test('should show admin button for admin users', async ({ page }) => { 138 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL); 139 | await assertions.assertUserAuthenticated(page, BASE_URL); 140 | 141 | // Admin button should be visible 142 | const adminButton = page.locator('[data-testid="admin-button"]'); 143 | await expect(adminButton).toBeVisible(); 144 | }); 145 | 146 | test('should allow switching between LDAP and local login tabs', async ({ page }) => { 147 | const ldapTab = page.locator('[data-testid="ldap-login-tab"]'); 148 | const localTab = page.locator('[data-testid="local-login-tab"]'); 149 | 150 | // Start on LDAP tab 151 | let ldapForm = page.locator('[data-testid="ldap-login-form"]'); 152 | await expect(ldapForm).toBeVisible(); 153 | 154 | // Switch to local 155 | await localTab.click(); 156 | ldapForm = page.locator('[data-testid="ldap-login-form"]'); 157 | await expect(ldapForm).not.toBeVisible(); 158 | 159 | // Switch back to LDAP 160 | await ldapTab.click(); 161 | ldapForm = page.locator('[data-testid="ldap-login-form"]'); 162 | await expect(ldapForm).toBeVisible(); 163 | }); 164 | 165 | test('should remember login preference on revisit', async ({ page, context }) => { 166 | // Login with local user 167 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL); 168 | 169 | // Close and reopen browser 170 | await page.close(); 171 | const newPage = await context.newPage(); 172 | await newPage.goto(BASE_URL); 173 | 174 | // Should still be authenticated 175 | const authenticated = await auth.isAuthenticated(newPage); 176 | expect(authenticated).toBe(true); 177 | 178 | await newPage.close(); 179 | }); 180 | 181 | test('should handle network errors gracefully', async ({ page }) => { 182 | // Enable offline mode 183 | await page.context().setOffline(true); 184 | 185 | // Try to login 186 | const usernameInput = page.locator('[data-testid="username-input"]'); 187 | const passwordInput = page.locator('[data-testid="password-input"]'); 188 | const submitButton = page.locator('[data-testid="login-submit"]'); 189 | > 190 | await usernameInput.fill(LOCAL_USERS.admin.username); | ^ Error: locator.fill: Test timeout of 30000ms exceeded. 191 | await passwordInput.fill(LOCAL_USERS.admin.password); 192 | await submitButton.click(); 193 | 194 | // Should show network error 195 | const errorToast = page.locator('[data-testid="toast-error"]'); 196 | await expect(errorToast).toBeVisible({ timeout: 5000 }); 197 | 198 | // Re-enable network 199 | await page.context().setOffline(false); 200 | }); 201 | 202 | test('should display user list in local login tab', async ({ page }) => { 203 | const localTab = page.locator('[data-testid="local-login-tab"]'); 204 | await localTab.click(); 205 | 206 | // User list should be visible 207 | const userList = page.locator('[data-testid="user-list"]'); 208 | await expect(userList).toBeVisible(); 209 | 210 | // Should have at least one user 211 | const userItems = page.locator('[data-testid="user-list-item"]'); 212 | expect(await userItems.count()).toBeGreaterThan(0); 213 | }); 214 | 215 | test('should auto-login when clicking user from list', async ({ page }) => { 216 | const localTab = page.locator('[data-testid="local-login-tab"]'); 217 | await localTab.click(); 218 | 219 | // Click first user in list 220 | const firstUser = page.locator('[data-testid="user-list-item"]').first(); 221 | await firstUser.click(); 222 | 223 | // Password input should be focused 224 | const passwordInput = page.locator('[data-testid="local-password-input"]'); 225 | await expect(passwordInput).toBeFocused(); 226 | }); 227 | }); 228 | ```