import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ExportEngagementButton } from '@/components/ExportEngagementButton'; import { ToastViewport } from '@/components/Toast'; import { renderWithProviders } from './utils'; function ExportButtonWithToast({ engagementId }: { engagementId: number }) { return ( <> ); } vi.mock('@/api/exports', () => ({ downloadEngagementExport: vi.fn(), })); import { downloadEngagementExport } from '@/api/exports'; const mockDownload = downloadEngagementExport as ReturnType; vi.mock('@/hooks/useAuth', () => ({ useAuth: () => ({ user: { id: 1, username: 'alice', role: 'admin', created_at: '2026-01-01' }, status: 'authenticated', login: vi.fn(), logout: vi.fn(), isAdmin: true, isRedteam: false, isSoc: false, canEditEngagements: true, }), })); describe('ExportEngagementButton', () => { beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { vi.clearAllMocks(); }); it('renders Export button with chevron', () => { renderWithProviders(); expect(screen.getByTestId('export-btn')).toBeInTheDocument(); expect(screen.getByTestId('export-dropdown-toggle')).toBeInTheDocument(); expect(screen.getByText('Export')).toBeInTheDocument(); }); it('clicking primary opens dropdown with three formats', async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); expect(screen.getByText('Markdown')).toBeInTheDocument(); expect(screen.getByText('CSV')).toBeInTheDocument(); expect(screen.getByText('PDF')).toBeInTheDocument(); }); it('clicking outside closes dropdown', async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); expect(screen.getByText('Markdown')).toBeInTheDocument(); await user.click(document.body); expect(screen.queryByText('Markdown')).toBeNull(); }); it('Escape closes dropdown', async () => { const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-dropdown-toggle')); expect(screen.getByText('Markdown')).toBeInTheDocument(); await user.keyboard('{Escape}'); expect(screen.queryByText('Markdown')).toBeNull(); }); it('clicking Markdown triggers download with format=md', async () => { mockDownload.mockResolvedValue(undefined); const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); await user.click(screen.getByTestId('export-format-md')); expect(mockDownload).toHaveBeenCalledWith(42, 'md'); }); it('clicking CSV triggers download with format=csv', async () => { mockDownload.mockResolvedValue(undefined); const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); await user.click(screen.getByTestId('export-format-csv')); expect(mockDownload).toHaveBeenCalledWith(42, 'csv'); }); it('clicking PDF triggers download with format=pdf', async () => { mockDownload.mockResolvedValue(undefined); const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); await user.click(screen.getByTestId('export-format-pdf')); expect(mockDownload).toHaveBeenCalledWith(42, 'pdf'); }); it('loading state disables items during in-flight', async () => { let resolve!: () => void; mockDownload.mockReturnValue(new Promise((r) => { resolve = r; })); const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); await user.click(screen.getByTestId('export-format-md')); // Items should be disabled while in-flight await waitFor(() => { expect(screen.getByTestId('export-format-csv')).toBeDisabled(); expect(screen.getByTestId('export-format-pdf')).toBeDisabled(); }); resolve(); }); it('error response shows toast', async () => { mockDownload.mockRejectedValue(new Error('Export failed: 403 Forbidden')); const user = userEvent.setup(); renderWithProviders(); await user.click(screen.getByTestId('export-btn')); await user.click(screen.getByTestId('export-format-md')); await waitFor(() => { expect(screen.getByTestId('toast')).toBeInTheDocument(); expect(screen.getByTestId('toast')).toHaveTextContent('Export failed: 403 Forbidden'); }); }); });