Fix truncation for AskQuestion (#18001)

This commit is contained in:
Jacob Richman
2026-01-30 17:07:41 -08:00
committed by GitHub
parent 00fdb30211
commit 7469ea0fca
4 changed files with 210 additions and 76 deletions

View File

@@ -10,6 +10,7 @@ import { renderWithProviders } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { AskUserDialog } from './AskUserDialog.js';
import { QuestionType, type Question } from '@google/gemini-cli-core';
import { UIStateContext, type UIState } from '../contexts/UIStateContext.js';
// Helper to write to stdin with proper act() wrapping
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
@@ -42,7 +43,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -108,7 +108,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -129,7 +128,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -159,33 +157,49 @@ describe('AskUserDialog', () => {
});
});
it('shows scroll arrows when options exceed available height', async () => {
const questions: Question[] = [
{
question: 'Choose an option',
header: 'Scroll Test',
options: Array.from({ length: 15 }, (_, i) => ({
label: `Option ${i + 1}`,
description: `Description ${i + 1}`,
})),
multiSelect: false,
},
];
describe.each([
{ useAlternateBuffer: true, expectedArrows: false },
{ useAlternateBuffer: false, expectedArrows: true },
])(
'Scroll Arrows (useAlternateBuffer: $useAlternateBuffer)',
({ useAlternateBuffer, expectedArrows }) => {
it(`shows scroll arrows correctly when useAlternateBuffer is ${useAlternateBuffer}`, async () => {
const questions: Question[] = [
{
question: 'Choose an option',
header: 'Scroll Test',
options: Array.from({ length: 15 }, (_, i) => ({
label: `Option ${i + 1}`,
description: `Description ${i + 1}`,
})),
multiSelect: false,
},
];
const { lastFrame } = renderWithProviders(
<AskUserDialog
questions={questions}
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={80}
availableHeight={10} // Small height to force scrolling
/>,
);
const { lastFrame } = renderWithProviders(
<AskUserDialog
questions={questions}
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={80}
availableHeight={10} // Small height to force scrolling
/>,
{ useAlternateBuffer },
);
await waitFor(() => {
expect(lastFrame()).toMatchSnapshot();
});
});
await waitFor(() => {
if (expectedArrows) {
expect(lastFrame()).toContain('▲');
expect(lastFrame()).toContain('▼');
} else {
expect(lastFrame()).not.toContain('▲');
expect(lastFrame()).not.toContain('▼');
}
expect(lastFrame()).toMatchSnapshot();
});
});
},
);
it('navigates to custom option when typing unbound characters (Type-to-Jump)', async () => {
const { stdin, lastFrame } = renderWithProviders(
@@ -194,7 +208,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -246,7 +259,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -261,7 +273,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -276,7 +287,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -308,7 +318,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -351,7 +360,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -420,7 +428,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -450,7 +457,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -496,7 +502,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -533,7 +538,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -567,7 +571,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -590,7 +593,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -613,7 +615,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -649,7 +650,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -681,7 +681,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -729,7 +728,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -780,7 +778,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -807,7 +804,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={onCancel}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -854,7 +850,6 @@ describe('AskUserDialog', () => {
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -914,7 +909,6 @@ describe('AskUserDialog', () => {
onSubmit={onSubmit}
onCancel={vi.fn()}
width={120}
availableHeight={20}
/>,
{ width: 120 },
);
@@ -946,4 +940,72 @@ describe('AskUserDialog', () => {
});
});
});
it('uses availableTerminalHeight from UIStateContext if availableHeight prop is missing', () => {
const questions: Question[] = [
{
question: 'Choose an option',
header: 'Context Test',
options: Array.from({ length: 10 }, (_, i) => ({
label: `Option ${i + 1}`,
description: `Description ${i + 1}`,
})),
multiSelect: false,
},
];
const mockUIState = {
availableTerminalHeight: 5, // Small height to force scroll arrows
} as UIState;
const { lastFrame } = renderWithProviders(
<UIStateContext.Provider value={mockUIState}>
<AskUserDialog
questions={questions}
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={80}
/>
</UIStateContext.Provider>,
{ useAlternateBuffer: false },
);
// With height 5 and alternate buffer disabled, it should show scroll arrows (▲)
expect(lastFrame()).toContain('▲');
expect(lastFrame()).toContain('▼');
});
it('does NOT truncate the question when in alternate buffer mode even with small height', () => {
const longQuestion =
'This is a very long question ' + 'with many words '.repeat(10);
const questions: Question[] = [
{
question: longQuestion,
header: 'Alternate Buffer Test',
options: [{ label: 'Option 1', description: 'Desc 1' }],
multiSelect: false,
},
];
const mockUIState = {
availableTerminalHeight: 5,
} as UIState;
const { lastFrame } = renderWithProviders(
<UIStateContext.Provider value={mockUIState}>
<AskUserDialog
questions={questions}
onSubmit={vi.fn()}
onCancel={vi.fn()}
width={40} // Small width to force wrapping
/>
</UIStateContext.Provider>,
{ useAlternateBuffer: true },
);
// Should NOT contain the truncation message
expect(lastFrame()).not.toContain('hidden ...');
// Should contain the full long question (or at least its parts)
expect(lastFrame()).toContain('This is a very long question');
});
});