mirror of
https://github.com/stablyai/orca.git
synced 2026-10-21 08:03:33 +00:00
test(agent-status): cover needs-you glyph surfaces
This commit is contained in:
@@ -51,12 +51,10 @@ describe('AgentStateDot', () => {
|
||||
(state) => {
|
||||
const markup = renderMarkup(state)
|
||||
|
||||
// Why: the "needs you" states share the dashboard's MessageCircleQuestion
|
||||
// glyph rather than a bare dot. Assert the lucide class hook + amber text
|
||||
// color without coupling to the exact SVG path lucide emits.
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(markup).toContain('text-amber-500')
|
||||
expect(markup).not.toContain('bg-amber-500')
|
||||
expect(markup).not.toContain('data-agent-spinner')
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -96,11 +96,6 @@ export const AgentStateDot = React.memo(function AgentStateDot({
|
||||
}
|
||||
|
||||
if (state === 'permission' || state === 'waiting') {
|
||||
// Why: the "needs you" states (an agent asking a question / awaiting
|
||||
// input) render the same MessageCircleQuestion glyph the dashboard card
|
||||
// uses for its pending-question pill, so "an agent needs you" reads
|
||||
// identically across the board, sidebar rows, and terminal tabs instead
|
||||
// of as an ambiguous amber dot. Amber still carries the attention color.
|
||||
return (
|
||||
<span
|
||||
className={cn('inline-flex shrink-0 items-center justify-center', box, className)}
|
||||
|
||||
@@ -60,6 +60,30 @@ describe('AgentKanbanCard', () => {
|
||||
expect(screen.queryByText(/\d+d/)).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the question glyph once when a summary is available', () => {
|
||||
const attentionCard = card({
|
||||
bucket: 'attention',
|
||||
dotState: 'waiting',
|
||||
askSummary: 'Approve deploy?'
|
||||
})
|
||||
const { container, rerender } = render(
|
||||
<AgentKanbanCard card={attentionCard} now={2_000} onOpenTerminal={vi.fn()} />
|
||||
)
|
||||
|
||||
expect(screen.queryByTestId('state-dot')).not.toBeInTheDocument()
|
||||
expect(container.querySelectorAll('.lucide-message-circle-question-mark')).toHaveLength(1)
|
||||
|
||||
rerender(
|
||||
<AgentKanbanCard
|
||||
card={{ ...attentionCard, askSummary: undefined }}
|
||||
now={2_000}
|
||||
onOpenTerminal={vi.fn()}
|
||||
/>
|
||||
)
|
||||
expect(screen.getByTestId('state-dot')).toBeInTheDocument()
|
||||
expect(container.querySelector('.lucide-message-circle-question-mark')).toBeNull()
|
||||
})
|
||||
|
||||
it('skips structured-clone rerenders until visible card data or its age changes', () => {
|
||||
const onOpenTerminal = vi.fn()
|
||||
const initial = card({ startedAt: 1_000 })
|
||||
|
||||
@@ -98,9 +98,7 @@ export const AgentKanbanCard = memo(
|
||||
>
|
||||
{card.worktreeName}
|
||||
</span>
|
||||
{/* Why: a pending question already renders the amber
|
||||
MessageCircleQuestion glyph in the summary pill below, so suppress
|
||||
the header state glyph here to avoid showing "needs you" twice. */}
|
||||
{/* The summary pill already carries the attention glyph. */}
|
||||
{card.askSummary ? null : <AgentStateDot state={card.dotState} className="ml-auto" />}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -252,9 +252,7 @@ describe('DashboardAgentRow', () => {
|
||||
const tokens = classTokens(markup)
|
||||
|
||||
expect(markup).toContain('aria-label="Waiting for input"')
|
||||
// Why: the "needs you" states render the amber MessageCircleQuestion glyph
|
||||
// rather than a bare amber dot (unified across the board/sidebar).
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(tokens).toContain('text-amber-500')
|
||||
expect(tokens).not.toContain('bg-red-500')
|
||||
})
|
||||
|
||||
@@ -20,6 +20,7 @@ const mocks = vi.hoisted(() => ({
|
||||
refreshPreflightStatus: vi.fn(),
|
||||
checkLinearConnection: vi.fn(),
|
||||
hasPairedMobileDevice: false,
|
||||
agentBucketCounts: { attention: 0, working: 0, idle: 0 },
|
||||
dismissMobileOnboardingBadge: vi.fn(),
|
||||
setSetupGuideSidebarDismissed: vi.fn()
|
||||
}))
|
||||
@@ -39,6 +40,10 @@ vi.mock('@/components/activity/useActivityUnreadCount', () => ({
|
||||
useActivityUnreadCount: () => 0
|
||||
}))
|
||||
|
||||
vi.mock('@/components/dashboard/useAgentBucketCounts', () => ({
|
||||
useAgentBucketCounts: () => mocks.agentBucketCounts
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useShortcutLabel', () => ({
|
||||
useShortcutKeyComboDetails: () => [{ keys: ['⌘', 'J'], doubleTap: false }]
|
||||
}))
|
||||
@@ -203,6 +208,7 @@ describe('SidebarNav', () => {
|
||||
vi.clearAllMocks()
|
||||
await i18n.changeLanguage('en')
|
||||
mocks.hasPairedMobileDevice = false
|
||||
mocks.agentBucketCounts = { attention: 0, working: 0, idle: 0 }
|
||||
setSidebarState()
|
||||
})
|
||||
|
||||
@@ -252,6 +258,26 @@ describe('SidebarNav', () => {
|
||||
expect(queryButtonByText(container, 'Agent Dashboard')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('uses a question glyph only for the Needs You count', async () => {
|
||||
mocks.agentBucketCounts = { attention: 2, working: 3, idle: 4 }
|
||||
setSidebarState({
|
||||
settings: {
|
||||
...getDefaultSettings('/tmp'),
|
||||
experimentalAgentDashboardPopout: true
|
||||
}
|
||||
})
|
||||
const container = await renderSidebarNav()
|
||||
|
||||
const attention = container.querySelector('[aria-label="Needs You: 2"]')
|
||||
const working = container.querySelector('[aria-label="Working: 3"]')
|
||||
const idle = container.querySelector('[aria-label="Idle: 4"]')
|
||||
expect(attention?.querySelector('.lucide-message-circle-question-mark')).not.toBeNull()
|
||||
expect(working?.querySelector('.rounded-full')).not.toBeNull()
|
||||
expect(idle?.querySelector('.rounded-full')).not.toBeNull()
|
||||
expect(working?.querySelector('svg')).toBeNull()
|
||||
expect(idle?.querySelector('svg')).toBeNull()
|
||||
})
|
||||
|
||||
it('shows the Mobile entry by default for older settings', () => {
|
||||
expect(shouldShowMobileButton(null)).toBe(true)
|
||||
expect(shouldShowMobileButton({})).toBe(true)
|
||||
|
||||
@@ -52,10 +52,6 @@ export function shouldShowAutomationsButton(
|
||||
return settings?.showAutomationsButton !== false
|
||||
}
|
||||
|
||||
// Per-state markers mirror AgentStateDot so the sidebar counts read the same as
|
||||
// the board: 'attention' (needs you) renders the amber MessageCircleQuestion
|
||||
// glyph — the same one the board and status dots now use — while the calmer
|
||||
// buckets stay dots (yellow = working, neutral = idle).
|
||||
const DASHBOARD_BUCKET_DOT_CLASS: Record<'working' | 'idle', string> = {
|
||||
working: 'bg-yellow-500',
|
||||
idle: 'bg-neutral-500/50'
|
||||
|
||||
@@ -35,12 +35,10 @@ describe('StatusIndicator', () => {
|
||||
it('renders permission as an amber question glyph', () => {
|
||||
const markup = renderMarkup('permission')
|
||||
|
||||
// Why: the "needs permission / needs you" state renders the dashboard's
|
||||
// MessageCircleQuestion glyph rather than a bare amber dot. Assert the
|
||||
// lucide class hook + amber text color without coupling to the SVG path.
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(markup).toContain('text-amber-500')
|
||||
expect(markup).not.toContain('bg-amber-500')
|
||||
expect(markup).not.toContain('data-agent-spinner')
|
||||
})
|
||||
|
||||
it('renders active as full emerald dot', () => {
|
||||
|
||||
@@ -41,10 +41,6 @@ const StatusIndicator = React.memo(function StatusIndicator({
|
||||
}
|
||||
|
||||
if (status === 'permission') {
|
||||
// Why: the "needs permission / needs you" state renders the same
|
||||
// MessageCircleQuestion glyph the agent dashboard uses, so the sidebar's
|
||||
// worktree-level status matches the board and agent-row indicators
|
||||
// instead of showing a bare amber dot. Amber keeps the attention color.
|
||||
return (
|
||||
<span
|
||||
className={cn('inline-flex h-3 w-3 shrink-0 items-center justify-center', className)}
|
||||
|
||||
@@ -126,9 +126,7 @@ describe('WorktreeCardStatusSlot', () => {
|
||||
)
|
||||
|
||||
expect(markup).toContain('Needs permission · Unread')
|
||||
// Why: the permission status renders the amber MessageCircleQuestion glyph
|
||||
// rather than a bare amber dot.
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(markup).toContain('text-amber-500')
|
||||
expect(markup).not.toContain('data-worktree-status-lane-unread=""')
|
||||
expect(markup).not.toContain('data-worktree-unread-alert=""')
|
||||
@@ -381,7 +379,7 @@ describe('WorktreeCardStatusSlot', () => {
|
||||
)
|
||||
|
||||
expect(markup).toContain('Needs permission')
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(markup).toContain('text-amber-500')
|
||||
expect(markup).not.toContain('PR checks: Failed')
|
||||
})
|
||||
|
||||
@@ -40,9 +40,7 @@ describe('TerminalTabLeadingIcon', () => {
|
||||
const markup = renderStatus('permission')
|
||||
|
||||
expect(markup).toContain('data-agent-activity-status="permission"')
|
||||
// Why: the "needs you" states render the amber MessageCircleQuestion glyph
|
||||
// rather than a bare amber dot (unified across board/sidebar/tabs).
|
||||
expect(markup).toContain('lucide-message-circle-question')
|
||||
expect(markup).toContain('lucide-message-circle-question-mark')
|
||||
expect(markup).toContain('text-amber-500')
|
||||
expect(markup).not.toContain('bg-red-500')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user