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();
});
});