import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { RecentActivity } from '@/components/projects/RecentActivity'; import type { ActivityItem } from '@/components/projects/types'; const mockActivities: ActivityItem[] = [ { id: 'act-001', type: 'agent_message', agent: 'Product Owner', message: 'Approved user story #42', timestamp: new Date().toISOString(), }, { id: 'act-002', type: 'issue_update', agent: 'Backend Engineer', message: 'Moved issue #38 to review', timestamp: new Date().toISOString(), }, { id: 'act-003', type: 'approval_request', agent: 'Architect', message: 'Requesting API design approval', timestamp: new Date().toISOString(), requires_action: true, }, ]; describe('RecentActivity', () => { it('renders recent activity with title', () => { render(); expect(screen.getByText('Recent Activity')).toBeInTheDocument(); }); it('displays all activities', () => { render(); expect(screen.getByText('Product Owner')).toBeInTheDocument(); expect(screen.getByText('Approved user story #42')).toBeInTheDocument(); expect(screen.getByText('Backend Engineer')).toBeInTheDocument(); expect(screen.getByText('Moved issue #38 to review')).toBeInTheDocument(); expect(screen.getByText('Architect')).toBeInTheDocument(); expect(screen.getByText('Requesting API design approval')).toBeInTheDocument(); }); it('renders empty state when no activities', () => { render(); expect(screen.getByText('No recent activity')).toBeInTheDocument(); }); it('shows loading skeleton when isLoading is true', () => { const { container } = render(); expect(container.querySelectorAll('.animate-pulse').length).toBeGreaterThan(0); }); it('respects maxItems prop', () => { render(); expect(screen.getByText('Product Owner')).toBeInTheDocument(); expect(screen.getByText('Backend Engineer')).toBeInTheDocument(); expect(screen.queryByText('Architect')).not.toBeInTheDocument(); }); it('shows View All button when there are more activities than maxItems', () => { const onViewAll = jest.fn(); render(); expect(screen.getByRole('button', { name: /view all/i })).toBeInTheDocument(); }); it('does not show View All button when all activities are shown', () => { const onViewAll = jest.fn(); render(); expect(screen.queryByRole('button', { name: /view all/i })).not.toBeInTheDocument(); }); it('calls onViewAll when View All button is clicked', async () => { const user = userEvent.setup(); const onViewAll = jest.fn(); render(); await user.click(screen.getByRole('button', { name: /view all/i })); expect(onViewAll).toHaveBeenCalledTimes(1); }); it('shows Review Request button for items requiring action', () => { const onActionClick = jest.fn(); render(); expect(screen.getByRole('button', { name: /review request/i })).toBeInTheDocument(); }); it('calls onActionClick when Review Request button is clicked', async () => { const user = userEvent.setup(); const onActionClick = jest.fn(); render(); await user.click(screen.getByRole('button', { name: /review request/i })); expect(onActionClick).toHaveBeenCalledWith('act-003'); }); it('highlights activities requiring action', () => { render(); const activityItem = screen.getByTestId('activity-item-act-003'); const iconContainer = activityItem.querySelector('.bg-yellow-100'); expect(iconContainer).toBeInTheDocument(); }); it('applies custom className', () => { render(); expect(screen.getByTestId('recent-activity')).toHaveClass('custom-class'); }); it('has accessible list role', () => { render(); expect(screen.getByRole('list', { name: /recent project activity/i })).toBeInTheDocument(); }); });