/**
* 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');
});
});