diff --git a/src/pages/agent-office/components/AgentCard.jsx b/src/pages/agent-office/components/AgentCard.jsx new file mode 100644 index 0000000..50e1089 --- /dev/null +++ b/src/pages/agent-office/components/AgentCard.jsx @@ -0,0 +1,30 @@ +// src/pages/agent-office/components/AgentCard.jsx +import { AGENT_META, STATE_COLORS, DEFAULT_STATE_COLOR } from '../constants.js'; + +export default function AgentCard({ agentId, agentState, notificationCount = 0, active = false, onClick }) { + const meta = AGENT_META[agentId]; + if (!meta) return null; + + const state = agentState?.state || 'idle'; + const stateInfo = STATE_COLORS[state] || DEFAULT_STATE_COLOR; + const dotClass = `ao-card-dot ${state}${stateInfo.pulse ? ' pulse' : ''}`; + const badgeText = notificationCount > 9 ? '9+' : String(notificationCount); + + return ( + + ); +} diff --git a/src/pages/agent-office/components/AgentCard.test.jsx b/src/pages/agent-office/components/AgentCard.test.jsx new file mode 100644 index 0000000..2d51148 --- /dev/null +++ b/src/pages/agent-office/components/AgentCard.test.jsx @@ -0,0 +1,60 @@ +// src/pages/agent-office/components/AgentCard.test.jsx +import { describe, it, expect, vi } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import AgentCard from './AgentCard.jsx'; + +describe('AgentCard', () => { + it('에이전트의 displayName을 표시', () => { + render( {}} />); + expect(screen.getByText('주식 트레이더')).toBeInTheDocument(); + }); + + it('working 상태일 때 dot에 working 클래스 부여', () => { + const { container } = render( + {}} /> + ); + const dot = container.querySelector('.ao-card-dot'); + expect(dot).toHaveClass('working'); + }); + + it('agentState 없으면 idle로 fallback', () => { + const { container } = render( + {}} /> + ); + const dot = container.querySelector('.ao-card-dot'); + expect(dot).toHaveClass('idle'); + }); + + it('notificationCount > 0이면 뱃지 표시', () => { + render( {}} />); + expect(screen.getByText('3')).toBeInTheDocument(); + }); + + it('notificationCount === 0이면 뱃지 없음', () => { + const { container } = render( + {}} /> + ); + expect(container.querySelector('.ao-card-badge')).toBeNull(); + }); + + it('notificationCount > 9이면 9+ 표시', () => { + render( {}} />); + expect(screen.getByText('9+')).toBeInTheDocument(); + }); + + it('클릭 시 onClick 호출', () => { + const onClick = vi.fn(); + const { container } = render( + + ); + fireEvent.click(container.querySelector('.ao-card')); + expect(onClick).toHaveBeenCalledTimes(1); + }); + + it('active prop 시 카드에 active 클래스 부여', () => { + const { container } = render( + {}} /> + ); + expect(container.querySelector('.ao-card')).toHaveClass('active'); + }); +});