Download packages/cli/src/ui/components/AnsiOutput.test.tsx from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 6.09 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/components/AnsiOutput.test.tsx
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/ui/components/AnsiOutput.test.tsx
-
curl -L -o AnsiOutput.test.tsx https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/components/AnsiOutput.test.tsx
6.09 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { render } from '../../test-utils/render.js'; | |
| import { AnsiOutputText } from './AnsiOutput.js'; | |
| import type { AnsiOutput, AnsiToken } from '@google/gemini-cli-core'; | |
| // Helper to create a valid AnsiToken with default values | |
| const createAnsiToken = (overrides: Partial<AnsiToken>): AnsiToken => ({ | |
| text: '', | |
| bold: false, | |
| italic: false, | |
| underline: false, | |
| dim: false, | |
| inverse: false, | |
| isUninitialized: false, | |
| fg: '#ffffff', | |
| bg: '#000000', | |
| ...overrides, | |
| }); | |
| describe('<AnsiOutputText />', () => { | |
| it('renders a simple AnsiOutput object correctly', async () => { | |
| const data: AnsiOutput = [ | |
| [ | |
| createAnsiToken({ text: 'Hello, ' }), | |
| createAnsiToken({ text: 'world!' }), | |
| ], | |
| ]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} width={80} />, | |
| ); | |
| expect(lastFrame().trim()).toBe('Hello, world!'); | |
| unmount(); | |
| }); | |
| // Note: ink-testing-library doesn't render styles, so we can only check the text. | |
| // We are testing that it renders without crashing. | |
| it.each([ | |
| { style: { bold: true }, text: 'Bold' }, | |
| { style: { italic: true }, text: 'Italic' }, | |
| { style: { underline: true }, text: 'Underline' }, | |
| { style: { dim: true }, text: 'Dim' }, | |
| { style: { inverse: true }, text: 'Inverse' }, | |
| ])('correctly applies style $text', async ({ style, text }) => { | |
| const data: AnsiOutput = [[createAnsiToken({ text, ...style })]]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} width={80} />, | |
| ); | |
| expect(lastFrame().trim()).toBe(text); | |
| unmount(); | |
| }); | |
| it.each([ | |
| { color: { fg: '#ff0000' }, text: 'Red FG' }, | |
| { color: { bg: '#0000ff' }, text: 'Blue BG' }, | |
| { color: { fg: '#00ff00', bg: '#ff00ff' }, text: 'Green FG Magenta BG' }, | |
| ])('correctly applies color $text', async ({ color, text }) => { | |
| const data: AnsiOutput = [[createAnsiToken({ text, ...color })]]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} width={80} />, | |
| ); | |
| expect(lastFrame().trim()).toBe(text); | |
| unmount(); | |
| }); | |
| it('handles empty lines and empty tokens', async () => { | |
| const data: AnsiOutput = [ | |
| [createAnsiToken({ text: 'First line' })], | |
| [], | |
| [createAnsiToken({ text: 'Third line' })], | |
| [createAnsiToken({ text: '' })], | |
| ]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} width={80} />, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toBeDefined(); | |
| const lines = output.split('\n'); | |
| expect(lines[0].trim()).toBe('First line'); | |
| expect(lines[1].trim()).toBe(''); | |
| expect(lines[2].trim()).toBe('Third line'); | |
| unmount(); | |
| }); | |
| it('respects the availableTerminalHeight prop and slices the lines correctly', async () => { | |
| const data: AnsiOutput = [ | |
| [createAnsiToken({ text: 'Line 1' })], | |
| [createAnsiToken({ text: 'Line 2' })], | |
| [createAnsiToken({ text: 'Line 3' })], | |
| [createAnsiToken({ text: 'Line 4' })], | |
| ]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} availableTerminalHeight={2} width={80} />, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).not.toContain('Line 1'); | |
| expect(output).not.toContain('Line 2'); | |
| expect(output).toContain('Line 3'); | |
| expect(output).toContain('Line 4'); | |
| unmount(); | |
| }); | |
| it('respects the maxLines prop and slices the lines correctly', async () => { | |
| const data: AnsiOutput = [ | |
| [createAnsiToken({ text: 'Line 1' })], | |
| [createAnsiToken({ text: 'Line 2' })], | |
| [createAnsiToken({ text: 'Line 3' })], | |
| [createAnsiToken({ text: 'Line 4' })], | |
| ]; | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={data} maxLines={2} width={80} />, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).not.toContain('Line 1'); | |
| expect(output).not.toContain('Line 2'); | |
| expect(output).toContain('Line 3'); | |
| expect(output).toContain('Line 4'); | |
| unmount(); | |
| }); | |
| it('prioritizes maxLines over availableTerminalHeight if maxLines is smaller', async () => { | |
| const data: AnsiOutput = [ | |
| [createAnsiToken({ text: 'Line 1' })], | |
| [createAnsiToken({ text: 'Line 2' })], | |
| [createAnsiToken({ text: 'Line 3' })], | |
| [createAnsiToken({ text: 'Line 4' })], | |
| ]; | |
| // availableTerminalHeight=3, maxLines=2 => show 2 lines | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText | |
| data={data} | |
| availableTerminalHeight={3} | |
| maxLines={2} | |
| width={80} | |
| />, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).not.toContain('Line 2'); | |
| expect(output).toContain('Line 3'); | |
| expect(output).toContain('Line 4'); | |
| unmount(); | |
| }); | |
| it('renders a large AnsiOutput object without crashing', async () => { | |
| const largeData: AnsiOutput = []; | |
| for (let i = 0; i < 1000; i++) { | |
| largeData.push([createAnsiToken({ text: `Line ${i}` })]); | |
| } | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText data={largeData} width={80} />, | |
| ); | |
| // We are just checking that it renders something without crashing. | |
| expect(lastFrame()).toBeDefined(); | |
| unmount(); | |
| }); | |
| describe('robustness', () => { | |
| it('does NOT crash when data is undefined', async () => { | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText | |
| data={undefined as unknown as AnsiOutput} | |
| width={80} | |
| disableTruncation={true} | |
| />, | |
| ); | |
| expect(lastFrame({ allowEmpty: true }).trim()).toBe(''); | |
| unmount(); | |
| }); | |
| it('does NOT crash when data is an object but not an array', async () => { | |
| const { lastFrame, unmount } = await render( | |
| <AnsiOutputText | |
| data={{ summary: 'test' } as unknown as AnsiOutput} | |
| width={80} | |
| disableTruncation={true} | |
| />, | |
| ); | |
| expect(lastFrame({ allowEmpty: true }).trim()).toBe(''); | |
| unmount(); | |
| }); | |
| }); | |
| }); | |