🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety

refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase

refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids

chore: move sharing related components to dedicated "Sharing" directory

chore: remove PublicSharingToggle component and update index exports

chore: move non-sidepanel agent components to `~/components/Agents`

chore: move AgentCategoryDisplay component with tests

chore: remove commented out code

refactor: change PERMISSION_BITS from const to enum for better type safety

refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks

refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety

refactor: remove unused canAccessPromptResource middleware and related code

refactor: remove unused prompt access roles from createAccessRoleMethods

refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value

refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety

refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety

refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety

refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components

refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency

chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions

chore: move SearchPicker to PeoplePicker dir

refactor: implement debouncing for query changes in SearchPicker for improved performance

chore: fix typing, import order for agent admin settings

fix: agent admin settings, prevent agent form submission

refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds`

refactor: replace PermissionBits with PERMISSION_BITS

refactor: replace PERMISSION_BITS with PermissionBits
This commit is contained in:
Danny Avila 2025-07-28 17:52:36 -04:00
parent ae732b2ebc
commit 81b32e400a
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
96 changed files with 781 additions and 798 deletions

View file

@ -0,0 +1,210 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import AgentCard from '../AgentCard';
import type t from 'librechat-data-provider';
// Mock useLocalize hook
jest.mock('~/hooks/useLocalize', () => () => (key: string) => {
const mockTranslations: Record<string, string> = {
com_agents_created_by: 'Created by',
};
return mockTranslations[key] || key;
});
describe('AgentCard', () => {
const mockAgent: t.Agent = {
id: '1',
name: 'Test Agent',
description: 'A test agent for testing purposes',
support_contact: {
name: 'Test Support',
email: 'test@example.com',
},
avatar: { filepath: '/test-avatar.png', source: 'local' },
created_at: 1672531200000,
instructions: 'Test instructions',
provider: 'openai' as const,
model: 'gpt-4',
model_parameters: {
temperature: 0.7,
maxContextTokens: 4096,
max_context_tokens: 4096,
max_output_tokens: 1024,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
},
};
const mockOnClick = jest.fn();
beforeEach(() => {
mockOnClick.mockClear();
});
it('renders agent information correctly', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
expect(screen.getByText('Test Agent')).toBeInTheDocument();
expect(screen.getByText('A test agent for testing purposes')).toBeInTheDocument();
expect(screen.getByText('Created by')).toBeInTheDocument();
expect(screen.getByText('Test Support')).toBeInTheDocument();
});
it('displays avatar when provided as object', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const avatarImg = screen.getByAltText('Test Agent avatar');
expect(avatarImg).toBeInTheDocument();
expect(avatarImg).toHaveAttribute('src', '/test-avatar.png');
});
it('displays avatar when provided as string', () => {
const agentWithStringAvatar = {
...mockAgent,
avatar: '/string-avatar.png' as any, // Legacy support for string avatars
};
render(<AgentCard agent={agentWithStringAvatar} onClick={mockOnClick} />);
const avatarImg = screen.getByAltText('Test Agent avatar');
expect(avatarImg).toBeInTheDocument();
expect(avatarImg).toHaveAttribute('src', '/string-avatar.png');
});
it('displays Bot icon fallback when no avatar is provided', () => {
const agentWithoutAvatar = {
...mockAgent,
avatar: undefined,
};
render(<AgentCard agent={agentWithoutAvatar as any as t.Agent} onClick={mockOnClick} />);
// Check for Bot icon presence by looking for the svg with lucide-bot class
const botIcon = document.querySelector('.lucide-bot');
expect(botIcon).toBeInTheDocument();
});
it('calls onClick when card is clicked', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const card = screen.getByRole('button');
fireEvent.click(card);
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
it('calls onClick when Enter key is pressed', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const card = screen.getByRole('button');
fireEvent.keyDown(card, { key: 'Enter' });
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
it('calls onClick when Space key is pressed', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const card = screen.getByRole('button');
fireEvent.keyDown(card, { key: ' ' });
expect(mockOnClick).toHaveBeenCalledTimes(1);
});
it('does not call onClick for other keys', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const card = screen.getByRole('button');
fireEvent.keyDown(card, { key: 'Escape' });
expect(mockOnClick).not.toHaveBeenCalled();
});
it('applies additional className when provided', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} className="custom-class" />);
const card = screen.getByRole('button');
expect(card).toHaveClass('custom-class');
});
it('handles missing support contact gracefully', () => {
const agentWithoutContact = {
...mockAgent,
support_contact: undefined,
authorName: undefined,
};
render(<AgentCard agent={agentWithoutContact} onClick={mockOnClick} />);
expect(screen.getByText('Test Agent')).toBeInTheDocument();
expect(screen.getByText('A test agent for testing purposes')).toBeInTheDocument();
expect(screen.queryByText(/Created by/)).not.toBeInTheDocument();
});
it('displays authorName when support_contact is missing', () => {
const agentWithAuthorName = {
...mockAgent,
support_contact: undefined,
authorName: 'John Doe',
};
render(<AgentCard agent={agentWithAuthorName} onClick={mockOnClick} />);
expect(screen.getByText('Created by')).toBeInTheDocument();
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
it('displays support_contact email when name is missing', () => {
const agentWithEmailOnly = {
...mockAgent,
support_contact: { email: 'contact@example.com' },
authorName: undefined,
};
render(<AgentCard agent={agentWithEmailOnly} onClick={mockOnClick} />);
expect(screen.getByText('Created by')).toBeInTheDocument();
expect(screen.getByText('contact@example.com')).toBeInTheDocument();
});
it('prioritizes support_contact name over authorName', () => {
const agentWithBoth = {
...mockAgent,
support_contact: { name: 'Support Team' },
authorName: 'John Doe',
};
render(<AgentCard agent={agentWithBoth} onClick={mockOnClick} />);
expect(screen.getByText('Created by')).toBeInTheDocument();
expect(screen.getByText('Support Team')).toBeInTheDocument();
expect(screen.queryByText('John Doe')).not.toBeInTheDocument();
});
it('prioritizes name over email in support_contact', () => {
const agentWithNameAndEmail = {
...mockAgent,
support_contact: {
name: 'Support Team',
email: 'support@example.com',
},
authorName: undefined,
};
render(<AgentCard agent={agentWithNameAndEmail} onClick={mockOnClick} />);
expect(screen.getByText('Created by')).toBeInTheDocument();
expect(screen.getByText('Support Team')).toBeInTheDocument();
expect(screen.queryByText('support@example.com')).not.toBeInTheDocument();
});
it('has proper accessibility attributes', () => {
render(<AgentCard agent={mockAgent} onClick={mockOnClick} />);
const card = screen.getByRole('button');
expect(card).toHaveAttribute('tabIndex', '0');
expect(card).toHaveAttribute('aria-label', 'com_agents_agent_card_label');
});
});