/** * IssueTable Component Tests */ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { IssueTable } from '@/features/issues/components/IssueTable'; import type { IssueSummary, IssueSort } from '@/features/issues/types'; const mockIssues: IssueSummary[] = [ { id: 'issue-1', number: 42, title: 'Test Issue 1', description: 'Description 1', status: 'open', priority: 'high', labels: ['bug', 'frontend'], sprint: 'Sprint 1', assignee: { id: 'user-1', name: 'Test User', type: 'human' }, created_at: '2025-01-01T00:00:00Z', updated_at: '2025-01-02T00:00:00Z', sync_status: 'synced', }, { id: 'issue-2', number: 43, title: 'Test Issue 2', description: 'Description 2', status: 'in_progress', priority: 'medium', labels: ['feature'], sprint: null, assignee: null, created_at: '2025-01-02T00:00:00Z', updated_at: '2025-01-03T00:00:00Z', sync_status: 'pending', }, ]; describe('IssueTable', () => { const defaultSort: IssueSort = { field: 'number', direction: 'asc' }; const mockOnSelectionChange = jest.fn(); const mockOnIssueClick = jest.fn(); const mockOnSortChange = jest.fn(); beforeEach(() => { mockOnSelectionChange.mockClear(); mockOnIssueClick.mockClear(); mockOnSortChange.mockClear(); }); it('renders issue rows', () => { render( ); expect(screen.getByText('Test Issue 1')).toBeInTheDocument(); expect(screen.getByText('Test Issue 2')).toBeInTheDocument(); }); it('displays issue numbers', () => { render( ); expect(screen.getByText('42')).toBeInTheDocument(); expect(screen.getByText('43')).toBeInTheDocument(); }); it('shows labels for issues', () => { render( ); expect(screen.getByText('bug')).toBeInTheDocument(); expect(screen.getByText('frontend')).toBeInTheDocument(); expect(screen.getByText('feature')).toBeInTheDocument(); }); it('calls onIssueClick when row is clicked', async () => { const user = userEvent.setup(); render( ); const row = screen.getByTestId('issue-row-issue-1'); await user.click(row); expect(mockOnIssueClick).toHaveBeenCalledWith('issue-1'); }); it('handles issue selection', async () => { const user = userEvent.setup(); render( ); // Find checkbox for first issue const checkbox = screen.getByRole('checkbox', { name: /select issue 42/i }); await user.click(checkbox); expect(mockOnSelectionChange).toHaveBeenCalledWith(['issue-1']); }); it('handles select all', async () => { const user = userEvent.setup(); render( ); // Find select all checkbox const selectAllCheckbox = screen.getByRole('checkbox', { name: /select all issues/i }); await user.click(selectAllCheckbox); expect(mockOnSelectionChange).toHaveBeenCalledWith(['issue-1', 'issue-2']); }); it('handles deselect all when all selected', async () => { const user = userEvent.setup(); render( ); // Find deselect all checkbox const selectAllCheckbox = screen.getByRole('checkbox', { name: /deselect all issues/i }); await user.click(selectAllCheckbox); expect(mockOnSelectionChange).toHaveBeenCalledWith([]); }); it('handles sorting by number', async () => { const user = userEvent.setup(); render( ); // Click the # column header const numberHeader = screen.getByRole('button', { name: /#/i }); await user.click(numberHeader); expect(mockOnSortChange).toHaveBeenCalled(); }); it('handles sorting by priority', async () => { const user = userEvent.setup(); render( ); // Click the Priority column header const priorityHeader = screen.getByRole('button', { name: /priority/i }); await user.click(priorityHeader); expect(mockOnSortChange).toHaveBeenCalledWith({ field: 'priority', direction: 'desc' }); }); it('shows empty state when no issues', () => { render( ); expect(screen.getByText('No issues found')).toBeInTheDocument(); expect(screen.getByText('Try adjusting your search or filters')).toBeInTheDocument(); }); it('shows unassigned text for issues without assignee', () => { render( ); expect(screen.getByText('Unassigned')).toBeInTheDocument(); }); it('shows backlog text for issues without sprint', () => { render( ); expect(screen.getByText('Backlog')).toBeInTheDocument(); }); });