/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { render, renderWithProviders } from '../../../test-utils/render.js'; import { OverflowProvider } from '../../contexts/OverflowContext.js'; import { MaxSizedBox } from './MaxSizedBox.js'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import { Box, Text } from 'ink'; import { describe, it, expect } from 'vitest'; describe('', () => { it('renders children without truncation when they fit', async () => { const { lastFrame, waitUntilReady, unmount } = render( Hello, World! , ); await waitUntilReady(); expect(lastFrame()).toContain('Hello, World!'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('hides lines when content exceeds maxHeight', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 Line 2 Line 3 , ); await waitUntilReady(); expect(lastFrame()).toContain( '... first 2 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('hides lines at the end when content exceeds maxHeight and overflowDirection is bottom', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 Line 2 Line 3 , ); await waitUntilReady(); expect(lastFrame()).toContain( '... last 2 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('shows plural "lines" when more than one line is hidden', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 Line 2 Line 3 , ); await waitUntilReady(); expect(lastFrame()).toContain( '... first 2 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('shows singular "line" when exactly one line is hidden', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 , ); await waitUntilReady(); expect(lastFrame()).toContain( '... first 1 line hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('accounts for additionalHiddenLinesCount', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 Line 2 Line 3 , ); await waitUntilReady(); expect(lastFrame()).toContain( '... first 7 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('wraps text that exceeds maxWidth', async () => { const { lastFrame, waitUntilReady, unmount } = render( This is a long line of text , ); await waitUntilReady(); expect(lastFrame()).toContain('This is a'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('does not truncate when maxHeight is undefined', async () => { const { lastFrame, waitUntilReady, unmount } = render( Line 1 Line 2 , ); await waitUntilReady(); expect(lastFrame()).toContain('Line 1'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('renders an empty box for empty children', async () => { const { lastFrame, waitUntilReady, unmount } = render( , ); await waitUntilReady(); expect(lastFrame({ allowEmpty: true })?.trim()).equals(''); unmount(); }); it('handles React.Fragment as a child', async () => { const { lastFrame, waitUntilReady, unmount } = render( <> Line 1 from Fragment Line 2 from Fragment Line 3 direct child , ); await waitUntilReady(); expect(lastFrame()).toContain('Line 1 from Fragment'); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('clips a long single text child from the top', async () => { const THIRTY_LINES = Array.from( { length: 30 }, (_, i) => `Line ${i + 1}`, ).join('\n'); const { lastFrame, waitUntilReady, unmount } = render( {THIRTY_LINES} , ); await waitUntilReady(); expect(lastFrame()).toContain( '... first 21 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('clips a long single text child from the bottom', async () => { const THIRTY_LINES = Array.from( { length: 30 }, (_, i) => `Line ${i + 1}`, ).join('\n'); const { lastFrame, waitUntilReady, unmount } = render( {THIRTY_LINES} , ); await waitUntilReady(); expect(lastFrame()).toContain( '... last 21 lines hidden (Ctrl+O to show) ...', ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); it('does not leak content after hidden indicator with bottom overflow', async () => { const markdownContent = Array.from( { length: 20 }, (_, i) => `- Step ${i + 1}: Do something important`, ).join('\n'); const { lastFrame, waitUntilReady, unmount } = renderWithProviders( , { width: 80 }, ); await waitUntilReady(); expect(lastFrame()).toContain('... last'); const frame = lastFrame(); const lines = frame.trim().split('\n'); const lastLine = lines[lines.length - 1]; // The last line should only contain the hidden indicator, no leaked content expect(lastLine).toMatch( /^\.\.\. last \d+ lines? hidden \(Ctrl\+O to show\) \.\.\.$/, ); expect(lastFrame()).toMatchSnapshot(); unmount(); }); });