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();
});
});