185 lines
7.1 KiB
Markdown
185 lines
7.1 KiB
Markdown
# 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 display identity check overlay on app load
|
|
- Location: e2e/workflows/1-login.spec.ts:16:7
|
|
|
|
# Error details
|
|
|
|
```
|
|
Error: expect(locator).toBeVisible() failed
|
|
|
|
Locator: locator('[data-testid="ldap-login-tab"]')
|
|
Expected: visible
|
|
Timeout: 5000ms
|
|
Error: element(s) not found
|
|
|
|
Call log:
|
|
- Expect "toBeVisible" with timeout 5000ms
|
|
- waiting for locator('[data-testid="ldap-login-tab"]')
|
|
|
|
```
|
|
|
|
# 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 { test, expect } from '@playwright/test';
|
|
2 | import * as auth from '../fixtures/auth';
|
|
3 | import * as assertions from '../utils/assertions';
|
|
4 | import { LDAP_USERS, LOCAL_USERS } from '../fixtures/test-data';
|
|
5 |
|
|
6 | test.describe('Login Workflow', () => {
|
|
7 | const BASE_URL = process.env.BASE_URL || 'http://localhost:8917';
|
|
8 |
|
|
9 | test.beforeEach(async ({ page }) => {
|
|
10 | // Navigate to login page
|
|
11 | await page.goto(BASE_URL);
|
|
12 | // Wait for identity check overlay
|
|
13 | await page.waitForSelector('[data-testid="identity-check-overlay"]', { timeout: 10000 });
|
|
14 | });
|
|
15 |
|
|
16 | test('should display identity check overlay on app load', async ({ page }) => {
|
|
17 | const overlay = page.locator('[data-testid="identity-check-overlay"]');
|
|
18 | await expect(overlay).toBeVisible();
|
|
19 |
|
|
20 | // Verify tabs are available
|
|
21 | const ldapTab = page.locator('[data-testid="ldap-login-tab"]');
|
|
22 | const localTab = page.locator('[data-testid="local-login-tab"]');
|
|
23 |
|
|
> 24 | await expect(ldapTab).toBeVisible();
|
|
| ^ Error: expect(locator).toBeVisible() failed
|
|
25 | await expect(localTab).toBeVisible();
|
|
26 | });
|
|
27 |
|
|
28 | test('should display login form with username and password fields', async ({ page }) => {
|
|
29 | await assertions.assertLoginFormVisible(page);
|
|
30 | });
|
|
31 |
|
|
32 | test('should reject empty login credentials', async ({ page }) => {
|
|
33 | const submitButton = page.locator('[data-testid="login-submit"]');
|
|
34 | await submitButton.click();
|
|
35 |
|
|
36 | // Should show validation error
|
|
37 | const errorMessage = page.locator('[data-testid="toast-error"]');
|
|
38 | await expect(errorMessage).toBeVisible({ timeout: 3000 });
|
|
39 | });
|
|
40 |
|
|
41 | test('should reject invalid LDAP credentials', async ({ page }) => {
|
|
42 | await auth.loginWithLdap(page, LDAP_USERS.invalid, BASE_URL);
|
|
43 |
|
|
44 | // Should show error message
|
|
45 | const errorToast = page.locator('[data-testid="toast-error"]');
|
|
46 | await expect(errorToast).toBeVisible({ timeout: 5000 });
|
|
47 |
|
|
48 | // Should still be on login page
|
|
49 | await expect(page).toHaveURL(BASE_URL);
|
|
50 | });
|
|
51 |
|
|
52 | test('should successfully login with valid LDAP credentials', async ({ page }) => {
|
|
53 | // Skip if LDAP is not configured in test environment
|
|
54 | test.skip(process.env.SKIP_LDAP === 'true', 'LDAP not available in test environment');
|
|
55 |
|
|
56 | await auth.loginWithLdap(page, LDAP_USERS.valid, BASE_URL);
|
|
57 |
|
|
58 | // Verify we're authenticated
|
|
59 | await assertions.assertUserAuthenticated(page, BASE_URL);
|
|
60 |
|
|
61 | // Verify token is stored
|
|
62 | const token = await auth.getAuthToken(page);
|
|
63 | expect(token).toBeTruthy();
|
|
64 | });
|
|
65 |
|
|
66 | test('should reject invalid local user credentials', async ({ page }) => {
|
|
67 | // Switch to local user tab
|
|
68 | const localTab = page.locator('[data-testid="local-login-tab"]');
|
|
69 | await localTab.click();
|
|
70 |
|
|
71 | // Try to login with invalid credentials
|
|
72 | await page.fill('[data-testid="local-username-input"]', 'invalid_user');
|
|
73 | await page.fill('[data-testid="local-password-input"]', 'wrong_password');
|
|
74 | await page.click('[data-testid="local-login-submit"]');
|
|
75 |
|
|
76 | // Should show error
|
|
77 | const errorToast = page.locator('[data-testid="toast-error"]');
|
|
78 | await expect(errorToast).toBeVisible({ timeout: 5000 });
|
|
79 | });
|
|
80 |
|
|
81 | test('should successfully login with valid local user credentials', async ({ page }) => {
|
|
82 | // Switch to local user tab
|
|
83 | const localTab = page.locator('[data-testid="local-login-tab"]');
|
|
84 | await localTab.click();
|
|
85 |
|
|
86 | // Login with valid credentials
|
|
87 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL);
|
|
88 |
|
|
89 | // Verify authenticated
|
|
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 |
|
|
``` |