import { describe, it, expect, vi, beforeEach } from 'vitest'; import { render, screen, waitFor } from '@testing-library/react'; const { mockUseApi } = vi.hoisted(() => ({ mockUseApi: vi.fn(), })); const { mockGetRecipe } = vi.hoisted(() => ({ mockGetRecipe: vi.fn(), })); vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi })); vi.mock('@/services/recipes', () => ({ getRecipe: mockGetRecipe, createRecipe: vi.fn(), updateRecipe: vi.fn(), listRecipes: vi.fn(), deleteRecipe: vi.fn(), scaleRecipe: vi.fn(), importRecipeFromText: vi.fn(), importRecipeFromUrl: vi.fn(), listRecipesByProduct: vi.fn(), })); vi.mock('next/navigation', () => ({ useParams: () => ({ id: 'r1' }), useRouter: () => ({ push: vi.fn(), back: vi.fn() }), })); vi.mock('next/link', () => ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any default: (props: any) => {props.children}, })); import EditRecipePage from '../../../../../../src/app/(dashboard)/recipes/[id]/edit/page'; beforeEach(() => vi.clearAllMocks()); const SAMPLE_RECIPE = { _id: 'r1', name: 'Pasta', description: '', servings: 4, tags: [], isFavorite: false, ingredients: [ { productId: 'p1', productName: 'Flour', quantity: 200, unit: 'g', isOptional: false }, ], steps: [{ order: 1, instruction: 'Mix' }], perServingNutrition: { calories: 200, protein: 5, carbs: 30, fat: 4 }, totalNutrition: { calories: 800, protein: 20, carbs: 120, fat: 16 }, warnings: [], createdBy: 'u1', createdAt: '2026-01-01T00:00:00.000Z', updatedAt: '2026-01-01T00:00:00.000Z', householdId: 'hh1', }; describe('EditRecipePage', () => { it('shows loading state', () => { mockUseApi.mockReturnValue({ householdId: null, isLoading: true }); render(); expect(screen.getByText('Edit Recipe')).toBeInTheDocument(); expect(screen.getByText('Loading...')).toBeInTheDocument(); }); it('shows error when fetch fails', async () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false }); mockGetRecipe.mockRejectedValue(new Error('Not found')); render(); await waitFor(() => { expect(screen.getByText('Not found')).toBeInTheDocument(); }); }); it('renders editor when recipe loads', async () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false }); mockGetRecipe.mockResolvedValue(SAMPLE_RECIPE); render(); await waitFor(() => { expect(screen.getByDisplayValue('Pasta')).toBeInTheDocument(); expect(screen.getByText('Save changes')).toBeInTheDocument(); }); }); it('shows fallback when recipe is not found', async () => { mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false }); mockGetRecipe.mockRejectedValue('unexpected'); render(); await waitFor(() => { expect(screen.getByText('Failed to load recipe')).toBeInTheDocument(); }); }); });