Add OMS WMS ERP platform
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
|
||||
test.describe("Authentication System", () => {
|
||||
test("Redirect to login when not authenticated", async ({ page }) => {
|
||||
// Try to access admin page without login
|
||||
await page.goto("http://localhost:5173/admin/dashboard")
|
||||
|
||||
// Should redirect to login
|
||||
await expect(page).toHaveURL(/login/)
|
||||
console.log("✅ Not authenticated users redirected to login")
|
||||
})
|
||||
|
||||
test("Login with valid credentials", async ({ page }) => {
|
||||
// Go to login page
|
||||
await page.goto("http://localhost:5173/login")
|
||||
|
||||
// Fill login form
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
|
||||
// Submit
|
||||
await page.click("button[type='submit']")
|
||||
|
||||
// Should redirect to dashboard
|
||||
await page.waitForURL(/admin\/dashboard/, { timeout: 5000 })
|
||||
console.log("✅ Login successful")
|
||||
|
||||
// Check if dashboard loads
|
||||
const dashboard = await page.locator("h1, .dashboard-title").first().isVisible().catch(() => false)
|
||||
console.log("✅ Dashboard visible after login")
|
||||
})
|
||||
|
||||
test("Access admin pages after login", async ({ page }) => {
|
||||
// Login first
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Check sidebar is visible
|
||||
const sidebar = await page.locator(".sidebar").isVisible()
|
||||
expect(sidebar).toBeTruthy()
|
||||
console.log("✅ Sidebar visible")
|
||||
|
||||
// Check user info in sidebar
|
||||
const userInfo = await page.locator(".user-info").isVisible()
|
||||
expect(userInfo).toBeTruthy()
|
||||
console.log("✅ User info displayed in sidebar")
|
||||
|
||||
// Check logout button
|
||||
const logoutBtn = await page.locator(".logout-btn").isVisible()
|
||||
expect(logoutBtn).toBeTruthy()
|
||||
console.log("✅ Logout button visible")
|
||||
|
||||
// Navigate to Orders page
|
||||
await page.click("text=Orders")
|
||||
await page.waitForURL(/admin\/orders/)
|
||||
console.log("✅ Can navigate to Orders page")
|
||||
|
||||
// Navigate to Products page
|
||||
await page.click("text=Products")
|
||||
await page.waitForURL(/admin\/products/)
|
||||
console.log("✅ Can navigate to Products page")
|
||||
})
|
||||
|
||||
test("Logout functionality", async ({ page }) => {
|
||||
// Login first
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Register dialog handler BEFORE clicking logout (timing-critical)
|
||||
page.once("dialog", async (dialog) => {
|
||||
console.log(`Dialog: ${dialog.message()}`)
|
||||
await dialog.accept()
|
||||
})
|
||||
|
||||
// Click logout
|
||||
await page.click(".logout-btn")
|
||||
|
||||
// Should redirect to login
|
||||
await page.waitForURL(/login/, { timeout: 5000 })
|
||||
console.log("✅ Logout successful")
|
||||
})
|
||||
|
||||
test("Session persistence", async ({ page, context }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Check localStorage
|
||||
const token = await page.evaluate(() => localStorage.getItem("auth-token"))
|
||||
expect(token).toBeTruthy()
|
||||
console.log("✅ Auth token stored in localStorage")
|
||||
|
||||
// Open new page in same context (shares storage)
|
||||
const page2 = await context.newPage()
|
||||
await page2.goto("http://localhost:5173/admin/dashboard")
|
||||
|
||||
// Should be authenticated
|
||||
await expect(page2).toHaveURL(/admin\/dashboard/)
|
||||
console.log("✅ Session persists across pages")
|
||||
|
||||
await page2.close()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* E2E Tests: Complete Order-to-Inventory Flow
|
||||
* Tests the full business flow from order creation to inventory update
|
||||
*
|
||||
* Run: npm run test:e2e
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('OMS·WMS·ERP Complete Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to application
|
||||
await page.goto('http://localhost:5173')
|
||||
|
||||
// Wait for app to load
|
||||
await page.waitForLoadState('networkidle')
|
||||
})
|
||||
|
||||
test('orders page loads with navigation', async ({ page }) => {
|
||||
// Find navigation to orders
|
||||
const ordersNav = page.getByRole('link', { name: /orders/i })
|
||||
await expect(ordersNav).toBeVisible()
|
||||
|
||||
// Click and verify page loads
|
||||
await ordersNav.click()
|
||||
await page.waitForURL('**/orders**')
|
||||
await expect(page).toHaveTitle(/.*orders.*/i)
|
||||
})
|
||||
|
||||
test('inventory page loads with navigation', async ({ page }) => {
|
||||
const inventoryNav = page.getByRole('link', { name: /inventory/i })
|
||||
await expect(inventoryNav).toBeVisible()
|
||||
|
||||
await inventoryNav.click()
|
||||
await page.waitForURL('**/inventory**')
|
||||
await expect(page).toHaveTitle(/.*inventory.*/i)
|
||||
})
|
||||
|
||||
test('products page loads with navigation', async ({ page }) => {
|
||||
const productsNav = page.getByRole('link', { name: /products/i })
|
||||
await expect(productsNav).toBeVisible()
|
||||
|
||||
await productsNav.click()
|
||||
await page.waitForURL('**/products**')
|
||||
await expect(page).toHaveTitle(/.*products.*/i)
|
||||
})
|
||||
|
||||
test('responsive navigation on mobile', async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
// Look for mobile menu toggle
|
||||
const menuToggle = page.getByRole('button', { name: /menu|toggle/i })
|
||||
if (await menuToggle.isVisible()) {
|
||||
await menuToggle.click()
|
||||
await expect(page.getByRole('navigation')).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test('orders page displays data', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Wait for table to load
|
||||
const table = page.locator('table')
|
||||
if (await table.isVisible({ timeout: 5000 }).catch(() => false)) {
|
||||
const rows = await page.locator('tbody tr').count()
|
||||
// Should have at least mock data
|
||||
expect(rows).toBeGreaterThan(0)
|
||||
}
|
||||
})
|
||||
|
||||
test('inventory page displays data', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/inventory')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
const content = page.locator('main, [role="main"]')
|
||||
await expect(content).toBeVisible()
|
||||
})
|
||||
|
||||
test('products page displays data', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/products')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
const content = page.locator('main, [role="main"]')
|
||||
await expect(content).toBeVisible()
|
||||
})
|
||||
|
||||
test('buttons are accessible and interactive', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Look for action buttons
|
||||
const buttons = page.getByRole('button')
|
||||
const count = await buttons.count()
|
||||
|
||||
if (count > 0) {
|
||||
const firstButton = buttons.first()
|
||||
await expect(firstButton).toBeEnabled()
|
||||
}
|
||||
})
|
||||
|
||||
test('forms have proper validation', async ({ page }) => {
|
||||
// Navigate to create order (if form exists)
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
|
||||
// Look for create button or form
|
||||
const createButton = page.getByRole('button', { name: /create|new|add/i })
|
||||
if (await createButton.isVisible({ timeout: 3000 }).catch(() => false)) {
|
||||
await createButton.click()
|
||||
|
||||
// Look for form inputs
|
||||
const inputs = page.locator('input')
|
||||
const inputCount = await inputs.count()
|
||||
|
||||
if (inputCount > 0) {
|
||||
// Verify inputs are interactive
|
||||
const firstInput = inputs.first()
|
||||
await expect(firstInput).toBeFocusable()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('error handling shows user-friendly messages', async ({ page }) => {
|
||||
// This test would require a specific error scenario
|
||||
// For now, verify that error states don't break the UI
|
||||
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Verify page content is visible even if API might fail
|
||||
const mainContent = page.locator('main, [role="main"]')
|
||||
await expect(mainContent).toBeVisible()
|
||||
})
|
||||
|
||||
test('page navigation history works', async ({ page }) => {
|
||||
// Start at home
|
||||
await page.goto('http://localhost:5173')
|
||||
|
||||
// Navigate to orders
|
||||
await page.getByRole('link', { name: /orders/i }).click()
|
||||
await page.waitForURL('**/orders**')
|
||||
|
||||
// Navigate to products
|
||||
await page.getByRole('link', { name: /products/i }).click()
|
||||
await page.waitForURL('**/products**')
|
||||
|
||||
// Go back
|
||||
await page.goBack()
|
||||
await page.waitForURL('**/orders**')
|
||||
|
||||
// Go forward
|
||||
await page.goForward()
|
||||
await page.waitForURL('**/products**')
|
||||
})
|
||||
|
||||
test('accessibility: keyboard navigation', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173')
|
||||
|
||||
// Tab through navigation
|
||||
await page.keyboard.press('Tab')
|
||||
const focusedElement = await page.evaluate(() => {
|
||||
return document.activeElement?.tagName
|
||||
})
|
||||
|
||||
// Should focus on an interactive element
|
||||
expect(['A', 'BUTTON', 'INPUT', 'SELECT']).toContain(focusedElement)
|
||||
})
|
||||
|
||||
test('accessibility: color contrast', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Run axe accessibility audit
|
||||
const { injectAxe, checkA11y } = require('axe-playwright')
|
||||
|
||||
await injectAxe(page)
|
||||
await checkA11y(page, null, {
|
||||
detailedReport: true,
|
||||
detailedReportOptions: {
|
||||
html: true
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Order Management Flow', () => {
|
||||
test('create order workflow', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Look for create order button
|
||||
const createBtn = page.getByRole('button', { name: /create|new|add/i })
|
||||
|
||||
if (await createBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
await createBtn.click()
|
||||
|
||||
// Verify modal or form appears
|
||||
const form = page.locator('form')
|
||||
if (await form.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
// Fill in sample data
|
||||
const inputs = form.locator('input, select')
|
||||
const inputCount = await inputs.count()
|
||||
|
||||
expect(inputCount).toBeGreaterThan(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('view order details', async ({ page }) => {
|
||||
await page.goto('http://localhost:5173/orders')
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
// Find first order row
|
||||
const firstRow = page.locator('tbody tr').first()
|
||||
if (await firstRow.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
await firstRow.click()
|
||||
|
||||
// Verify details view loads
|
||||
await page.waitForLoadState('networkidle')
|
||||
const details = page.locator('[data-test="order-details"], .order-details')
|
||||
if (await details.isVisible({ timeout: 2000 }).catch(() => false)) {
|
||||
expect(details).toBeVisible()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test.describe('Performance Checks', () => {
|
||||
test('page loads in acceptable time', async ({ page }) => {
|
||||
const startTime = Date.now()
|
||||
|
||||
await page.goto('http://localhost:5173/orders', {
|
||||
waitUntil: 'networkidle'
|
||||
})
|
||||
|
||||
const loadTime = Date.now() - startTime
|
||||
|
||||
// Should load within 3 seconds (including network delays)
|
||||
expect(loadTime).toBeLessThan(3000)
|
||||
})
|
||||
|
||||
test('no console errors on navigation', async ({ page }) => {
|
||||
const errors: string[] = []
|
||||
|
||||
page.on('console', (msg) => {
|
||||
if (msg.type() === 'error') {
|
||||
errors.push(msg.text())
|
||||
}
|
||||
})
|
||||
|
||||
await page.goto('http://localhost:5173')
|
||||
await page.getByRole('link', { name: /orders/i }).click()
|
||||
await page.waitForLoadState('networkidle')
|
||||
|
||||
expect(errors).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,136 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
|
||||
test.describe("Form Submission & CRUD Operations", () => {
|
||||
test("Create customer form submission", async ({ page }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Navigate to customers
|
||||
await page.click("text=Customers")
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
|
||||
// Click create customer
|
||||
await page.click("text=Create Customer")
|
||||
await page.waitForURL(/admin\/customers\/new/)
|
||||
|
||||
// Fill form
|
||||
await page.fill("input#name", "Test Company ABC")
|
||||
await page.fill("input#email", "test@company.com")
|
||||
await page.fill("input#phone", "010-1234-5678")
|
||||
await page.fill("input#address", "Seoul, Korea")
|
||||
await page.fill("input#creditLimit", "5000000")
|
||||
|
||||
// Submit
|
||||
await page.click("button[type='submit']:has-text('Create')")
|
||||
|
||||
// Should redirect to customers list
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
console.log("✅ Customer created successfully")
|
||||
|
||||
// Verify in list (should show newly created customer)
|
||||
const customerNameVisible = await page.locator("text=Test Company ABC").isVisible()
|
||||
expect(customerNameVisible).toBeTruthy()
|
||||
console.log("✅ Customer appears in list")
|
||||
})
|
||||
|
||||
test("View customer list with data", async ({ page }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Navigate to customers
|
||||
await page.click("text=Customers")
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
|
||||
// Check if table has data rows
|
||||
const tableRows = await page.locator("table tbody tr").count()
|
||||
console.log(`✅ Found ${tableRows} customer rows`)
|
||||
|
||||
// Verify table structure (headers)
|
||||
const headers = await page.locator("table thead th").allTextContents()
|
||||
expect(headers).toContain("Customer Name")
|
||||
expect(headers).toContain("Email")
|
||||
expect(headers).toContain("Status")
|
||||
console.log("✅ Customer table has correct structure")
|
||||
})
|
||||
|
||||
test("Edit customer (if data available)", async ({ page }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Navigate to customers
|
||||
await page.click("text=Customers")
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
|
||||
// Check if edit button exists
|
||||
const editButton = page.locator("a:text('Edit')").first()
|
||||
const isVisible = await editButton.isVisible().catch(() => false)
|
||||
|
||||
if (isVisible) {
|
||||
await editButton.click()
|
||||
await page.waitForURL(/admin\/customers\/.*\/edit/)
|
||||
|
||||
// Verify form loaded
|
||||
const form = await page.locator("form").first()
|
||||
expect(form).toBeTruthy()
|
||||
console.log("✅ Edit form loaded successfully")
|
||||
|
||||
// Make a change
|
||||
const nameInput = page.locator("input#name")
|
||||
const currentValue = await nameInput.inputValue()
|
||||
await nameInput.fill(currentValue + " (Updated)")
|
||||
|
||||
// Submit
|
||||
await page.click("button[type='submit']:has-text('Update')")
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
console.log("✅ Customer updated successfully")
|
||||
} else {
|
||||
console.log("⚠️ No customers in list to edit (this is OK for fresh database)")
|
||||
}
|
||||
})
|
||||
|
||||
test("Form validation on customer creation", async ({ page }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/)
|
||||
|
||||
// Navigate to create customer
|
||||
await page.click("text=Customers")
|
||||
await page.waitForURL(/admin\/customers/)
|
||||
await page.click("text=Create Customer")
|
||||
await page.waitForURL(/admin\/customers\/new/)
|
||||
|
||||
// Try to submit empty form
|
||||
await page.click("button[type='submit']:has-text('Create')")
|
||||
|
||||
// Should show validation error (page should not navigate)
|
||||
await page.waitForTimeout(500)
|
||||
const currentUrl = page.url()
|
||||
expect(currentUrl).toContain("/customers/new")
|
||||
console.log("✅ Form validation prevents empty submission")
|
||||
|
||||
// Fill only name and try again
|
||||
await page.fill("input#name", "Incomplete Company")
|
||||
await page.fill("input#email", "invalid-email")
|
||||
await page.click("button[type='submit']:has-text('Create')")
|
||||
|
||||
// Should still be on form (email validation failed)
|
||||
await page.waitForTimeout(500)
|
||||
const stillOnForm = page.url().includes("/customers/new")
|
||||
console.log(`✅ Email validation ${stillOnForm ? "working" : "bypassed (may be OK)"}`)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
|
||||
test("Complete admin navigation", async ({ page }) => {
|
||||
// Login
|
||||
await page.goto("http://localhost:5173/login")
|
||||
await page.fill("input[type='text']", "admin")
|
||||
await page.fill("input[type='password']", "password")
|
||||
await page.click("button[type='submit']")
|
||||
await page.waitForURL(/admin\/dashboard/, { timeout: 5000 })
|
||||
console.log("✅ Logged in")
|
||||
|
||||
// Test all sidebar links
|
||||
const links = [
|
||||
{ text: "Dashboard", path: /admin\/dashboard/ },
|
||||
{ text: "Orders", path: /admin\/orders/ },
|
||||
{ text: "Products", path: /admin\/products/ },
|
||||
{ text: "Customers", path: /admin\/customers/ },
|
||||
{ text: "Inventory", path: /admin\/inventory/ },
|
||||
{ text: "Warehouses", path: /admin\/warehouses/ }
|
||||
]
|
||||
|
||||
for (const link of links) {
|
||||
await page.click(`text=${link.text}`)
|
||||
await page.waitForURL(link.path, { timeout: 5000 })
|
||||
const heading = await page.locator("h2.page-title, h1").first()
|
||||
expect(heading).toBeTruthy()
|
||||
console.log(`✅ ${link.text} page loaded`)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* E2E Tests Index
|
||||
* Full workflow tests using Playwright
|
||||
*
|
||||
* Test Scenarios:
|
||||
* - Order Flow (create, filter, edit, delete)
|
||||
* - Inventory Flow (transfer, low stock alerts)
|
||||
* - Dashboard flows (KPI cards, navigation)
|
||||
*/
|
||||
|
||||
export * from './order-flow.spec'
|
||||
export * from './inventory-flow.spec'
|
||||
@@ -0,0 +1,49 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Inventory Management E2E Tests', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('http://localhost:5173')
|
||||
})
|
||||
|
||||
test('should transfer inventory between warehouses', async ({ page }) => {
|
||||
await page.click('a[href="/inventory"]')
|
||||
await page.click('button:has-text("Transfer")')
|
||||
|
||||
// Select warehouses
|
||||
await page.selectOption('select:has-text("From Warehouse")', 'WH-001')
|
||||
await page.selectOption('select:has-text("To Warehouse")', 'WH-002')
|
||||
|
||||
// Enter product and quantity
|
||||
await page.fill('input[name="productSku"]', 'SKU-12345')
|
||||
await page.fill('input[name="quantity"]', '100')
|
||||
|
||||
// Submit transfer
|
||||
await page.click('button:has-text("Transfer")')
|
||||
|
||||
// Verify success
|
||||
await expect(page.locator('text=Transfer completed')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should view inventory levels', async ({ page }) => {
|
||||
await page.click('a[href="/inventory"]')
|
||||
|
||||
// Verify table displays
|
||||
const table = await page.locator('table').isVisible()
|
||||
expect(table).toBeTruthy()
|
||||
|
||||
// Verify columns
|
||||
await expect(page.locator('th:has-text("Product")')).toBeVisible()
|
||||
await expect(page.locator('th:has-text("Quantity")')).toBeVisible()
|
||||
await expect(page.locator('th:has-text("Available")')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should alert on low stock items', async ({ page }) => {
|
||||
await page.click('a[href="/inventory"]')
|
||||
await page.click('button:has-text("Low Stock")')
|
||||
|
||||
// Verify low stock items displayed
|
||||
const lowStockBadges = await page.locator('.badge-warning')
|
||||
const count = await lowStockBadges.count()
|
||||
expect(count).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,122 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
test.describe('Order Management E2E Tests', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to app
|
||||
await page.goto('http://localhost:5173')
|
||||
})
|
||||
|
||||
test('should complete full order workflow', async ({ page }) => {
|
||||
// Navigate to orders
|
||||
await page.click('a[href="/orders"]')
|
||||
await expect(page).toHaveURL(/orders/)
|
||||
|
||||
// Create new order
|
||||
await page.click('button:has-text("New Order")')
|
||||
await expect(page).toHaveTitle(/Create Order/)
|
||||
|
||||
// Select customer
|
||||
await page.selectOption('select[name="customerId"]', 'cust-001')
|
||||
|
||||
// Set dates
|
||||
await page.fill('input[name="orderDate"]', '2026-08-01')
|
||||
await page.fill('input[name="dueDate"]', '2026-08-15')
|
||||
|
||||
// Add items
|
||||
await page.click('button:has-text("Add Item")')
|
||||
await page.fill('input[name="productSku"]', 'SKU-12345')
|
||||
await page.fill('input[name="quantity"]', '100')
|
||||
await page.fill('input[name="unitPrice"]', '50')
|
||||
|
||||
// Verify totals
|
||||
const subtotal = await page.locator('text=Subtotal: $5000').isVisible()
|
||||
expect(subtotal).toBeTruthy()
|
||||
|
||||
// Save order
|
||||
await page.click('button:has-text("Create Order")')
|
||||
|
||||
// Verify order created
|
||||
await expect(page).toHaveURL(/orders\//)
|
||||
const orderNumber = await page.locator('h1').textContent()
|
||||
expect(orderNumber).toMatch(/ORD-/)
|
||||
})
|
||||
|
||||
test('should filter orders by status', async ({ page }) => {
|
||||
await page.click('a[href="/orders"]')
|
||||
|
||||
// Open filter
|
||||
await page.click('button:has-text("Filter")')
|
||||
|
||||
// Select status
|
||||
await page.selectOption('select[name="status"]', 'CONFIRMED')
|
||||
|
||||
// Apply
|
||||
await page.click('button:has-text("Apply")')
|
||||
|
||||
// Verify filtered results
|
||||
const rows = await page.locator('table tbody tr')
|
||||
const count = await rows.count()
|
||||
expect(count).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('should edit order', async ({ page }) => {
|
||||
await page.click('a[href="/orders"]')
|
||||
|
||||
// Find and click first order
|
||||
await page.click('table tbody tr:first-child')
|
||||
await page.click('button:has-text("Edit")')
|
||||
|
||||
// Update status
|
||||
await page.selectOption('select[name="status"]', 'SHIPPED')
|
||||
|
||||
// Save
|
||||
await page.click('button:has-text("Save")')
|
||||
|
||||
// Verify update
|
||||
await expect(page.locator('text=Order updated successfully')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should delete order with confirmation', async ({ page }) => {
|
||||
await page.click('a[href="/orders"]')
|
||||
|
||||
const initialCount = await page.locator('table tbody tr').count()
|
||||
|
||||
// Find and click first order
|
||||
await page.click('table tbody tr:first-child')
|
||||
await page.click('button:has-text("Delete")')
|
||||
|
||||
// Confirm deletion
|
||||
await page.click('button:has-text("Confirm")')
|
||||
|
||||
// Verify deletion
|
||||
await expect(page.locator('text=Order deleted successfully')).toBeVisible()
|
||||
const finalCount = await page.locator('table tbody tr').count()
|
||||
expect(finalCount).toBe(initialCount - 1)
|
||||
})
|
||||
|
||||
test('should calculate order totals correctly', async ({ page }) => {
|
||||
await page.click('a[href="/orders"]')
|
||||
await page.click('button:has-text("New Order")')
|
||||
|
||||
await page.selectOption('select[name="customerId"]', 'cust-001')
|
||||
await page.fill('input[name="orderDate"]', '2026-08-01')
|
||||
await page.fill('input[name="dueDate"]', '2026-08-15')
|
||||
|
||||
// Add multiple items
|
||||
await page.click('button:has-text("Add Item")')
|
||||
await page.fill('input[name="productSku"]', 'SKU-001')
|
||||
await page.fill('input[name="quantity"]', '100')
|
||||
await page.fill('input[name="unitPrice"]', '50') // 5000
|
||||
|
||||
await page.click('button:has-text("Add Item")')
|
||||
const skuInputs = await page.locator('input[name="productSku"]')
|
||||
await skuInputs.nth(1).fill('SKU-002')
|
||||
await page.locator('input[name="quantity"]').nth(1).fill('50')
|
||||
await page.locator('input[name="unitPrice"]').nth(1).fill('30') // 1500
|
||||
|
||||
// Verify totals: 6500 subtotal, 650 tax, 7150 total
|
||||
await expect(page.locator('text=Subtotal: $6500')).toBeVisible()
|
||||
await expect(page.locator('text=Tax: $650')).toBeVisible()
|
||||
await expect(page.locator('text=Total: $7150')).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user