Download packages/cli/src/ui/components/LoadingIndicator.test.tsx from Jimmi42/openCLI: direct link, hf CLI and curl.
- Browser
- Download file 6.56 kB
-
https://huggingface.co/Jimmi42/openCLI/resolve/main/packages/cli/src/ui/components/LoadingIndicator.test.tsx
- Command line
-
hf download hf://Jimmi42/openCLI/packages/cli/src/ui/components/LoadingIndicator.test.tsx
-
curl -L -o LoadingIndicator.test.tsx https://huggingface.co/Jimmi42/openCLI/resolve/main/packages/cli/src/ui/components/LoadingIndicator.test.tsx
6.56 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import React from 'react'; | |
| import { render } from 'ink-testing-library'; | |
| import { Text } from 'ink'; | |
| import { LoadingIndicator } from './LoadingIndicator.js'; | |
| import { StreamingContext } from '../contexts/StreamingContext.js'; | |
| import { StreamingState } from '../types.js'; | |
| import { vi } from 'vitest'; | |
| // Mock GeminiRespondingSpinner | |
| vi.mock('./GeminiRespondingSpinner.js', () => ({ | |
| GeminiRespondingSpinner: ({ | |
| nonRespondingDisplay, | |
| }: { | |
| nonRespondingDisplay?: string; | |
| }) => { | |
| const streamingState = React.useContext(StreamingContext)!; | |
| if (streamingState === StreamingState.Responding) { | |
| return <Text>MockRespondingSpinner</Text>; | |
| } else if (nonRespondingDisplay) { | |
| return <Text>{nonRespondingDisplay}</Text>; | |
| } | |
| return null; | |
| }, | |
| })); | |
| const renderWithContext = ( | |
| ui: React.ReactElement, | |
| streamingStateValue: StreamingState, | |
| ) => { | |
| const contextValue: StreamingState = streamingStateValue; | |
| return render( | |
| <StreamingContext.Provider value={contextValue}> | |
| {ui} | |
| </StreamingContext.Provider>, | |
| ); | |
| }; | |
| describe('<LoadingIndicator />', () => { | |
| const defaultProps = { | |
| currentLoadingPhrase: 'Loading...', | |
| elapsedTime: 5, | |
| }; | |
| it('should not render when streamingState is Idle', () => { | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...defaultProps} />, | |
| StreamingState.Idle, | |
| ); | |
| expect(lastFrame()).toBe(''); | |
| }); | |
| it('should render spinner, phrase, and time when streamingState is Responding', () => { | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...defaultProps} />, | |
| StreamingState.Responding, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toContain('MockRespondingSpinner'); | |
| expect(output).toContain('Loading...'); | |
| expect(output).toContain('(esc to cancel, 5s)'); | |
| }); | |
| it('should render spinner (static), phrase but no time/cancel when streamingState is WaitingForConfirmation', () => { | |
| const props = { | |
| currentLoadingPhrase: 'Confirm action', | |
| elapsedTime: 10, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.WaitingForConfirmation, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toContain('⠏'); // Static char for WaitingForConfirmation | |
| expect(output).toContain('Confirm action'); | |
| expect(output).not.toContain('(esc to cancel)'); | |
| expect(output).not.toContain(', 10s'); | |
| }); | |
| it('should display the currentLoadingPhrase correctly', () => { | |
| const props = { | |
| currentLoadingPhrase: 'Processing data...', | |
| elapsedTime: 3, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.Responding, | |
| ); | |
| expect(lastFrame()).toContain('Processing data...'); | |
| }); | |
| it('should display the elapsedTime correctly when Responding', () => { | |
| const props = { | |
| currentLoadingPhrase: 'Working...', | |
| elapsedTime: 8, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.Responding, | |
| ); | |
| expect(lastFrame()).toContain('(esc to cancel, 8s)'); | |
| }); | |
| it('should render rightContent when provided', () => { | |
| const rightContent = <Text>Extra Info</Text>; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...defaultProps} rightContent={rightContent} />, | |
| StreamingState.Responding, | |
| ); | |
| expect(lastFrame()).toContain('Extra Info'); | |
| }); | |
| it('should transition correctly between states using rerender', () => { | |
| const { lastFrame, rerender } = renderWithContext( | |
| <LoadingIndicator {...defaultProps} />, | |
| StreamingState.Idle, | |
| ); | |
| expect(lastFrame()).toBe(''); // Initial: Idle | |
| // Transition to Responding | |
| rerender( | |
| <StreamingContext.Provider value={StreamingState.Responding}> | |
| <LoadingIndicator | |
| currentLoadingPhrase="Now Responding" | |
| elapsedTime={2} | |
| /> | |
| </StreamingContext.Provider>, | |
| ); | |
| let output = lastFrame(); | |
| expect(output).toContain('MockRespondingSpinner'); | |
| expect(output).toContain('Now Responding'); | |
| expect(output).toContain('(esc to cancel, 2s)'); | |
| // Transition to WaitingForConfirmation | |
| rerender( | |
| <StreamingContext.Provider value={StreamingState.WaitingForConfirmation}> | |
| <LoadingIndicator | |
| currentLoadingPhrase="Please Confirm" | |
| elapsedTime={15} | |
| /> | |
| </StreamingContext.Provider>, | |
| ); | |
| output = lastFrame(); | |
| expect(output).toContain('⠏'); | |
| expect(output).toContain('Please Confirm'); | |
| expect(output).not.toContain('(esc to cancel)'); | |
| expect(output).not.toContain(', 15s'); | |
| // Transition back to Idle | |
| rerender( | |
| <StreamingContext.Provider value={StreamingState.Idle}> | |
| <LoadingIndicator {...defaultProps} /> | |
| </StreamingContext.Provider>, | |
| ); | |
| expect(lastFrame()).toBe(''); | |
| }); | |
| it('should display fallback phrase if thought is empty', () => { | |
| const props = { | |
| thought: null, | |
| currentLoadingPhrase: 'Loading...', | |
| elapsedTime: 5, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.Responding, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toContain('Loading...'); | |
| }); | |
| it('should display the subject of a thought', () => { | |
| const props = { | |
| thought: { | |
| subject: 'Thinking about something...', | |
| description: 'and other stuff.', | |
| }, | |
| elapsedTime: 5, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.Responding, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toBeDefined(); | |
| if (output) { | |
| expect(output).toContain('Thinking about something...'); | |
| expect(output).not.toContain('and other stuff.'); | |
| } | |
| }); | |
| it('should prioritize thought.subject over currentLoadingPhrase', () => { | |
| const props = { | |
| thought: { | |
| subject: 'This should be displayed', | |
| description: 'A description', | |
| }, | |
| currentLoadingPhrase: 'This should not be displayed', | |
| elapsedTime: 5, | |
| }; | |
| const { lastFrame } = renderWithContext( | |
| <LoadingIndicator {...props} />, | |
| StreamingState.Responding, | |
| ); | |
| const output = lastFrame(); | |
| expect(output).toContain('This should be displayed'); | |
| expect(output).not.toContain('This should not be displayed'); | |
| }); | |
| }); | |