import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { DeliverableStatus } from '../../../types/deliverable.types'; import { StatusTransitionModal } from '../StatusTransitionModal'; const mockShowToast = vi.fn(); vi.mock('../../ui/Toast', () => ({ useToast: () => ({ showToast: mockShowToast }), })); const mockMutate = vi.fn(); const mockAllowedTransitions: { data: DeliverableStatus[]; isLoading: boolean } = { data: [DeliverableStatus.EMITIDA, DeliverableStatus.CANCELADA], isLoading: false, }; vi.mock('../../../hooks/useDeliverables', () => ({ useAllowedTransitions: () => mockAllowedTransitions, useChangeStatus: () => ({ mutate: mockMutate, isPending: false, }), })); function renderModal(props: Partial> = {}) { const queryClient = new QueryClient(); return render( , ); } describe('StatusTransitionModal', () => { beforeEach(() => { vi.clearAllMocks(); mockAllowedTransitions.data = [DeliverableStatus.EMITIDA, DeliverableStatus.CANCELADA]; }); it('exibe apenas status permitidos', () => { renderModal(); expect(screen.getByText('Emitida')).toBeInTheDocument(); expect(screen.getByText('Cancelada')).toBeInTheDocument(); expect(screen.queryByText('Aprovada')).not.toBeInTheDocument(); }); it('campo de observação aparece como obrigatório para CANCELADA', () => { renderModal(); fireEvent.click(screen.getByText('Cancelada')); expect(screen.getByText('*')).toBeInTheDocument(); expect(screen.getByText('Observação obrigatória para esta transição')).toBeInTheDocument(); }); it('campo de observação não é obrigatório para EMITIDA', () => { renderModal(); fireEvent.click(screen.getByText('Emitida')); expect( screen.queryByText('Observação obrigatória para esta transição'), ).not.toBeInTheDocument(); }); it('botão confirmar desabilitado sem seleção de status', () => { renderModal(); const confirmButton = screen.getByText('Confirmar'); expect(confirmButton).toBeDisabled(); }); it('botão confirmar desabilitado quando observação obrigatória está vazia', () => { renderModal(); fireEvent.click(screen.getByText('Cancelada')); const confirmButton = screen.getByText('Confirmar'); expect(confirmButton).toBeDisabled(); }); it('botão confirmar habilitado quando observação obrigatória está preenchida', () => { renderModal(); fireEvent.click(screen.getByText('Cancelada')); fireEvent.change(screen.getByPlaceholderText('Informe o motivo da mudança de status...'), { target: { value: 'Motivo do cancelamento' }, }); const confirmButton = screen.getByText('Confirmar'); expect(confirmButton).not.toBeDisabled(); }); it('chama mutate ao confirmar com dados corretos', async () => { const onClose = vi.fn(); renderModal({ onClose }); fireEvent.click(screen.getByText('Emitida')); fireEvent.click(screen.getByText('Confirmar')); expect(mockMutate).toHaveBeenCalledWith( { id: 'os-id-1', data: { status: DeliverableStatus.EMITIDA, observation: undefined, }, }, expect.objectContaining({ onSuccess: expect.any(Function), onError: expect.any(Function), }), ); }); it('não renderiza quando isOpen é false', () => { renderModal({ isOpen: false }); expect(screen.queryByText('Alterar Status')).not.toBeInTheDocument(); }); it('exibe aviso de sprint quando OS está em RASCUNHO sem sprint vinculada', () => { renderModal({ sprintLinked: false }); expect( screen.getByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'), ).toBeInTheDocument(); }); it('não exibe aviso de sprint quando OS está em RASCUNHO com sprint vinculada', () => { renderModal({ sprintLinked: true }); expect( screen.queryByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'), ).not.toBeInTheDocument(); }); it('não exibe aviso de sprint para status diferente de RASCUNHO', () => { mockAllowedTransitions.data = [DeliverableStatus.EM_EXECUCAO, DeliverableStatus.CANCELADA]; renderModal({ currentStatus: DeliverableStatus.EMITIDA, sprintLinked: false }); expect( screen.queryByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'), ).not.toBeInTheDocument(); }); });