oxios 1.7.0

Oxios Agent OS — Agent Operating System powered by oxi-sdk
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, expect, it, vi } from 'vitest'
import { ServerCard } from '@/components/mcp/server-card'
import type { McpServer } from '@/types/mcp'

// Mock i18next
vi.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string) => key,
    i18n: { language: 'en' },
  }),
}))

const connectedServer: McpServer = {
  name: 'test-server',
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
  enabled: true,
  initialized: true,
}

const disconnectedServer: McpServer = {
  name: 'broken-server',
  command: 'node',
  args: ['server.js'],
  enabled: true,
  initialized: false,
}

const disabledServer: McpServer = {
  name: 'disabled-server',
  command: 'python',
  args: ['mcp_server.py'],
  enabled: false,
  initialized: false,
}

const defaultProps = {
  onToggle: vi.fn(),
  onRefresh: vi.fn(),
  onDelete: vi.fn(),
  isToggling: false,
  isRefreshing: false,
  isDeleting: false,
}

describe('ServerCard', () => {
  it('renders server name', () => {
    render(<ServerCard server={connectedServer} {...defaultProps} />)

    expect(screen.getByText('test-server')).toBeInTheDocument()
  })

  it('renders command with args', () => {
    render(<ServerCard server={connectedServer} {...defaultProps} />)

    expect(
      screen.getByText('npx -y @modelcontextprotocol/server-filesystem /tmp'),
    ).toBeInTheDocument()
  })

  it('renders command without args', () => {
    const noArgsServer = { ...connectedServer, args: [] }
    render(<ServerCard server={noArgsServer} {...defaultProps} />)

    expect(screen.getByText('npx')).toBeInTheDocument()
  })

  it('shows connected state with green dot', () => {
    render(<ServerCard server={connectedServer} {...defaultProps} />)

    expect(screen.getByText('mcp.connected')).toBeInTheDocument()
    const dot = document.querySelector('.bg-success')
    expect(dot).toBeInTheDocument()
  })

  it('shows disconnected state with red dot', () => {
    render(<ServerCard server={disconnectedServer} {...defaultProps} />)

    expect(screen.getByText('mcp.disconnected')).toBeInTheDocument()
    const dot = document.querySelector('.bg-error')
    expect(dot).toBeInTheDocument()
  })

  it('shows disabled state with gray dot', () => {
    render(<ServerCard server={disabledServer} {...defaultProps} />)

    expect(screen.getByText('common.disabled')).toBeInTheDocument()
    const dot = document.querySelector('.bg-muted-foreground')
    expect(dot).toBeInTheDocument()
  })

  it('calls onToggle when power button is clicked', async () => {
    const onToggle = vi.fn()
    render(<ServerCard server={connectedServer} {...defaultProps} onToggle={onToggle} />)

    const powerBtn = screen.getByTitle('mcp.disable')
    await userEvent.click(powerBtn)

    expect(onToggle).toHaveBeenCalledOnce()
  })

  it('calls onRefresh when refresh button is clicked', async () => {
    const onRefresh = vi.fn()
    render(<ServerCard server={connectedServer} {...defaultProps} onRefresh={onRefresh} />)

    const refreshBtn = screen.getByTitle('mcp.refresh')
    await userEvent.click(refreshBtn)

    expect(onRefresh).toHaveBeenCalledOnce()
  })

  it('calls onDelete when delete button is clicked', async () => {
    const onDelete = vi.fn()
    render(<ServerCard server={connectedServer} {...defaultProps} onDelete={onDelete} />)

    const deleteBtn = screen.getByTitle('mcp.remove')
    await userEvent.click(deleteBtn)

    expect(onDelete).toHaveBeenCalledOnce()
  })

  it('disables refresh button when server is not enabled', () => {
    render(<ServerCard server={disabledServer} {...defaultProps} />)

    const refreshBtn = screen.getByTitle('mcp.refresh')
    expect(refreshBtn).toBeDisabled()
  })

  it('disables toggle button when isToggling', () => {
    render(<ServerCard server={connectedServer} {...defaultProps} isToggling={true} />)

    const powerBtn = screen.getByTitle('mcp.disable')
    expect(powerBtn).toBeDisabled()
  })

  it('disables delete button when isDeleting', () => {
    render(<ServerCard server={connectedServer} {...defaultProps} isDeleting={true} />)

    const deleteBtn = screen.getByTitle('mcp.remove')
    expect(deleteBtn).toBeDisabled()
  })
})