fix: update test files to match actual backend schemas and endpoints
@@ -46,15 +46,8 @@
|
|||||||
"mcp__plugin_context-mode_context-mode__ctx_stats",
|
"mcp__plugin_context-mode_context-mode__ctx_stats",
|
||||||
"mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
"mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
||||||
"Bash(git checkout *)",
|
"Bash(git checkout *)",
|
||||||
"Bash(python -m py_compile backend/tests/conftest.py)",
|
"Bash(git stash *)",
|
||||||
"Bash(python -m py_compile backend/tests/test_users.py)",
|
"Bash(python -m pytest backend/tests/ -v --tb=short)"
|
||||||
"Bash(python *)",
|
|
||||||
"Bash(python3.12 -m venv backend/venv)",
|
|
||||||
"Bash(backend/venv/bin/pip install *)",
|
|
||||||
"Bash(sudo apt-get *)",
|
|
||||||
"Bash(PYTHONPATH=/data/programare_AI/tfm_ainventory:/data/programare_AI/tfm_ainventory/backend backend/venv/bin/pytest backend/tests/test_items.py::TestItemCRUD::test_create_item -v)",
|
|
||||||
"Bash(PYTHONPATH=/data/programare_AI/tfm_ainventory:/data/programare_AI/tfm_ainventory/backend backend/venv/bin/pytest backend/tests/test_items.py::TestItemCRUD::test_create_item -vv)",
|
|
||||||
"Bash(git tag *)"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class TestItemCRUD:
|
|||||||
json={
|
json={
|
||||||
"name": "Test Item",
|
"name": "Test Item",
|
||||||
"category": "Electronics",
|
"category": "Electronics",
|
||||||
"item_type": "Component",
|
"type": "Component",
|
||||||
"quantity": 10,
|
"quantity": 10,
|
||||||
"barcode": "123456789",
|
"barcode": "123456789",
|
||||||
"part_number": "PN-12345"
|
"part_number": "PN-12345"
|
||||||
@@ -141,7 +141,7 @@ class TestItemValidation:
|
|||||||
json={
|
json={
|
||||||
"name": "Item2",
|
"name": "Item2",
|
||||||
"category": "B",
|
"category": "B",
|
||||||
"item_type": "Type",
|
"type": "Type",
|
||||||
"quantity": 5,
|
"quantity": 5,
|
||||||
"barcode": "UNIQUE123",
|
"barcode": "UNIQUE123",
|
||||||
"part_number": "PN2"
|
"part_number": "PN2"
|
||||||
@@ -156,7 +156,7 @@ class TestItemValidation:
|
|||||||
json={
|
json={
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"category": "A",
|
"category": "A",
|
||||||
"item_type": "Type",
|
"type": "Type",
|
||||||
"quantity": -5,
|
"quantity": -5,
|
||||||
"barcode": "123",
|
"barcode": "123",
|
||||||
"part_number": "PN"
|
"part_number": "PN"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class TestUserAuthentication:
|
|||||||
def test_login_ldap_success(self, test_client, mock_ldap):
|
def test_login_ldap_success(self, test_client, mock_ldap):
|
||||||
"""Test successful LDAP login."""
|
"""Test successful LDAP login."""
|
||||||
response = test_client.post(
|
response = test_client.post(
|
||||||
"/api/auth/login",
|
"/api/users/login",
|
||||||
json={"username": "testuser", "password": "password123"}
|
json={"username": "testuser", "password": "password123"}
|
||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
@@ -21,7 +21,7 @@ class TestUserAuthentication:
|
|||||||
mock_ldap.bind.side_effect = Exception("Invalid credentials")
|
mock_ldap.bind.side_effect = Exception("Invalid credentials")
|
||||||
|
|
||||||
response = test_client.post(
|
response = test_client.post(
|
||||||
"/api/auth/login",
|
"/api/users/login",
|
||||||
json={"username": "testuser", "password": "wrongpassword"}
|
json={"username": "testuser", "password": "wrongpassword"}
|
||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||||
@@ -29,13 +29,12 @@ class TestUserAuthentication:
|
|||||||
def test_login_local_password(self, test_client, test_db):
|
def test_login_local_password(self, test_client, test_db):
|
||||||
"""Test local password authentication (fallback)."""
|
"""Test local password authentication (fallback)."""
|
||||||
from backend.models import User
|
from backend.models import User
|
||||||
from backend.auth import hash_password
|
from backend.routers.users import get_password_hash
|
||||||
|
|
||||||
# Create local user
|
# Create local user
|
||||||
user = User(
|
user = User(
|
||||||
username="localuser",
|
username="localuser",
|
||||||
email="local@test.com",
|
hashed_password=get_password_hash("password123"),
|
||||||
hashed_password=hash_password("password123"),
|
|
||||||
role="user",
|
role="user",
|
||||||
origin="local"
|
origin="local"
|
||||||
)
|
)
|
||||||
@@ -43,7 +42,7 @@ class TestUserAuthentication:
|
|||||||
test_db.commit()
|
test_db.commit()
|
||||||
|
|
||||||
response = test_client.post(
|
response = test_client.post(
|
||||||
"/api/auth/login",
|
"/api/users/login",
|
||||||
json={"username": "localuser", "password": "password123"}
|
json={"username": "localuser", "password": "password123"}
|
||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
@@ -59,7 +58,7 @@ class TestUserCRUD:
|
|||||||
"/api/users",
|
"/api/users",
|
||||||
json={
|
json={
|
||||||
"username": "newuser",
|
"username": "newuser",
|
||||||
"email": "new@test.com",
|
"password": "NewPass123!",
|
||||||
"role": "user"
|
"role": "user"
|
||||||
},
|
},
|
||||||
headers={"Authorization": f"Bearer {admin_token}"}
|
headers={"Authorization": f"Bearer {admin_token}"}
|
||||||
@@ -67,7 +66,7 @@ class TestUserCRUD:
|
|||||||
assert response.status_code == status.HTTP_201_CREATED
|
assert response.status_code == status.HTTP_201_CREATED
|
||||||
data = response.json()
|
data = response.json()
|
||||||
assert data["username"] == "newuser"
|
assert data["username"] == "newuser"
|
||||||
assert data["email"] == "new@test.com"
|
assert data["role"] == "user"
|
||||||
|
|
||||||
def test_create_user_non_admin_denied(self, test_client, user_token):
|
def test_create_user_non_admin_denied(self, test_client, user_token):
|
||||||
"""Test that non-admin users cannot create users."""
|
"""Test that non-admin users cannot create users."""
|
||||||
@@ -75,7 +74,7 @@ class TestUserCRUD:
|
|||||||
"/api/users",
|
"/api/users",
|
||||||
json={
|
json={
|
||||||
"username": "newuser",
|
"username": "newuser",
|
||||||
"email": "new@test.com",
|
"password": "Pass123!",
|
||||||
"role": "user"
|
"role": "user"
|
||||||
},
|
},
|
||||||
headers={"Authorization": f"Bearer {user_token}"}
|
headers={"Authorization": f"Bearer {user_token}"}
|
||||||
@@ -88,7 +87,6 @@ class TestUserCRUD:
|
|||||||
|
|
||||||
user = User(
|
user = User(
|
||||||
username="testuser",
|
username="testuser",
|
||||||
email="test@test.com",
|
|
||||||
hashed_password="hashed",
|
hashed_password="hashed",
|
||||||
role="user",
|
role="user",
|
||||||
origin="local"
|
origin="local"
|
||||||
@@ -105,8 +103,8 @@ class TestUserCRUD:
|
|||||||
"""Test listing all users."""
|
"""Test listing all users."""
|
||||||
from backend.models import User
|
from backend.models import User
|
||||||
|
|
||||||
user1 = User(username="user1", email="user1@test.com", hashed_password="h", role="user", origin="local")
|
user1 = User(username="user1", hashed_password="h", role="user", origin="local")
|
||||||
user2 = User(username="user2", email="user2@test.com", hashed_password="h", role="user", origin="local")
|
user2 = User(username="user2", hashed_password="h", role="user", origin="local")
|
||||||
test_db.add_all([user1, user2])
|
test_db.add_all([user1, user2])
|
||||||
test_db.commit()
|
test_db.commit()
|
||||||
|
|
||||||
@@ -119,24 +117,24 @@ class TestUserCRUD:
|
|||||||
"""Test updating user (admin only)."""
|
"""Test updating user (admin only)."""
|
||||||
from backend.models import User
|
from backend.models import User
|
||||||
|
|
||||||
user = User(username="testuser", email="old@test.com", hashed_password="h", role="user", origin="local")
|
user = User(username="testuser", hashed_password="h", role="user", origin="local")
|
||||||
test_db.add(user)
|
test_db.add(user)
|
||||||
test_db.commit()
|
test_db.commit()
|
||||||
|
|
||||||
response = test_client.put(
|
response = test_client.put(
|
||||||
f"/api/users/{user.id}",
|
f"/api/users/{user.id}",
|
||||||
json={"email": "new@test.com", "role": "admin"},
|
json={"username": "updateduser", "role": "admin"},
|
||||||
headers={"Authorization": f"Bearer {admin_token}"}
|
headers={"Authorization": f"Bearer {admin_token}"}
|
||||||
)
|
)
|
||||||
assert response.status_code == status.HTTP_200_OK
|
assert response.status_code == status.HTTP_200_OK
|
||||||
data = response.json()
|
data = response.json()
|
||||||
assert data["email"] == "new@test.com"
|
assert data["role"] == "admin"
|
||||||
|
|
||||||
def test_delete_user_admin_only(self, test_client, test_db, admin_token):
|
def test_delete_user_admin_only(self, test_client, test_db, admin_token):
|
||||||
"""Test deleting user (admin only)."""
|
"""Test deleting user (admin only)."""
|
||||||
from backend.models import User
|
from backend.models import User
|
||||||
|
|
||||||
user = User(username="testuser", email="test@test.com", hashed_password="h", role="user", origin="local")
|
user = User(username="testuser", hashed_password="h", role="user", origin="local")
|
||||||
test_db.add(user)
|
test_db.add(user)
|
||||||
test_db.commit()
|
test_db.commit()
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 allow switching between LDAP and local login tabs
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:146:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 auto-login when clicking user from list
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:215:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 local user credentials
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:81:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 reject invalid local user credentials
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:66:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 login form with username and password fields
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:28:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 show admin button for admin users
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:137:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 show user identity after successful login
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:93:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 remember login preference on revisit
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:165:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 reject invalid LDAP credentials
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:41:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 user list in local login tab
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:202:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 reject empty login credentials
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:32:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,136 @@
|
|||||||
|
# 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 support logout functionality
|
||||||
|
- Location: e2e/workflows/1-login.spec.ts:120:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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:3000';
|
||||||
|
8 |
|
||||||
|
9 | test.beforeEach(async ({ page }) => {
|
||||||
|
10 | // Navigate to login page
|
||||||
|
> 11 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
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();
|
||||||
|
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 |
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,137 @@
|
|||||||
|
# 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: 2-scan-adjust.spec.ts >> Scan and Adjust Workflow >> should display scan input field
|
||||||
|
- Location: e2e/workflows/2-scan-adjust.spec.ts:32:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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 * as helpers from '../utils/helpers';
|
||||||
|
5 | import { LOCAL_USERS, TEST_ITEMS } from '../fixtures/test-data';
|
||||||
|
6 |
|
||||||
|
7 | test.describe('Scan and Adjust Workflow', () => {
|
||||||
|
8 | const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||||
|
9 |
|
||||||
|
10 | test.beforeEach(async ({ page }) => {
|
||||||
|
11 | // Navigate to app and login
|
||||||
|
> 12 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
13 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL);
|
||||||
|
14 | await assertions.assertUserAuthenticated(page, BASE_URL);
|
||||||
|
15 |
|
||||||
|
16 | // Navigate to scanner page
|
||||||
|
17 | await page.goto(`${BASE_URL}/scanner`);
|
||||||
|
18 | await assertions.assertScannerReady(page);
|
||||||
|
19 | });
|
||||||
|
20 |
|
||||||
|
21 | test('should display scanner interface with camera view', async ({ page }) => {
|
||||||
|
22 | await assertions.assertScannerReady(page);
|
||||||
|
23 |
|
||||||
|
24 | // Verify camera controls
|
||||||
|
25 | const zoomButton = page.locator('[data-testid="zoom-control"]');
|
||||||
|
26 | const cameraIndicator = page.locator('[data-testid="camera-indicator"]');
|
||||||
|
27 |
|
||||||
|
28 | await expect(zoomButton).toBeVisible();
|
||||||
|
29 | await expect(cameraIndicator).toBeVisible();
|
||||||
|
30 | });
|
||||||
|
31 |
|
||||||
|
32 | test('should display scan input field', async ({ page }) => {
|
||||||
|
33 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
34 | await expect(scanInput).toBeVisible();
|
||||||
|
35 | await expect(scanInput).toBeFocused();
|
||||||
|
36 | });
|
||||||
|
37 |
|
||||||
|
38 | test('should handle barcode scan and match to inventory item', async ({ page }) => {
|
||||||
|
39 | // Simulate barcode scan by entering barcode in input
|
||||||
|
40 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
41 |
|
||||||
|
42 | // Enter first test item's barcode
|
||||||
|
43 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
44 | await scanInput.press('Enter');
|
||||||
|
45 |
|
||||||
|
46 | // Should show stock adjustment form
|
||||||
|
47 | await assertions.assertStockAdjustmentVisible(page, TEST_ITEMS[0].name);
|
||||||
|
48 |
|
||||||
|
49 | // Verify item details are shown
|
||||||
|
50 | const itemName = page.locator('[data-testid="adjustment-item-name"]');
|
||||||
|
51 | await expect(itemName).toContainText(TEST_ITEMS[0].name);
|
||||||
|
52 | });
|
||||||
|
53 |
|
||||||
|
54 | test('should default to check-in operation', async ({ page }) => {
|
||||||
|
55 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
56 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
57 | await scanInput.press('Enter');
|
||||||
|
58 |
|
||||||
|
59 | // Check-in radio should be selected
|
||||||
|
60 | const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||||
|
61 | await expect(checkInRadio).toBeChecked();
|
||||||
|
62 | });
|
||||||
|
63 |
|
||||||
|
64 | test('should allow switching between check-in and check-out', async ({ page }) => {
|
||||||
|
65 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
66 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
67 | await scanInput.press('Enter');
|
||||||
|
68 |
|
||||||
|
69 | // Switch to check-out
|
||||||
|
70 | const checkOutRadio = page.locator('[data-testid="operation-checkout"]');
|
||||||
|
71 | await checkOutRadio.click();
|
||||||
|
72 | await expect(checkOutRadio).toBeChecked();
|
||||||
|
73 |
|
||||||
|
74 | // Switch back to check-in
|
||||||
|
75 | const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||||
|
76 | await checkInRadio.click();
|
||||||
|
77 | await expect(checkInRadio).toBeChecked();
|
||||||
|
78 | });
|
||||||
|
79 |
|
||||||
|
80 | test('should allow quantity adjustment', async ({ page }) => {
|
||||||
|
81 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
82 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
83 | await scanInput.press('Enter');
|
||||||
|
84 |
|
||||||
|
85 | // Change quantity
|
||||||
|
86 | const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||||
|
87 | await quantityInput.fill('10');
|
||||||
|
88 |
|
||||||
|
89 | // Verify value changed
|
||||||
|
90 | const value = await helpers.getInputValue(page, '[data-testid="adjustment-quantity-input"]');
|
||||||
|
91 | expect(value).toBe('10');
|
||||||
|
92 | });
|
||||||
|
93 |
|
||||||
|
94 | test('should reject invalid quantities', async ({ page }) => {
|
||||||
|
95 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
96 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
97 | await scanInput.press('Enter');
|
||||||
|
98 |
|
||||||
|
99 | // Try invalid quantity
|
||||||
|
100 | const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||||
|
101 | await quantityInput.fill('-5');
|
||||||
|
102 |
|
||||||
|
103 | // Submit should be disabled or show error
|
||||||
|
104 | const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||||
|
105 | if (await submitButton.isEnabled()) {
|
||||||
|
106 | await submitButton.click();
|
||||||
|
107 | const errorToast = page.locator('[data-testid="toast-error"]');
|
||||||
|
108 | await expect(errorToast).toBeVisible({ timeout: 3000 });
|
||||||
|
109 | }
|
||||||
|
110 | });
|
||||||
|
111 |
|
||||||
|
112 | test('should submit stock adjustment successfully', async ({ page }) => {
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,137 @@
|
|||||||
|
# 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: 2-scan-adjust.spec.ts >> Scan and Adjust Workflow >> should display scanner interface with camera view
|
||||||
|
- Location: e2e/workflows/2-scan-adjust.spec.ts:21:7
|
||||||
|
|
||||||
|
# Error details
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
Call log:
|
||||||
|
- navigating to "http://localhost:3000/", waiting until "load"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# 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 * as helpers from '../utils/helpers';
|
||||||
|
5 | import { LOCAL_USERS, TEST_ITEMS } from '../fixtures/test-data';
|
||||||
|
6 |
|
||||||
|
7 | test.describe('Scan and Adjust Workflow', () => {
|
||||||
|
8 | const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||||
|
9 |
|
||||||
|
10 | test.beforeEach(async ({ page }) => {
|
||||||
|
11 | // Navigate to app and login
|
||||||
|
> 12 | await page.goto(BASE_URL);
|
||||||
|
| ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:3000/
|
||||||
|
13 | await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL);
|
||||||
|
14 | await assertions.assertUserAuthenticated(page, BASE_URL);
|
||||||
|
15 |
|
||||||
|
16 | // Navigate to scanner page
|
||||||
|
17 | await page.goto(`${BASE_URL}/scanner`);
|
||||||
|
18 | await assertions.assertScannerReady(page);
|
||||||
|
19 | });
|
||||||
|
20 |
|
||||||
|
21 | test('should display scanner interface with camera view', async ({ page }) => {
|
||||||
|
22 | await assertions.assertScannerReady(page);
|
||||||
|
23 |
|
||||||
|
24 | // Verify camera controls
|
||||||
|
25 | const zoomButton = page.locator('[data-testid="zoom-control"]');
|
||||||
|
26 | const cameraIndicator = page.locator('[data-testid="camera-indicator"]');
|
||||||
|
27 |
|
||||||
|
28 | await expect(zoomButton).toBeVisible();
|
||||||
|
29 | await expect(cameraIndicator).toBeVisible();
|
||||||
|
30 | });
|
||||||
|
31 |
|
||||||
|
32 | test('should display scan input field', async ({ page }) => {
|
||||||
|
33 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
34 | await expect(scanInput).toBeVisible();
|
||||||
|
35 | await expect(scanInput).toBeFocused();
|
||||||
|
36 | });
|
||||||
|
37 |
|
||||||
|
38 | test('should handle barcode scan and match to inventory item', async ({ page }) => {
|
||||||
|
39 | // Simulate barcode scan by entering barcode in input
|
||||||
|
40 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
41 |
|
||||||
|
42 | // Enter first test item's barcode
|
||||||
|
43 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
44 | await scanInput.press('Enter');
|
||||||
|
45 |
|
||||||
|
46 | // Should show stock adjustment form
|
||||||
|
47 | await assertions.assertStockAdjustmentVisible(page, TEST_ITEMS[0].name);
|
||||||
|
48 |
|
||||||
|
49 | // Verify item details are shown
|
||||||
|
50 | const itemName = page.locator('[data-testid="adjustment-item-name"]');
|
||||||
|
51 | await expect(itemName).toContainText(TEST_ITEMS[0].name);
|
||||||
|
52 | });
|
||||||
|
53 |
|
||||||
|
54 | test('should default to check-in operation', async ({ page }) => {
|
||||||
|
55 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
56 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
57 | await scanInput.press('Enter');
|
||||||
|
58 |
|
||||||
|
59 | // Check-in radio should be selected
|
||||||
|
60 | const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||||
|
61 | await expect(checkInRadio).toBeChecked();
|
||||||
|
62 | });
|
||||||
|
63 |
|
||||||
|
64 | test('should allow switching between check-in and check-out', async ({ page }) => {
|
||||||
|
65 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
66 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
67 | await scanInput.press('Enter');
|
||||||
|
68 |
|
||||||
|
69 | // Switch to check-out
|
||||||
|
70 | const checkOutRadio = page.locator('[data-testid="operation-checkout"]');
|
||||||
|
71 | await checkOutRadio.click();
|
||||||
|
72 | await expect(checkOutRadio).toBeChecked();
|
||||||
|
73 |
|
||||||
|
74 | // Switch back to check-in
|
||||||
|
75 | const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||||
|
76 | await checkInRadio.click();
|
||||||
|
77 | await expect(checkInRadio).toBeChecked();
|
||||||
|
78 | });
|
||||||
|
79 |
|
||||||
|
80 | test('should allow quantity adjustment', async ({ page }) => {
|
||||||
|
81 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
82 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
83 | await scanInput.press('Enter');
|
||||||
|
84 |
|
||||||
|
85 | // Change quantity
|
||||||
|
86 | const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||||
|
87 | await quantityInput.fill('10');
|
||||||
|
88 |
|
||||||
|
89 | // Verify value changed
|
||||||
|
90 | const value = await helpers.getInputValue(page, '[data-testid="adjustment-quantity-input"]');
|
||||||
|
91 | expect(value).toBe('10');
|
||||||
|
92 | });
|
||||||
|
93 |
|
||||||
|
94 | test('should reject invalid quantities', async ({ page }) => {
|
||||||
|
95 | const scanInput = page.locator('[data-testid="scan-input"]');
|
||||||
|
96 | await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||||
|
97 | await scanInput.press('Enter');
|
||||||
|
98 |
|
||||||
|
99 | // Try invalid quantity
|
||||||
|
100 | const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||||
|
101 | await quantityInput.fill('-5');
|
||||||
|
102 |
|
||||||
|
103 | // Submit should be disabled or show error
|
||||||
|
104 | const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||||
|
105 | if (await submitButton.isEnabled()) {
|
||||||
|
106 | await submitButton.click();
|
||||||
|
107 | const errorToast = page.locator('[data-testid="toast-error"]');
|
||||||
|
108 | await expect(errorToast).toBeVisible({ timeout: 3000 });
|
||||||
|
109 | }
|
||||||
|
110 | });
|
||||||
|
111 |
|
||||||
|
112 | test('should submit stock adjustment successfully', async ({ page }) => {
|
||||||
|
```
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |