/** * PendingApprovals Component Tests */ import { render, screen, fireEvent } from '@testing-library/react'; import { PendingApprovals } from '@/components/dashboard/PendingApprovals'; import type { PendingApproval } from '@/lib/api/hooks/useDashboard'; // Mock next-intl navigation jest.mock('@/lib/i18n/routing', () => ({ Link: ({ children, href }: { children: React.ReactNode; href: string }) => ( {children} ), })); describe('PendingApprovals', () => { const mockApprovals: PendingApproval[] = [ { id: 'approval-1', type: 'sprint_boundary', title: 'Sprint 3 Completion', description: 'Review sprint deliverables', projectId: 'proj-1', projectName: 'E-Commerce Platform', requestedBy: 'Product Owner Agent', requestedAt: new Date().toISOString(), priority: 'high', }, { id: 'approval-2', type: 'code_review', title: 'PR #123 Review', description: 'Authentication module changes', projectId: 'proj-2', projectName: 'Banking App', requestedBy: 'Developer Agent', requestedAt: new Date().toISOString(), priority: 'medium', }, ]; it('renders approval items', () => { render(); expect(screen.getByText('Sprint 3 Completion')).toBeInTheDocument(); expect(screen.getByText('PR #123 Review')).toBeInTheDocument(); }); it('displays section header with count', () => { render(); expect(screen.getByText('Pending Approvals')).toBeInTheDocument(); expect(screen.getByText('2')).toBeInTheDocument(); }); it('displays approval descriptions', () => { render(); expect(screen.getByText('Review sprint deliverables')).toBeInTheDocument(); expect(screen.getByText('Authentication module changes')).toBeInTheDocument(); }); it('displays project names with links', () => { render(); const projectLink = screen.getByRole('link', { name: 'E-Commerce Platform' }); expect(projectLink).toHaveAttribute('href', '/projects/proj-1'); }); it('displays requestor information', () => { render(); expect(screen.getByText(/Product Owner Agent/)).toBeInTheDocument(); expect(screen.getByText(/Developer Agent/)).toBeInTheDocument(); }); it('displays priority badges', () => { render(); expect(screen.getByText('High')).toBeInTheDocument(); expect(screen.getByText('Medium')).toBeInTheDocument(); }); it('calls onApprove when Approve button clicked', () => { const onApprove = jest.fn(); render(); const approveButtons = screen.getAllByRole('button', { name: /Approve/i }); fireEvent.click(approveButtons[0]); expect(onApprove).toHaveBeenCalledWith(mockApprovals[0]); }); it('calls onReject when Reject button clicked', () => { const onReject = jest.fn(); render(); const rejectButtons = screen.getAllByRole('button', { name: /Reject/i }); fireEvent.click(rejectButtons[0]); expect(onReject).toHaveBeenCalledWith(mockApprovals[0]); }); it('does not render when no approvals and not loading', () => { const { container } = render(); expect(container.firstChild).toBeNull(); }); it('shows loading skeletons when isLoading is true', () => { render(); expect(screen.getByText('Pending Approvals')).toBeInTheDocument(); }); it('applies custom className', () => { const { container } = render( ); expect(container.firstChild).toHaveClass('custom-class'); }); });