File size: 4,011 Bytes
9332ccf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { act } from 'react';
import type { EventEmitter } from 'node:events';
import { renderWithProviders } from '../../test-utils/render.js';
import { waitFor } from '../../test-utils/async.js';
import { ConfigInitDisplay } from './ConfigInitDisplay.js';
import {
describe,
it,
expect,
vi,
beforeEach,
afterEach,
type MockInstance,
} from 'vitest';
import {
CoreEvent,
MCPServerStatus,
type McpClient,
coreEvents,
} from '@google/gemini-cli-core';
import { Text } from 'ink';
// Mock GeminiSpinner
vi.mock('./GeminiSpinner.js', () => ({
GeminiSpinner: () => <Text>Spinner</Text>,
}));
describe('ConfigInitDisplay', () => {
let onSpy: MockInstance<EventEmitter['on']>;
beforeEach(() => {
onSpy = vi.spyOn(coreEvents as EventEmitter, 'on');
});
afterEach(() => {
vi.restoreAllMocks();
});
it('renders initial state', async () => {
const { lastFrame } = await renderWithProviders(<ConfigInitDisplay />);
expect(lastFrame()).toMatchSnapshot();
});
it('updates message on McpClientUpdate event', async () => {
let listener: ((clients?: Map<string, McpClient>) => void) | undefined;
onSpy.mockImplementation((event: unknown, fn: unknown) => {
if (event === CoreEvent.McpClientUpdate) {
listener = fn as (clients?: Map<string, McpClient>) => void;
}
return coreEvents;
});
const { lastFrame } = await renderWithProviders(<ConfigInitDisplay />);
// Wait for listener to be registered
await waitFor(() => {
if (!listener) throw new Error('Listener not registered yet');
});
const mockClient1 = {
getStatus: () => MCPServerStatus.CONNECTED,
} as McpClient;
const mockClient2 = {
getStatus: () => MCPServerStatus.CONNECTING,
} as McpClient;
const clients = new Map<string, McpClient>([
['server1', mockClient1],
['server2', mockClient2],
]);
// Trigger the listener manually since we mocked the event emitter
act(() => {
listener!(clients);
});
// Wait for the UI to update
await waitFor(() => {
expect(lastFrame()).toMatchSnapshot();
});
});
it('truncates list of waiting servers if too many', async () => {
let listener: ((clients?: Map<string, McpClient>) => void) | undefined;
onSpy.mockImplementation((event: unknown, fn: unknown) => {
if (event === CoreEvent.McpClientUpdate) {
listener = fn as (clients?: Map<string, McpClient>) => void;
}
return coreEvents;
});
const { lastFrame } = await renderWithProviders(<ConfigInitDisplay />);
await waitFor(() => {
if (!listener) throw new Error('Listener not registered yet');
});
const mockClientConnecting = {
getStatus: () => MCPServerStatus.CONNECTING,
} as McpClient;
const clients = new Map<string, McpClient>([
['s1', mockClientConnecting],
['s2', mockClientConnecting],
['s3', mockClientConnecting],
['s4', mockClientConnecting],
['s5', mockClientConnecting],
]);
act(() => {
listener!(clients);
});
await waitFor(() => {
expect(lastFrame()).toMatchSnapshot();
});
});
it('handles empty clients map', async () => {
let listener: ((clients?: Map<string, McpClient>) => void) | undefined;
onSpy.mockImplementation((event: unknown, fn: unknown) => {
if (event === CoreEvent.McpClientUpdate) {
listener = fn as (clients?: Map<string, McpClient>) => void;
}
return coreEvents;
});
const { lastFrame } = await renderWithProviders(<ConfigInitDisplay />);
await waitFor(() => {
if (!listener) throw new Error('Listener not registered yet');
});
if (listener) {
const safeListener = listener;
act(() => {
safeListener(new Map());
});
}
await waitFor(() => {
expect(lastFrame()).toMatchSnapshot();
});
});
});
|