Files
syndarix/frontend/tests/components/projects/ProjectCard.test.tsx
Felipe Cardoso a78b903f5a test(frontend): add unit tests for Projects list components
Add comprehensive test coverage for projects list components:
- ProjectCard.test.tsx: Card rendering, status badges, actions menu
- ProjectFilters.test.tsx: Search, filters, view mode toggle
- ProjectsGrid.test.tsx: Grid/list layout, loading, empty states

30 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>
2026-01-01 17:20:51 +01:00

106 lines
3.6 KiB
TypeScript

/**
* ProjectCard Component Tests
*/
import { render, screen, fireEvent } from '@testing-library/react';
import { ProjectCard, ProjectCardSkeleton } from '@/components/projects/ProjectCard';
import type { ProjectListItem } from '@/lib/api/hooks/useProjects';
describe('ProjectCard', () => {
const mockProject: ProjectListItem = {
id: 'proj-1',
name: 'Test Project',
description: 'This is a test project description',
status: 'active',
complexity: 'medium',
progress: 65,
openIssues: 5,
activeAgents: 3,
currentSprint: 'Sprint 2',
lastActivity: '5 minutes ago',
createdAt: '2025-01-01T00:00:00Z',
owner: { id: 'user-1', name: 'Test User' },
tags: ['frontend', 'react', 'typescript'],
};
it('renders project name', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('Test Project')).toBeInTheDocument();
});
it('renders project description', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('This is a test project description')).toBeInTheDocument();
});
it('renders project status badge', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('Active')).toBeInTheDocument();
});
it('renders current sprint', () => {
render(<ProjectCard project={mockProject} />);
// Sprint not shown in current card design, but progress is
expect(screen.getByText('65%')).toBeInTheDocument();
});
it('renders project metrics', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('3')).toBeInTheDocument(); // agents
expect(screen.getByText('5')).toBeInTheDocument(); // issues
});
it('renders last activity time', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('5 minutes ago')).toBeInTheDocument();
});
it('renders project tags', () => {
render(<ProjectCard project={mockProject} />);
expect(screen.getByText('frontend')).toBeInTheDocument();
expect(screen.getByText('react')).toBeInTheDocument();
expect(screen.getByText('typescript')).toBeInTheDocument();
});
it('calls onClick when card is clicked', () => {
const onClick = jest.fn();
render(<ProjectCard project={mockProject} onClick={onClick} />);
// Click the card (first button, which is the card itself)
const buttons = screen.getAllByRole('button');
fireEvent.click(buttons[0]);
expect(onClick).toHaveBeenCalledTimes(1);
});
it('renders action menu when onAction is provided', () => {
const onAction = jest.fn();
render(<ProjectCard project={mockProject} onAction={onAction} />);
// Menu button should exist with sr-only text
const menuButtons = screen.getAllByRole('button');
const menuButton = menuButtons.find(btn => btn.querySelector('.sr-only'));
expect(menuButton).toBeDefined();
expect(menuButton!.querySelector('.sr-only')).toHaveTextContent('Project actions');
});
it('does not render action menu when onAction is not provided', () => {
render(<ProjectCard project={mockProject} />);
// Only the card itself should be a button
const buttons = screen.getAllByRole('button');
expect(buttons.length).toBe(1);
});
it('applies custom className', () => {
const { container } = render(<ProjectCard project={mockProject} className="custom-class" />);
expect(container.querySelector('.custom-class')).toBeInTheDocument();
});
});
describe('ProjectCardSkeleton', () => {
it('renders skeleton elements', () => {
const { container } = render(<ProjectCardSkeleton />);
expect(container.querySelectorAll('.animate-pulse').length).toBeGreaterThan(0);
});
});