187 lines
6.0 KiB
Markdown
187 lines
6.0 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 successfully login with valid LDAP credentials
|
|
- Location: e2e/workflows/1-login.spec.ts:52:7
|
|
|
|
# Error details
|
|
|
|
```
|
|
TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.
|
|
Call log:
|
|
- waiting for locator('[data-testid="login-form"]') to be visible
|
|
- waiting for" http://localhost:8917/login" navigation to finish...
|
|
- navigated to "http://localhost:8917/login"
|
|
|
|
```
|
|
|
|
# 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<void> {
|
|
28 | await page.goto(`${baseUrl}`);
|
|
29 |
|
|
30 | // Wait for login form to appear
|
|
> 31 | await page.waitForSelector('[data-testid="login-form"]', { timeout: 5000 });
|
|
| ^ TimeoutError: page.waitForSelector: Timeout 5000ms exceeded.
|
|
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<void> {
|
|
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);
|
|
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<void> {
|
|
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<string | null> {
|
|
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<void> {
|
|
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<void> {
|
|
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
|
|
``` |