103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
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) => <a href={props.href}>{props.children}</a>,
|
|
}));
|
|
|
|
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(<EditRecipePage />);
|
|
|
|
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(<EditRecipePage />);
|
|
|
|
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(<EditRecipePage />);
|
|
|
|
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(<EditRecipePage />);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText('Failed to load recipe')).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|