diff --git a/frontend/e2e/utils/assertions.ts b/frontend/e2e/utils/assertions.ts new file mode 100644 index 00000000..d269d6c9 --- /dev/null +++ b/frontend/e2e/utils/assertions.ts @@ -0,0 +1,261 @@ +import { expect, Page, Locator } from '@playwright/test'; + +/** + * Custom Playwright assertions for E2E tests + * Provides domain-specific matchers for inventory operations + */ + +/** + * Assert that an inventory item is visible with expected data + */ +export async function assertItemVisible( + page: Page, + itemName: string, + expectedData?: { quantity?: number; category?: string } +): Promise { + const itemRow = page.locator(`[data-testid="item-row"][data-item-name="${itemName}"]`); + await expect(itemRow).toBeVisible(); + + if (expectedData?.quantity !== undefined) { + const quantityCell = itemRow.locator('[data-testid="quantity-cell"]'); + await expect(quantityCell).toContainText(expectedData.quantity.toString()); + } + + if (expectedData?.category !== undefined) { + const categoryCell = itemRow.locator('[data-testid="category-cell"]'); + await expect(categoryCell).toContainText(expectedData.category); + } +} + +/** + * Assert that a barcode scan was successful + */ +export async function assertScanSuccess( + page: Page, + expectedItemName: string, + expectedQuantityChange: number +): Promise { + // Check for success toast notification + const successToast = page.locator('[data-testid="toast-success"]'); + await expect(successToast).toBeVisible({ timeout: 3000 }); + + // Verify item quantity was updated + const itemRow = page.locator(`[data-testid="item-row"][data-item-name="${expectedItemName}"]`); + await expect(itemRow).toBeVisible(); +} + +/** + * Assert that login form is displayed + */ +export async function assertLoginFormVisible(page: Page): Promise { + const loginForm = page.locator('[data-testid="login-form"]'); + await expect(loginForm).toBeVisible(); + + const usernameInput = page.locator('[data-testid="username-input"]'); + const passwordInput = page.locator('[data-testid="password-input"]'); + const submitButton = page.locator('[data-testid="login-submit"]'); + + await expect(usernameInput).toBeVisible(); + await expect(passwordInput).toBeVisible(); + await expect(submitButton).toBeVisible(); +} + +/** + * Assert that user is authenticated (on inventory page) + */ +export async function assertUserAuthenticated(page: Page, baseUrl: string = 'http://localhost'): Promise { + // Check URL is on an authenticated page + await expect(page).toHaveURL(/inventory|dashboard|scanner/); + + // Verify logout button is visible + const logoutButton = page.locator('[data-testid="logout-button"]'); + await expect(logoutButton).toBeVisible(); +} + +/** + * Assert that admin dashboard is visible + */ +export async function assertAdminDashboardVisible(page: Page): Promise { + const adminOverlay = page.locator('[data-testid="admin-overlay"]'); + await expect(adminOverlay).toBeVisible(); + + // Check for tabs + const identityTab = page.locator('[data-testid="admin-tab-identity"]'); + const databaseTab = page.locator('[data-testid="admin-tab-database"]'); + + await expect(identityTab).toBeVisible(); + await expect(databaseTab).toBeVisible(); +} + +/** + * Assert that stock adjustment form is visible + */ +export async function assertStockAdjustmentVisible(page: Page, itemName: string): Promise { + const adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]'); + await expect(adjustmentForm).toBeVisible(); + + const itemNameDisplay = page.locator(`[data-testid="adjustment-item-name"]`); + await expect(itemNameDisplay).toContainText(itemName); + + const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]'); + await expect(quantityInput).toBeVisible(); +} + +/** + * Assert that AI extraction is in progress + */ +export async function assertAiExtractionInProgress(page: Page): Promise { + const extractionOverlay = page.locator('[data-testid="ai-extraction-overlay"]'); + await expect(extractionOverlay).toBeVisible(); + + const loadingSpinner = page.locator('[data-testid="extraction-loading"]'); + await expect(loadingSpinner).toBeVisible(); +} + +/** + * Assert that AI extraction results are displayed + */ +export async function assertAiExtractionResults( + page: Page, + expectedFields: { name?: string; partNumber?: string; quantity?: string } +): Promise { + const resultsForm = page.locator('[data-testid="extraction-results-form"]'); + await expect(resultsForm).toBeVisible(); + + if (expectedFields.name) { + const nameInput = resultsForm.locator('[data-testid="extracted-name"]'); + await expect(nameInput).toHaveValue(expectedFields.name); + } + + if (expectedFields.partNumber) { + const pnInput = resultsForm.locator('[data-testid="extracted-part-number"]'); + await expect(pnInput).toHaveValue(expectedFields.partNumber); + } + + if (expectedFields.quantity) { + const quantityInput = resultsForm.locator('[data-testid="extracted-quantity"]'); + await expect(quantityInput).toHaveValue(expectedFields.quantity); + } +} + +/** + * Assert that offline sync is pending + */ +export async function assertOfflineSyncPending(page: Page): Promise { + const syncIndicator = page.locator('[data-testid="offline-sync-indicator"]'); + await expect(syncIndicator).toBeVisible(); + + const pendingBadge = page.locator('[data-testid="sync-pending-badge"]'); + await expect(pendingBadge).toBeVisible(); +} + +/** + * Assert that offline sync completed + */ +export async function assertOfflineSyncComplete(page: Page): Promise { + const syncIndicator = page.locator('[data-testid="offline-sync-indicator"]'); + await expect(syncIndicator).not.toBeVisible({ timeout: 10000 }); + + const successBadge = page.locator('[data-testid="sync-success-badge"]'); + await expect(successBadge).toBeVisible(); +} + +/** + * Assert that error message is displayed + */ +export async function assertErrorMessage(page: Page, errorText?: string): Promise { + const errorToast = page.locator('[data-testid="toast-error"]'); + await expect(errorToast).toBeVisible(); + + if (errorText) { + await expect(errorToast).toContainText(errorText); + } +} + +/** + * Assert that category list is visible + */ +export async function assertCategoryListVisible(page: Page): Promise { + const categoryList = page.locator('[data-testid="category-list"]'); + await expect(categoryList).toBeVisible(); + + const categoryItems = page.locator('[data-testid="category-item"]'); + const count = await categoryItems.count(); + expect(count).toBeGreaterThan(0); +} + +/** + * Assert that scanner interface is ready + */ +export async function assertScannerReady(page: Page): Promise { + const scannerContainer = page.locator('[data-testid="scanner-container"]'); + await expect(scannerContainer).toBeVisible(); + + const cameraView = page.locator('[data-testid="camera-view"]'); + await expect(cameraView).toBeVisible(); + + const scanInput = page.locator('[data-testid="scan-input"]'); + await expect(scanInput).toBeVisible(); +} + +/** + * Assert that item creation form is visible + */ +export async function assertItemCreationFormVisible(page: Page): Promise { + const createForm = page.locator('[data-testid="create-item-form"]'); + await expect(createForm).toBeVisible(); + + const nameInput = page.locator('[data-testid="item-name-input"]'); + const categorySelect = page.locator('[data-testid="item-category-select"]'); + const submitButton = page.locator('[data-testid="item-create-submit"]'); + + await expect(nameInput).toBeVisible(); + await expect(categorySelect).toBeVisible(); + await expect(submitButton).toBeVisible(); +} + +/** + * Assert element count matches expected + */ +export async function assertElementCount( + page: Page, + selector: string, + expectedCount: number +): Promise { + const elements = page.locator(selector); + const count = await elements.count(); + expect(count).toBe(expectedCount); +} + +/** + * Assert element is disabled + */ +export async function assertElementDisabled(locator: Locator): Promise { + await expect(locator).toBeDisabled(); +} + +/** + * Assert element is enabled + */ +export async function assertElementEnabled(locator: Locator): Promise { + await expect(locator).toBeEnabled(); +} + +/** + * Assert modal is open + */ +export async function assertModalOpen(page: Page, modalId: string): Promise { + const modal = page.locator(`[data-testid="${modalId}"]`); + await expect(modal).toBeVisible(); + + const backdrop = page.locator('[data-testid="modal-backdrop"]'); + await expect(backdrop).toBeVisible(); +} + +/** + * Assert modal is closed + */ +export async function assertModalClosed(page: Page, modalId: string): Promise { + const modal = page.locator(`[data-testid="${modalId}"]`); + await expect(modal).not.toBeVisible(); +}