From cec1ec0c79dba3bbdbc65694edabd17e9d6e2257 Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Thu, 24 Jul 2025 11:20:16 -0400 Subject: [PATCH] chore: fix ESLint issues and Test Mocks --- api/models/File.spec.js | 4 +- api/server/routes/files/files.agents.test.js | 1 - api/server/services/start/interface.spec.js | 6 +- .../SidePanel/Agents/MarketplaceContext.tsx | 5 +- .../Agents/__tests__/AgentDetail.spec.tsx | 60 ++++++++++++++++--- .../__tests__/MarketplaceContext.spec.tsx | 18 +++++- client/src/hooks/Input/useQueryParams.spec.ts | 42 +++++++++++++ 7 files changed, 119 insertions(+), 17 deletions(-) diff --git a/api/models/File.spec.js b/api/models/File.spec.js index 7eabc37553..99464fdbd0 100644 --- a/api/models/File.spec.js +++ b/api/models/File.spec.js @@ -11,7 +11,6 @@ let File; let Agent; let AclEntry; let User; -let AccessRole; let modelsToCleanup = []; describe('File Access Control', () => { @@ -36,7 +35,6 @@ describe('File Access Control', () => { Agent = dbModels.Agent; AclEntry = dbModels.AclEntry; User = dbModels.User; - AccessRole = dbModels.AccessRole; // Seed default roles await seedDefaultRoles(); @@ -149,7 +147,7 @@ describe('File Access Control', () => { }); // Create agent - const agent = await createAgent({ + await createAgent({ id: agentId, name: 'Test Agent', author: authorId, diff --git a/api/server/routes/files/files.agents.test.js b/api/server/routes/files/files.agents.test.js index eebc591f8c..0ef5580ebc 100644 --- a/api/server/routes/files/files.agents.test.js +++ b/api/server/routes/files/files.agents.test.js @@ -2,7 +2,6 @@ const express = require('express'); const request = require('supertest'); const mongoose = require('mongoose'); const { v4: uuidv4 } = require('uuid'); -const { PERMISSION_BITS } = require('librechat-data-provider'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { createMethods } = require('@librechat/data-schemas'); const { createAgent } = require('~/models/Agent'); diff --git a/api/server/services/start/interface.spec.js b/api/server/services/start/interface.spec.js index 974becb5bb..e50f4514c2 100644 --- a/api/server/services/start/interface.spec.js +++ b/api/server/services/start/interface.spec.js @@ -28,7 +28,11 @@ describe('loadDefaultInterface', () => { expect(updateAccessPermissions).toHaveBeenCalledWith(SystemRoles.USER, { [PermissionTypes.PROMPTS]: { [Permissions.USE]: true }, [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: true }, - [PermissionTypes.MEMORIES]: { [Permissions.USE]: true, [Permissions.OPT_OUT]: undefined, [Permissions.OPT_OUT]: undefined }, + [PermissionTypes.MEMORIES]: { + [Permissions.USE]: true, + [Permissions.OPT_OUT]: undefined, + [Permissions.OPT_OUT]: undefined, + }, [PermissionTypes.MULTI_CONVO]: { [Permissions.USE]: true }, [PermissionTypes.AGENTS]: { [Permissions.USE]: true }, [PermissionTypes.TEMPORARY_CHAT]: { [Permissions.USE]: true }, diff --git a/client/src/components/SidePanel/Agents/MarketplaceContext.tsx b/client/src/components/SidePanel/Agents/MarketplaceContext.tsx index f77ddc0cae..09c88e3291 100644 --- a/client/src/components/SidePanel/Agents/MarketplaceContext.tsx +++ b/client/src/components/SidePanel/Agents/MarketplaceContext.tsx @@ -1,7 +1,4 @@ -import React, { useMemo } from 'react'; - -import { EModelEndpoint } from 'librechat-data-provider'; - +import React from 'react'; import { ChatContext } from '~/Providers'; import { useChatHelpers } from '~/hooks'; diff --git a/client/src/components/SidePanel/Agents/__tests__/AgentDetail.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/AgentDetail.spec.tsx index 364ac82aec..9bf44b1f96 100644 --- a/client/src/components/SidePanel/Agents/__tests__/AgentDetail.spec.tsx +++ b/client/src/components/SidePanel/Agents/__tests__/AgentDetail.spec.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ import React from 'react'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; @@ -6,10 +7,10 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RecoilRoot } from 'recoil'; import type t from 'librechat-data-provider'; +import { Constants, EModelEndpoint } from 'librechat-data-provider'; import AgentDetail from '../AgentDetail'; import { useToast } from '~/hooks'; -import useLocalize from '~/hooks/useLocalize'; // Mock dependencies jest.mock('react-router-dom', () => ({ @@ -20,11 +21,7 @@ jest.mock('react-router-dom', () => ({ jest.mock('~/hooks', () => ({ useToast: jest.fn(), useMediaQuery: jest.fn(() => false), // Mock as desktop by default -})); - -jest.mock('~/hooks/useLocalize', () => ({ - __esModule: true, - default: jest.fn(), + useLocalize: jest.fn(), })); jest.mock('~/utils/agents', () => ({ @@ -33,6 +30,15 @@ jest.mock('~/utils/agents', () => ({ )), })); +jest.mock('~/Providers', () => ({ + useChatContext: jest.fn(), +})); + +jest.mock('@tanstack/react-query', () => ({ + ...jest.requireActual('@tanstack/react-query'), + useQueryClient: jest.fn(), +})); + // Mock clipboard API const mockWriteText = jest.fn(); @@ -96,8 +102,24 @@ describe('AgentDetail', () => { jest.clearAllMocks(); (useNavigate as jest.Mock).mockReturnValue(mockNavigate); (useToast as jest.Mock).mockReturnValue({ showToast: mockShowToast }); + const { useLocalize } = require('~/hooks'); (useLocalize as jest.Mock).mockReturnValue(mockLocalize); + // Mock useChatContext + const { useChatContext } = require('~/Providers'); + (useChatContext as jest.Mock).mockReturnValue({ + conversation: { conversationId: 'test-convo-id' }, + newConversation: jest.fn(), + }); + + // Mock useQueryClient + const { useQueryClient } = require('@tanstack/react-query'); + (useQueryClient as jest.Mock).mockReturnValue({ + getQueryData: jest.fn(), + setQueryData: jest.fn(), + invalidateQueries: jest.fn(), + }); + // Setup clipboard mock if it doesn't exist if (!navigator.clipboard) { Object.defineProperty(navigator, 'clipboard', { @@ -176,12 +198,36 @@ describe('AgentDetail', () => { describe('Interactions', () => { it('should navigate to chat when Start Chat button is clicked', async () => { const user = userEvent.setup(); + const mockNewConversation = jest.fn(); + const mockQueryClient = { + getQueryData: jest.fn().mockReturnValue(null), + setQueryData: jest.fn(), + invalidateQueries: jest.fn(), + }; + + // Update mocks for this test + const { useChatContext } = require('~/Providers'); + (useChatContext as jest.Mock).mockReturnValue({ + conversation: { conversationId: 'test-convo-id' }, + newConversation: mockNewConversation, + }); + + const { useQueryClient } = require('@tanstack/react-query'); + (useQueryClient as jest.Mock).mockReturnValue(mockQueryClient); + renderWithProviders(); const startChatButton = screen.getByRole('button', { name: 'com_agents_start_chat' }); await user.click(startChatButton); - expect(mockNavigate).toHaveBeenCalledWith('/c/new?agent_id=test-agent-id'); + expect(mockNewConversation).toHaveBeenCalledWith({ + template: { + conversationId: Constants.NEW_CONVO, + endpoint: EModelEndpoint.agents, + agent_id: 'test-agent-id', + title: 'Chat with Test Agent', + }, + }); }); it('should not navigate when agent is null', async () => { diff --git a/client/src/components/SidePanel/Agents/__tests__/MarketplaceContext.spec.tsx b/client/src/components/SidePanel/Agents/__tests__/MarketplaceContext.spec.tsx index d61ed00544..49ff4cfda0 100644 --- a/client/src/components/SidePanel/Agents/__tests__/MarketplaceContext.spec.tsx +++ b/client/src/components/SidePanel/Agents/__tests__/MarketplaceContext.spec.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-require-imports */ import React from 'react'; import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; @@ -17,6 +18,11 @@ jest.mock('~/Providers', () => ({ useChatContext: jest.fn(), })); +// Mock useChatHelpers to avoid Recoil dependency +jest.mock('~/hooks', () => ({ + useChatHelpers: jest.fn(), +})); + const mockedUseChatContext = useChatContext as jest.MockedFunction; // Test component that consumes the context @@ -35,6 +41,16 @@ const TestConsumer: React.FC = () => { describe('MarketplaceProvider', () => { beforeEach(() => { mockedUseChatContext.mockClear(); + + // Mock useChatHelpers return value + const { useChatHelpers } = require('~/hooks'); + (useChatHelpers as jest.Mock).mockReturnValue({ + conversation: { + endpoint: EModelEndpoint.agents, + conversationId: 'marketplace', + title: 'Agent Marketplace', + }, + }); }); it('provides correct marketplace context values', () => { @@ -46,7 +62,7 @@ describe('MarketplaceProvider', () => { }, }; - mockedUseChatContext.mockReturnValue(mockContext); + mockedUseChatContext.mockReturnValue(mockContext as ReturnType); render( diff --git a/client/src/hooks/Input/useQueryParams.spec.ts b/client/src/hooks/Input/useQueryParams.spec.ts index 52a5a877a0..9647fc2d76 100644 --- a/client/src/hooks/Input/useQueryParams.spec.ts +++ b/client/src/hooks/Input/useQueryParams.spec.ts @@ -34,6 +34,7 @@ jest.mock('react-router-dom', () => ({ jest.mock('@tanstack/react-query', () => ({ useQueryClient: jest.fn(), + useQuery: jest.fn(), })); jest.mock('~/Providers', () => ({ @@ -51,6 +52,15 @@ jest.mock('~/hooks/Conversations/useDefaultConvo', () => ({ default: jest.fn(), })); +jest.mock('~/hooks/AuthContext', () => ({ + useAuthContext: jest.fn(), +})); + +jest.mock('~/hooks/Agents/useAgentsMap', () => ({ + __esModule: true, + default: jest.fn(() => ({})), +})); + jest.mock('~/utils', () => ({ getConvoSwitchLogic: jest.fn(() => ({ template: {}, @@ -63,6 +73,8 @@ jest.mock('~/utils', () => ({ getModelSpecIconURL: jest.fn(() => 'icon-url'), removeUnavailableTools: jest.fn((preset) => preset), logger: { log: jest.fn() }, + getInitialTheme: jest.fn(() => 'light'), + applyFontSize: jest.fn(), })); // Mock the tQueryParamsSchema @@ -82,6 +94,21 @@ jest.mock('librechat-data-provider', () => ({ EModelEndpoint: { custom: 'custom', assistants: 'assistants', agents: 'agents' }, })); +// Mock data-provider hooks +jest.mock('~/data-provider', () => ({ + useGetAgentByIdQuery: jest.fn(() => ({ + data: null, + isLoading: false, + error: null, + })), + useAgentListingDefaultPermissionLevel: jest.fn(() => 'view'), + useListAgentsQuery: jest.fn(() => ({ + data: null, + isLoading: false, + error: null, + })), +})); + // Mock global window.history global.window = Object.create(window); global.window.history = { @@ -103,6 +130,14 @@ describe('useQueryParams', () => { // Reset mock for window.history.replaceState jest.spyOn(window.history, 'replaceState').mockClear(); + // Reset data-provider mocks + const dataProvider = jest.requireMock('~/data-provider'); + (dataProvider.useGetAgentByIdQuery as jest.Mock).mockReturnValue({ + data: null, + isLoading: false, + error: null, + }); + // Create mocks for all dependencies const mockSearchParams = new URLSearchParams(); (useSearchParams as jest.Mock).mockReturnValue([mockSearchParams, jest.fn()]); @@ -147,6 +182,13 @@ describe('useQueryParams', () => { const mockGetDefaultConversation = jest.fn().mockReturnValue({}); (useDefaultConvo as jest.Mock).mockReturnValue(mockGetDefaultConversation); + + // Mock useAuthContext + const { useAuthContext } = jest.requireMock('~/hooks/AuthContext'); + (useAuthContext as jest.Mock).mockReturnValue({ + user: { id: 'test-user-id' }, + isAuthenticated: true, + }); }); afterEach(() => {