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