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 client name client-side without calling the backend', () => { fetchMock = installFetchMock([]); renderWithProviders(); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); expect(screen.getByText(/client requis/i)).toBeInTheDocument(); expect(fetchMock.calls).toHaveLength(0); }); it('maps 422 backend errors (details[].loc) to per-field messages', async () => { fetchMock = installFetchMock([ { status: 422, body: { error: 'validation_error', message: 'request failed', details: [ { loc: ['client_name'], msg: 'String should have at least 1 character', type: 'string_too_short', }, ], }, }, ]); renderWithProviders(); fireEvent.change(screen.getByLabelText(/client name/i), { target: { value: 'x' } }); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); await waitFor(() => { expect(screen.getByText(/at least 1 character/i)).toBeInTheDocument(); }); }); it('invalidates the engagements query and closes on success', async () => { const onClose = vi.fn(); fetchMock = installFetchMock([ { status: 201, body: { id: 'eng_new', client_name: 'OPERATION ZETA', description: null, status: 'draft', c2_type: 'mythic', start_date: null, end_date: null, }, }, ]); renderWithProviders(); fireEvent.change(screen.getByLabelText(/client 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'); }); it('surfaces a generic top-of-form error on 401', async () => { fetchMock = installFetchMock([ { status: 401, body: { error: 'not_authenticated', message: 'no active session' }, }, ]); renderWithProviders(); fireEvent.change(screen.getByLabelText(/client name/i), { target: { value: 'Anyone' }, }); fireEvent.click(screen.getByRole('button', { name: /arm engagement/i })); const alert = await screen.findByRole('alert'); expect(alert.textContent).toMatch(/session expirée/i); }); });