import { describe, it, expect, afterEach, vi } from 'vitest'; import { screen, fireEvent, waitFor } from '@testing-library/react'; import { EngagementCreateDialog } from './EngagementCreateDialog'; import { installFetchMock, renderWithProviders } from '@/test/testUtils'; describe('EngagementCreateDialog', () => { let fetchMock: ReturnType; afterEach(() => { fetchMock?.restore(); }); it('rejects empty name client-side without calling the backend', () => { fetchMock = installFetchMock([]); renderWithProviders(); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); expect(screen.getByText(/nom requis/i)).toBeInTheDocument(); expect(fetchMock.calls).toHaveLength(0); }); it('maps 422 Pydantic errors to per-field messages', async () => { fetchMock = installFetchMock([ { status: 422, body: { detail: [ { loc: ['body', 'name'], msg: 'String should have at least 3 characters', type: 'string_too_short', }, ], }, }, ]); renderWithProviders(); fireEvent.change(screen.getByLabelText(/engagement name/i), { target: { value: 'AB' } }); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); await waitFor(() => { expect(screen.getByText(/at least 3 characters/i)).toBeInTheDocument(); }); }); it('invalidates the engagements query and closes on success', async () => { const onClose = vi.fn(); fetchMock = installFetchMock([ { status: 201, body: { id: 'eng_new', name: 'OPERATION ZETA', client_name: null, description: null, status: 'planning', c2_type: null, start_date: null, end_date: null, created_at: '2026-05-23T08:00:00Z', }, }, ]); renderWithProviders(); fireEvent.change(screen.getByLabelText(/engagement name/i), { target: { value: 'OPERATION ZETA' }, }); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); await waitFor(() => { expect(onClose).toHaveBeenCalledTimes(1); }); expect(fetchMock.calls[0]?.url).toBe('/api/v1/engagements'); expect(fetchMock.calls[0]?.init?.method).toBe('POST'); }); });