test: save E2E test progress - data-testid attributes added, 1/16 login tests passing
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
# 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: expect(locator).toBeVisible() failed
|
||||
|
||||
Locator: locator('[data-testid="login-form"]')
|
||||
Expected: visible
|
||||
Timeout: 5000ms
|
||||
Error: element(s) not found
|
||||
|
||||
Call log:
|
||||
- Expect "toBeVisible" with timeout 5000ms
|
||||
- waiting for locator('[data-testid="login-form"]')
|
||||
|
||||
```
|
||||
|
||||
# Page snapshot
|
||||
|
||||
```yaml
|
||||
- generic [active] [ref=e1]:
|
||||
- generic [ref=e3]:
|
||||
- generic [ref=e4]:
|
||||
- img [ref=e6]
|
||||
- heading "Identity Check" [level=2] [ref=e9]
|
||||
- paragraph [ref=e10]: Select operator profile or use direct login
|
||||
- generic [ref=e11]:
|
||||
- button "newuser user" [ref=e12] [cursor=pointer]:
|
||||
- generic [ref=e13]:
|
||||
- img [ref=e15]
|
||||
- generic [ref=e18]:
|
||||
- paragraph [ref=e19]: newuser
|
||||
- paragraph [ref=e20]: user
|
||||
- img [ref=e21]
|
||||
- generic [ref=e23]:
|
||||
- button "Enterprise" [ref=e24] [cursor=pointer]:
|
||||
- img [ref=e25]
|
||||
- text: Enterprise
|
||||
- button "Manual Login" [ref=e28] [cursor=pointer]:
|
||||
- img [ref=e29]
|
||||
- text: Manual Login
|
||||
- button "Open Next.js Dev Tools" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- alert [ref=e41]
|
||||
```
|
||||
|
||||
# Test source
|
||||
|
||||
```ts
|
||||
1 | import { expect, Page, Locator } from '@playwright/test';
|
||||
2 |
|
||||
3 | /**
|
||||
4 | * Custom Playwright assertions for E2E tests
|
||||
5 | * Provides domain-specific matchers for inventory operations
|
||||
6 | */
|
||||
7 |
|
||||
8 | /**
|
||||
9 | * Assert that an inventory item is visible with expected data
|
||||
10 | */
|
||||
11 | export async function assertItemVisible(
|
||||
12 | page: Page,
|
||||
13 | itemName: string,
|
||||
14 | expectedData?: { quantity?: number; category?: string }
|
||||
15 | ): Promise<void> {
|
||||
16 | const itemRow = page.locator(`[data-testid="item-row"][data-item-name="${itemName}"]`);
|
||||
17 | await expect(itemRow).toBeVisible();
|
||||
18 |
|
||||
19 | if (expectedData?.quantity !== undefined) {
|
||||
20 | const quantityCell = itemRow.locator('[data-testid="quantity-cell"]');
|
||||
21 | await expect(quantityCell).toContainText(expectedData.quantity.toString());
|
||||
22 | }
|
||||
23 |
|
||||
24 | if (expectedData?.category !== undefined) {
|
||||
25 | const categoryCell = itemRow.locator('[data-testid="category-cell"]');
|
||||
26 | await expect(categoryCell).toContainText(expectedData.category);
|
||||
27 | }
|
||||
28 | }
|
||||
29 |
|
||||
30 | /**
|
||||
31 | * Assert that a barcode scan was successful
|
||||
32 | */
|
||||
33 | export async function assertScanSuccess(
|
||||
34 | page: Page,
|
||||
35 | expectedItemName: string,
|
||||
36 | expectedQuantityChange: number
|
||||
37 | ): Promise<void> {
|
||||
38 | // Check for success toast notification
|
||||
39 | const successToast = page.locator('[data-testid="toast-success"]');
|
||||
40 | await expect(successToast).toBeVisible({ timeout: 3000 });
|
||||
41 |
|
||||
42 | // Verify item quantity was updated
|
||||
43 | const itemRow = page.locator(`[data-testid="item-row"][data-item-name="${expectedItemName}"]`);
|
||||
44 | await expect(itemRow).toBeVisible();
|
||||
45 | }
|
||||
46 |
|
||||
47 | /**
|
||||
48 | * Assert that login form is displayed
|
||||
49 | */
|
||||
50 | export async function assertLoginFormVisible(page: Page): Promise<void> {
|
||||
51 | const loginForm = page.locator('[data-testid="login-form"]');
|
||||
> 52 | await expect(loginForm).toBeVisible();
|
||||
| ^ Error: expect(locator).toBeVisible() failed
|
||||
53 |
|
||||
54 | const usernameInput = page.locator('[data-testid="username-input"]');
|
||||
55 | const passwordInput = page.locator('[data-testid="password-input"]');
|
||||
56 | const submitButton = page.locator('[data-testid="login-submit"]');
|
||||
57 |
|
||||
58 | await expect(usernameInput).toBeVisible();
|
||||
59 | await expect(passwordInput).toBeVisible();
|
||||
60 | await expect(submitButton).toBeVisible();
|
||||
61 | }
|
||||
62 |
|
||||
63 | /**
|
||||
64 | * Assert that user is authenticated (on inventory page)
|
||||
65 | */
|
||||
66 | export async function assertUserAuthenticated(page: Page, baseUrl: string = 'http://localhost'): Promise<void> {
|
||||
67 | // Check URL is on an authenticated page
|
||||
68 | await expect(page).toHaveURL(/inventory|dashboard|scanner/);
|
||||
69 |
|
||||
70 | // Verify logout button is visible
|
||||
71 | const logoutButton = page.locator('[data-testid="logout-button"]');
|
||||
72 | await expect(logoutButton).toBeVisible();
|
||||
73 | }
|
||||
74 |
|
||||
75 | /**
|
||||
76 | * Assert that admin dashboard is visible
|
||||
77 | */
|
||||
78 | export async function assertAdminDashboardVisible(page: Page): Promise<void> {
|
||||
79 | const adminOverlay = page.locator('[data-testid="admin-overlay"]');
|
||||
80 | await expect(adminOverlay).toBeVisible();
|
||||
81 |
|
||||
82 | // Check for tabs
|
||||
83 | const identityTab = page.locator('[data-testid="admin-tab-identity"]');
|
||||
84 | const databaseTab = page.locator('[data-testid="admin-tab-database"]');
|
||||
85 |
|
||||
86 | await expect(identityTab).toBeVisible();
|
||||
87 | await expect(databaseTab).toBeVisible();
|
||||
88 | }
|
||||
89 |
|
||||
90 | /**
|
||||
91 | * Assert that stock adjustment form is visible
|
||||
92 | */
|
||||
93 | export async function assertStockAdjustmentVisible(page: Page, itemName: string): Promise<void> {
|
||||
94 | const adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]');
|
||||
95 | await expect(adjustmentForm).toBeVisible();
|
||||
96 |
|
||||
97 | const itemNameDisplay = page.locator(`[data-testid="adjustment-item-name"]`);
|
||||
98 | await expect(itemNameDisplay).toContainText(itemName);
|
||||
99 |
|
||||
100 | const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
101 | await expect(quantityInput).toBeVisible();
|
||||
102 | }
|
||||
103 |
|
||||
104 | /**
|
||||
105 | * Assert that AI extraction is in progress
|
||||
106 | */
|
||||
107 | export async function assertAiExtractionInProgress(page: Page): Promise<void> {
|
||||
108 | const extractionOverlay = page.locator('[data-testid="ai-extraction-overlay"]');
|
||||
109 | await expect(extractionOverlay).toBeVisible();
|
||||
110 |
|
||||
111 | const loadingSpinner = page.locator('[data-testid="extraction-loading"]');
|
||||
112 | await expect(loadingSpinner).toBeVisible();
|
||||
113 | }
|
||||
114 |
|
||||
115 | /**
|
||||
116 | * Assert that AI extraction results are displayed
|
||||
117 | */
|
||||
118 | export async function assertAiExtractionResults(
|
||||
119 | page: Page,
|
||||
120 | expectedFields: { name?: string; partNumber?: string; quantity?: string }
|
||||
121 | ): Promise<void> {
|
||||
122 | const resultsForm = page.locator('[data-testid="extraction-results-form"]');
|
||||
123 | await expect(resultsForm).toBeVisible();
|
||||
124 |
|
||||
125 | if (expectedFields.name) {
|
||||
126 | const nameInput = resultsForm.locator('[data-testid="extracted-name"]');
|
||||
127 | await expect(nameInput).toHaveValue(expectedFields.name);
|
||||
128 | }
|
||||
129 |
|
||||
130 | if (expectedFields.partNumber) {
|
||||
131 | const pnInput = resultsForm.locator('[data-testid="extracted-part-number"]');
|
||||
132 | await expect(pnInput).toHaveValue(expectedFields.partNumber);
|
||||
133 | }
|
||||
134 |
|
||||
135 | if (expectedFields.quantity) {
|
||||
136 | const quantityInput = resultsForm.locator('[data-testid="extracted-quantity"]');
|
||||
137 | await expect(quantityInput).toHaveValue(expectedFields.quantity);
|
||||
138 | }
|
||||
139 | }
|
||||
140 |
|
||||
141 | /**
|
||||
142 | * Assert that offline sync is pending
|
||||
143 | */
|
||||
144 | export async function assertOfflineSyncPending(page: Page): Promise<void> {
|
||||
145 | const syncIndicator = page.locator('[data-testid="offline-sync-indicator"]');
|
||||
146 | await expect(syncIndicator).toBeVisible();
|
||||
147 |
|
||||
148 | const pendingBadge = page.locator('[data-testid="sync-pending-badge"]');
|
||||
149 | await expect(pendingBadge).toBeVisible();
|
||||
150 | }
|
||||
151 |
|
||||
152 | /**
|
||||
```
|
||||
Reference in New Issue
Block a user