/** * @license * Copyright 2025 AionUi (aionui.com) * SPDX-License-Identifier: Apache-2.0 */ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import type { ConversationCommandQueueItem } from '@/renderer/pages/conversation/platforms/useConversationCommandQueue'; import CommandQueuePanel from '@/renderer/components/chat/CommandQueuePanel'; vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (_key: string, options?: { defaultValue?: string }) => options?.defaultValue ?? _key, }), })); const confirmMock = vi.fn(); vi.mock('@arco-design/web-react', () => { const Button = ({ children, ...props }: React.PropsWithChildren & { status?: string }>) => ( ); const Dropdown = ({ children, droplist }: React.PropsWithChildren<{ droplist: React.ReactNode }>) => (
{children} {droplist}
); const Menu = ({ children }: React.PropsWithChildren) =>
{children}
; Menu.Item = ({ children, onClick, }: React.PropsWithChildren<{ onClick?: () => void; }>) => ( ); const Typography = { Ellipsis: ({ children, ...props }: React.PropsWithChildren) => {children}, }; const Tooltip = ({ children }: React.PropsWithChildren) => <>{children}; const Modal = { confirm: (config: { onOk?: () => void }) => confirmMock(config), }; return { Button, Dropdown, Menu, Modal, Tooltip, Typography }; }); vi.mock('@icon-park/react', () => ({ CornerDownRight: () => , Delete: () => , Drag: () => , Edit: () => , Inbox: () => , SortTwo: () => , MoreOne: () => , SendOne: () => , })); const item: ConversationCommandQueueItem = { id: 'queued-1', input: 'queued follow-up', files: [], created_at: 1, }; const renderPanel = (overrides: Partial> = {}) => { const props: React.ComponentProps = { items: [item], mode: 'auto', interactionLocked: false, onInteractionLock: vi.fn(), onInteractionUnlock: vi.fn(), onEdit: vi.fn(), onSendNow: vi.fn(), onToggleMode: vi.fn(), onReorder: vi.fn(), onRemove: vi.fn(), onClear: vi.fn(), ...overrides, }; render(); return props; }; describe('CommandQueuePanel', () => { it('renders the three per-item actions: send now, edit, remove', () => { renderPanel(); expect(screen.getByRole('button', { name: 'Send now' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Edit' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Remove' })).toBeInTheDocument(); }); it('wires send now, edit and remove callbacks per item', () => { const onSendNow = vi.fn(); const onEdit = vi.fn(); const onRemove = vi.fn(); renderPanel({ onSendNow, onEdit, onRemove }); fireEvent.click(screen.getByRole('button', { name: 'Send now' })); fireEvent.click(screen.getByRole('button', { name: 'Edit' })); fireEvent.click(screen.getByRole('button', { name: 'Remove' })); expect(onSendNow).toHaveBeenCalledExactlyOnceWith(item); expect(onEdit).toHaveBeenCalledExactlyOnceWith(item); expect(onRemove).toHaveBeenCalledExactlyOnceWith('queued-1'); }); it('shows the current mode and toggles it', () => { const onToggleMode = vi.fn(); renderPanel({ mode: 'auto', onToggleMode }); const toggle = screen.getByRole('button', { name: 'Toggle send mode' }); expect(toggle).toHaveTextContent('Auto'); fireEvent.click(toggle); expect(onToggleMode).toHaveBeenCalledTimes(1); }); it('renders the manual label when in manual mode', () => { renderPanel({ mode: 'manual' }); expect(screen.getByRole('button', { name: 'Toggle send mode' })).toHaveTextContent('Manual'); }); it('does not render a separate help button (help lives on the mode toggle)', () => { renderPanel(); expect(screen.queryByRole('button', { name: 'Help' })).not.toBeInTheDocument(); }); it('clears the draft box through a confirm dialog', () => { confirmMock.mockReset(); const onClear = vi.fn(); renderPanel({ onClear }); fireEvent.click(screen.getByRole('button', { name: 'Clear draft box' })); // Clearing must go through a confirm step, not fire immediately. expect(onClear).not.toHaveBeenCalled(); expect(confirmMock).toHaveBeenCalledTimes(1); // Simulate the user confirming. const config = confirmMock.mock.calls[0][0] as { onOk?: () => void }; config.onOk?.(); expect(onClear).toHaveBeenCalledTimes(1); }); });