qwen-code/packages/cli/src/ui/App.test.tsx
tanzhenxin 5d07c495f1 feat(cli): Add agent tab navigation and live tool output for in-process arena mode
Add AgentViewContext, AgentTabBar, and AgentChatView components for tab-based
agent switching. Add useArenaInProcess hook bridging ArenaManager events to
React state. Add agentHistoryAdapter converting AgentMessage[] to HistoryItem[].

Core support changes:
- Replace stream buffers with ROUND_TEXT events (complete round text)
- Add TOOL_OUTPUT_UPDATE events for live tool output streaming
- Add pendingApprovals/liveOutputs/shellPids state to AgentInteractive
- Fix missing ROUND_END emission for final text rounds

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-02-23 13:21:16 +08:00

156 lines
4.4 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
import { render } from 'ink-testing-library';
import { Text, useIsScreenReaderEnabled } from 'ink';
import { App } from './App.js';
import { UIStateContext, type UIState } from './contexts/UIStateContext.js';
import {
UIActionsContext,
type UIActions,
} from './contexts/UIActionsContext.js';
import { AgentViewProvider } from './contexts/AgentViewContext.js';
import { StreamingState } from './types.js';
vi.mock('ink', async (importOriginal) => {
const original = await importOriginal<typeof import('ink')>();
return {
...original,
useIsScreenReaderEnabled: vi.fn(),
};
});
vi.mock('./components/MainContent.js', () => ({
MainContent: () => <Text>MainContent</Text>,
}));
vi.mock('./components/DialogManager.js', () => ({
DialogManager: () => <Text>DialogManager</Text>,
}));
vi.mock('./components/Composer.js', () => ({
Composer: () => <Text>Composer</Text>,
}));
vi.mock('./components/Notifications.js', () => ({
Notifications: () => <Text>Notifications</Text>,
}));
vi.mock('./components/QuittingDisplay.js', () => ({
QuittingDisplay: () => <Text>Quitting...</Text>,
}));
vi.mock('./components/Footer.js', () => ({
Footer: () => <Text>Footer</Text>,
}));
vi.mock('./components/agent-view/AgentTabBar.js', () => ({
AgentTabBar: () => null,
}));
describe('App', () => {
const mockUIState: Partial<UIState> = {
streamingState: StreamingState.Idle,
quittingMessages: null,
dialogsVisible: false,
mainControlsRef: { current: null },
historyManager: {
addItem: vi.fn(),
history: [],
updateItem: vi.fn(),
clearItems: vi.fn(),
loadHistory: vi.fn(),
},
};
const mockUIActions = {
refreshStatic: vi.fn(),
} as unknown as UIActions;
const renderWithProviders = (uiState: UIState) =>
render(
<UIActionsContext.Provider value={mockUIActions}>
<AgentViewProvider>
<UIStateContext.Provider value={uiState}>
<App />
</UIStateContext.Provider>
</AgentViewProvider>
</UIActionsContext.Provider>,
);
it('should render main content and composer when not quitting', () => {
const { lastFrame } = renderWithProviders(mockUIState as UIState);
expect(lastFrame()).toContain('MainContent');
expect(lastFrame()).toContain('Composer');
});
it('should render quitting display when quittingMessages is set', () => {
const quittingUIState = {
...mockUIState,
quittingMessages: [{ id: 1, type: 'user', text: 'test' }],
} as UIState;
const { lastFrame } = renderWithProviders(quittingUIState);
expect(lastFrame()).toContain('Quitting...');
});
it('should render dialog manager when dialogs are visible', () => {
const dialogUIState = {
...mockUIState,
dialogsVisible: true,
} as UIState;
const { lastFrame } = renderWithProviders(dialogUIState);
expect(lastFrame()).toContain('MainContent');
expect(lastFrame()).toContain('DialogManager');
});
it('should show Ctrl+C exit prompt when dialogs are visible and ctrlCPressedOnce is true', () => {
const ctrlCUIState = {
...mockUIState,
dialogsVisible: true,
ctrlCPressedOnce: true,
} as UIState;
const { lastFrame } = renderWithProviders(ctrlCUIState);
expect(lastFrame()).toContain('Press Ctrl+C again to exit.');
});
it('should show Ctrl+D exit prompt when dialogs are visible and ctrlDPressedOnce is true', () => {
const ctrlDUIState = {
...mockUIState,
dialogsVisible: true,
ctrlDPressedOnce: true,
} as UIState;
const { lastFrame } = renderWithProviders(ctrlDUIState);
expect(lastFrame()).toContain('Press Ctrl+D again to exit.');
});
it('should render ScreenReaderAppLayout when screen reader is enabled', () => {
(useIsScreenReaderEnabled as vi.Mock).mockReturnValue(true);
const { lastFrame } = renderWithProviders(mockUIState as UIState);
expect(lastFrame()).toContain(
'Notifications\nFooter\nMainContent\nComposer',
);
});
it('should render DefaultAppLayout when screen reader is not enabled', () => {
(useIsScreenReaderEnabled as vi.Mock).mockReturnValue(false);
const { lastFrame } = renderWithProviders(mockUIState as UIState);
expect(lastFrame()).toContain('MainContent\nComposer');
});
});