|
15 | 15 | // ***************************************************************************** |
16 | 16 |
|
17 | 17 | import { expect } from 'chai'; |
18 | | -import { condenseArguments } from './toolcall-utils'; |
| 18 | +import { condenseArguments, formatArgsForTooltip } from './toolcall-utils'; |
19 | 19 |
|
20 | 20 | describe('condenseArguments', () => { |
21 | 21 |
|
@@ -130,3 +130,147 @@ describe('condenseArguments', () => { |
130 | 130 | }); |
131 | 131 |
|
132 | 132 | }); |
| 133 | + |
| 134 | +describe('formatArgsForTooltip', () => { |
| 135 | + |
| 136 | + it('renders short single-line args as inline code', () => { |
| 137 | + const args = JSON.stringify({ path: 'test.ts' }); |
| 138 | + const result = formatArgsForTooltip(args); |
| 139 | + expect(result.value).to.contain('**path:** `test.ts`'); |
| 140 | + expect(result.value).to.not.contain('```'); |
| 141 | + }); |
| 142 | + |
| 143 | + it('renders long single-line string as inline code (no newlines)', () => { |
| 144 | + const longPath = 'src/browser/chat-response-renderer/toolcall-utils.ts'; |
| 145 | + const args = JSON.stringify({ path: longPath }); |
| 146 | + const result = formatArgsForTooltip(args); |
| 147 | + expect(result.value).to.contain(`**path:** \`${longPath}\``); |
| 148 | + expect(result.value).to.not.contain('```'); |
| 149 | + }); |
| 150 | + |
| 151 | + it('renders multi-line string as code block', () => { |
| 152 | + const multiLine = 'line one\nline two\nline three'; |
| 153 | + const args = JSON.stringify({ content: multiLine }); |
| 154 | + const result = formatArgsForTooltip(args); |
| 155 | + expect(result.value).to.contain('**content:**'); |
| 156 | + expect(result.value).to.contain('```\n'); |
| 157 | + expect(result.value).to.contain(multiLine); |
| 158 | + }); |
| 159 | + |
| 160 | + it('renders JSON object value as code block (serialization produces newlines)', () => { |
| 161 | + const args = JSON.stringify({ config: { nested: true, key: 'value' } }); |
| 162 | + const result = formatArgsForTooltip(args); |
| 163 | + expect(result.value).to.contain('**config:**'); |
| 164 | + expect(result.value).to.contain('```'); |
| 165 | + }); |
| 166 | + |
| 167 | + it('renders array value as code block (serialization produces newlines)', () => { |
| 168 | + const bigArray = Array.from({ length: 5 }, (_, i) => i); |
| 169 | + const args = JSON.stringify({ data: bigArray }); |
| 170 | + const result = formatArgsForTooltip(args); |
| 171 | + expect(result.value).to.contain('**data:**'); |
| 172 | + expect(result.value).to.contain('```'); |
| 173 | + }); |
| 174 | + |
| 175 | + it('renders simple non-string values as inline code (no code blocks)', () => { |
| 176 | + const args = '{"count": 42, "enabled": true, "value": null}'; |
| 177 | + const result = formatArgsForTooltip(args); |
| 178 | + expect(result.value).to.contain('**count:** `42`'); |
| 179 | + expect(result.value).to.contain('**enabled:** `true`'); |
| 180 | + expect(result.value).to.contain('**value:** `null`'); |
| 181 | + expect(result.value).to.not.contain('```'); |
| 182 | + }); |
| 183 | + |
| 184 | + it('renders mixed single-line and multi-line values correctly', () => { |
| 185 | + const args = JSON.stringify({ path: 'test.ts', content: 'line1\nline2' }); |
| 186 | + const result = formatArgsForTooltip(args); |
| 187 | + expect(result.value).to.contain('**path:** `test.ts`'); |
| 188 | + expect(result.value).to.contain('**content:**'); |
| 189 | + expect(result.value).to.contain('```'); |
| 190 | + }); |
| 191 | + |
| 192 | + it('falls back to code block for short unparseable JSON', () => { |
| 193 | + const invalidJson = 'not json at all'; |
| 194 | + const result = formatArgsForTooltip(invalidJson); |
| 195 | + expect(result.value).to.contain('```'); |
| 196 | + expect(result.value).to.contain('not json at all'); |
| 197 | + }); |
| 198 | + |
| 199 | + it('falls back to code block for multi-line unparseable JSON', () => { |
| 200 | + const invalidJson = 'not json\nat all'; |
| 201 | + const result = formatArgsForTooltip(invalidJson); |
| 202 | + expect(result.value).to.contain('```'); |
| 203 | + expect(result.value).to.contain(invalidJson); |
| 204 | + }); |
| 205 | + |
| 206 | + it('handles top-level non-object parsed value as code block', () => { |
| 207 | + const longString = '"a string that is longer than fifty characters total and keeps going"'; |
| 208 | + expect(longString.length).to.be.greaterThan(50); |
| 209 | + const result = formatArgsForTooltip(longString); |
| 210 | + expect(result.value).to.contain('```'); |
| 211 | + expect(result.value).to.contain('a string that is longer than fifty characters total and keeps going'); |
| 212 | + }); |
| 213 | + |
| 214 | + it('handles top-level array as code block (serialization produces newlines)', () => { |
| 215 | + const args = JSON.stringify([1, 2, 3]); |
| 216 | + const result = formatArgsForTooltip(args); |
| 217 | + expect(result.value).to.contain('```'); |
| 218 | + }); |
| 219 | + |
| 220 | + it('separates top-level entries with horizontal rules', () => { |
| 221 | + const args = JSON.stringify({ path: 'test.ts', enabled: true }); |
| 222 | + const result = formatArgsForTooltip(args); |
| 223 | + expect(result.value).to.contain('---'); |
| 224 | + }); |
| 225 | + |
| 226 | + it('renders primitive array as JSON code block', () => { |
| 227 | + const args = JSON.stringify({ tags: ['a', 'b', 'c'] }); |
| 228 | + const result = formatArgsForTooltip(args); |
| 229 | + expect(result.value).to.contain('**tags:**'); |
| 230 | + expect(result.value).to.contain('```'); |
| 231 | + expect(result.value).to.contain('"a"'); |
| 232 | + expect(result.value).to.contain('"b"'); |
| 233 | + }); |
| 234 | + |
| 235 | + it('expands array of objects with string values into sections', () => { |
| 236 | + const args = JSON.stringify({ |
| 237 | + replacements: [ |
| 238 | + { oldContent: 'line one\nline two', newContent: 'line three\nline four' } |
| 239 | + ] |
| 240 | + }); |
| 241 | + const result = formatArgsForTooltip(args); |
| 242 | + expect(result.value).to.contain('**oldContent:**'); |
| 243 | + expect(result.value).to.contain('**newContent:**'); |
| 244 | + expect(result.value).to.contain('line one\nline two'); |
| 245 | + expect(result.value).to.contain('line three\nline four'); |
| 246 | + }); |
| 247 | + |
| 248 | + it('renders short strings as code blocks inside array items', () => { |
| 249 | + const args = JSON.stringify({ |
| 250 | + replacements: [ |
| 251 | + { oldContent: '// 2024', newContent: '// 2025' } |
| 252 | + ] |
| 253 | + }); |
| 254 | + const result = formatArgsForTooltip(args); |
| 255 | + expect(result.value).to.contain('**oldContent:**'); |
| 256 | + expect(result.value).to.contain('**newContent:**'); |
| 257 | + expect(result.value).to.contain('// 2024'); |
| 258 | + expect(result.value).to.contain('// 2025'); |
| 259 | + // Both values should be in code blocks, not inline code |
| 260 | + const codeBlockCount = (result.value.match(/```/g) || []).length; |
| 261 | + expect(codeBlockCount).to.be.greaterThanOrEqual(4); // 2 code blocks = 4 fences |
| 262 | + }); |
| 263 | + |
| 264 | + it('numbers array items when there are multiple', () => { |
| 265 | + const args = JSON.stringify({ |
| 266 | + replacements: [ |
| 267 | + { old: 'aaa\nbbb', new: 'ccc\nddd' }, |
| 268 | + { old: 'eee\nfff', new: 'ggg\nhhh' } |
| 269 | + ] |
| 270 | + }); |
| 271 | + const result = formatArgsForTooltip(args); |
| 272 | + expect(result.value).to.contain('\\[0\\]'); |
| 273 | + expect(result.value).to.contain('\\[1\\]'); |
| 274 | + }); |
| 275 | + |
| 276 | +}); |
0 commit comments