207 lines
6.8 KiB
TypeScript
207 lines
6.8 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { generateTestId } from '../helpers/utils';
|
|
|
|
test.describe('Add New Product/Recipe', () => {
|
|
// Use authenticated state
|
|
test.use({ storageState: 'tests/.auth/user.json' });
|
|
|
|
test('should open Add wizard from dashboard', async ({ page }) => {
|
|
await page.goto('/app/dashboard');
|
|
|
|
// Click unified Add button
|
|
const addButton = page.getByRole('button', { name: /^add$|^añadir$|^\+$/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
|
|
// Wait for wizard/modal to open
|
|
await page.waitForTimeout(500);
|
|
|
|
// Should show options (Recipe, Order, Production, etc.)
|
|
await expect(page.locator('body')).toContainText(/recipe|receta|product|producto/i);
|
|
}
|
|
});
|
|
|
|
test('should successfully add a new product', async ({ page }) => {
|
|
await page.goto('/app/operations/recipes');
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Look for Add/Create button
|
|
const addButton = page.getByRole('button', { name: /add|create|new|añadir|crear|nuevo/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
|
|
await page.waitForTimeout(500);
|
|
|
|
// Fill in product details
|
|
const productName = `Test Product ${generateTestId()}`;
|
|
|
|
const nameInput = page.getByLabel(/product.*name|name|nombre.*producto|nombre/i);
|
|
|
|
if (await nameInput.isVisible().catch(() => false)) {
|
|
await nameInput.fill(productName);
|
|
|
|
// Fill in price
|
|
const priceInput = page.getByLabel(/price|precio|cost|costo/i);
|
|
|
|
if (await priceInput.isVisible().catch(() => false)) {
|
|
await priceInput.fill('9.99');
|
|
}
|
|
|
|
// Select category if available
|
|
const categorySelect = page.getByLabel(/category|categoría|type|tipo/i);
|
|
|
|
if (await categorySelect.isVisible().catch(() => false)) {
|
|
await categorySelect.click();
|
|
|
|
// Select first option
|
|
const firstOption = page.getByRole('option').first();
|
|
|
|
if (await firstOption.isVisible({ timeout: 2000 }).catch(() => false)) {
|
|
await firstOption.click();
|
|
}
|
|
}
|
|
|
|
// Submit form
|
|
const submitButton = page.getByRole('button', { name: /save|create|submit|guardar|crear|enviar/i });
|
|
await submitButton.click();
|
|
|
|
// Should show success message
|
|
await expect(page.locator('body')).toContainText(/success|created|éxito|creado/i, {
|
|
timeout: 5000,
|
|
});
|
|
|
|
// Product should appear in list
|
|
await expect(page.locator('body')).toContainText(productName, { timeout: 5000 });
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should show validation errors for missing required fields', async ({ page }) => {
|
|
await page.goto('/app/operations/recipes');
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Look for Add button
|
|
const addButton = page.getByRole('button', { name: /add|create|new|añadir|crear|nuevo/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Try to submit without filling fields
|
|
const submitButton = page.getByRole('button', { name: /save|create|submit|guardar|crear|enviar/i });
|
|
|
|
if (await submitButton.isVisible().catch(() => false)) {
|
|
await submitButton.click();
|
|
|
|
// Should show validation errors
|
|
await expect(page.locator('body')).toContainText(/required|obligatorio|necesario/i, {
|
|
timeout: 3000,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should add ingredients to a recipe', async ({ page }) => {
|
|
await page.goto('/app/operations/recipes');
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const addButton = page.getByRole('button', { name: /add|create|new|añadir|crear|nuevo/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Fill basic info
|
|
const nameInput = page.getByLabel(/product.*name|name|nombre.*producto|nombre/i);
|
|
|
|
if (await nameInput.isVisible().catch(() => false)) {
|
|
await nameInput.fill(`Test Recipe ${generateTestId()}`);
|
|
|
|
// Look for "Add Ingredient" button
|
|
const addIngredientButton = page.getByRole('button', { name: /add.*ingredient|añadir.*ingrediente/i });
|
|
|
|
if (await addIngredientButton.isVisible().catch(() => false)) {
|
|
await addIngredientButton.click();
|
|
|
|
// Fill ingredient details
|
|
const ingredientInput = page.getByLabel(/ingredient|ingrediente/i).first();
|
|
|
|
if (await ingredientInput.isVisible().catch(() => false)) {
|
|
await ingredientInput.fill('Flour');
|
|
|
|
// Fill quantity
|
|
const quantityInput = page.getByLabel(/quantity|cantidad/i).first();
|
|
|
|
if (await quantityInput.isVisible().catch(() => false)) {
|
|
await quantityInput.fill('500');
|
|
}
|
|
|
|
// Ingredient should be added
|
|
await expect(page.locator('body')).toContainText(/flour|harina/i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should upload product image', async ({ page }) => {
|
|
await page.goto('/app/operations/recipes');
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const addButton = page.getByRole('button', { name: /add|create|new|añadir|crear|nuevo/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for file upload
|
|
const fileInput = page.locator('input[type="file"]');
|
|
|
|
if (await fileInput.isVisible().catch(() => false)) {
|
|
// File upload is available
|
|
await expect(fileInput).toBeAttached();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('should cancel product creation', async ({ page }) => {
|
|
await page.goto('/app/operations/recipes');
|
|
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const addButton = page.getByRole('button', { name: /add|create|new|añadir|crear|nuevo/i }).first();
|
|
|
|
if (await addButton.isVisible().catch(() => false)) {
|
|
await addButton.click();
|
|
await page.waitForTimeout(500);
|
|
|
|
// Fill some data
|
|
const nameInput = page.getByLabel(/product.*name|name|nombre.*producto|nombre/i);
|
|
|
|
if (await nameInput.isVisible().catch(() => false)) {
|
|
await nameInput.fill('Test Product to Cancel');
|
|
|
|
// Look for Cancel button
|
|
const cancelButton = page.getByRole('button', { name: /cancel|cancelar|close|cerrar/i });
|
|
|
|
if (await cancelButton.isVisible().catch(() => false)) {
|
|
await cancelButton.click();
|
|
|
|
// Should close form/modal
|
|
await page.waitForTimeout(500);
|
|
|
|
// Should not show the test product
|
|
const bodyText = await page.locator('body').textContent();
|
|
expect(bodyText).not.toContain('Test Product to Cancel');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|