feat: create scan and adjust workflow e2e tests
This commit is contained in:
257
frontend/e2e/workflows/2-scan-adjust.spec.ts
Normal file
257
frontend/e2e/workflows/2-scan-adjust.spec.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import * as auth from '../fixtures/auth';
|
||||
import * as assertions from '../utils/assertions';
|
||||
import * as helpers from '../utils/helpers';
|
||||
import { LOCAL_USERS, TEST_ITEMS } from '../fixtures/test-data';
|
||||
|
||||
test.describe('Scan and Adjust Workflow', () => {
|
||||
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to app and login
|
||||
await page.goto(BASE_URL);
|
||||
await auth.loginWithLocalUser(page, LOCAL_USERS.admin, BASE_URL);
|
||||
await assertions.assertUserAuthenticated(page, BASE_URL);
|
||||
|
||||
// Navigate to scanner page
|
||||
await page.goto(`${BASE_URL}/scanner`);
|
||||
await assertions.assertScannerReady(page);
|
||||
});
|
||||
|
||||
test('should display scanner interface with camera view', async ({ page }) => {
|
||||
await assertions.assertScannerReady(page);
|
||||
|
||||
// Verify camera controls
|
||||
const zoomButton = page.locator('[data-testid="zoom-control"]');
|
||||
const cameraIndicator = page.locator('[data-testid="camera-indicator"]');
|
||||
|
||||
await expect(zoomButton).toBeVisible();
|
||||
await expect(cameraIndicator).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display scan input field', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await expect(scanInput).toBeVisible();
|
||||
await expect(scanInput).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle barcode scan and match to inventory item', async ({ page }) => {
|
||||
// Simulate barcode scan by entering barcode in input
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
|
||||
// Enter first test item's barcode
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Should show stock adjustment form
|
||||
await assertions.assertStockAdjustmentVisible(page, TEST_ITEMS[0].name);
|
||||
|
||||
// Verify item details are shown
|
||||
const itemName = page.locator('[data-testid="adjustment-item-name"]');
|
||||
await expect(itemName).toContainText(TEST_ITEMS[0].name);
|
||||
});
|
||||
|
||||
test('should default to check-in operation', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Check-in radio should be selected
|
||||
const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||
await expect(checkInRadio).toBeChecked();
|
||||
});
|
||||
|
||||
test('should allow switching between check-in and check-out', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Switch to check-out
|
||||
const checkOutRadio = page.locator('[data-testid="operation-checkout"]');
|
||||
await checkOutRadio.click();
|
||||
await expect(checkOutRadio).toBeChecked();
|
||||
|
||||
// Switch back to check-in
|
||||
const checkInRadio = page.locator('[data-testid="operation-checkin"]');
|
||||
await checkInRadio.click();
|
||||
await expect(checkInRadio).toBeChecked();
|
||||
});
|
||||
|
||||
test('should allow quantity adjustment', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Change quantity
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('10');
|
||||
|
||||
// Verify value changed
|
||||
const value = await helpers.getInputValue(page, '[data-testid="adjustment-quantity-input"]');
|
||||
expect(value).toBe('10');
|
||||
});
|
||||
|
||||
test('should reject invalid quantities', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Try invalid quantity
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('-5');
|
||||
|
||||
// Submit should be disabled or show error
|
||||
const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||
if (await submitButton.isEnabled()) {
|
||||
await submitButton.click();
|
||||
const errorToast = page.locator('[data-testid="toast-error"]');
|
||||
await expect(errorToast).toBeVisible({ timeout: 3000 });
|
||||
}
|
||||
});
|
||||
|
||||
test('should submit stock adjustment successfully', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Set quantity
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('5');
|
||||
|
||||
// Submit
|
||||
const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Should show success message
|
||||
const successToast = page.locator('[data-testid="toast-success"]');
|
||||
await expect(successToast).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
test('should close adjustment form after successful submission', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('5');
|
||||
|
||||
const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Wait for success and form to close
|
||||
const adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]');
|
||||
await expect(adjustmentForm).not.toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Scanner input should be focused again
|
||||
const scanner = page.locator('[data-testid="scan-input"]');
|
||||
await expect(scanner).toBeFocused();
|
||||
});
|
||||
|
||||
test('should handle unknown barcode', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill('999999999');
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Should show error or create new item dialog
|
||||
const errorOrDialog = page.locator(
|
||||
'[data-testid="toast-error"], [data-testid="new-item-dialog"]'
|
||||
);
|
||||
await expect(errorOrDialog).toBeVisible({ timeout: 3000 });
|
||||
});
|
||||
|
||||
test('should support multiple consecutive scans', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
|
||||
// First scan
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
let adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]');
|
||||
await expect(adjustmentForm).toBeVisible();
|
||||
|
||||
// Complete first adjustment
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('5');
|
||||
const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Wait for form to close
|
||||
await expect(adjustmentForm).not.toBeVisible({ timeout: 5000 });
|
||||
|
||||
// Second scan
|
||||
await scanInput.fill(TEST_ITEMS[1].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Second adjustment form should appear
|
||||
adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]');
|
||||
await expect(adjustmentForm).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display item quantity history', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Check if history is displayed
|
||||
const historySection = page.locator('[data-testid="adjustment-history"]');
|
||||
if (await historySection.isVisible()) {
|
||||
const historyItems = page.locator('[data-testid="history-item"]');
|
||||
expect(await historyItems.count()).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('should allow canceling adjustment', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Click cancel button
|
||||
const cancelButton = page.locator('[data-testid="adjustment-cancel"]');
|
||||
if (await cancelButton.isVisible()) {
|
||||
await cancelButton.click();
|
||||
|
||||
// Form should close
|
||||
const adjustmentForm = page.locator('[data-testid="stock-adjustment-form"]');
|
||||
await expect(adjustmentForm).not.toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('should display current inventory count', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
// Current quantity should be displayed
|
||||
const currentQuantity = page.locator('[data-testid="current-quantity"]');
|
||||
await expect(currentQuantity).toBeVisible();
|
||||
|
||||
const quantityText = await currentQuantity.textContent();
|
||||
expect(quantityText).toMatch(/\d+/);
|
||||
});
|
||||
|
||||
test('should show category in item details', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
const categoryDisplay = page.locator('[data-testid="item-category"]');
|
||||
if (await categoryDisplay.isVisible()) {
|
||||
const categoryText = await categoryDisplay.textContent();
|
||||
expect(categoryText).toContain(TEST_ITEMS[0].category);
|
||||
}
|
||||
});
|
||||
|
||||
test('should clear scan input after successful operation', async ({ page }) => {
|
||||
const scanInput = page.locator('[data-testid="scan-input"]');
|
||||
await scanInput.fill(TEST_ITEMS[0].barcode);
|
||||
await scanInput.press('Enter');
|
||||
|
||||
const quantityInput = page.locator('[data-testid="adjustment-quantity-input"]');
|
||||
await quantityInput.fill('5');
|
||||
|
||||
const submitButton = page.locator('[data-testid="adjustment-submit"]');
|
||||
await submitButton.click();
|
||||
|
||||
// Wait for form to close and input to clear
|
||||
await expect(scanInput).toHaveValue('', { timeout: 5000 });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user