/** * Dashboard Component Tests */ import { render, screen } from '@testing-library/react'; import { Dashboard } from '@/components/dashboard/Dashboard'; import { useAuth } from '@/lib/auth/AuthContext'; import { useDashboard } from '@/lib/api/hooks/useDashboard'; import { useProjectEvents } from '@/lib/hooks/useProjectEvents'; import { useProjectEventsFromStore } from '@/lib/stores/eventStore'; // Mock dependencies jest.mock('@/lib/auth/AuthContext', () => ({ useAuth: jest.fn(), })); jest.mock('@/lib/api/hooks/useDashboard', () => ({ useDashboard: jest.fn(), })); jest.mock('@/lib/hooks/useProjectEvents', () => ({ useProjectEvents: jest.fn(), })); jest.mock('@/lib/stores/eventStore', () => ({ useProjectEventsFromStore: jest.fn(), })); // Mock next-intl navigation jest.mock('@/lib/i18n/routing', () => ({ Link: ({ children, href }: { children: React.ReactNode; href: string }) => ( {children} ), })); // Mock sonner jest.mock('sonner', () => ({ toast: { success: jest.fn(), info: jest.fn(), error: jest.fn(), }, })); const mockUseAuth = useAuth as jest.MockedFunction; const mockUseDashboard = useDashboard as jest.MockedFunction; const mockUseProjectEvents = useProjectEvents as jest.MockedFunction; const mockUseProjectEventsFromStore = useProjectEventsFromStore as jest.MockedFunction< typeof useProjectEventsFromStore >; describe('Dashboard', () => { const mockUser = { id: '1', email: 'test@example.com', first_name: 'Test', is_active: true, is_superuser: false, created_at: '', }; const mockDashboardData = { stats: { activeProjects: 3, runningAgents: 8, openIssues: 24, pendingApprovals: 2, }, recentProjects: [ { id: 'proj-1', name: 'Test Project', description: 'Test description', status: 'active' as const, autonomy_level: 'milestone' as const, created_at: '2025-01-01T00:00:00Z', owner_id: 'user-1', progress: 50, openIssues: 5, activeAgents: 2, lastActivity: '5 min ago', }, ], pendingApprovals: [ { id: 'approval-1', type: 'sprint_boundary' as const, title: 'Sprint Review', description: 'Review sprint', projectId: 'proj-1', projectName: 'Test Project', requestedBy: 'Agent', requestedAt: new Date().toISOString(), priority: 'high' as const, }, ], }; beforeEach(() => { jest.clearAllMocks(); mockUseAuth.mockReturnValue({ user: mockUser, isAuthenticated: true, isLoading: false, error: null, login: jest.fn(), logout: jest.fn(), clearError: jest.fn(), checkAuth: jest.fn(), }); mockUseDashboard.mockReturnValue({ data: mockDashboardData, isLoading: false, error: null, isError: false, isPending: false, isSuccess: true, status: 'success', } as ReturnType); mockUseProjectEvents.mockReturnValue({ connectionState: 'connected', events: [], isConnected: true, error: null, retryCount: 0, reconnect: jest.fn(), disconnect: jest.fn(), clearEvents: jest.fn(), }); mockUseProjectEventsFromStore.mockReturnValue([]); }); it('renders welcome header', () => { render(); // User first name appears in welcome message expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument(); }); it('renders quick stats', () => { render(); expect(screen.getByText('Active Projects')).toBeInTheDocument(); expect(screen.getByText('Running Agents')).toBeInTheDocument(); expect(screen.getByText('Open Issues')).toBeInTheDocument(); // "Pending Approvals" appears in both stats and approvals section const pendingApprovalsTexts = screen.getAllByText('Pending Approvals'); expect(pendingApprovalsTexts.length).toBeGreaterThan(0); }); it('renders recent projects section', () => { render(); expect(screen.getByText('Recent Projects')).toBeInTheDocument(); // Use getAllByText since project name appears in multiple places const projectNames = screen.getAllByText('Test Project'); expect(projectNames.length).toBeGreaterThan(0); }); it('renders pending approvals section when approvals exist', () => { render(); // Check for pending approvals header const approvalHeaders = screen.getAllByText('Pending Approvals'); expect(approvalHeaders.length).toBeGreaterThan(0); }); it('shows empty state when no projects', () => { mockUseDashboard.mockReturnValue({ data: { ...mockDashboardData, recentProjects: [] }, isLoading: false, error: null, isError: false, isPending: false, isSuccess: true, status: 'success', } as unknown as ReturnType); render(); expect(screen.getByText(/Welcome to Syndarix/)).toBeInTheDocument(); expect(screen.getByText(/Create Your First Project/)).toBeInTheDocument(); }); it('shows loading state', () => { mockUseDashboard.mockReturnValue({ data: undefined, isLoading: true, error: null, isError: false, isPending: true, isSuccess: false, status: 'pending', } as ReturnType); render(); // Should show skeleton loading states expect(screen.getByText('Recent Projects')).toBeInTheDocument(); }); it('applies custom className', () => { const { container } = render(); expect(container.firstChild).toHaveClass('custom-class'); }); });