godcrm/components/__tests__/SubAgentSelector.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

250 lines
6.5 KiB
TypeScript

/**
* SubAgentSelector Component Tests
* Rewritten for database-driven agents with numeric row_ids
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { SubAgentSelector, type AvailableAgent } from '../SubAgentSelector';
// Mock useLanguage
vi.mock('@/shared/i18n/LanguageContext', () => ({
useLanguage: () => ({
language: 'en',
setLanguage: vi.fn(),
t: (key: string) => {
const map: Record<string, string> = {
'chat.subAgents': 'Sub-agents',
'chat.noAgentsAvailable': 'No agents available',
};
return map[key] ?? key;
},
}),
}));
const mockAgents: AvailableAgent[] = [
{ row_id: 1, name: 'Translator', icon: '\uD83C\uDF10', description: 'Translates messages automatically' },
{ row_id: 2, name: 'Summarizer', icon: '\uD83D\uDCC4', description: 'Creates summaries of long discussions' },
{ row_id: 3, name: 'Vector Search', icon: '\uD83D\uDD0D', description: 'Semantic search in knowledge base' },
];
describe('SubAgentSelector', () => {
const mockOnChange = vi.fn();
beforeEach(() => {
vi.clearAllMocks();
});
it('renders all available agents', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
expect(screen.getByText('Translator')).toBeInTheDocument();
expect(screen.getByText('Summarizer')).toBeInTheDocument();
expect(screen.getByText('Vector Search')).toBeInTheDocument();
});
it('shows header label', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
expect(screen.getByText('Sub-agents')).toBeInTheDocument();
});
it('shows descriptions for each agent', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
expect(screen.getByText('Translates messages automatically')).toBeInTheDocument();
expect(screen.getByText('Creates summaries of long discussions')).toBeInTheDocument();
expect(screen.getByText('Semantic search in knowledge base')).toBeInTheDocument();
});
it('shows agent icon emoji', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
expect(screen.getByText('\uD83C\uDF10')).toBeInTheDocument();
expect(screen.getByText('\uD83D\uDCC4')).toBeInTheDocument();
expect(screen.getByText('\uD83D\uDD0D')).toBeInTheDocument();
});
it('adds agent row_id when toggled on', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
fireEvent.click(screen.getByText('Translator').closest('button')!);
expect(mockOnChange).toHaveBeenCalledWith([1]);
});
it('removes agent row_id when toggled off', () => {
render(
<SubAgentSelector
value={[1, 2]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
fireEvent.click(screen.getByText('Translator').closest('button')!);
expect(mockOnChange).toHaveBeenCalledWith([2]);
});
it('preserves other selected agents when toggling', () => {
render(
<SubAgentSelector
value={[1]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
fireEvent.click(screen.getByText('Vector Search').closest('button')!);
expect(mockOnChange).toHaveBeenCalledWith([1, 3]);
});
it('reflects selected state visually', () => {
render(
<SubAgentSelector
value={[2]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
const summaryButton = screen.getByText('Summarizer').closest('button');
expect(summaryButton).toHaveClass('bg-[var(--color-primary-500)]/10');
});
it('does not call onChange when disabled', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
disabled={true}
/>
);
fireEvent.click(screen.getByText('Translator').closest('button')!);
expect(mockOnChange).not.toHaveBeenCalled();
});
it('shows loading state', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={[]}
isLoading={true}
/>
);
expect(screen.getByTestId('sub-agent-loading')).toBeInTheDocument();
expect(screen.queryByTestId('sub-agent-empty')).not.toBeInTheDocument();
});
it('shows empty state when no agents available', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={[]}
/>
);
expect(screen.getByTestId('sub-agent-empty')).toBeInTheDocument();
expect(screen.getByText('No agents available')).toBeInTheDocument();
});
it('does not show empty state while loading', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={[]}
isLoading={true}
/>
);
expect(screen.queryByTestId('sub-agent-empty')).not.toBeInTheDocument();
});
it('renders agent without description', () => {
const agentNoDesc: AvailableAgent[] = [
{ row_id: 10, name: 'Simple Agent' },
];
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={agentNoDesc}
/>
);
expect(screen.getByText('Simple Agent')).toBeInTheDocument();
});
it('renders agent without icon using fallback Bot icon', () => {
const agentNoIcon: AvailableAgent[] = [
{ row_id: 10, name: 'No Icon Agent', icon: null },
];
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={agentNoIcon}
/>
);
expect(screen.getByText('No Icon Agent')).toBeInTheDocument();
// The button should exist and be clickable
const button = screen.getByText('No Icon Agent').closest('button');
expect(button).toBeInTheDocument();
});
it('has data-testid on each agent item', () => {
render(
<SubAgentSelector
value={[]}
onChange={mockOnChange}
availableAgents={mockAgents}
/>
);
expect(screen.getByTestId('sub-agent-item-1')).toBeInTheDocument();
expect(screen.getByTestId('sub-agent-item-2')).toBeInTheDocument();
expect(screen.getByTestId('sub-agent-item-3')).toBeInTheDocument();
});
});