@eeacms/volto-eea-chatbot 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -752
- package/package.json +1 -1
- package/razzle.extend.js +8 -4
- package/src/ChatBlock/ChatBlockView.jsx +26 -2
- package/src/ChatBlock/chat/AIMessage.tsx +5 -1
- package/src/ChatBlock/chat/ChatWindow.tsx +12 -3
- package/src/ChatBlock/components/AutoResizeTextarea.jsx +2 -1
- package/src/ChatBlock/components/QualityCheckToggle.jsx +1 -1
- package/src/ChatBlock/hooks/useChatController.ts +10 -2
- package/src/ChatBlock/index.js +1 -1
- package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +8 -0
- package/src/ChatBlock/services/streamingService.ts +30 -26
- package/src/ChatBlock/style.less +3 -1
- package/src/ChatBlock/tests/ChatMessage.test.jsx +75 -0
- package/src/ChatBlock/tests/ClaimModal.test.jsx +136 -0
- package/src/ChatBlock/tests/ClaimSegments.test.jsx +206 -0
- package/src/ChatBlock/tests/CustomToolRenderer.test.jsx +241 -0
- package/src/ChatBlock/tests/FetchToolRenderer.test.jsx +161 -0
- package/src/ChatBlock/tests/ImageToolRenderer.test.jsx +178 -0
- package/src/ChatBlock/tests/MessageTextRenderer.test.jsx +227 -0
- package/src/ChatBlock/tests/MultiToolRenderer.test.jsx +134 -0
- package/src/ChatBlock/tests/ReasoningRenderer.test.jsx +163 -0
- package/src/ChatBlock/tests/RenderClaimView.test.jsx +191 -0
- package/src/ChatBlock/tests/RendererComponent.test.jsx +139 -0
- package/src/ChatBlock/tests/SearchToolRenderer.test.jsx +295 -0
- package/src/ChatBlock/tests/SourceChip.test.jsx +108 -0
- package/src/ChatBlock/tests/UserActionsToolbar.test.jsx +135 -0
- package/src/ChatBlock/tests/UserMessage.test.jsx +83 -0
- package/src/ChatBlock/tests/WebResultIcon.test.jsx +61 -0
- package/src/ChatBlock/tests/citations.test.js +114 -0
- package/src/ChatBlock/tests/messageProcessor.test.jsx +285 -1
- package/src/ChatBlock/tests/packetUtils.test.js +158 -0
- package/src/ChatBlock/tests/streamingService.test.js +467 -0
- package/src/ChatBlock/tests/useChatController.test.jsx +268 -0
- package/src/ChatBlock/tests/useChatStreaming.test.jsx +163 -0
- package/src/ChatBlock/tests/useMarked.test.jsx +107 -0
- package/src/ChatBlock/tests/useQualityMarkers.test.jsx +150 -0
- package/src/ChatBlock/tests/useScrollonStream.test.jsx +121 -0
- package/src/ChatBlock/tests/utils.test.jsx +241 -0
- package/src/ChatBlock/tests/withOnyxData.test.jsx +81 -0
- package/src/ChatBlock/utils/citations.ts +1 -1
- package/src/halloumi/generative.js +1 -0
- package/src/halloumi/generative.test.js +278 -0
- package/src/halloumi/middleware.test.js +69 -0
- package/src/index.js +1 -0
- package/src/middleware.js +21 -13
- package/src/middleware.test.js +221 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import renderer from 'react-test-renderer';
|
|
2
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
3
|
+
import { ClaimSegments } from '../components/markdown/ClaimSegments';
|
|
4
|
+
|
|
5
|
+
// Mock semantic-ui-react
|
|
6
|
+
jest.mock('semantic-ui-react', () => ({
|
|
7
|
+
Tab: ({ panes, activeIndex }) => (
|
|
8
|
+
<div data-testid="tab">
|
|
9
|
+
<div data-testid="tab-menu">
|
|
10
|
+
{panes.map((pane, i) => (
|
|
11
|
+
<div
|
|
12
|
+
key={i}
|
|
13
|
+
data-testid={`menu-item-${i}`}
|
|
14
|
+
className={pane.menuItem.className}
|
|
15
|
+
onClick={pane.menuItem.onClick}
|
|
16
|
+
onKeydown={() => {}}
|
|
17
|
+
role="tab"
|
|
18
|
+
tabIndex={i === activeIndex ? 0 : -1}
|
|
19
|
+
aria-selected={i === activeIndex}
|
|
20
|
+
aria-controls={`tab-pane-${i}`}
|
|
21
|
+
>
|
|
22
|
+
{pane.menuItem.content}
|
|
23
|
+
</div>
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
<div data-testid="tab-content">{panes[activeIndex]?.render()}</div>
|
|
27
|
+
</div>
|
|
28
|
+
),
|
|
29
|
+
TabPane: ({ children }) => <div data-testid="tab-pane">{children}</div>,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Mock RenderClaimView
|
|
33
|
+
jest.mock('../components/markdown/RenderClaimView', () => ({
|
|
34
|
+
RenderClaimView: () => (
|
|
35
|
+
<div data-testid="render-claim-view">RenderClaimView</div>
|
|
36
|
+
),
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
describe('ClaimSegments', () => {
|
|
40
|
+
const defaultProps = {
|
|
41
|
+
segmentIds: [1, 2],
|
|
42
|
+
segments: {
|
|
43
|
+
1: { id: 1, startOffset: 0, endOffset: 10 },
|
|
44
|
+
2: { id: 2, startOffset: 15, endOffset: 25 },
|
|
45
|
+
},
|
|
46
|
+
citedSources: [
|
|
47
|
+
{
|
|
48
|
+
id: 'source1',
|
|
49
|
+
semantic_identifier: 'Source Document 1',
|
|
50
|
+
link: 'https://example.com/1',
|
|
51
|
+
source_type: 'web',
|
|
52
|
+
halloumiContext: 'This is the context text for source 1.',
|
|
53
|
+
index: 1,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
it('renders with basic props', () => {
|
|
59
|
+
const component = renderer.create(<ClaimSegments {...defaultProps} />);
|
|
60
|
+
const json = component.toJSON();
|
|
61
|
+
expect(json).toMatchSnapshot();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('renders with multiple sources', () => {
|
|
65
|
+
const props = {
|
|
66
|
+
...defaultProps,
|
|
67
|
+
citedSources: [
|
|
68
|
+
{
|
|
69
|
+
id: 'source1',
|
|
70
|
+
semantic_identifier: 'Source 1',
|
|
71
|
+
link: 'https://example.com/1',
|
|
72
|
+
source_type: 'web',
|
|
73
|
+
halloumiContext: 'Context for source 1.',
|
|
74
|
+
index: 1,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'source2',
|
|
78
|
+
semantic_identifier: 'Source 2',
|
|
79
|
+
link: 'https://example.com/2',
|
|
80
|
+
source_type: 'file',
|
|
81
|
+
halloumiContext: 'Context for source 2.',
|
|
82
|
+
index: 2,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
segments: {
|
|
86
|
+
1: { id: 1, startOffset: 0, endOffset: 10 },
|
|
87
|
+
2: { id: 2, startOffset: 22, endOffset: 32 },
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
92
|
+
const json = component.toJSON();
|
|
93
|
+
expect(json).toMatchSnapshot();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('handles empty segmentIds', () => {
|
|
97
|
+
const props = {
|
|
98
|
+
...defaultProps,
|
|
99
|
+
segmentIds: [],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
103
|
+
const json = component.toJSON();
|
|
104
|
+
expect(json).toMatchSnapshot();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('handles null segmentIds', () => {
|
|
108
|
+
const props = {
|
|
109
|
+
...defaultProps,
|
|
110
|
+
segmentIds: null,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
114
|
+
const json = component.toJSON();
|
|
115
|
+
expect(json).toMatchSnapshot();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('handles missing segments in segments object', () => {
|
|
119
|
+
// Suppress console.warn for this test
|
|
120
|
+
const originalWarn = console.warn;
|
|
121
|
+
console.warn = jest.fn();
|
|
122
|
+
|
|
123
|
+
const props = {
|
|
124
|
+
...defaultProps,
|
|
125
|
+
segmentIds: [1, 999], // 999 doesn't exist
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
129
|
+
const json = component.toJSON();
|
|
130
|
+
expect(json).toMatchSnapshot();
|
|
131
|
+
expect(console.warn).toHaveBeenCalled();
|
|
132
|
+
|
|
133
|
+
console.warn = originalWarn;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('renders source without link', () => {
|
|
137
|
+
const props = {
|
|
138
|
+
...defaultProps,
|
|
139
|
+
citedSources: [
|
|
140
|
+
{
|
|
141
|
+
id: 'source1',
|
|
142
|
+
semantic_identifier: 'Source 1',
|
|
143
|
+
link: null,
|
|
144
|
+
source_type: 'file',
|
|
145
|
+
halloumiContext: 'Context for source 1.',
|
|
146
|
+
index: 1,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
152
|
+
const json = component.toJSON();
|
|
153
|
+
expect(json).toMatchSnapshot();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('renders with file source type icon', () => {
|
|
157
|
+
const props = {
|
|
158
|
+
...defaultProps,
|
|
159
|
+
citedSources: [
|
|
160
|
+
{
|
|
161
|
+
id: 'source1',
|
|
162
|
+
semantic_identifier: 'Internal Document',
|
|
163
|
+
link: 'https://example.com/1',
|
|
164
|
+
source_type: 'file',
|
|
165
|
+
halloumiContext: 'Context text.',
|
|
166
|
+
index: 1,
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
172
|
+
const json = component.toJSON();
|
|
173
|
+
expect(json).toMatchSnapshot();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('filters out sources without matching snippets', () => {
|
|
177
|
+
const props = {
|
|
178
|
+
segmentIds: [1],
|
|
179
|
+
segments: {
|
|
180
|
+
1: { id: 1, startOffset: 0, endOffset: 10 },
|
|
181
|
+
},
|
|
182
|
+
citedSources: [
|
|
183
|
+
{
|
|
184
|
+
id: 'source1',
|
|
185
|
+
semantic_identifier: 'Source 1',
|
|
186
|
+
link: null,
|
|
187
|
+
source_type: 'web',
|
|
188
|
+
halloumiContext: 'Short context.',
|
|
189
|
+
index: 1,
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'source2',
|
|
193
|
+
semantic_identifier: 'Source 2 (no snippets)',
|
|
194
|
+
link: null,
|
|
195
|
+
source_type: 'web',
|
|
196
|
+
halloumiContext: 'This source has no matching segments.',
|
|
197
|
+
index: 2,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const component = renderer.create(<ClaimSegments {...props} />);
|
|
203
|
+
const json = component.toJSON();
|
|
204
|
+
expect(json).toMatchSnapshot();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { CustomToolRenderer } from '../packets/renderers/CustomToolRenderer';
|
|
5
|
+
import { PacketType } from '../types/streamingModels';
|
|
6
|
+
|
|
7
|
+
describe('CustomToolRenderer', () => {
|
|
8
|
+
const mockChildRenderer = (result) => (
|
|
9
|
+
<div data-testid="renderer-result">
|
|
10
|
+
<div data-testid="status">{result.status}</div>
|
|
11
|
+
<div data-testid="content">{result.content}</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
it('renders running state when not complete', () => {
|
|
16
|
+
const props = {
|
|
17
|
+
packets: [
|
|
18
|
+
{
|
|
19
|
+
ind: 1,
|
|
20
|
+
obj: {
|
|
21
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
22
|
+
tool_name: 'MyTool',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
onComplete: jest.fn(),
|
|
27
|
+
children: mockChildRenderer,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
31
|
+
const json = component.toJSON();
|
|
32
|
+
expect(json).toMatchSnapshot();
|
|
33
|
+
expect(props.onComplete).not.toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('renders complete state', () => {
|
|
37
|
+
const props = {
|
|
38
|
+
packets: [
|
|
39
|
+
{
|
|
40
|
+
ind: 1,
|
|
41
|
+
obj: {
|
|
42
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
43
|
+
tool_name: 'Calculator',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
ind: 1,
|
|
48
|
+
obj: {
|
|
49
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
50
|
+
tool_name: 'Calculator',
|
|
51
|
+
response_type: 'calculation_result',
|
|
52
|
+
data: { result: 42 },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
56
|
+
],
|
|
57
|
+
onComplete: jest.fn(),
|
|
58
|
+
children: mockChildRenderer,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
62
|
+
const json = component.toJSON();
|
|
63
|
+
expect(json).toMatchSnapshot();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('renders with multiple deltas', () => {
|
|
67
|
+
const props = {
|
|
68
|
+
packets: [
|
|
69
|
+
{
|
|
70
|
+
ind: 1,
|
|
71
|
+
obj: {
|
|
72
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
73
|
+
tool_name: 'MultiStepTool',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
ind: 1,
|
|
78
|
+
obj: {
|
|
79
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
80
|
+
tool_name: 'MultiStepTool',
|
|
81
|
+
response_type: 'step1',
|
|
82
|
+
data: { step: 1, status: 'processing' },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
ind: 1,
|
|
87
|
+
obj: {
|
|
88
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
89
|
+
tool_name: 'MultiStepTool',
|
|
90
|
+
response_type: 'step2',
|
|
91
|
+
data: { step: 2, status: 'complete' },
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
95
|
+
],
|
|
96
|
+
onComplete: jest.fn(),
|
|
97
|
+
children: mockChildRenderer,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
101
|
+
const json = component.toJSON();
|
|
102
|
+
expect(json).toMatchSnapshot();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('handles delta without response_type', () => {
|
|
106
|
+
const props = {
|
|
107
|
+
packets: [
|
|
108
|
+
{
|
|
109
|
+
ind: 1,
|
|
110
|
+
obj: {
|
|
111
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
112
|
+
tool_name: 'SimpleTool',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
ind: 1,
|
|
117
|
+
obj: {
|
|
118
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
119
|
+
tool_name: 'SimpleTool',
|
|
120
|
+
data: { result: 'done' },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
124
|
+
],
|
|
125
|
+
onComplete: jest.fn(),
|
|
126
|
+
children: mockChildRenderer,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
130
|
+
const json = component.toJSON();
|
|
131
|
+
expect(json).toMatchSnapshot();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('handles delta without data', () => {
|
|
135
|
+
const props = {
|
|
136
|
+
packets: [
|
|
137
|
+
{
|
|
138
|
+
ind: 1,
|
|
139
|
+
obj: {
|
|
140
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
141
|
+
tool_name: 'NoDataTool',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
ind: 1,
|
|
146
|
+
obj: {
|
|
147
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
148
|
+
tool_name: 'NoDataTool',
|
|
149
|
+
response_type: 'status',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
153
|
+
],
|
|
154
|
+
onComplete: jest.fn(),
|
|
155
|
+
children: mockChildRenderer,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
159
|
+
const json = component.toJSON();
|
|
160
|
+
expect(json).toMatchSnapshot();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('uses default tool name when not provided', () => {
|
|
164
|
+
const props = {
|
|
165
|
+
packets: [{ ind: 1, obj: { type: PacketType.SECTION_END } }],
|
|
166
|
+
onComplete: jest.fn(),
|
|
167
|
+
children: mockChildRenderer,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
171
|
+
const json = component.toJSON();
|
|
172
|
+
expect(json).toMatchSnapshot();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('handles empty packets array', () => {
|
|
176
|
+
const props = {
|
|
177
|
+
packets: [],
|
|
178
|
+
onComplete: jest.fn(),
|
|
179
|
+
children: mockChildRenderer,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
183
|
+
const json = component.toJSON();
|
|
184
|
+
expect(json).toMatchSnapshot();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('calls onComplete when section end is received', () => {
|
|
188
|
+
const onComplete = jest.fn();
|
|
189
|
+
const props = {
|
|
190
|
+
packets: [
|
|
191
|
+
{
|
|
192
|
+
ind: 1,
|
|
193
|
+
obj: { type: PacketType.CUSTOM_TOOL_START, tool_name: 'Test' },
|
|
194
|
+
},
|
|
195
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
196
|
+
],
|
|
197
|
+
onComplete,
|
|
198
|
+
children: mockChildRenderer,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
act(() => {
|
|
202
|
+
renderer.create(<CustomToolRenderer {...props} />);
|
|
203
|
+
});
|
|
204
|
+
expect(onComplete).toHaveBeenCalled();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('renders complex nested data correctly', () => {
|
|
208
|
+
const props = {
|
|
209
|
+
packets: [
|
|
210
|
+
{
|
|
211
|
+
ind: 1,
|
|
212
|
+
obj: {
|
|
213
|
+
type: PacketType.CUSTOM_TOOL_START,
|
|
214
|
+
tool_name: 'DataTool',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
ind: 1,
|
|
219
|
+
obj: {
|
|
220
|
+
type: PacketType.CUSTOM_TOOL_DELTA,
|
|
221
|
+
tool_name: 'DataTool',
|
|
222
|
+
response_type: 'complex',
|
|
223
|
+
data: {
|
|
224
|
+
nested: {
|
|
225
|
+
array: [1, 2, 3],
|
|
226
|
+
object: { key: 'value' },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
232
|
+
],
|
|
233
|
+
onComplete: jest.fn(),
|
|
234
|
+
children: mockChildRenderer,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const component = renderer.create(<CustomToolRenderer {...props} />);
|
|
238
|
+
const json = component.toJSON();
|
|
239
|
+
expect(json).toMatchSnapshot();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer, { act } from 'react-test-renderer';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { FetchToolRenderer } from '../packets/renderers/FetchToolRenderer';
|
|
5
|
+
import { PacketType } from '../types/streamingModels';
|
|
6
|
+
|
|
7
|
+
describe('FetchToolRenderer', () => {
|
|
8
|
+
const mockChildRenderer = (result) => (
|
|
9
|
+
<div data-testid="renderer-result">
|
|
10
|
+
<div data-testid="status">{result.status}</div>
|
|
11
|
+
<div data-testid="content">{result.content}</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
it('renders fetching state when not complete', () => {
|
|
16
|
+
const props = {
|
|
17
|
+
packets: [
|
|
18
|
+
{
|
|
19
|
+
ind: 1,
|
|
20
|
+
obj: {
|
|
21
|
+
type: PacketType.FETCH_TOOL_START,
|
|
22
|
+
queries: ['search query'],
|
|
23
|
+
documents: null,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
onComplete: jest.fn(),
|
|
28
|
+
children: mockChildRenderer,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
32
|
+
const json = component.toJSON();
|
|
33
|
+
expect(json).toMatchSnapshot();
|
|
34
|
+
expect(props.onComplete).not.toHaveBeenCalled();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders complete state', () => {
|
|
38
|
+
const props = {
|
|
39
|
+
packets: [
|
|
40
|
+
{
|
|
41
|
+
ind: 1,
|
|
42
|
+
obj: {
|
|
43
|
+
type: PacketType.FETCH_TOOL_START,
|
|
44
|
+
queries: ['search query'],
|
|
45
|
+
documents: [
|
|
46
|
+
{
|
|
47
|
+
document_id: 'doc1',
|
|
48
|
+
semantic_identifier: 'Document 1',
|
|
49
|
+
link: 'https://example.com/1',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
55
|
+
],
|
|
56
|
+
onComplete: jest.fn(),
|
|
57
|
+
children: mockChildRenderer,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
61
|
+
const json = component.toJSON();
|
|
62
|
+
expect(json).toMatchSnapshot();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('renders with multiple queries', () => {
|
|
66
|
+
const props = {
|
|
67
|
+
packets: [
|
|
68
|
+
{
|
|
69
|
+
ind: 1,
|
|
70
|
+
obj: {
|
|
71
|
+
type: PacketType.FETCH_TOOL_START,
|
|
72
|
+
queries: ['first query', 'second query', 'third query'],
|
|
73
|
+
documents: null,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
onComplete: jest.fn(),
|
|
78
|
+
children: mockChildRenderer,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
82
|
+
const json = component.toJSON();
|
|
83
|
+
expect(json).toMatchSnapshot();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('renders with multiple documents', () => {
|
|
87
|
+
const props = {
|
|
88
|
+
packets: [
|
|
89
|
+
{
|
|
90
|
+
ind: 1,
|
|
91
|
+
obj: {
|
|
92
|
+
type: PacketType.FETCH_TOOL_START,
|
|
93
|
+
queries: ['query'],
|
|
94
|
+
documents: [
|
|
95
|
+
{ document_id: 'doc1' },
|
|
96
|
+
{ document_id: 'doc2' },
|
|
97
|
+
{ document_id: 'doc3' },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
102
|
+
],
|
|
103
|
+
onComplete: jest.fn(),
|
|
104
|
+
children: mockChildRenderer,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
108
|
+
const json = component.toJSON();
|
|
109
|
+
expect(json).toMatchSnapshot();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('handles empty queries and documents', () => {
|
|
113
|
+
const props = {
|
|
114
|
+
packets: [
|
|
115
|
+
{
|
|
116
|
+
ind: 1,
|
|
117
|
+
obj: {
|
|
118
|
+
type: PacketType.FETCH_TOOL_START,
|
|
119
|
+
queries: null,
|
|
120
|
+
documents: null,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
onComplete: jest.fn(),
|
|
125
|
+
children: mockChildRenderer,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
129
|
+
const json = component.toJSON();
|
|
130
|
+
expect(json).toMatchSnapshot();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('handles empty packets array', () => {
|
|
134
|
+
const props = {
|
|
135
|
+
packets: [],
|
|
136
|
+
onComplete: jest.fn(),
|
|
137
|
+
children: mockChildRenderer,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const component = renderer.create(<FetchToolRenderer {...props} />);
|
|
141
|
+
const json = component.toJSON();
|
|
142
|
+
expect(json).toMatchSnapshot();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('calls onComplete when section end is received', () => {
|
|
146
|
+
const onComplete = jest.fn();
|
|
147
|
+
const props = {
|
|
148
|
+
packets: [
|
|
149
|
+
{ ind: 1, obj: { type: PacketType.FETCH_TOOL_START, queries: [] } },
|
|
150
|
+
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
151
|
+
],
|
|
152
|
+
onComplete,
|
|
153
|
+
children: mockChildRenderer,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
act(() => {
|
|
157
|
+
renderer.create(<FetchToolRenderer {...props} />);
|
|
158
|
+
});
|
|
159
|
+
expect(onComplete).toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
});
|