# 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 | /** ```