feat(plan): support opening and modifying plan in external editor (#20348)

This commit is contained in:
Adib234
2026-02-25 23:38:44 -05:00
committed by GitHub
parent 39938000a9
commit ef247e220d
15 changed files with 297 additions and 47 deletions

View File

@@ -11,6 +11,7 @@ import { waitFor } from '../../test-utils/async.js';
import { ExitPlanModeDialog } from './ExitPlanModeDialog.js';
import { useKeypress } from '../hooks/useKeypress.js';
import { keyMatchers, Command } from '../keyMatchers.js';
import { openFileInEditor } from '../utils/editorUtils.js';
import {
ApprovalMode,
validatePlanContent,
@@ -19,6 +20,10 @@ import {
} from '@google/gemini-cli-core';
import * as fs from 'node:fs';
vi.mock('../utils/editorUtils.js', () => ({
openFileInEditor: vi.fn(),
}));
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
const actual =
await importOriginal<typeof import('@google/gemini-cli-core')>();
@@ -144,6 +149,7 @@ Implement a comprehensive authentication system with multiple providers.
onApprove={onApprove}
onFeedback={onFeedback}
onCancel={onCancel}
getPreferredEditor={vi.fn()}
width={80}
availableHeight={24}
/>,
@@ -153,6 +159,7 @@ Implement a comprehensive authentication system with multiple providers.
getTargetDir: () => mockTargetDir,
getIdeMode: () => false,
isTrustedFolder: () => true,
getPreferredEditor: () => undefined,
storage: {
getPlansDir: () => mockPlansDir,
},
@@ -418,6 +425,7 @@ Implement a comprehensive authentication system with multiple providers.
onApprove={onApprove}
onFeedback={onFeedback}
onCancel={onCancel}
getPreferredEditor={vi.fn()}
width={80}
availableHeight={24}
/>
@@ -535,6 +543,40 @@ Implement a comprehensive authentication system with multiple providers.
});
expect(onFeedback).not.toHaveBeenCalled();
});
it('opens plan in external editor when Ctrl+X is pressed', async () => {
const { stdin, lastFrame } = renderDialog({ useAlternateBuffer });
await act(async () => {
vi.runAllTimers();
});
await waitFor(() => {
expect(lastFrame()).toContain('Add user authentication');
});
// Reset the mock to track the second call during refresh
vi.mocked(processSingleFileContent).mockClear();
// Press Ctrl+X
await act(async () => {
writeKey(stdin, '\x18'); // Ctrl+X
});
await waitFor(() => {
expect(openFileInEditor).toHaveBeenCalledWith(
mockPlanFullPath,
expect.anything(),
expect.anything(),
undefined,
);
});
// Verify that content is refreshed (processSingleFileContent called again)
await waitFor(() => {
expect(processSingleFileContent).toHaveBeenCalled();
});
});
},
);
});