/** * BulkActions Component Tests * * Comprehensive tests for the bulk actions toolbar component. */ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { BulkActions } from '@/features/issues/components/BulkActions'; describe('BulkActions', () => { const defaultProps = { selectedCount: 3, onChangeStatus: jest.fn(), onAssign: jest.fn(), onAddLabels: jest.fn(), onDelete: jest.fn(), }; beforeEach(() => { jest.clearAllMocks(); }); describe('Visibility', () => { it('renders when selectedCount > 0', () => { render(); expect(screen.getByRole('toolbar')).toBeInTheDocument(); }); it('does not render when selectedCount is 0', () => { render(); expect(screen.queryByRole('toolbar')).not.toBeInTheDocument(); }); }); describe('Selected count display', () => { it('displays the selected count', () => { render(); expect(screen.getByText('5 selected')).toBeInTheDocument(); }); it('displays singular count correctly', () => { render(); expect(screen.getByText('1 selected')).toBeInTheDocument(); }); it('displays large count correctly', () => { render(); expect(screen.getByText('100 selected')).toBeInTheDocument(); }); }); describe('Action buttons', () => { it('renders Change Status button', () => { render(); expect(screen.getByRole('button', { name: 'Change Status' })).toBeInTheDocument(); }); it('renders Assign button', () => { render(); expect(screen.getByRole('button', { name: 'Assign' })).toBeInTheDocument(); }); it('renders Add Labels button', () => { render(); expect(screen.getByRole('button', { name: 'Add Labels' })).toBeInTheDocument(); }); it('renders Delete button', () => { render(); expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument(); }); }); describe('Button callbacks', () => { it('calls onChangeStatus when Change Status is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Change Status' })); expect(defaultProps.onChangeStatus).toHaveBeenCalledTimes(1); }); it('calls onAssign when Assign is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Assign' })); expect(defaultProps.onAssign).toHaveBeenCalledTimes(1); }); it('calls onAddLabels when Add Labels is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Add Labels' })); expect(defaultProps.onAddLabels).toHaveBeenCalledTimes(1); }); it('calls onDelete when Delete is clicked', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: /delete/i })); expect(defaultProps.onDelete).toHaveBeenCalledTimes(1); }); }); describe('Accessibility', () => { it('has accessible toolbar role', () => { render(); const toolbar = screen.getByRole('toolbar'); expect(toolbar).toHaveAttribute('aria-label', 'Bulk actions for selected issues'); }); it('delete icon has aria-hidden', () => { render(); const icons = document.querySelectorAll('[aria-hidden="true"]'); expect(icons.length).toBeGreaterThan(0); }); }); describe('Styling', () => { it('applies custom className', () => { render(); expect(screen.getByRole('toolbar')).toHaveClass('custom-toolbar-class'); }); it('delete button has destructive styling', () => { render(); const deleteButton = screen.getByRole('button', { name: /delete/i }); expect(deleteButton).toHaveClass('text-destructive'); }); }); describe('Edge cases', () => { it('handles rapid clicks on all buttons', async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole('button', { name: 'Change Status' })); await user.click(screen.getByRole('button', { name: 'Assign' })); await user.click(screen.getByRole('button', { name: 'Add Labels' })); await user.click(screen.getByRole('button', { name: /delete/i })); expect(defaultProps.onChangeStatus).toHaveBeenCalledTimes(1); expect(defaultProps.onAssign).toHaveBeenCalledTimes(1); expect(defaultProps.onAddLabels).toHaveBeenCalledTimes(1); expect(defaultProps.onDelete).toHaveBeenCalledTimes(1); }); it('works with very large selected count', () => { render(); expect(screen.getByText('999999 selected')).toBeInTheDocument(); }); }); });