/**
* @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 } from 'ink';
import { App } from './App.js';
import { UIStateContext, type UIState } from './contexts/UIStateContext.js';
import { StreamingState } from './types.js';
// Mock components to isolate App component testing
vi.mock('./components/MainContent.js', () => ({
MainContent: () => MainContent,
}));
vi.mock('./components/DialogManager.js', () => ({
DialogManager: () => DialogManager,
}));
vi.mock('./components/Composer.js', () => ({
Composer: () => Composer,
}));
vi.mock('./components/Notifications.js', () => ({
Notifications: () => Notifications,
}));
vi.mock('./components/QuittingDisplay.js', () => ({
QuittingDisplay: () => Quitting...,
}));
vi.mock('./components/Footer.js', () => ({
Footer: () => Footer,
}));
vi.mock('./semantic-colors.js', () => ({
theme: {
status: {
warning: 'yellow',
},
},
}));
// Don't mock the layout components - let them render normally so tests can see the Ctrl messages
vi.mock('./hooks/useLayoutConfig.js', () => ({
useLayoutConfig: () => ({
mode: 'default',
shouldUseStatic: true,
shouldShowFooterInComposer: true,
}),
}));
vi.mock('./hooks/useFooterProps.js', () => ({
useFooterProps: () => ({
model: 'test-model',
targetDir: '/test',
debugMode: false,
branchName: 'test-branch',
debugMessage: '',
corgiMode: false,
errorCount: 0,
showErrorDetails: false,
showMemoryUsage: false,
promptTokenCount: 0,
nightly: false,
isTrustedFolder: true,
vimMode: undefined,
}),
}));
describe('App', () => {
const mockUIState: Partial = {
streamingState: StreamingState.Idle,
quittingMessages: null,
dialogsVisible: false,
mainControlsRef: { current: null },
historyManager: {
addItem: vi.fn(),
history: [],
updateItem: vi.fn(),
clearItems: vi.fn(),
loadHistory: vi.fn(),
},
};
it('should render main content and composer when not quitting', () => {
const { lastFrame } = render(
,
);
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 } = render(
,
);
expect(lastFrame()).toContain('Quitting...');
});
it('should render dialog manager when dialogs are visible', () => {
const dialogUIState = {
...mockUIState,
dialogsVisible: true,
} as UIState;
const { lastFrame } = render(
,
);
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 } = render(
,
);
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 } = render(
,
);
expect(lastFrame()).toContain('Press Ctrl+D again to exit.');
});
});