|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { render } from 'ink-testing-library'; |
|
|
import { OverflowProvider } from '../../contexts/OverflowContext.js'; |
|
|
import { MaxSizedBox, setMaxSizedBoxDebugging } from './MaxSizedBox.js'; |
|
|
import { Box, Text } from 'ink'; |
|
|
import { describe, it, expect } from 'vitest'; |
|
|
|
|
|
describe('<MaxSizedBox />', () => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setMaxSizedBoxDebugging(true); |
|
|
|
|
|
it('renders children without truncation when they fit', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={10}> |
|
|
<Box> |
|
|
<Text>Hello, World!</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals('Hello, World!'); |
|
|
}); |
|
|
|
|
|
it('hides lines when content exceeds maxHeight', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={2}> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 3</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`... first 2 lines hidden ... |
|
|
Line 3`); |
|
|
}); |
|
|
|
|
|
it('hides lines at the end when content exceeds maxHeight and overflowDirection is bottom', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={2} overflowDirection="bottom"> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 3</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`Line 1 |
|
|
... last 2 lines hidden ...`); |
|
|
}); |
|
|
|
|
|
it('wraps text that exceeds maxWidth', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={10} maxHeight={5}> |
|
|
<Box> |
|
|
<Text wrap="wrap">This is a long line of text</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
expect(lastFrame()).equals(`This is a |
|
|
long line |
|
|
of text`); |
|
|
}); |
|
|
|
|
|
it('handles mixed wrapping and non-wrapping segments', () => { |
|
|
const multilineText = `This part will wrap around. |
|
|
And has a line break. |
|
|
Leading spaces preserved.`; |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={20} maxHeight={20}> |
|
|
<Box> |
|
|
<Text>Example</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>No Wrap: </Text> |
|
|
<Text wrap="wrap">{multilineText}</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Longer No Wrap: </Text> |
|
|
<Text wrap="wrap">This part will wrap around.</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
expect(lastFrame()).equals( |
|
|
`Example |
|
|
No Wrap: This part |
|
|
will wrap |
|
|
around. |
|
|
And has a |
|
|
line break. |
|
|
Leading |
|
|
spaces |
|
|
preserved. |
|
|
Longer No Wrap: This |
|
|
part |
|
|
will |
|
|
wrap |
|
|
arou |
|
|
nd.`, |
|
|
); |
|
|
}); |
|
|
|
|
|
it('handles words longer than maxWidth by splitting them', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={5} maxHeight={5}> |
|
|
<Box> |
|
|
<Text wrap="wrap">Supercalifragilisticexpialidocious</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
expect(lastFrame()).equals(`... โฆ |
|
|
istic |
|
|
expia |
|
|
lidoc |
|
|
ious`); |
|
|
}); |
|
|
|
|
|
it('does not truncate when maxHeight is undefined', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={undefined}> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`Line 1 |
|
|
Line 2`); |
|
|
}); |
|
|
|
|
|
it('shows plural "lines" when more than one line is hidden', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={2}> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 3</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`... first 2 lines hidden ... |
|
|
Line 3`); |
|
|
}); |
|
|
|
|
|
it('shows plural "lines" when more than one line is hidden and overflowDirection is bottom', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={2} overflowDirection="bottom"> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 3</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`Line 1 |
|
|
... last 2 lines hidden ...`); |
|
|
}); |
|
|
|
|
|
it('renders an empty box for empty children', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={10}></MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
|
|
|
expect(lastFrame()).equals(''); |
|
|
}); |
|
|
|
|
|
it('wraps text with multi-byte unicode characters correctly', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={5} maxHeight={5}> |
|
|
<Box> |
|
|
<Text wrap="wrap">ไฝ ๅฅฝไธ็</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
expect(lastFrame()).equals(`ไฝ ๅฅฝ |
|
|
ไธ็`); |
|
|
}); |
|
|
|
|
|
it('wraps text with multi-byte emoji characters correctly', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={5} maxHeight={5}> |
|
|
<Box> |
|
|
<Text wrap="wrap">๐ถ๐ถ๐ถ๐ถ๐ถ</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
expect(lastFrame()).equals(`๐ถ๐ถ |
|
|
๐ถ๐ถ |
|
|
๐ถ`); |
|
|
}); |
|
|
|
|
|
it('accounts for additionalHiddenLinesCount', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={2} additionalHiddenLinesCount={5}> |
|
|
<Box> |
|
|
<Text>Line 1</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 3</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
expect(lastFrame()).equals(`... first 7 lines hidden ... |
|
|
Line 3`); |
|
|
}); |
|
|
|
|
|
it('handles React.Fragment as a child', () => { |
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={10}> |
|
|
<> |
|
|
<Box> |
|
|
<Text>Line 1 from Fragment</Text> |
|
|
</Box> |
|
|
<Box> |
|
|
<Text>Line 2 from Fragment</Text> |
|
|
</Box> |
|
|
</> |
|
|
<Box> |
|
|
<Text>Line 3 direct child</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
expect(lastFrame()).equals(`Line 1 from Fragment |
|
|
Line 2 from Fragment |
|
|
Line 3 direct child`); |
|
|
}); |
|
|
|
|
|
it('clips a long single text child from the top', () => { |
|
|
const THIRTY_LINES = Array.from( |
|
|
{ length: 30 }, |
|
|
(_, i) => `Line ${i + 1}`, |
|
|
).join('\n'); |
|
|
|
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={10}> |
|
|
<Box> |
|
|
<Text>{THIRTY_LINES}</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
const expected = [ |
|
|
'... first 21 lines hidden ...', |
|
|
...Array.from({ length: 9 }, (_, i) => `Line ${22 + i}`), |
|
|
].join('\n'); |
|
|
|
|
|
expect(lastFrame()).equals(expected); |
|
|
}); |
|
|
|
|
|
it('clips a long single text child from the bottom', () => { |
|
|
const THIRTY_LINES = Array.from( |
|
|
{ length: 30 }, |
|
|
(_, i) => `Line ${i + 1}`, |
|
|
).join('\n'); |
|
|
|
|
|
const { lastFrame } = render( |
|
|
<OverflowProvider> |
|
|
<MaxSizedBox maxWidth={80} maxHeight={10} overflowDirection="bottom"> |
|
|
<Box> |
|
|
<Text>{THIRTY_LINES}</Text> |
|
|
</Box> |
|
|
</MaxSizedBox> |
|
|
</OverflowProvider>, |
|
|
); |
|
|
|
|
|
const expected = [ |
|
|
...Array.from({ length: 9 }, (_, i) => `Line ${i + 1}`), |
|
|
'... last 21 lines hidden ...', |
|
|
].join('\n'); |
|
|
|
|
|
expect(lastFrame()).equals(expected); |
|
|
}); |
|
|
}); |
|
|
|