mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-17 17:41:24 -07:00
Issue#9751 - fix the gemini crash on startup in tmux environments (#11637)
This commit is contained in:
@@ -5,50 +5,117 @@
|
||||
*/
|
||||
|
||||
import { render } from '../../test-utils/render.js';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
||||
import { Header } from './Header.js';
|
||||
import * as useTerminalSize from '../hooks/useTerminalSize.js';
|
||||
import { longAsciiLogo } from './AsciiArt.js';
|
||||
import * as semanticColors from '../semantic-colors.js';
|
||||
import { Text } from 'ink';
|
||||
import type React from 'react';
|
||||
|
||||
vi.mock('../hooks/useTerminalSize.js');
|
||||
vi.mock('ink-gradient', () => {
|
||||
const MockGradient = ({ children }: { children: React.ReactNode }) => (
|
||||
<>{children}</>
|
||||
);
|
||||
return {
|
||||
default: vi.fn(MockGradient),
|
||||
};
|
||||
});
|
||||
vi.mock('../semantic-colors.js');
|
||||
vi.mock('ink', async () => {
|
||||
const originalInk = await vi.importActual<typeof import('ink')>('ink');
|
||||
return {
|
||||
...originalInk,
|
||||
Text: vi.fn(originalInk.Text),
|
||||
};
|
||||
});
|
||||
|
||||
describe('<Header />', () => {
|
||||
beforeEach(() => {});
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the long logo on a wide terminal', () => {
|
||||
vi.spyOn(useTerminalSize, 'useTerminalSize').mockReturnValue({
|
||||
columns: 120,
|
||||
rows: 20,
|
||||
});
|
||||
const { lastFrame, unmount } = render(
|
||||
<Header version="1.0.0" nightly={false} />,
|
||||
render(<Header version="1.0.0" nightly={false} />);
|
||||
expect(Text).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
children: longAsciiLogo,
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(lastFrame()).toContain(longAsciiLogo);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('renders custom ASCII art when provided', () => {
|
||||
const customArt = 'CUSTOM ART';
|
||||
const { lastFrame, unmount } = render(
|
||||
render(
|
||||
<Header version="1.0.0" nightly={false} customAsciiArt={customArt} />,
|
||||
);
|
||||
expect(lastFrame()).toContain(customArt);
|
||||
unmount();
|
||||
expect(Text).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
children: customArt,
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('displays the version number when nightly is true', () => {
|
||||
const { lastFrame, unmount } = render(
|
||||
<Header version="1.0.0" nightly={true} />,
|
||||
);
|
||||
expect(lastFrame()).toContain('v1.0.0');
|
||||
unmount();
|
||||
render(<Header version="1.0.0" nightly={true} />);
|
||||
const textCalls = (Text as Mock).mock.calls;
|
||||
expect(textCalls[1][0].children.join('')).toBe('v1.0.0');
|
||||
});
|
||||
|
||||
it('does not display the version number when nightly is false', () => {
|
||||
const { lastFrame, unmount } = render(
|
||||
<Header version="1.0.0" nightly={false} />,
|
||||
render(<Header version="1.0.0" nightly={false} />);
|
||||
expect(Text).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
children: 'v1.0.0',
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('renders with no gradient when theme.ui.gradient is undefined', async () => {
|
||||
vi.spyOn(semanticColors, 'theme', 'get').mockReturnValue({
|
||||
ui: { gradient: undefined },
|
||||
} as typeof semanticColors.theme);
|
||||
const Gradient = await import('ink-gradient');
|
||||
render(<Header version="1.0.0" nightly={false} />);
|
||||
expect(Gradient.default).not.toHaveBeenCalled();
|
||||
const textCalls = (Text as Mock).mock.calls;
|
||||
expect(textCalls[0][0]).not.toHaveProperty('color');
|
||||
});
|
||||
|
||||
it('renders with a single color when theme.ui.gradient has one color', async () => {
|
||||
const singleColor = '#FF0000';
|
||||
vi.spyOn(semanticColors, 'theme', 'get').mockReturnValue({
|
||||
ui: { gradient: [singleColor] },
|
||||
} as typeof semanticColors.theme);
|
||||
const Gradient = await import('ink-gradient');
|
||||
render(<Header version="1.0.0" nightly={false} />);
|
||||
expect(Gradient.default).not.toHaveBeenCalled();
|
||||
const textCalls = (Text as Mock).mock.calls;
|
||||
console.log(JSON.stringify(textCalls, null, 2));
|
||||
expect(textCalls.length).toBe(1);
|
||||
expect(textCalls[0][0]).toHaveProperty('color', singleColor);
|
||||
});
|
||||
|
||||
it('renders with a gradient when theme.ui.gradient has two or more colors', async () => {
|
||||
const gradientColors = ['#FF0000', '#00FF00'];
|
||||
vi.spyOn(semanticColors, 'theme', 'get').mockReturnValue({
|
||||
ui: { gradient: gradientColors },
|
||||
} as typeof semanticColors.theme);
|
||||
const Gradient = await import('ink-gradient');
|
||||
render(<Header version="1.0.0" nightly={false} />);
|
||||
expect(Gradient.default).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
colors: gradientColors,
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(lastFrame()).not.toContain('v1.0.0');
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,26 @@ interface HeaderProps {
|
||||
nightly: boolean;
|
||||
}
|
||||
|
||||
const ThemedGradient: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const gradient = theme.ui.gradient;
|
||||
|
||||
if (gradient && gradient.length >= 2) {
|
||||
return (
|
||||
<Gradient colors={gradient}>
|
||||
<Text>{children}</Text>
|
||||
</Gradient>
|
||||
);
|
||||
}
|
||||
|
||||
if (gradient && gradient.length === 1) {
|
||||
return <Text color={gradient[0]}>{children}</Text>;
|
||||
}
|
||||
|
||||
return <Text>{children}</Text>;
|
||||
};
|
||||
|
||||
export const Header: React.FC<HeaderProps> = ({
|
||||
customAsciiArt,
|
||||
version,
|
||||
@@ -47,22 +67,10 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
flexShrink={0}
|
||||
flexDirection="column"
|
||||
>
|
||||
{theme.ui.gradient ? (
|
||||
<Gradient colors={theme.ui.gradient}>
|
||||
<Text>{displayTitle}</Text>
|
||||
</Gradient>
|
||||
) : (
|
||||
<Text>{displayTitle}</Text>
|
||||
)}
|
||||
<ThemedGradient>{displayTitle}</ThemedGradient>
|
||||
{nightly && (
|
||||
<Box width="100%" flexDirection="row" justifyContent="flex-end">
|
||||
{theme.ui.gradient ? (
|
||||
<Gradient colors={theme.ui.gradient}>
|
||||
<Text>v{version}</Text>
|
||||
</Gradient>
|
||||
) : (
|
||||
<Text>v{version}</Text>
|
||||
)}
|
||||
<ThemedGradient>v{version}</ThemedGradient>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user