forked from cardosofelipe/fast-next-template
Add comprehensive test coverage for dashboard components: - Dashboard.test.tsx: Main component integration tests - WelcomeHeader.test.tsx: User greeting and time-based messages - DashboardQuickStats.test.tsx: Stats cards rendering and links - RecentProjects.test.tsx: Project cards grid and navigation - PendingApprovals.test.tsx: Approval items and actions - EmptyState.test.tsx: New user onboarding experience 46 tests covering rendering, interactions, and edge cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
/**
|
|
* RecentProjects Component Tests
|
|
*/
|
|
|
|
import { render, screen } from '@testing-library/react';
|
|
import { RecentProjects } from '@/components/dashboard/RecentProjects';
|
|
import type { DashboardProject } from '@/lib/api/hooks/useDashboard';
|
|
|
|
// Mock next-intl navigation
|
|
jest.mock('@/lib/i18n/routing', () => ({
|
|
Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
|
|
<a href={href}>{children}</a>
|
|
),
|
|
}));
|
|
|
|
describe('RecentProjects', () => {
|
|
const mockProjects: DashboardProject[] = [
|
|
{
|
|
id: 'proj-1',
|
|
name: 'Project One',
|
|
description: 'First project description',
|
|
status: 'active',
|
|
autonomy_level: 'milestone',
|
|
created_at: '2025-01-01T00:00:00Z',
|
|
owner_id: 'user-1',
|
|
progress: 75,
|
|
openIssues: 5,
|
|
activeAgents: 3,
|
|
currentSprint: 'Sprint 2',
|
|
lastActivity: '5 minutes ago',
|
|
},
|
|
{
|
|
id: 'proj-2',
|
|
name: 'Project Two',
|
|
description: 'Second project description',
|
|
status: 'paused',
|
|
autonomy_level: 'full_control',
|
|
created_at: '2025-01-02T00:00:00Z',
|
|
owner_id: 'user-1',
|
|
progress: 30,
|
|
openIssues: 8,
|
|
activeAgents: 0,
|
|
lastActivity: '2 days ago',
|
|
},
|
|
];
|
|
|
|
it('renders project cards', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
expect(screen.getByText('Project One')).toBeInTheDocument();
|
|
expect(screen.getByText('Project Two')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays section header with View all link', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
expect(screen.getByText('Recent Projects')).toBeInTheDocument();
|
|
const viewAllLink = screen.getByRole('link', { name: /View all/i });
|
|
expect(viewAllLink).toHaveAttribute('href', '/projects');
|
|
});
|
|
|
|
it('displays project descriptions', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
expect(screen.getByText('First project description')).toBeInTheDocument();
|
|
expect(screen.getByText('Second project description')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays project metrics', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
// Check agents count
|
|
expect(screen.getByText('3 agents')).toBeInTheDocument();
|
|
expect(screen.getByText('0 agents')).toBeInTheDocument();
|
|
|
|
// Check issues count
|
|
expect(screen.getByText('5 issues')).toBeInTheDocument();
|
|
expect(screen.getByText('8 issues')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays sprint info when available', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
expect(screen.getByText('Sprint 2')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays last activity time', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
expect(screen.getByText('5 minutes ago')).toBeInTheDocument();
|
|
expect(screen.getByText('2 days ago')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows loading skeletons when isLoading is true', () => {
|
|
render(<RecentProjects isLoading />);
|
|
|
|
// Should show skeleton cards
|
|
expect(screen.getByText('Recent Projects')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows empty state when no projects', () => {
|
|
render(<RecentProjects projects={[]} />);
|
|
|
|
expect(screen.getByText('No projects yet')).toBeInTheDocument();
|
|
expect(screen.getByRole('link', { name: /Create your first project/i })).toBeInTheDocument();
|
|
});
|
|
|
|
it('links project cards to project detail page', () => {
|
|
render(<RecentProjects projects={mockProjects} />);
|
|
|
|
const projectLink = screen.getByRole('link', { name: /Project One/i });
|
|
expect(projectLink).toHaveAttribute('href', '/projects/proj-1');
|
|
});
|
|
|
|
it('applies custom className', () => {
|
|
const { container } = render(<RecentProjects projects={mockProjects} className="custom-class" />);
|
|
|
|
expect(container.firstChild).toHaveClass('custom-class');
|
|
});
|
|
});
|