import { test, expect } from '@playwright/test'; test.describe('Recipe Management E2E Tests', () => { test.beforeEach(async ({ page }) => { // Navigate to the app before each test await page.goto('/'); }); test('should display the recipe list page', async ({ page }) => { // Wait for the page to load await page.waitForLoadState('networkidle'); // Check that we're on the recipe list page await expect(page.locator('h2')).toContainText('My Recipes'); }); test('should show empty state when no recipes exist', async ({ page }) => { // Mock API to return empty recipe list await page.route('**/api/recipes', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [], total: 0, page: 1, pageSize: 20, }), }); }); await page.goto('/'); // Check for empty state message await expect(page.getByText(/No recipes yet/i)).toBeVisible(); }); test('should display recipes when available', async ({ page }) => { // Mock API to return recipes await page.route('**/api/recipes', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [ { id: '1', title: 'Spaghetti Carbonara', description: 'Classic Italian pasta', totalTime: 30, servings: 4, }, { id: '2', title: 'Chocolate Cake', description: 'Rich chocolate dessert', totalTime: 60, servings: 8, }, ], total: 2, page: 1, pageSize: 20, }), }); }); await page.goto('/'); // Check that recipes are displayed await expect(page.getByText('Spaghetti Carbonara')).toBeVisible(); await expect(page.getByText('Chocolate Cake')).toBeVisible(); }); test('should navigate to recipe detail when clicking a recipe', async ({ page }) => { // Mock recipes list await page.route('**/api/recipes', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [ { id: '1', title: 'Test Recipe', description: 'Test Description', }, ], total: 1, page: 1, pageSize: 20, }), }); }); // Mock recipe detail await page.route('**/api/recipes/1', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: { id: '1', title: 'Test Recipe', description: 'Detailed description', ingredients: [ { id: '1', name: 'Flour', amount: '2', unit: 'cups', order: 0 }, ], instructions: [ { id: '1', step: 1, text: 'Mix ingredients' }, ], }, }), }); }); await page.goto('/'); // Click on the recipe await page.getByText('Test Recipe').click(); // Verify we navigated to the detail page await expect(page).toHaveURL(/\/recipes\/1/); }); test('should import recipe from URL', async ({ page }) => { // Mock import endpoint await page.route('**/api/recipes/import', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ success: true, recipe: { title: 'Imported Recipe', description: 'Recipe from URL', ingredients: [], instructions: [], }, }), }); }); // Navigate to import page (adjust based on your routing) await page.goto('/import'); // Fill in URL await page.fill('input[type="url"]', 'https://example.com/recipe'); // Click import button await page.click('button:has-text("Import")'); // Wait for success message or redirect await expect(page.getByText(/Imported Recipe|Success/i)).toBeVisible({ timeout: 5000, }); }); test('should handle API errors gracefully', async ({ page }) => { // Mock API to return error await page.route('**/api/recipes', async (route) => { await route.fulfill({ status: 500, contentType: 'application/json', body: JSON.stringify({ error: 'Internal server error', }), }); }); await page.goto('/'); // Check for error message await expect(page.getByText(/Failed to load recipes|error/i)).toBeVisible(); }); test('should be responsive on mobile devices', async ({ page }) => { // Set mobile viewport await page.setViewportSize({ width: 375, height: 667 }); // Mock API response await page.route('**/api/recipes', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [ { id: '1', title: 'Mobile Recipe', }, ], total: 1, page: 1, pageSize: 20, }), }); }); await page.goto('/'); // Verify content is visible on mobile await expect(page.getByText('Mobile Recipe')).toBeVisible(); }); }); test.describe('Recipe Search and Filter', () => { test('should filter recipes by search term', async ({ page }) => { // This test assumes there's a search functionality await page.goto('/'); // Wait for search input to be available const searchInput = page.locator('input[type="search"], input[placeholder*="Search"]'); if (await searchInput.count() > 0) { await searchInput.fill('pasta'); // Mock filtered results await page.route('**/api/recipes?*search=pasta*', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ data: [ { id: '1', title: 'Pasta Carbonara', }, ], total: 1, page: 1, pageSize: 20, }), }); }); // Verify filtered results await expect(page.getByText('Pasta Carbonara')).toBeVisible(); } }); });