godcrm/components/__tests__/ParticipantSelector.test.tsx
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

281 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ParticipantSelector Component Tests
* ADR-024: Chat & Message Architecture
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ParticipantSelector, Participant } from '../ParticipantSelector';
// Mock apiClient
vi.mock('@/shared/utils/apiClient', () => ({
apiClient: {
get: vi.fn()
}
}));
import { apiClient } from '@/shared/utils/apiClient';
const mockUsers = [
{ id: 1, name: 'John Doe', email: 'john@test.com', status: 'online' },
{ id: 2, name: 'Jane Smith', email: 'jane@test.com', status: 'offline' }
];
const mockAgents = [
{ id: 10, name: 'AI Assistant', description: 'General purpose assistant' },
{ id: 11, name: 'Code Helper', description: 'Programming assistant' }
];
describe('ParticipantSelector', () => {
let queryClient: QueryClient;
const mockOnSelect = vi.fn();
const mockOnMultiSelect = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false }
}
});
// Setup API mocks
(apiClient.get as any).mockImplementation((url: string) => {
if (url === '/users') {
return Promise.resolve({ success: true, data: mockUsers });
}
if (url === '/ai-agents') {
return Promise.resolve({ success: true, data: mockAgents });
}
return Promise.resolve({ success: false, data: [] });
});
});
const renderComponent = (props = {}) => {
return render(
<QueryClientProvider client={queryClient}>
<ParticipantSelector
onSelect={mockOnSelect}
{...props}
/>
</QueryClientProvider>
);
};
it('should render placeholder when no value', () => {
renderComponent();
expect(screen.getByText('Выберите участника...')).toBeInTheDocument();
});
it('should show custom placeholder', () => {
renderComponent({ placeholder: 'Select user' });
expect(screen.getByText('Select user')).toBeInTheDocument();
});
it('should display selected value', () => {
const value: Participant = { type: 'user', id: 1, name: 'John Doe' };
renderComponent({ value });
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
it('should open dropdown on click', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByPlaceholderText('Поиск...')).toBeInTheDocument();
});
});
it('should show tabs for filtering', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('Все')).toBeInTheDocument();
expect(screen.getByText('Пользователи')).toBeInTheDocument();
expect(screen.getByText('AI Агенты')).toBeInTheDocument();
});
});
it('should load and display users', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
it('should load and display agents', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('AI Assistant')).toBeInTheDocument();
expect(screen.getByText('Code Helper')).toBeInTheDocument();
});
});
it('should call onSelect when participant clicked', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('John Doe'));
expect(mockOnSelect).toHaveBeenCalledWith(expect.objectContaining({
type: 'user',
id: 1,
name: 'John Doe'
}));
});
it('should filter participants by search', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
const searchInput = screen.getByPlaceholderText('Поиск...');
fireEvent.change(searchInput, { target: { value: 'Jane' } });
await waitFor(() => {
expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
it('should filter by tab - users only', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('AI Assistant')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('Пользователи'));
await waitFor(() => {
expect(screen.queryByText('AI Assistant')).not.toBeInTheDocument();
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
});
it('should filter by tab - agents only', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('AI Агенты'));
await waitFor(() => {
expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
expect(screen.getByText('AI Assistant')).toBeInTheDocument();
});
});
it('should support multi-select mode', async () => {
const participants: Participant[] = [];
renderComponent({
multiSelect: true,
participants,
onMultiSelect: mockOnMultiSelect
});
fireEvent.click(screen.getByRole('button'));
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
fireEvent.click(screen.getByText('John Doe'));
expect(mockOnMultiSelect).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ id: 1, name: 'John Doe' })
])
);
});
it('should show participant count in multi-select', () => {
const participants: Participant[] = [
{ type: 'user', id: 1, name: 'John' },
{ type: 'user', id: 2, name: 'Jane' }
];
renderComponent({ multiSelect: true, participants });
expect(screen.getByText('2 участник(ов)')).toBeInTheDocument();
});
it('should exclude specified user ids', async () => {
renderComponent({ excludeIds: { users: [1] } });
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
it('should exclude specified agent ids', async () => {
renderComponent({ excludeIds: { agents: [10] } });
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.queryByText('AI Assistant')).not.toBeInTheDocument();
expect(screen.getByText('Code Helper')).toBeInTheDocument();
});
});
it('should show AI badge for agents', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByText('AI Assistant')).toBeInTheDocument();
});
// Multiple AI badges expected
const aiBadges = screen.getAllByText('AI');
expect(aiBadges.length).toBeGreaterThan(0);
});
it('should close dropdown on outside click', async () => {
renderComponent();
fireEvent.click(screen.getByText('Выберите участника...'));
await waitFor(() => {
expect(screen.getByPlaceholderText('Поиск...')).toBeInTheDocument();
});
// Click outside
fireEvent.mouseDown(document.body);
await waitFor(() => {
expect(screen.queryByPlaceholderText('Поиск...')).not.toBeInTheDocument();
});
});
});