import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import DailyStatusForm from '../src/components/DailyStatusForm'; import * as client from '../src/api/client'; jest.mock('../src/api/client', () => ({ dailyStatusesApi: { create: jest.fn(), update: jest.fn(), }, experimentsApi: { updateTemplate: jest.fn(), }, })); const ANIMAL_ID = 'animal-001'; const onSuccess = jest.fn(); const onCancel = jest.fn(); const BUILTIN_TEMPLATE = [ { fieldId: 'f1', key: 'vitals', label: 'Vitals', type: 'textarea', builtin: true, active: true, width: 100 }, { fieldId: 'f2', key: 'treatment', label: 'Treatment', type: 'textarea', builtin: true, active: true, width: 100 }, { fieldId: 'f3', key: 'notes', label: 'Notes', type: 'textarea', builtin: true, active: true, width: 100 }, { fieldId: 'f4', key: 'experiment_description', label: 'Experiment Description', type: 'textarea', builtin: true, active: true, width: 100 }, ]; beforeEach(() => jest.clearAllMocks()); describe('DailyStatusForm', () => { it('renders date field (required) and optional fields', () => { render(); expect(screen.getByLabelText(/date/i)).toBeInTheDocument(); expect(screen.getByLabelText(/vitals/i)).toBeInTheDocument(); expect(screen.getByLabelText(/treatment/i)).toBeInTheDocument(); expect(screen.getByLabelText(/notes/i)).toBeInTheDocument(); expect(screen.getByLabelText(/experiment description/i)).toBeInTheDocument(); }); it('pre-fills date with today by default', () => { render(); const today = new Date().toISOString().slice(0, 10); expect(screen.getByLabelText(/date/i)).toHaveValue(today); }); it('shows error when date is cleared', async () => { render(); const dateInput = screen.getByLabelText(/date/i); await userEvent.clear(dateInput); fireEvent.click(screen.getByRole('button', { name: /log status/i })); expect(await screen.findByRole('alert')).toHaveTextContent(/date is required/i); expect(client.dailyStatusesApi.create).not.toHaveBeenCalled(); }); it('calls create with correct payload including animal_id', async () => { const status = { id: 's-1', animal_id: ANIMAL_ID, date: '2024-06-01', vitals: 'HR 72' }; client.dailyStatusesApi.create.mockResolvedValue(status); render(); const dateInput = screen.getByLabelText(/date/i); fireEvent.change(dateInput, { target: { value: '2024-06-01' } }); await userEvent.type(screen.getByLabelText(/vitals/i), 'HR 72'); fireEvent.click(screen.getByRole('button', { name: /log status/i })); await waitFor(() => expect(client.dailyStatusesApi.create).toHaveBeenCalledWith( expect.objectContaining({ animal_id: ANIMAL_ID, vitals: 'HR 72' }) ) ); await waitFor(() => expect(onSuccess).toHaveBeenCalled()); }); it('shows "Save Changes" and pre-fills fields for existing status', () => { const existing = { id: 's-1', date: '2024-06-01T00:00:00.000Z', vitals: 'HR 72', treatment: 'Control', notes: 'Normal', experiment_description: 'Baseline', }; render( ); expect(screen.getByRole('button', { name: /save changes/i })).toBeInTheDocument(); expect(screen.getByDisplayValue('HR 72')).toBeInTheDocument(); }); it('calls onCancel when Cancel is clicked', () => { render(); fireEvent.click(screen.getByRole('button', { name: /cancel/i })); expect(onCancel).toHaveBeenCalled(); }); });