import { describe, it, expect, vi, beforeEach } from 'vitest' import { render, screen, fireEvent, act } from '@testing-library/react' import App from '../App' import { useConnection } from '../lib/hooks/useConnection' import { useDraggablePane } from '../lib/hooks/useDraggablePane' // Mock URL params const mockURLSearchParams = vi.fn() vi.stubGlobal('URLSearchParams', mockURLSearchParams) // Mock the hooks vi.mock('../lib/hooks/useConnection', () => ({ useConnection: vi.fn() })) vi.mock('../lib/hooks/useDraggablePane', () => ({ useDraggablePane: vi.fn() })) // Mock fetch for config const mockFetch = vi.fn() global.fetch = mockFetch describe('App', () => { beforeEach(() => { // Reset all mocks vi.clearAllMocks() // Mock URL params mockURLSearchParams.mockReturnValue({ get: () => '3000' }) // Mock fetch response mockFetch.mockResolvedValue({ json: () => Promise.resolve({ defaultEnvironment: {}, defaultCommand: 'test-command', defaultArgs: '--test' }) }) // Mock useConnection hook const mockUseConnection = useConnection as jest.Mock mockUseConnection.mockReturnValue({ connectionStatus: 'disconnected', serverCapabilities: null, mcpClient: null, requestHistory: [], makeRequest: vi.fn(), sendNotification: vi.fn(), connect: vi.fn() }) // Mock useDraggablePane hook const mockUseDraggablePane = useDraggablePane as jest.Mock mockUseDraggablePane.mockReturnValue({ height: 300, handleDragStart: vi.fn() }) }) it('renders initial disconnected state', async () => { await act(async () => { render() }) expect(screen.getByText('Connect to an MCP server to start inspecting')).toBeInTheDocument() }) it('loads config on mount', async () => { await act(async () => { render() }) expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/config') }) it('shows connected interface when mcpClient is available', async () => { const mockUseConnection = useConnection as jest.Mock mockUseConnection.mockReturnValue({ connectionStatus: 'connected', serverCapabilities: { resources: true, prompts: true, tools: true }, mcpClient: {}, requestHistory: [], makeRequest: vi.fn(), sendNotification: vi.fn(), connect: vi.fn() }) await act(async () => { render() }) // Use more specific selectors const resourcesTab = screen.getByRole('tab', { name: /resources/i }) const promptsTab = screen.getByRole('tab', { name: /prompts/i }) const toolsTab = screen.getByRole('tab', { name: /tools/i }) expect(resourcesTab).toBeInTheDocument() expect(promptsTab).toBeInTheDocument() expect(toolsTab).toBeInTheDocument() }) it('disables tabs based on server capabilities', async () => { const mockUseConnection = useConnection as jest.Mock mockUseConnection.mockReturnValue({ connectionStatus: 'connected', serverCapabilities: { resources: false, prompts: true, tools: false }, mcpClient: {}, requestHistory: [], makeRequest: vi.fn(), sendNotification: vi.fn(), connect: vi.fn() }) await act(async () => { render() }) // Resources tab should be disabled const resourcesTab = screen.getByRole('tab', { name: /resources/i }) expect(resourcesTab).toHaveAttribute('disabled') // Prompts tab should be enabled const promptsTab = screen.getByRole('tab', { name: /prompts/i }) expect(promptsTab).not.toHaveAttribute('disabled') // Tools tab should be disabled const toolsTab = screen.getByRole('tab', { name: /tools/i }) expect(toolsTab).toHaveAttribute('disabled') }) it('shows notification count in sampling tab', async () => { const mockUseConnection = useConnection as jest.Mock mockUseConnection.mockReturnValue({ connectionStatus: 'connected', serverCapabilities: { sampling: true }, mcpClient: {}, requestHistory: [], makeRequest: vi.fn(), sendNotification: vi.fn(), connect: vi.fn(), onPendingRequest: (request, resolve, reject) => { // Simulate a pending request setPendingSampleRequests(prev => [ ...prev, { id: 1, request, resolve, reject } ]) } }) await act(async () => { render() }) // Initially no notification count const samplingTab = screen.getByRole('tab', { name: /sampling/i }) expect(samplingTab.querySelector('.bg-red-500')).not.toBeInTheDocument() // Simulate a pending request await act(async () => { mockUseConnection.mock.calls[0][0].onPendingRequest( { method: 'test', params: {} }, () => {}, () => {} ) }) // Should show notification count expect(samplingTab.querySelector('.bg-red-500')).toBeInTheDocument() }) it('persists command and args to localStorage', async () => { const setItemSpy = vi.spyOn(Storage.prototype, 'setItem') await act(async () => { render() }) // Simulate command change await act(async () => { const commandInput = screen.getByPlaceholderText(/command/i) fireEvent.change(commandInput, { target: { value: 'new-command' } }) }) expect(setItemSpy).toHaveBeenCalledWith('lastCommand', 'new-command') }) it('shows error message when server has no capabilities', async () => { const mockUseConnection = useConnection as jest.Mock mockUseConnection.mockReturnValue({ connectionStatus: 'connected', serverCapabilities: {}, mcpClient: {}, requestHistory: [], makeRequest: vi.fn(), sendNotification: vi.fn(), connect: vi.fn() }) await act(async () => { render() }) expect(screen.getByText('The connected server does not support any MCP capabilities')).toBeInTheDocument() }) })