258 lines
9.6 KiB
TypeScript
258 lines
9.6 KiB
TypeScript
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 });
|
|
});
|
|
});
|