2025-11-24 23:11:46 +05:30
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
2025-12-29 15:46:10 -05:00
|
|
|
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
|
|
|
|
|
import { render } from '../test-utils/render.js';
|
2025-11-24 23:11:46 +05:30
|
|
|
import { act } from 'react';
|
|
|
|
|
import { IdeIntegrationNudge } from './IdeIntegrationNudge.js';
|
|
|
|
|
import { KeypressProvider } from './contexts/KeypressContext.js';
|
2025-12-29 15:46:10 -05:00
|
|
|
import { debugLogger } from '@google/gemini-cli-core';
|
2025-11-24 23:11:46 +05:30
|
|
|
|
2025-12-29 15:46:10 -05:00
|
|
|
// Mock debugLogger
|
|
|
|
|
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|
|
|
|
const actual =
|
|
|
|
|
await importOriginal<typeof import('@google/gemini-cli-core')>();
|
|
|
|
|
return {
|
|
|
|
|
...actual,
|
|
|
|
|
debugLogger: {
|
|
|
|
|
log: vi.fn(),
|
|
|
|
|
warn: vi.fn(),
|
|
|
|
|
error: vi.fn(),
|
|
|
|
|
debug: vi.fn(),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-24 23:11:46 +05:30
|
|
|
describe('IdeIntegrationNudge', () => {
|
|
|
|
|
const defaultProps = {
|
|
|
|
|
ide: {
|
|
|
|
|
name: 'vscode',
|
|
|
|
|
displayName: 'VS Code',
|
|
|
|
|
},
|
|
|
|
|
onComplete: vi.fn(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
vi.restoreAllMocks();
|
|
|
|
|
vi.unstubAllEnvs();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2025-12-29 15:46:10 -05:00
|
|
|
vi.mocked(debugLogger.warn).mockImplementation((...args) => {
|
2025-11-24 23:11:46 +05:30
|
|
|
if (
|
|
|
|
|
typeof args[0] === 'string' &&
|
|
|
|
|
/was not wrapped in act/.test(args[0])
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-12-29 15:46:10 -05:00
|
|
|
});
|
2025-11-24 23:11:46 +05:30
|
|
|
vi.stubEnv('GEMINI_CLI_IDE_SERVER_PORT', '');
|
|
|
|
|
vi.stubEnv('GEMINI_CLI_IDE_WORKSPACE_PATH', '');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('renders correctly with default options', async () => {
|
2026-02-18 16:46:50 -08:00
|
|
|
const { lastFrame, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
const frame = lastFrame();
|
|
|
|
|
|
|
|
|
|
expect(frame).toContain('Do you want to connect VS Code to Gemini CLI?');
|
|
|
|
|
expect(frame).toContain('Yes');
|
|
|
|
|
expect(frame).toContain('No (esc)');
|
|
|
|
|
expect(frame).toContain("No, don't ask again");
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('handles "Yes" selection', async () => {
|
|
|
|
|
const onComplete = vi.fn();
|
2026-02-18 16:46:50 -08:00
|
|
|
const { stdin, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
// "Yes" is the first option and selected by default usually.
|
|
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\r');
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
expect(onComplete).toHaveBeenCalledWith({
|
|
|
|
|
userSelection: 'yes',
|
|
|
|
|
isExtensionPreInstalled: false,
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('handles "No" selection', async () => {
|
|
|
|
|
const onComplete = vi.fn();
|
2026-02-18 16:46:50 -08:00
|
|
|
const { stdin, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
// Navigate down to "No (esc)"
|
|
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\u001B[B'); // Down arrow
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
|
|
|
|
|
2025-11-24 23:11:46 +05:30
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\r'); // Enter
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
expect(onComplete).toHaveBeenCalledWith({
|
|
|
|
|
userSelection: 'no',
|
|
|
|
|
isExtensionPreInstalled: false,
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('handles "Dismiss" selection', async () => {
|
|
|
|
|
const onComplete = vi.fn();
|
2026-02-18 16:46:50 -08:00
|
|
|
const { stdin, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
// Navigate down to "No, don't ask again"
|
|
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\u001B[B'); // Down arrow
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
|
|
|
|
|
2025-11-24 23:11:46 +05:30
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\u001B[B'); // Down arrow
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
|
|
|
|
|
2025-11-24 23:11:46 +05:30
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\r'); // Enter
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
expect(onComplete).toHaveBeenCalledWith({
|
|
|
|
|
userSelection: 'dismiss',
|
|
|
|
|
isExtensionPreInstalled: false,
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('handles Escape key press', async () => {
|
|
|
|
|
const onComplete = vi.fn();
|
2026-02-18 16:46:50 -08:00
|
|
|
const { stdin, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
// Press Escape
|
|
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\u001B');
|
2026-02-18 16:46:50 -08:00
|
|
|
});
|
|
|
|
|
// Escape key has a timeout in KeypressContext, so we need to wrap waitUntilReady in act
|
|
|
|
|
await act(async () => {
|
|
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(onComplete).toHaveBeenCalledWith({
|
|
|
|
|
userSelection: 'no',
|
|
|
|
|
isExtensionPreInstalled: false,
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('displays correct text and handles selection when extension is pre-installed', async () => {
|
|
|
|
|
vi.stubEnv('GEMINI_CLI_IDE_SERVER_PORT', '1234');
|
|
|
|
|
vi.stubEnv('GEMINI_CLI_IDE_WORKSPACE_PATH', '/tmp');
|
|
|
|
|
|
|
|
|
|
const onComplete = vi.fn();
|
2026-02-18 16:46:50 -08:00
|
|
|
const { lastFrame, stdin, waitUntilReady, unmount } = render(
|
2025-11-24 23:11:46 +05:30
|
|
|
<KeypressProvider>
|
|
|
|
|
<IdeIntegrationNudge {...defaultProps} onComplete={onComplete} />
|
|
|
|
|
</KeypressProvider>,
|
|
|
|
|
);
|
|
|
|
|
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
const frame = lastFrame();
|
|
|
|
|
|
|
|
|
|
expect(frame).toContain(
|
|
|
|
|
'If you select Yes, the CLI will have access to your open files',
|
|
|
|
|
);
|
|
|
|
|
expect(frame).not.toContain("we'll install an extension");
|
|
|
|
|
|
|
|
|
|
// Select "Yes"
|
|
|
|
|
await act(async () => {
|
|
|
|
|
stdin.write('\r');
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
await waitUntilReady();
|
2025-11-24 23:11:46 +05:30
|
|
|
|
|
|
|
|
expect(onComplete).toHaveBeenCalledWith({
|
|
|
|
|
userSelection: 'yes',
|
|
|
|
|
isExtensionPreInstalled: true,
|
|
|
|
|
});
|
2026-02-18 16:46:50 -08:00
|
|
|
unmount();
|
2025-11-24 23:11:46 +05:30
|
|
|
});
|
|
|
|
|
});
|