/* eslint-disable react/no-unnecessary-use-prefix */ import type { ReactNode } from 'react' import type { AppDetailResponse } from '@/models/app' import type { AppSSO } from '@/types/app' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { AppModeEnum } from '@/types/app' import MCPServiceCard from './mcp-service-card' // Mock MCPServerModal vi.mock('@/app/components/tools/mcp/mcp-server-modal', () => ({ default: ({ show, onHide }: { show: boolean, onHide: () => void }) => { if (!show) return null return (
) }, })) // Mock Confirm vi.mock('@/app/components/base/confirm', () => ({ default: ({ isShow, onConfirm, onCancel }: { isShow: boolean, onConfirm: () => void, onCancel: () => void }) => { if (!isShow) return null return (
) }, })) // Mutable mock handlers for hook const mockHandleStatusChange = vi.fn().mockResolvedValue({ activated: true }) const mockHandleServerModalHide = vi.fn().mockReturnValue({ shouldDeactivate: false }) const mockHandleGenCode = vi.fn() const mockOpenConfirmDelete = vi.fn() const mockCloseConfirmDelete = vi.fn() const mockOpenServerModal = vi.fn() // Type for mock hook state type MockHookState = { genLoading: boolean isLoading: boolean serverPublished: boolean serverActivated: boolean serverURL: string detail: { id: string status: string server_code: string description: string parameters: Record } | undefined isCurrentWorkspaceManager: boolean toggleDisabled: boolean isMinimalState: boolean appUnpublished: boolean missingStartNode: boolean showConfirmDelete: boolean showMCPServerModal: boolean latestParams: Array } // Default hook state factory - creates fresh state for each test const createDefaultHookState = (): MockHookState => ({ genLoading: false, isLoading: false, serverPublished: true, serverActivated: true, serverURL: 'https://api.example.com/mcp/server/abc123/mcp', detail: { id: 'server-123', status: 'active', server_code: 'abc123', description: 'Test server', parameters: {}, }, isCurrentWorkspaceManager: true, toggleDisabled: false, isMinimalState: false, appUnpublished: false, missingStartNode: false, showConfirmDelete: false, showMCPServerModal: false, latestParams: [], }) // Mutable hook state - modify this in tests to change component behavior let mockHookState = createDefaultHookState() // Mock the hook - uses mockHookState which can be modified per test vi.mock('./hooks/use-mcp-service-card', () => ({ useMCPServiceCardState: () => ({ ...mockHookState, handleStatusChange: mockHandleStatusChange, handleServerModalHide: mockHandleServerModalHide, handleGenCode: mockHandleGenCode, openConfirmDelete: mockOpenConfirmDelete, closeConfirmDelete: mockCloseConfirmDelete, openServerModal: mockOpenServerModal, }), })) describe('MCPServiceCard', () => { const createWrapper = () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, }, }, }) return ({ children }: { children: ReactNode }) => React.createElement(QueryClientProvider, { client: queryClient }, children) } const createMockAppInfo = (mode: AppModeEnum = AppModeEnum.CHAT): AppDetailResponse & Partial => ({ id: 'app-123', name: 'Test App', mode, api_base_url: 'https://api.example.com/v1', } as AppDetailResponse & Partial) beforeEach(() => { // Reset hook state to defaults before each test mockHookState = createDefaultHookState() // Reset all mock function call history mockHandleStatusChange.mockClear().mockResolvedValue({ activated: true }) mockHandleServerModalHide.mockClear().mockReturnValue({ shouldDeactivate: false }) mockHandleGenCode.mockClear() mockOpenConfirmDelete.mockClear() mockCloseConfirmDelete.mockClear() mockOpenServerModal.mockClear() }) describe('Rendering', () => { it('should render without crashing', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render the MCP icon', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // The Mcp icon should be present in the component expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render status indicator', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Status indicator shows running or disable expect(screen.getByText(/appOverview.overview.status/)).toBeInTheDocument() }) it('should render switch toggle', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) expect(screen.getByRole('switch')).toBeInTheDocument() }) it('should render in minimal or full state based on server status', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Component renders either in minimal or full state expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render edit button', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Edit or add description button const editOrAddButton = screen.queryByText('tools.mcp.server.edit') || screen.queryByText('tools.mcp.server.addDescription') expect(editOrAddButton).toBeInTheDocument() }) }) describe('Status Indicator', () => { it('should show running status when server is activated', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // The status text should be present expect(screen.getByText(/appOverview.overview.status/)).toBeInTheDocument() }) }) describe('Server URL Display', () => { it('should display title in both minimal and full state', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Title should always be displayed expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('Trigger Mode Disabled', () => { it('should apply opacity when triggerModeDisabled is true', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) // Component should have reduced opacity class const container = document.querySelector('.opacity-60') expect(container).toBeInTheDocument() }) it('should not apply opacity when triggerModeDisabled is false', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) // Component should not have reduced opacity class on the main content const opacityElements = document.querySelectorAll('.opacity-60') // The opacity-60 should not be present when not disabled expect(opacityElements.length).toBe(0) }) it('should render overlay when triggerModeDisabled is true', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) // Overlay should have cursor-not-allowed const overlay = document.querySelector('.cursor-not-allowed') expect(overlay).toBeInTheDocument() }) }) describe('Different App Modes', () => { it('should render for chat app', () => { const appInfo = createMockAppInfo(AppModeEnum.CHAT) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render for workflow app', () => { const appInfo = createMockAppInfo(AppModeEnum.WORKFLOW) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render for advanced chat app', () => { const appInfo = createMockAppInfo(AppModeEnum.ADVANCED_CHAT) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render for completion app', () => { const appInfo = createMockAppInfo(AppModeEnum.COMPLETION) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should render for agent chat app', () => { const appInfo = createMockAppInfo(AppModeEnum.AGENT_CHAT) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('User Interactions', () => { it('should toggle switch', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') fireEvent.click(switchElement) // Switch should be interactive await waitFor(() => { expect(switchElement).toBeInTheDocument() }) }) it('should have switch button available', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // The switch is a button role element const switchElement = screen.getByRole('switch') expect(switchElement).toBeInTheDocument() }) }) describe('Props', () => { it('should accept triggerModeMessage prop', () => { const appInfo = createMockAppInfo() const message = 'Custom trigger mode message' render( , { wrapper: createWrapper() }, ) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should handle empty triggerModeMessage', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should handle ReactNode as triggerModeMessage', () => { const appInfo = createMockAppInfo() const message = Custom message render( , { wrapper: createWrapper() }, ) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('Edge Cases', () => { it('should handle minimal app info', () => { const minimalAppInfo = { id: 'minimal-app', name: 'Minimal', mode: AppModeEnum.CHAT, api_base_url: 'https://api.example.com/v1', } as AppDetailResponse & Partial render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should handle app info with special characters in name', () => { const appInfo = { id: 'app-special', name: 'Test App ', mode: AppModeEnum.CHAT, api_base_url: 'https://api.example.com/v1', } as AppDetailResponse & Partial render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('Server Not Published', () => { beforeEach(() => { // Modify hookState to simulate unpublished server mockHookState = { ...createDefaultHookState(), serverPublished: false, serverActivated: false, serverURL: '***********', detail: undefined, isMinimalState: true, } }) it('should show add description button when server is not published', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const buttons = screen.queryAllByRole('button') const addDescButton = buttons.find(btn => btn.textContent?.includes('tools.mcp.server.addDescription'), ) expect(addDescButton || screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should show masked URL when server is not published', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // In minimal/unpublished state, the URL should be masked or not shown expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should open modal when enabling unpublished server', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') fireEvent.click(switchElement) await waitFor(() => { const modal = screen.queryByTestId('mcp-server-modal') if (modal) expect(modal).toBeInTheDocument() }) }) }) describe('Inactive Server', () => { beforeEach(() => { // Modify hookState to simulate inactive server mockHookState = { ...createDefaultHookState(), serverActivated: false, detail: { id: 'server-123', status: 'inactive', server_code: 'abc123', description: 'Test server', parameters: {}, }, } }) it('should show disabled status when server is inactive', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) expect(screen.getByText(/appOverview.overview.status/)).toBeInTheDocument() }) it('should toggle switch when server is inactive', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') expect(switchElement).toBeInTheDocument() fireEvent.click(switchElement) // Switch should be interactive when server is inactive but published await waitFor(() => { expect(switchElement).toBeInTheDocument() }) }) }) describe('Non-Manager User', () => { beforeEach(() => { // Modify hookState to simulate non-manager user mockHookState = { ...createDefaultHookState(), isCurrentWorkspaceManager: false, } }) it('should not show regenerate button for non-manager', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Regenerate button should not be visible expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('Non-Editor User', () => { it('should show disabled styling for non-editor switch', () => { mockHookState = { ...createDefaultHookState(), toggleDisabled: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Switch uses CSS classes for disabled state, not disabled attribute expect(switchElement.className).toContain('!cursor-not-allowed') expect(switchElement.className).toContain('!opacity-50') }) }) describe('Confirm Regenerate Dialog', () => { it('should open confirm dialog and regenerate on confirm', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Find and click regenerate button const regenerateButtons = document.querySelectorAll('.cursor-pointer') const regenerateBtn = Array.from(regenerateButtons).find(btn => btn.querySelector('svg.h-4.w-4'), ) if (regenerateBtn) { fireEvent.click(regenerateBtn) await waitFor(() => { const confirmDialog = screen.queryByTestId('confirm-dialog') if (confirmDialog) { expect(confirmDialog).toBeInTheDocument() const confirmBtn = screen.getByTestId('confirm-btn') fireEvent.click(confirmBtn) } }) } expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should close confirm dialog on cancel', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Find and click regenerate button const regenerateButtons = document.querySelectorAll('.cursor-pointer') const regenerateBtn = Array.from(regenerateButtons).find(btn => btn.querySelector('svg.h-4.w-4'), ) if (regenerateBtn) { fireEvent.click(regenerateBtn) await waitFor(() => { const confirmDialog = screen.queryByTestId('confirm-dialog') if (confirmDialog) { expect(confirmDialog).toBeInTheDocument() const cancelBtn = screen.getByTestId('cancel-btn') fireEvent.click(cancelBtn) } }) } expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('MCP Server Modal', () => { it('should open and close server modal', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Find edit button const buttons = screen.queryAllByRole('button') const editButton = buttons.find(btn => btn.textContent?.includes('tools.mcp.server.edit') || btn.textContent?.includes('tools.mcp.server.addDescription'), ) if (editButton) { fireEvent.click(editButton) await waitFor(() => { const modal = screen.queryByTestId('mcp-server-modal') if (modal) { expect(modal).toBeInTheDocument() const closeBtn = screen.getByTestId('close-modal-btn') fireEvent.click(closeBtn) } }) } expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should deactivate switch when modal closes without previous activation', async () => { // Simulate unpublished server state mockHookState = { ...createDefaultHookState(), serverPublished: false, serverActivated: false, detail: undefined, showMCPServerModal: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Modal should be visible const modal = screen.getByTestId('mcp-server-modal') expect(modal).toBeInTheDocument() const closeBtn = screen.getByTestId('close-modal-btn') fireEvent.click(closeBtn) await waitFor(() => { expect(mockHandleServerModalHide).toHaveBeenCalled() }) // Switch should be off after closing modal without activation const switchElement = screen.getByRole('switch') expect(switchElement).toBeInTheDocument() }) }) describe('Unpublished App', () => { it('should show minimal state for unpublished app', () => { mockHookState = { ...createDefaultHookState(), appUnpublished: true, isMinimalState: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should show disabled styling for unpublished app switch', () => { mockHookState = { ...createDefaultHookState(), appUnpublished: true, toggleDisabled: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Switch uses CSS classes for disabled state expect(switchElement.className).toContain('!cursor-not-allowed') expect(switchElement.className).toContain('!opacity-50') }) }) describe('Workflow App Without Start Node', () => { it('should show minimal state for workflow without start node', () => { mockHookState = { ...createDefaultHookState(), missingStartNode: true, isMinimalState: true, } const appInfo = createMockAppInfo(AppModeEnum.WORKFLOW) render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should show disabled styling for workflow without start node', () => { mockHookState = { ...createDefaultHookState(), missingStartNode: true, toggleDisabled: true, } const appInfo = createMockAppInfo(AppModeEnum.WORKFLOW) render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Switch uses CSS classes for disabled state expect(switchElement.className).toContain('!cursor-not-allowed') expect(switchElement.className).toContain('!opacity-50') }) }) describe('Loading State', () => { it('should return null when isLoading is true', () => { mockHookState = { ...createDefaultHookState(), isLoading: true, } const appInfo = createMockAppInfo() const { container } = render(, { wrapper: createWrapper() }) // Component returns null when loading expect(container.firstChild).toBeNull() }) it('should render content when isLoading is false', () => { mockHookState = { ...createDefaultHookState(), isLoading: false, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('TriggerModeOverlay', () => { it('should show overlay without tooltip when triggerModeMessage is empty', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) const overlay = document.querySelector('.cursor-not-allowed') expect(overlay).toBeInTheDocument() }) it('should show overlay with tooltip when triggerModeMessage is provided', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) const overlay = document.querySelector('.cursor-not-allowed') expect(overlay).toBeInTheDocument() }) }) describe('onChangeStatus Handler', () => { it('should call handleStatusChange with false when turning off', async () => { // Start with server activated mockHookState = { ...createDefaultHookState(), serverActivated: true, } mockHandleStatusChange.mockResolvedValue({ activated: false }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Click to turn off - this will trigger onChangeStatus(false) fireEvent.click(switchElement) await waitFor(() => { expect(mockHandleStatusChange).toHaveBeenCalledWith(false) }) }) it('should call handleStatusChange with true when turning on', async () => { // Start with server deactivated mockHookState = { ...createDefaultHookState(), serverActivated: false, } mockHandleStatusChange.mockResolvedValue({ activated: true }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Click to turn on - this will trigger onChangeStatus(true) fireEvent.click(switchElement) await waitFor(() => { expect(mockHandleStatusChange).toHaveBeenCalledWith(true) }) }) it('should set local activated to false when handleStatusChange returns activated: false and state is true', async () => { // Simulate unpublished server scenario where enabling opens modal mockHookState = { ...createDefaultHookState(), serverActivated: false, serverPublished: false, } // Handler returns activated: false (modal opened instead) mockHandleStatusChange.mockResolvedValue({ activated: false }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') // Click to turn on fireEvent.click(switchElement) await waitFor(() => { expect(mockHandleStatusChange).toHaveBeenCalledWith(true) }) // The local state should be set to false because result.activated is false expect(switchElement).toBeInTheDocument() }) }) describe('onServerModalHide Handler', () => { it('should deactivate when handleServerModalHide returns shouldDeactivate: true', async () => { // Set up to show modal mockHookState = { ...createDefaultHookState(), showMCPServerModal: true, serverActivated: false, // Server was not activated } mockHandleServerModalHide.mockReturnValue({ shouldDeactivate: true }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Close the modal const closeBtn = screen.getByTestId('close-modal-btn') fireEvent.click(closeBtn) await waitFor(() => { expect(mockHandleServerModalHide).toHaveBeenCalled() }) }) it('should not deactivate when handleServerModalHide returns shouldDeactivate: false', async () => { mockHookState = { ...createDefaultHookState(), showMCPServerModal: true, serverActivated: true, // Server was already activated } mockHandleServerModalHide.mockReturnValue({ shouldDeactivate: false }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Close the modal const closeBtn = screen.getByTestId('close-modal-btn') fireEvent.click(closeBtn) await waitFor(() => { expect(mockHandleServerModalHide).toHaveBeenCalled() }) }) }) describe('onConfirmRegenerate Handler', () => { it('should call handleGenCode and closeConfirmDelete when confirm is clicked', async () => { // Set up to show confirm dialog mockHookState = { ...createDefaultHookState(), showConfirmDelete: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Confirm dialog should be visible const confirmDialog = screen.getByTestId('confirm-dialog') expect(confirmDialog).toBeInTheDocument() // Click confirm button const confirmBtn = screen.getByTestId('confirm-btn') fireEvent.click(confirmBtn) await waitFor(() => { expect(mockHandleGenCode).toHaveBeenCalled() expect(mockCloseConfirmDelete).toHaveBeenCalled() }) }) it('should call closeConfirmDelete when cancel is clicked', async () => { mockHookState = { ...createDefaultHookState(), showConfirmDelete: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Click cancel button const cancelBtn = screen.getByTestId('cancel-btn') fireEvent.click(cancelBtn) await waitFor(() => { expect(mockCloseConfirmDelete).toHaveBeenCalled() }) }) }) describe('getTooltipContent Function', () => { it('should show publish tip when app is unpublished', () => { // Modify hookState to simulate unpublished app mockHookState = { ...createDefaultHookState(), appUnpublished: true, toggleDisabled: true, isMinimalState: true, } const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Tooltip should contain publish tip expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should show missing start node tooltip for workflow without start node', () => { // Modify hookState to simulate missing start node mockHookState = { ...createDefaultHookState(), missingStartNode: true, toggleDisabled: true, isMinimalState: true, } const appInfo = createMockAppInfo(AppModeEnum.WORKFLOW) render(, { wrapper: createWrapper() }) // The tooltip with learn more link should be available expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) it('should return triggerModeMessage when trigger mode is disabled', () => { const appInfo = createMockAppInfo() render( , { wrapper: createWrapper() }, ) expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) }) describe('State Synchronization', () => { it('should sync activated state when serverActivated changes', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Initial state expect(screen.getByRole('switch')).toBeInTheDocument() }) }) describe('Accessibility', () => { it('should have accessible switch', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) const switchElement = screen.getByRole('switch') expect(switchElement).toBeInTheDocument() }) it('should have accessible interactive elements', () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // The switch element with button type is an interactive element const switchElement = screen.getByRole('switch') expect(switchElement).toBeInTheDocument() expect(switchElement).toHaveAttribute('type', 'button') }) }) describe('Server URL Regeneration', () => { it('should open confirm dialog when regenerate is clicked', async () => { // Mock to show regenerate button vi.doMock('@/service/use-tools', async () => { return { useUpdateMCPServer: () => ({ mutateAsync: vi.fn().mockResolvedValue({}), }), useRefreshMCPServerCode: () => ({ mutateAsync: vi.fn().mockResolvedValue({}), isPending: false, }), useMCPServerDetail: () => ({ data: { id: 'server-123', status: 'active', server_code: 'abc123', description: 'Test server', parameters: {}, }, }), useInvalidateMCPServerDetail: () => vi.fn(), } }) const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Find the regenerate button and click it const regenerateButtons = document.querySelectorAll('.cursor-pointer') const regenerateBtn = Array.from(regenerateButtons).find(btn => btn.querySelector('svg'), ) if (regenerateBtn) { fireEvent.click(regenerateBtn) // Wait for confirm dialog to appear await waitFor(() => { const confirmTitle = screen.queryByText('appOverview.overview.appInfo.regenerate') if (confirmTitle) expect(confirmTitle).toBeInTheDocument() }, { timeout: 100 }) } }) }) describe('Edit Button', () => { it('should open MCP server modal when edit button is clicked', async () => { const appInfo = createMockAppInfo() render(, { wrapper: createWrapper() }) // Find button with edit text - use queryAllByRole since buttons may not exist const buttons = screen.queryAllByRole('button') const editButton = buttons.find(btn => btn.textContent?.includes('tools.mcp.server.edit') || btn.textContent?.includes('tools.mcp.server.addDescription'), ) if (editButton) { fireEvent.click(editButton) // Modal should open - check for any modal indicator await waitFor(() => { // If modal opens, we should see modal content expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() }) } else { // In minimal state, no edit button is shown - this is expected behavior expect(screen.getByText('tools.mcp.server.title')).toBeInTheDocument() } }) }) })