Skip to content

Commit 34d5b37

Browse files
test: renderer test fixes from PR review
1 parent 9126909 commit 34d5b37

20 files changed

Lines changed: 105 additions & 100 deletions

static/js/render/registry.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ describe('TOOL_USE_RENDERERS', () => {
3434
}
3535
});
3636

37+
it('CORE_TOOL_USE stays in sync with registry keys', () => {
38+
expect(new Set(CORE_TOOL_USE)).toEqual(new Set(Object.keys(TOOL_USE_RENDERERS)));
39+
});
40+
3741
it('does not register the unknown dispatch sentinel as a tool renderer', () => {
3842
expect(Object.prototype.hasOwnProperty.call(TOOL_USE_RENDERERS, UNKNOWN_DISPATCH_KEY)).toBe(false);
3943
});
@@ -69,6 +73,10 @@ describe('TOOL_RESULT_RENDERERS', () => {
6973
}
7074
});
7175

76+
it('CORE_TOOL_RESULT stays in sync with registry keys', () => {
77+
expect(new Set(CORE_TOOL_RESULT)).toEqual(new Set(Object.keys(TOOL_RESULT_RENDERERS)));
78+
});
79+
7280
it('renderBashResult escapes stdout', () => {
7381
const html = renderToolResult({
7482
result_type: 'bash',
@@ -221,6 +229,7 @@ const TOOL_RESULT_FIXTURES = {
221229
describe('registry behavioral smoke tests', () => {
222230
it('every registered tool_use renderer produces non-empty HTML', () => {
223231
for (const name of CORE_TOOL_USE) {
232+
expect(TOOL_USE_FIXTURES[name], name).toBeDefined();
224233
const html = renderToolUse({ name, ...TOOL_USE_FIXTURES[name] });
225234
expect(html, name).toContain('tool-call');
226235
expect(html.length, name).toBeGreaterThan(20);
@@ -229,6 +238,7 @@ describe('registry behavioral smoke tests', () => {
229238

230239
it('every registered tool_result renderer produces non-empty HTML', () => {
231240
for (const rt of CORE_TOOL_RESULT) {
241+
expect(TOOL_RESULT_FIXTURES[rt], rt).toBeDefined();
232242
const html = renderToolResult({ result_type: rt, ...TOOL_RESULT_FIXTURES[rt] });
233243
expect(html, rt).toContain('tool-result');
234244
expect(html.length, rt).toBeGreaterThan(20);

static/js/render/test_helpers.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,10 @@
1-
import { expect } from 'vitest';
2-
import { renderToolUse, renderToolResult } from './registry.js';
1+
import { describe, expect, it } from 'vitest';
2+
import { renderToolResult } from './registry.js';
33

44
/** Common XSS payloads for renderer escaping assertions. */
55
export const XSS_SCRIPT = '<script>alert(1)</script>';
66
export const XSS_IMG = '<img onerror=alert(1)>';
77

8-
/**
9-
* Render a tool-use card via the registry and return the HTML string.
10-
*/
11-
export function mountToolUse(tool) {
12-
return renderToolUse(tool);
13-
}
14-
15-
/**
16-
* Render a tool-result card via the registry and return the HTML string.
17-
*/
18-
export function mountToolResult(parsed) {
19-
return renderToolResult(parsed);
20-
}
21-
228
/**
239
* Assert raw HTML fragments are escaped (not present verbatim).
2410
*/
@@ -34,3 +20,33 @@ export function expectNoRawHtml(html, rawFragments) {
3420
export function expectEscaped(html, escapedFragment) {
3521
expect(html).toContain(escapedFragment);
3622
}
23+
24+
/**
25+
* Shared behavioral tests for summary-only tool_result renderers (Edited/Plan/Wrote).
26+
*/
27+
export function describeSummaryOnlyResult(
28+
render,
29+
{ suiteName, resultType, label, samplePath },
30+
) {
31+
describe(suiteName, () => {
32+
it(`renders ${label.toLowerCase()} file path in summary`, () => {
33+
const html = render({ result_type: resultType, file_path: samplePath });
34+
expect(html).toContain(`${label}: ${samplePath}`);
35+
expect(html).toContain('tool-result');
36+
});
37+
38+
it('handles missing file path', () => {
39+
const html = render({ result_type: resultType });
40+
expect(html).toContain(`${label}:`);
41+
expect(html).not.toContain('undefined');
42+
});
43+
44+
it('escapes HTML in file path via registry', () => {
45+
const html = renderToolResult({
46+
result_type: resultType,
47+
file_path: XSS_SCRIPT,
48+
});
49+
expectNoRawHtml(html, [XSS_SCRIPT]);
50+
});
51+
});
52+
}

static/js/render/tool_result/bash.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderBashResult } from './bash.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_IMG } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_IMG } from '../test_helpers.js';
45

56
describe('renderBashResult', () => {
67
it('renders success status with stdout', () => {
@@ -33,7 +34,7 @@ describe('renderBashResult', () => {
3334
});
3435

3536
it('escapes HTML in stdout and stderr', () => {
36-
const html = mountToolResult({
37+
const html = renderToolResult({
3738
result_type: 'bash',
3839
exit_code: 0,
3940
stdout: XSS_IMG,

static/js/render/tool_result/fallback.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderToolResultFallback } from './fallback.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
45

56
describe('renderToolResultFallback', () => {
67
it('renders unknown result type and JSON payload', () => {
@@ -22,7 +23,7 @@ describe('renderToolResultFallback', () => {
2223
});
2324

2425
it('escapes HTML in JSON fallback body', () => {
25-
const html = mountToolResult({
26+
const html = renderToolResult({
2627
result_type: 'mystery',
2728
content: XSS_SCRIPT,
2829
});
Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
1-
import { describe, expect, it } from 'vitest';
21
import { renderFileEditResult } from './file_edit.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
2+
import { describeSummaryOnlyResult } from '../test_helpers.js';
43

5-
describe('renderFileEditResult', () => {
6-
it('renders edited file path in summary', () => {
7-
const html = renderFileEditResult({
8-
result_type: 'file_edit',
9-
file_path: 'src/app.js',
10-
});
11-
expect(html).toContain('Edited: src/app.js');
12-
expect(html).toContain('tool-result');
13-
});
14-
15-
it('handles missing file path', () => {
16-
const html = renderFileEditResult({ result_type: 'file_edit' });
17-
expect(html).toContain('Edited:');
18-
expect(html).not.toContain('undefined');
19-
});
20-
21-
it('escapes HTML in file path via registry', () => {
22-
const html = mountToolResult({
23-
result_type: 'file_edit',
24-
file_path: XSS_SCRIPT,
25-
});
26-
expectNoRawHtml(html, [XSS_SCRIPT]);
27-
});
4+
describeSummaryOnlyResult(renderFileEditResult, {
5+
suiteName: 'renderFileEditResult',
6+
resultType: 'file_edit',
7+
label: 'Edited',
8+
samplePath: 'src/app.js',
289
});

static/js/render/tool_result/file_write.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderFileWriteResult } from './file_write.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
45

56
describe('renderFileWriteResult', () => {
67
it('renders written file path in summary', () => {
@@ -19,7 +20,7 @@ describe('renderFileWriteResult', () => {
1920
});
2021

2122
it('escapes HTML in file path via registry', () => {
22-
const html = mountToolResult({
23+
const html = renderToolResult({
2324
result_type: 'file_write',
2425
file_path: XSS_SCRIPT,
2526
});
Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,9 @@
1-
import { describe, expect, it } from 'vitest';
21
import { renderPlanResult } from './plan.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
2+
import { describeSummaryOnlyResult } from '../test_helpers.js';
43

5-
describe('renderPlanResult', () => {
6-
it('renders plan file path in summary', () => {
7-
const html = renderPlanResult({
8-
result_type: 'plan',
9-
file_path: '.cursor/plans/sprint.md',
10-
});
11-
expect(html).toContain('Plan: .cursor/plans/sprint.md');
12-
expect(html).toContain('tool-result');
13-
});
14-
15-
it('handles missing file path', () => {
16-
const html = renderPlanResult({ result_type: 'plan' });
17-
expect(html).toContain('Plan:');
18-
expect(html).not.toContain('undefined');
19-
});
20-
21-
it('escapes HTML in file path via registry', () => {
22-
const html = mountToolResult({
23-
result_type: 'plan',
24-
file_path: XSS_SCRIPT,
25-
});
26-
expectNoRawHtml(html, [XSS_SCRIPT]);
27-
});
4+
describeSummaryOnlyResult(renderPlanResult, {
5+
suiteName: 'renderPlanResult',
6+
resultType: 'plan',
7+
label: 'Plan',
8+
samplePath: '.cursor/plans/sprint.md',
289
});

static/js/render/tool_result/task.test.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderTaskResult } from './task.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
45

56
describe('renderTaskResult', () => {
67
it('renders completed task with duration and token stats', () => {
8+
const totalTokens = 1500;
79
const html = renderTaskResult({
810
result_type: 'task',
911
status: 'completed',
1012
total_duration_ms: 2500,
11-
total_tokens: 1500,
13+
total_tokens: totalTokens,
1214
total_tool_use_count: 3,
1315
});
1416
expect(html).toContain('Task completed');
1517
expect(html).toContain('2.5s');
16-
expect(html).toContain('1,500 tokens');
18+
expect(html).toContain(`${totalTokens.toLocaleString()} tokens`);
1719
expect(html).toContain('3 tool calls');
1820
expect(html).toContain('tool-result');
1921
});
@@ -37,7 +39,7 @@ describe('renderTaskResult', () => {
3739
});
3840

3941
it('escapes HTML in description via registry', () => {
40-
const html = mountToolResult({
42+
const html = renderToolResult({
4143
result_type: 'task',
4244
description: XSS_SCRIPT,
4345
});

static/js/render/tool_result/todo_write.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderTodoWriteResult } from './todo_write.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
45

56
describe('renderTodoWriteResult', () => {
67
it('renders todo items with emoji status icons', () => {
@@ -28,7 +29,7 @@ describe('renderTodoWriteResult', () => {
2829
});
2930

3031
it('escapes HTML in todo content', () => {
31-
const html = mountToolResult({
32+
const html = renderToolResult({
3233
result_type: 'todo_write',
3334
todos: [{ status: 'pending', content: XSS_SCRIPT }],
3435
});

static/js/render/tool_result/user_input.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from 'vitest';
22
import { renderUserInputResult } from './user_input.js';
3-
import { mountToolResult, expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
3+
import { renderToolResult } from '../registry.js';
4+
import { expectNoRawHtml, XSS_SCRIPT } from '../test_helpers.js';
45

56
describe('renderUserInputResult', () => {
67
it('renders questions and answers', () => {
@@ -25,7 +26,7 @@ describe('renderUserInputResult', () => {
2526
});
2627

2728
it('escapes HTML in questions and answers', () => {
28-
const html = mountToolResult({
29+
const html = renderToolResult({
2930
result_type: 'user_input',
3031
questions: [{ question: XSS_SCRIPT }],
3132
answers: { key: '<bad>answer</bad>' },

0 commit comments

Comments
 (0)