Migrate core render util to use xterm.js as part of the rendering loop. (#19044)

This commit is contained in:
Jacob Richman
2026-02-18 16:46:50 -08:00
committed by GitHub
parent 04c52513e7
commit 04f65f3d55
213 changed files with 7065 additions and 3852 deletions
@@ -33,10 +33,7 @@ describe('ToolResultDisplay', () => {
mockUseAlternateBuffer.mockReturnValue(false);
});
// Helper to use renderWithProviders
const render = (ui: React.ReactElement) => renderWithProviders(ui);
it('uses ScrollableList for ANSI output in alternate buffer mode', () => {
it('uses ScrollableList for ANSI output in alternate buffer mode', async () => {
mockUseAlternateBuffer.mockReturnValue(true);
const content = 'ansi content';
const ansiResult: AnsiOutput = [
@@ -53,57 +50,65 @@ describe('ToolResultDisplay', () => {
},
],
];
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={ansiResult}
terminalWidth={80}
maxLines={10}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toContain(content);
unmount();
});
it('uses Scrollable for non-ANSI output in alternate buffer mode', () => {
it('uses Scrollable for non-ANSI output in alternate buffer mode', async () => {
mockUseAlternateBuffer.mockReturnValue(true);
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay="**Markdown content**"
terminalWidth={80}
maxLines={10}
/>,
);
await waitUntilReady();
const output = lastFrame();
// With real components, we check for the content itself
expect(output).toContain('Markdown content');
unmount();
});
it('passes hasFocus prop to scrollable components', () => {
it('passes hasFocus prop to scrollable components', async () => {
mockUseAlternateBuffer.mockReturnValue(true);
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay="Some result"
terminalWidth={80}
hasFocus={true}
/>,
);
await waitUntilReady();
expect(lastFrame()).toContain('Some result');
unmount();
});
it('renders string result as markdown by default', () => {
const { lastFrame } = render(
it('renders string result as markdown by default', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay resultDisplay="**Some result**" terminalWidth={80} />,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('renders string result as plain text when renderOutputAsMarkdown is false', () => {
const { lastFrame } = render(
it('renders string result as plain text when renderOutputAsMarkdown is false', async () => {
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay="**Some result**"
terminalWidth={80}
@@ -111,43 +116,49 @@ describe('ToolResultDisplay', () => {
renderOutputAsMarkdown={false}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('truncates very long string results', { timeout: 20000 }, () => {
it('truncates very long string results', { timeout: 20000 }, async () => {
const longString = 'a'.repeat(1000005);
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={longString}
terminalWidth={80}
availableTerminalHeight={20}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('renders file diff result', () => {
it('renders file diff result', async () => {
const diffResult = {
fileDiff: 'diff content',
fileName: 'test.ts',
};
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={diffResult}
terminalWidth={80}
availableTerminalHeight={20}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('renders ANSI output result', () => {
it('renders ANSI output result', async () => {
const ansiResult: AnsiOutput = [
[
{
@@ -162,38 +173,42 @@ describe('ToolResultDisplay', () => {
},
],
];
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={ansiResult as unknown as AnsiOutput}
terminalWidth={80}
availableTerminalHeight={20}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('renders nothing for todos result', () => {
it('renders nothing for todos result', async () => {
const todoResult = {
todos: [],
};
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={todoResult}
terminalWidth={80}
availableTerminalHeight={20}
/>,
);
const output = lastFrame();
await waitUntilReady();
const output = lastFrame({ allowEmpty: true });
expect(output).toMatchSnapshot();
unmount();
});
it('does not fall back to plain text if availableHeight is set and not in alternate buffer', () => {
it('does not fall back to plain text if availableHeight is set and not in alternate buffer', async () => {
mockUseAlternateBuffer.mockReturnValue(false);
// availableHeight calculation: 20 - 1 - 5 = 14 > 3
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay="**Some result**"
terminalWidth={80}
@@ -201,13 +216,15 @@ describe('ToolResultDisplay', () => {
renderOutputAsMarkdown={true}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('keeps markdown if in alternate buffer even with availableHeight', () => {
it('keeps markdown if in alternate buffer even with availableHeight', async () => {
mockUseAlternateBuffer.mockReturnValue(true);
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay="**Some result**"
terminalWidth={80}
@@ -215,12 +232,14 @@ describe('ToolResultDisplay', () => {
renderOutputAsMarkdown={true}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).toMatchSnapshot();
unmount();
});
it('truncates ANSI output when maxLines is provided', () => {
it('truncates ANSI output when maxLines is provided', async () => {
const ansiResult: AnsiOutput = [
[
{
@@ -259,7 +278,7 @@ describe('ToolResultDisplay', () => {
},
],
];
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={ansiResult}
terminalWidth={80}
@@ -267,14 +286,16 @@ describe('ToolResultDisplay', () => {
maxLines={2}
/>,
);
await waitUntilReady();
const output = lastFrame();
expect(output).not.toContain('Line 1');
expect(output).toContain('Line 2');
expect(output).toContain('Line 3');
unmount();
});
it('truncates ANSI output when maxLines is provided, even if availableTerminalHeight is undefined', () => {
it('truncates ANSI output when maxLines is provided, even if availableTerminalHeight is undefined', async () => {
const ansiResult: AnsiOutput = Array.from({ length: 50 }, (_, i) => [
{
text: `Line ${i + 1}`,
@@ -287,7 +308,7 @@ describe('ToolResultDisplay', () => {
inverse: false,
},
]);
const { lastFrame } = render(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<ToolResultDisplay
resultDisplay={ansiResult}
terminalWidth={80}
@@ -295,10 +316,12 @@ describe('ToolResultDisplay', () => {
availableTerminalHeight={undefined}
/>,
);
await waitUntilReady();
const output = lastFrame();
// It SHOULD truncate to 25 lines because maxLines is provided
expect(output).not.toContain('Line 1');
expect(output).toContain('Line 50');
unmount();
});
});