mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 03:40:14 +01:00
ESLint fixes
This commit is contained in:
parent
33c4ef03c3
commit
c6e1c65fe7
9 changed files with 114 additions and 56 deletions
|
|
@ -456,7 +456,7 @@ describe('Accessibility Improvements', () => {
|
||||||
expect(alert).toHaveAttribute('aria-atomic', 'true');
|
expect(alert).toHaveAttribute('aria-atomic', 'true');
|
||||||
|
|
||||||
// Check heading structure
|
// Check heading structure
|
||||||
const heading = screen.getByRole('heading', { level: 2 });
|
const heading = screen.getByRole('heading', { level: 3 });
|
||||||
expect(heading).toHaveAttribute('id', 'error-title');
|
expect(heading).toHaveAttribute('id', 'error-title');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
import { render, screen, waitFor } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { MemoryRouter, useNavigate } from 'react-router-dom';
|
import { MemoryRouter, useNavigate } from 'react-router-dom';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
|
|
@ -19,6 +19,7 @@ jest.mock('react-router-dom', () => ({
|
||||||
|
|
||||||
jest.mock('~/hooks', () => ({
|
jest.mock('~/hooks', () => ({
|
||||||
useToast: jest.fn(),
|
useToast: jest.fn(),
|
||||||
|
useMediaQuery: jest.fn(() => false), // Mock as desktop by default
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('~/hooks/useLocalize', () => ({
|
jest.mock('~/hooks/useLocalize', () => ({
|
||||||
|
|
@ -33,11 +34,7 @@ jest.mock('~/utils/agents', () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock clipboard API
|
// Mock clipboard API
|
||||||
Object.assign(navigator, {
|
const mockWriteText = jest.fn();
|
||||||
clipboard: {
|
|
||||||
writeText: jest.fn(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const mockNavigate = jest.fn();
|
const mockNavigate = jest.fn();
|
||||||
const mockShowToast = jest.fn();
|
const mockShowToast = jest.fn();
|
||||||
|
|
@ -55,17 +52,23 @@ const mockAgent: t.Agent = {
|
||||||
provider: 'openai',
|
provider: 'openai',
|
||||||
instructions: 'You are a helpful test agent',
|
instructions: 'You are a helpful test agent',
|
||||||
tools: [],
|
tools: [],
|
||||||
code_interpreter: false,
|
|
||||||
file_search: false,
|
|
||||||
author: 'test-user-id',
|
author: 'test-user-id',
|
||||||
author_name: 'Test User',
|
created_at: new Date().getTime(),
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
updatedAt: new Date().toISOString(),
|
|
||||||
version: 1,
|
version: 1,
|
||||||
support_contact: {
|
support_contact: {
|
||||||
name: 'Support Team',
|
name: 'Support Team',
|
||||||
email: 'support@test.com',
|
email: 'support@test.com',
|
||||||
},
|
},
|
||||||
|
model_parameters: {
|
||||||
|
model: undefined,
|
||||||
|
temperature: null,
|
||||||
|
maxContextTokens: null,
|
||||||
|
max_context_tokens: null,
|
||||||
|
max_output_tokens: null,
|
||||||
|
top_p: null,
|
||||||
|
frequency_penalty: null,
|
||||||
|
presence_penalty: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to render with providers
|
// Helper function to render with providers
|
||||||
|
|
@ -95,8 +98,19 @@ describe('AgentDetail', () => {
|
||||||
(useToast as jest.Mock).mockReturnValue({ showToast: mockShowToast });
|
(useToast as jest.Mock).mockReturnValue({ showToast: mockShowToast });
|
||||||
(useLocalize as jest.Mock).mockReturnValue(mockLocalize);
|
(useLocalize as jest.Mock).mockReturnValue(mockLocalize);
|
||||||
|
|
||||||
// Reset clipboard mock
|
// Setup clipboard mock if it doesn't exist
|
||||||
(navigator.clipboard.writeText as jest.Mock).mockResolvedValue(undefined);
|
if (!navigator.clipboard) {
|
||||||
|
Object.defineProperty(navigator, 'clipboard', {
|
||||||
|
value: {
|
||||||
|
writeText: mockWriteText,
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If clipboard exists, spy on it
|
||||||
|
jest.spyOn(navigator.clipboard, 'writeText').mockImplementation(mockWriteText);
|
||||||
|
}
|
||||||
|
mockWriteText.mockResolvedValue(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
|
@ -224,11 +238,17 @@ describe('AgentDetail', () => {
|
||||||
const copyLinkButton = screen.getByRole('button', { name: 'com_agents_copy_link' });
|
const copyLinkButton = screen.getByRole('button', { name: 'com_agents_copy_link' });
|
||||||
await user.click(copyLinkButton);
|
await user.click(copyLinkButton);
|
||||||
|
|
||||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
// Wait for async clipboard operation to complete
|
||||||
`${window.location.origin}/c/new?agent_id=test-agent-id`,
|
await waitFor(() => {
|
||||||
);
|
expect(mockWriteText).toHaveBeenCalledWith(
|
||||||
expect(mockShowToast).toHaveBeenCalledWith({
|
`${window.location.origin}/c/new?agent_id=test-agent-id`,
|
||||||
message: 'Link copied',
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockShowToast).toHaveBeenCalledWith({
|
||||||
|
message: 'Link copied',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Dropdown should close
|
// Dropdown should close
|
||||||
|
|
@ -241,7 +261,7 @@ describe('AgentDetail', () => {
|
||||||
|
|
||||||
it('should show error toast when clipboard write fails', async () => {
|
it('should show error toast when clipboard write fails', async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
(navigator.clipboard.writeText as jest.Mock).mockRejectedValue(new Error('Clipboard error'));
|
mockWriteText.mockRejectedValue(new Error('Clipboard error'));
|
||||||
|
|
||||||
renderWithProviders(<AgentDetail {...defaultProps} />);
|
renderWithProviders(<AgentDetail {...defaultProps} />);
|
||||||
|
|
||||||
|
|
@ -252,6 +272,11 @@ describe('AgentDetail', () => {
|
||||||
const copyLinkButton = screen.getByRole('button', { name: 'com_agents_copy_link' });
|
const copyLinkButton = screen.getByRole('button', { name: 'com_agents_copy_link' });
|
||||||
await user.click(copyLinkButton);
|
await user.click(copyLinkButton);
|
||||||
|
|
||||||
|
// Wait for clipboard operation to fail and error toast to show
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(mockWriteText).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockShowToast).toHaveBeenCalledWith({
|
expect(mockShowToast).toHaveBeenCalledWith({
|
||||||
message: 'com_agents_link_copy_failed',
|
message: 'com_agents_link_copy_failed',
|
||||||
|
|
@ -261,7 +286,7 @@ describe('AgentDetail', () => {
|
||||||
|
|
||||||
it('should call onClose when dialog is closed', () => {
|
it('should call onClose when dialog is closed', () => {
|
||||||
const mockOnClose = jest.fn();
|
const mockOnClose = jest.fn();
|
||||||
render(<AgentDetail {...defaultProps} onClose={mockOnClose} isOpen={false} />);
|
renderWithProviders(<AgentDetail {...defaultProps} onClose={mockOnClose} isOpen={false} />);
|
||||||
|
|
||||||
// Since we're testing the onOpenChange callback, we need to trigger it
|
// Since we're testing the onOpenChange callback, we need to trigger it
|
||||||
// This would normally be done by the Dialog component when ESC is pressed or overlay is clicked
|
// This would normally be done by the Dialog component when ESC is pressed or overlay is clicked
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import CategoryTabs from '../CategoryTabs';
|
import CategoryTabs from '../CategoryTabs';
|
||||||
|
|
|
||||||
|
|
@ -301,5 +301,3 @@ describe('ErrorDisplay', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
export default {};
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import '@testing-library/jest-dom';
|
import '@testing-library/jest-dom';
|
||||||
import SearchBar from '../SearchBar';
|
import SearchBar from '../SearchBar';
|
||||||
|
|
@ -9,7 +9,7 @@ jest.mock('~/hooks/useLocalize', () => () => (key: string) => key);
|
||||||
|
|
||||||
// Mock useDebounce hook
|
// Mock useDebounce hook
|
||||||
jest.mock('~/hooks', () => ({
|
jest.mock('~/hooks', () => ({
|
||||||
useDebounce: (value: string, delay: number) => value, // Return value immediately for testing
|
useDebounce: (value: string) => value, // Return value immediately for testing
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('SearchBar', () => {
|
describe('SearchBar', () => {
|
||||||
|
|
|
||||||
|
|
@ -193,8 +193,6 @@ export const useUploadAgentAvatarMutation = (
|
||||||
t.AgentAvatarVariables, // request
|
t.AgentAvatarVariables, // request
|
||||||
unknown // context
|
unknown // context
|
||||||
> => {
|
> => {
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation([MutationKeys.agentAvatarUpload], {
|
return useMutation([MutationKeys.agentAvatarUpload], {
|
||||||
mutationFn: ({ postCreation, ...variables }: t.AgentAvatarVariables) =>
|
mutationFn: ({ postCreation, ...variables }: t.AgentAvatarVariables) =>
|
||||||
dataService.uploadAgentAvatar(variables),
|
dataService.uploadAgentAvatar(variables),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { renderHook } from '@testing-library/react';
|
import React from 'react';
|
||||||
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import useAgentCategories from '../useAgentCategories';
|
import useAgentCategories from '../useAgentCategories';
|
||||||
import { AGENT_CATEGORIES, EMPTY_AGENT_CATEGORY } from '~/constants/agentCategories';
|
import { EMPTY_AGENT_CATEGORY } from '~/constants/agentCategories';
|
||||||
|
|
||||||
// Mock the useLocalize hook
|
// Mock the useLocalize hook
|
||||||
jest.mock('~/hooks/useLocalize', () => ({
|
jest.mock('~/hooks/useLocalize', () => ({
|
||||||
|
|
@ -11,25 +13,68 @@ jest.mock('~/hooks/useLocalize', () => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('useAgentCategories', () => {
|
// Mock the data provider
|
||||||
it('should return processed categories with correct structure', () => {
|
jest.mock('~/data-provider/Agents', () => ({
|
||||||
const { result } = renderHook(() => useAgentCategories());
|
useGetAgentCategoriesQuery: jest.fn(() => ({
|
||||||
|
data: [
|
||||||
|
{ value: 'general', label: 'com_ui_agent_category_general' },
|
||||||
|
{ value: 'hr', label: 'com_ui_agent_category_hr' },
|
||||||
|
{ value: 'rd', label: 'com_ui_agent_category_rd' },
|
||||||
|
{ value: 'finance', label: 'com_ui_agent_category_finance' },
|
||||||
|
{ value: 'it', label: 'com_ui_agent_category_it' },
|
||||||
|
{ value: 'sales', label: 'com_ui_agent_category_sales' },
|
||||||
|
{ value: 'aftersales', label: 'com_ui_agent_category_aftersales' },
|
||||||
|
{ value: 'promoted', label: 'Promoted' }, // Should be filtered out
|
||||||
|
{ value: 'all', label: 'All' }, // Should be filtered out
|
||||||
|
],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
// Check that we have the expected number of categories
|
const createWrapper = () => {
|
||||||
expect(result.current.categories.length).toBe(AGENT_CATEGORIES.length);
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
retry: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('useAgentCategories', () => {
|
||||||
|
it('should return processed categories with correct structure', async () => {
|
||||||
|
const { result } = renderHook(() => useAgentCategories(), {
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
// Check that we have the expected number of categories (excluding 'promoted' and 'all')
|
||||||
|
expect(result.current.categories.length).toBe(7);
|
||||||
|
});
|
||||||
|
|
||||||
// Check that the first category has the expected structure
|
// Check that the first category has the expected structure
|
||||||
const firstCategory = result.current.categories[0];
|
const firstCategory = result.current.categories[0];
|
||||||
const firstOriginalCategory = AGENT_CATEGORIES[0];
|
expect(firstCategory.value).toBe('general');
|
||||||
|
expect(firstCategory.label).toBe('com_ui_agent_category_general');
|
||||||
expect(firstCategory.value).toBe(firstOriginalCategory.value);
|
|
||||||
|
|
||||||
// Check that labels are properly translated
|
|
||||||
expect(firstCategory.label).toBe('General (Translated)');
|
|
||||||
expect(firstCategory.className).toBe('w-full');
|
expect(firstCategory.className).toBe('w-full');
|
||||||
|
|
||||||
|
// Verify special categories are filtered out
|
||||||
|
const categoryValues = result.current.categories.map((cat) => cat.value);
|
||||||
|
expect(categoryValues).not.toContain('promoted');
|
||||||
|
expect(categoryValues).not.toContain('all');
|
||||||
|
|
||||||
// Check the empty category
|
// Check the empty category
|
||||||
expect(result.current.emptyCategory.value).toBe(EMPTY_AGENT_CATEGORY.value);
|
expect(result.current.emptyCategory.value).toBe(EMPTY_AGENT_CATEGORY.value);
|
||||||
expect(result.current.emptyCategory.label).toBeTruthy();
|
expect(result.current.emptyCategory.label).toBe('General (Translated)');
|
||||||
|
expect(result.current.emptyCategory.className).toBe('w-full');
|
||||||
|
|
||||||
|
// Check loading state
|
||||||
|
expect(result.current.isLoading).toBe(false);
|
||||||
|
expect(result.current.error).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import { MessageSquareQuote, ArrowRightToLine, Settings2, Database, Bookmark } from 'lucide-react';
|
||||||
MessageSquareQuote,
|
|
||||||
ArrowRightToLine,
|
|
||||||
Settings2, Database,
|
|
||||||
Bookmark,
|
|
||||||
LayoutGrid,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import {
|
import {
|
||||||
isAssistantsEndpoint,
|
isAssistantsEndpoint,
|
||||||
isAgentsEndpoint,
|
isAgentsEndpoint,
|
||||||
|
|
@ -27,7 +21,6 @@ import FilesPanel from '~/components/SidePanel/Files/Panel';
|
||||||
import MCPPanel from '~/components/SidePanel/MCP/MCPPanel';
|
import MCPPanel from '~/components/SidePanel/MCP/MCPPanel';
|
||||||
import { useGetStartupConfig } from '~/data-provider';
|
import { useGetStartupConfig } from '~/data-provider';
|
||||||
import { useHasAccess } from '~/hooks';
|
import { useHasAccess } from '~/hooks';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function useSideNavLinks({
|
export default function useSideNavLinks({
|
||||||
hidePanel,
|
hidePanel,
|
||||||
|
|
@ -44,7 +37,6 @@ export default function useSideNavLinks({
|
||||||
interfaceConfig: Partial<TInterfaceConfig>;
|
interfaceConfig: Partial<TInterfaceConfig>;
|
||||||
endpointsConfig: TEndpointsConfig;
|
endpointsConfig: TEndpointsConfig;
|
||||||
}) {
|
}) {
|
||||||
const navigate = useNavigate();
|
|
||||||
const hasAccessToPrompts = useHasAccess({
|
const hasAccessToPrompts = useHasAccess({
|
||||||
permissionType: PermissionTypes.PROMPTS,
|
permissionType: PermissionTypes.PROMPTS,
|
||||||
permission: Permissions.USE,
|
permission: Permissions.USE,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ describe('Agent Utilities', () => {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
avatar: '/path/to/avatar.png',
|
avatar: '/path/to/avatar.png',
|
||||||
} as t.Agent;
|
} as unknown as t.Agent;
|
||||||
expect(getAgentAvatarUrl(agent)).toBe('/path/to/avatar.png');
|
expect(getAgentAvatarUrl(agent)).toBe('/path/to/avatar.png');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ describe('Agent Utilities', () => {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
avatar: '/test-avatar.png',
|
avatar: '/test-avatar.png',
|
||||||
} as t.Agent;
|
} as unknown as t.Agent;
|
||||||
|
|
||||||
render(<div>{renderAgentAvatar(agent)}</div>);
|
render(<div>{renderAgentAvatar(agent)}</div>);
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ describe('Agent Utilities', () => {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
avatar: '/test-avatar.png',
|
avatar: '/test-avatar.png',
|
||||||
} as t.Agent;
|
} as unknown as t.Agent;
|
||||||
|
|
||||||
const { rerender } = render(<div>{renderAgentAvatar(agent, { size: 'sm' })}</div>);
|
const { rerender } = render(<div>{renderAgentAvatar(agent, { size: 'sm' })}</div>);
|
||||||
expect(screen.getByAltText('Test Agent avatar')).toHaveClass('h-12', 'w-12');
|
expect(screen.getByAltText('Test Agent avatar')).toHaveClass('h-12', 'w-12');
|
||||||
|
|
@ -107,7 +107,7 @@ describe('Agent Utilities', () => {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
avatar: '/test-avatar.png',
|
avatar: '/test-avatar.png',
|
||||||
} as t.Agent;
|
} as unknown as t.Agent;
|
||||||
|
|
||||||
render(<div>{renderAgentAvatar(agent, { className: 'custom-class' })}</div>);
|
render(<div>{renderAgentAvatar(agent, { className: 'custom-class' })}</div>);
|
||||||
|
|
||||||
|
|
@ -120,7 +120,7 @@ describe('Agent Utilities', () => {
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Agent',
|
name: 'Test Agent',
|
||||||
avatar: '/test-avatar.png',
|
avatar: '/test-avatar.png',
|
||||||
} as t.Agent;
|
} as unknown as t.Agent;
|
||||||
|
|
||||||
const { rerender } = render(<div>{renderAgentAvatar(agent, { showBorder: true })}</div>);
|
const { rerender } = render(<div>{renderAgentAvatar(agent, { showBorder: true })}</div>);
|
||||||
expect(screen.getByAltText('Test Agent avatar')).toHaveClass('border-2');
|
expect(screen.getByAltText('Test Agent avatar')).toHaveClass('border-2');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue