import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { render, screen, waitFor, fireEvent, cleanup, within } from '@testing-library/react'; import ViewIngredientModal from '@/app/dashboard/ingredients/ViewIngredientModal'; const baseIngredient = { _id: 'ing1', code: 'ING-001', name: 'Organic Butter', category: 'Dairy', subcategory: 'Fresh', quantity: 10, unit: 'kg', unitPrice: 5.00, vat: 9, supplier: 'Acme Foods', createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-02T00:00:00Z', }; function mockFetch(data: unknown) { return vi.spyOn(global, 'fetch').mockResolvedValue({ json: () => Promise.resolve(data), } as Response); } describe('ViewIngredientModal', () => { beforeEach(() => { vi.clearAllMocks(); vi.restoreAllMocks(); }); afterEach(() => { cleanup(); }); it('renders nothing when open=false', () => { const { container } = render( ); expect(container.innerHTML).toBe(''); }); it('shows loading spinner then ingredient data', async () => { mockFetch(baseIngredient); const { container } = render( ); expect(within(container).getByText('Loading...')).toBeInTheDocument(); await waitFor(() => { expect(within(container).getByText('Organic Butter')).toBeInTheDocument(); }); }); it('displays pricing section (gross, VAT, net price)', async () => { mockFetch(baseIngredient); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Pricing')).toBeInTheDocument(); }); // Gross: €5.00 expect(within(container).getByText(/€5\.00/)).toBeInTheDocument(); // VAT: rendered as {vat}% — separate text nodes so use regex expect(within(container).getByText(/9%/)).toBeInTheDocument(); // Net: 5.00 * 1.09 = 5.45 expect(within(container).getByText(/€5\.45/)).toBeInTheDocument(); }); it('shows discount section only when discountValue > 0', async () => { const withDiscount = { ...baseIngredient, discountType: 'percent' as const, discountValue: 10, applyDiscountToNet: false, }; mockFetch(withDiscount); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Discount')).toBeInTheDocument(); }); expect(within(container).getByText('Percentage')).toBeInTheDocument(); expect(within(container).getByText('10%')).toBeInTheDocument(); }); it('hides discount section when not present', async () => { mockFetch(baseIngredient); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Organic Butter')).toBeInTheDocument(); }); expect(within(container).queryByText('Discount')).not.toBeInTheDocument(); }); it('shows advanced section only when advanced fields present', async () => { const withAdvanced = { ...baseIngredient, minStockLevel: 5, storageInstructions: 'Keep refrigerated', shelfLifeDays: 30, notes: 'Premium quality', }; mockFetch(withAdvanced); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Additional Details')).toBeInTheDocument(); }); expect(within(container).getByText('Keep refrigerated')).toBeInTheDocument(); expect(within(container).getByText('30 days')).toBeInTheDocument(); expect(within(container).getByText('Premium quality')).toBeInTheDocument(); }); it('hides advanced section when not present', async () => { mockFetch(baseIngredient); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Organic Butter')).toBeInTheDocument(); }); expect(within(container).queryByText('Additional Details')).not.toBeInTheDocument(); }); it('calls onClose on close button', async () => { mockFetch(baseIngredient); const onClose = vi.fn(); const { container } = render( ); await waitFor(() => { expect(within(container).getByText('Organic Butter')).toBeInTheDocument(); }); fireEvent.click(within(container).getByRole('button', { name: 'Close' })); expect(onClose).toHaveBeenCalledOnce(); }); });