File size: 5,734 Bytes
52efc7b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | /**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { renderWithProviders } from '../../../test-utils/render.js';
import { ThinkingMessage } from './ThinkingMessage.js';
import React from 'react';
describe('ThinkingMessage', () => {
it('renders subject line with vertical rule and "Thinking..." header', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{ subject: 'Planning', description: 'test' }}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
const output = renderResult.lastFrame();
expect(output).toContain(' Thinking...');
expect(output).toContain('│');
expect(output).toContain('Planning');
expect(output).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('uses description when subject is empty', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{ subject: '', description: 'Processing details' }}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
const output = renderResult.lastFrame();
expect(output).toContain('Processing details');
expect(output).toContain('│');
expect(output).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('renders full mode with left border and full text', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{
subject: 'Planning',
description: 'I am planning the solution.',
}}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
const output = renderResult.lastFrame();
expect(output).toContain('│');
expect(output).toContain('Planning');
expect(output).toContain('I am planning the solution.');
expect(output).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('renders "Thinking..." header when isFirstThinking is true', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{
subject: 'Summary line',
description: 'First body line',
}}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
const output = renderResult.lastFrame();
expect(output).toContain(' Thinking...');
expect(output).toContain('Summary line');
expect(output).toContain('│');
expect(output).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('normalizes escaped newline tokens', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{
subject: 'Matching the Blocks',
description: '\\n\\nSome more text',
}}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
expect(renderResult.lastFrame()).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('renders empty state gracefully', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{ subject: '', description: '' }}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
expect(renderResult.lastFrame({ allowEmpty: true })).toBe('');
renderResult.unmount();
});
it('renders multiple thinking messages sequentially correctly', async () => {
const renderResult = await renderWithProviders(
<React.Fragment>
<ThinkingMessage
thought={{
subject: 'Initial analysis',
description:
'This is a multiple line paragraph for the first thinking message of how the model analyzes the problem.',
}}
terminalWidth={80}
isFirstThinking={true}
/>
<ThinkingMessage
thought={{
subject: 'Planning execution',
description:
'This a second multiple line paragraph for the second thinking message explaining the plan in detail so that it wraps around the terminal display.',
}}
terminalWidth={80}
/>
<ThinkingMessage
thought={{
subject: 'Refining approach',
description:
'And finally a third multiple line paragraph for the third thinking message to refine the solution.',
}}
terminalWidth={80}
/>
</React.Fragment>,
);
await renderResult.waitUntilReady();
expect(renderResult.lastFrame()).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
it('filters out progress dots and empty lines', async () => {
const renderResult = await renderWithProviders(
<ThinkingMessage
thought={{ subject: '...', description: 'Thinking\n.\n..\n...\nDone' }}
terminalWidth={80}
isFirstThinking={true}
/>,
);
await renderResult.waitUntilReady();
const output = renderResult.lastFrame();
expect(output).toContain('Thinking');
expect(output).toContain('Done');
expect(renderResult.lastFrame()).toMatchSnapshot();
await expect(renderResult).toMatchSvgSnapshot();
renderResult.unmount();
});
});
|