@eeacms/volto-eea-chatbot 2.0.0 → 2.0.2
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/.eslintrc.js +6 -6
- package/CHANGELOG.md +12 -1
- package/jest-addon.config.js +1 -0
- package/package.json +1 -1
- package/src/ChatBlock/ChatBlockEdit.jsx +2 -1
- package/src/ChatBlock/chat/AIMessage.tsx +20 -16
- package/src/ChatBlock/chat/ChatMessage.tsx +1 -1
- package/src/ChatBlock/chat/ChatWindow.tsx +10 -11
- package/src/ChatBlock/chat/UserMessage.tsx +4 -4
- package/src/ChatBlock/components/AutoResizeTextarea.jsx +1 -1
- package/src/ChatBlock/components/ChatMessageFeedback.jsx +2 -2
- package/src/ChatBlock/components/EmptyState.jsx +1 -1
- package/src/ChatBlock/components/FeedbackModal.jsx +1 -1
- package/src/ChatBlock/components/HalloumiFeedback.jsx +2 -2
- package/src/ChatBlock/components/Source.jsx +2 -2
- package/src/ChatBlock/components/UserActionsToolbar.jsx +3 -3
- package/src/ChatBlock/components/WebResultIcon.tsx +2 -2
- package/src/ChatBlock/components/markdown/ClaimModal.jsx +3 -3
- package/src/ChatBlock/components/markdown/ClaimSegments.jsx +4 -4
- package/src/ChatBlock/components/markdown/{index.js → index.jsx} +1 -1
- package/src/ChatBlock/hooks/useChatController.ts +7 -4
- package/src/ChatBlock/hooks/useChatStreaming.ts +4 -4
- package/src/ChatBlock/hooks/useToolDisplayTiming.ts +1 -1
- package/src/ChatBlock/index.js +8 -0
- package/src/ChatBlock/packets/MultiToolRenderer.tsx +11 -12
- package/src/ChatBlock/packets/RendererComponent.tsx +6 -3
- package/src/ChatBlock/packets/renderers/CustomToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/FetchToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/ImageToolRenderer.tsx +3 -3
- package/src/ChatBlock/packets/renderers/MessageTextRenderer.tsx +8 -8
- package/src/ChatBlock/packets/renderers/ReasoningRenderer.tsx +5 -5
- package/src/ChatBlock/packets/renderers/SearchToolRenderer.tsx +10 -10
- package/src/ChatBlock/services/messageProcessor.ts +6 -3
- package/src/ChatBlock/services/packetUtils.ts +2 -2
- package/src/ChatBlock/services/streamingService.ts +8 -2
- package/src/ChatBlock/utils/citations.ts +1 -1
- package/src/halloumi/filtering.test.js +199 -1
- package/src/ChatBlock/tests/AIMessage.test.jsx +0 -95
- package/src/ChatBlock/tests/AutoResizeTextarea.test.jsx +0 -49
- package/src/ChatBlock/tests/BlinkingDot.test.jsx +0 -71
- package/src/ChatBlock/tests/ChatMessage.test.jsx +0 -75
- package/src/ChatBlock/tests/ChatMessageFeedback.test.jsx +0 -73
- package/src/ChatBlock/tests/Citation.test.jsx +0 -107
- package/src/ChatBlock/tests/ClaimModal.test.jsx +0 -136
- package/src/ChatBlock/tests/ClaimSegments.test.jsx +0 -206
- package/src/ChatBlock/tests/CustomToolRenderer.test.jsx +0 -241
- package/src/ChatBlock/tests/EmptyState.test.jsx +0 -137
- package/src/ChatBlock/tests/FeedbackModal.test.jsx +0 -138
- package/src/ChatBlock/tests/FetchToolRenderer.test.jsx +0 -161
- package/src/ChatBlock/tests/HalloumiFeedback.test.jsx +0 -94
- package/src/ChatBlock/tests/ImageToolRenderer.test.jsx +0 -178
- package/src/ChatBlock/tests/MessageTextRenderer.test.jsx +0 -227
- package/src/ChatBlock/tests/MultiToolRenderer.test.jsx +0 -134
- package/src/ChatBlock/tests/QualityCheckToggle.test.jsx +0 -105
- package/src/ChatBlock/tests/ReasoningRenderer.test.jsx +0 -163
- package/src/ChatBlock/tests/RelatedQuestions.test.jsx +0 -215
- package/src/ChatBlock/tests/RenderClaimView.test.jsx +0 -191
- package/src/ChatBlock/tests/RendererComponent.test.jsx +0 -139
- package/src/ChatBlock/tests/SearchToolRenderer.test.jsx +0 -295
- package/src/ChatBlock/tests/Source.test.jsx +0 -79
- package/src/ChatBlock/tests/SourceChip.test.jsx +0 -108
- package/src/ChatBlock/tests/Spinner.test.jsx +0 -18
- package/src/ChatBlock/tests/UserActionsToolbar.test.jsx +0 -135
- package/src/ChatBlock/tests/UserMessage.test.jsx +0 -83
- package/src/ChatBlock/tests/WebResultIcon.test.jsx +0 -61
- package/src/ChatBlock/tests/citations.test.js +0 -114
- package/src/ChatBlock/tests/index.test.js +0 -51
- package/src/ChatBlock/tests/messageProcessor.test.jsx +0 -438
- package/src/ChatBlock/tests/packetUtils.test.js +0 -158
- package/src/ChatBlock/tests/schema.test.js +0 -166
- package/src/ChatBlock/tests/streamingService.test.js +0 -467
- package/src/ChatBlock/tests/useChatController.test.jsx +0 -268
- package/src/ChatBlock/tests/useChatStreaming.test.jsx +0 -163
- package/src/ChatBlock/tests/useDeepCompareMemoize.test.js +0 -107
- package/src/ChatBlock/tests/useMarked.test.jsx +0 -107
- package/src/ChatBlock/tests/useQualityMarkers.test.jsx +0 -150
- package/src/ChatBlock/tests/useScrollonStream.test.jsx +0 -121
- package/src/ChatBlock/tests/useToolDisplayTiming.test.jsx +0 -151
- package/src/ChatBlock/tests/utils.test.jsx +0 -241
- package/src/ChatBlock/tests/withOnyxData.test.jsx +0 -81
- /package/src/ChatBlock/{schema.js → schema.jsx} +0 -0
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import renderer, { act } from 'react-test-renderer';
|
|
3
|
-
import '@testing-library/jest-dom';
|
|
4
|
-
import { SearchToolRenderer } from '../packets/renderers/SearchToolRenderer';
|
|
5
|
-
import { PacketType } from '../types/streamingModels';
|
|
6
|
-
|
|
7
|
-
describe('SearchToolRenderer', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.useFakeTimers();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
jest.useRealTimers();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const mockChildRenderer = (result) => (
|
|
17
|
-
<div data-testid="renderer-result">
|
|
18
|
-
<div data-testid="status">{result.status}</div>
|
|
19
|
-
<div data-testid="content">{result.content}</div>
|
|
20
|
-
</div>
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
it('renders searching state for internal documents', () => {
|
|
24
|
-
const props = {
|
|
25
|
-
packets: [
|
|
26
|
-
{
|
|
27
|
-
ind: 1,
|
|
28
|
-
obj: {
|
|
29
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
30
|
-
is_internet_search: false,
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
ind: 1,
|
|
35
|
-
obj: {
|
|
36
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
37
|
-
queries: ['test query'],
|
|
38
|
-
documents: null,
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
onComplete: jest.fn(),
|
|
43
|
-
animate: false,
|
|
44
|
-
children: mockChildRenderer,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const component = renderer.create(<SearchToolRenderer {...props} />);
|
|
48
|
-
const json = component.toJSON();
|
|
49
|
-
expect(json).toMatchSnapshot();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('renders searching state for internet search', () => {
|
|
53
|
-
const props = {
|
|
54
|
-
packets: [
|
|
55
|
-
{
|
|
56
|
-
ind: 1,
|
|
57
|
-
obj: {
|
|
58
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
59
|
-
is_internet_search: true,
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
ind: 1,
|
|
64
|
-
obj: {
|
|
65
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
66
|
-
queries: ['web query'],
|
|
67
|
-
documents: null,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
],
|
|
71
|
-
onComplete: jest.fn(),
|
|
72
|
-
animate: false,
|
|
73
|
-
children: mockChildRenderer,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const component = renderer.create(<SearchToolRenderer {...props} />);
|
|
77
|
-
const json = component.toJSON();
|
|
78
|
-
expect(json).toMatchSnapshot();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('renders with search results', () => {
|
|
82
|
-
const props = {
|
|
83
|
-
packets: [
|
|
84
|
-
{
|
|
85
|
-
ind: 1,
|
|
86
|
-
obj: {
|
|
87
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
88
|
-
is_internet_search: false,
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
ind: 1,
|
|
93
|
-
obj: {
|
|
94
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
95
|
-
queries: ['query'],
|
|
96
|
-
documents: [
|
|
97
|
-
{
|
|
98
|
-
document_id: 'doc1',
|
|
99
|
-
semantic_identifier: 'Document 1',
|
|
100
|
-
link: 'https://example.com/1',
|
|
101
|
-
source_type: 'file',
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
107
|
-
],
|
|
108
|
-
onComplete: jest.fn(),
|
|
109
|
-
animate: false,
|
|
110
|
-
children: mockChildRenderer,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
let component;
|
|
114
|
-
act(() => {
|
|
115
|
-
component = renderer.create(<SearchToolRenderer {...props} />);
|
|
116
|
-
});
|
|
117
|
-
const json = component.toJSON();
|
|
118
|
-
expect(json).toMatchSnapshot();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('renders empty state when no queries', () => {
|
|
122
|
-
const props = {
|
|
123
|
-
packets: [
|
|
124
|
-
{
|
|
125
|
-
ind: 1,
|
|
126
|
-
obj: {
|
|
127
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
128
|
-
is_internet_search: false,
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
onComplete: jest.fn(),
|
|
133
|
-
animate: false,
|
|
134
|
-
children: mockChildRenderer,
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const component = renderer.create(<SearchToolRenderer {...props} />);
|
|
138
|
-
const json = component.toJSON();
|
|
139
|
-
expect(json).toMatchSnapshot();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('removes duplicate queries', () => {
|
|
143
|
-
const props = {
|
|
144
|
-
packets: [
|
|
145
|
-
{
|
|
146
|
-
ind: 1,
|
|
147
|
-
obj: {
|
|
148
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
149
|
-
is_internet_search: false,
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
ind: 1,
|
|
154
|
-
obj: {
|
|
155
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
156
|
-
queries: ['duplicate', 'unique', 'duplicate'],
|
|
157
|
-
documents: null,
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
],
|
|
161
|
-
onComplete: jest.fn(),
|
|
162
|
-
animate: false,
|
|
163
|
-
children: mockChildRenderer,
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const component = renderer.create(<SearchToolRenderer {...props} />);
|
|
167
|
-
const json = component.toJSON();
|
|
168
|
-
expect(json).toMatchSnapshot();
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('removes duplicate documents by document_id', () => {
|
|
172
|
-
const props = {
|
|
173
|
-
packets: [
|
|
174
|
-
{
|
|
175
|
-
ind: 1,
|
|
176
|
-
obj: {
|
|
177
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
178
|
-
is_internet_search: false,
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
ind: 1,
|
|
183
|
-
obj: {
|
|
184
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
185
|
-
queries: ['query'],
|
|
186
|
-
documents: [
|
|
187
|
-
{ document_id: 'doc1', semantic_identifier: 'Doc 1' },
|
|
188
|
-
{ document_id: 'doc1', semantic_identifier: 'Doc 1 duplicate' },
|
|
189
|
-
{ document_id: 'doc2', semantic_identifier: 'Doc 2' },
|
|
190
|
-
],
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
194
|
-
],
|
|
195
|
-
onComplete: jest.fn(),
|
|
196
|
-
animate: false,
|
|
197
|
-
children: mockChildRenderer,
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
let component;
|
|
201
|
-
act(() => {
|
|
202
|
-
component = renderer.create(<SearchToolRenderer {...props} />);
|
|
203
|
-
});
|
|
204
|
-
const json = component.toJSON();
|
|
205
|
-
expect(json).toMatchSnapshot();
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('handles web results with favicon', () => {
|
|
209
|
-
const props = {
|
|
210
|
-
packets: [
|
|
211
|
-
{
|
|
212
|
-
ind: 1,
|
|
213
|
-
obj: {
|
|
214
|
-
type: PacketType.SEARCH_TOOL_START,
|
|
215
|
-
is_internet_search: true,
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
ind: 1,
|
|
220
|
-
obj: {
|
|
221
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
222
|
-
queries: ['web search'],
|
|
223
|
-
documents: [
|
|
224
|
-
{
|
|
225
|
-
document_id: 'web1',
|
|
226
|
-
semantic_identifier: 'Web Result',
|
|
227
|
-
link: 'https://example.com/page',
|
|
228
|
-
source_type: 'web',
|
|
229
|
-
},
|
|
230
|
-
],
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
234
|
-
],
|
|
235
|
-
onComplete: jest.fn(),
|
|
236
|
-
animate: false,
|
|
237
|
-
children: mockChildRenderer,
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
let component;
|
|
241
|
-
act(() => {
|
|
242
|
-
component = renderer.create(<SearchToolRenderer {...props} />);
|
|
243
|
-
});
|
|
244
|
-
const json = component.toJSON();
|
|
245
|
-
expect(json).toMatchSnapshot();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('handles empty packets array', () => {
|
|
249
|
-
const props = {
|
|
250
|
-
packets: [],
|
|
251
|
-
onComplete: jest.fn(),
|
|
252
|
-
animate: false,
|
|
253
|
-
children: mockChildRenderer,
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
const component = renderer.create(<SearchToolRenderer {...props} />);
|
|
257
|
-
const json = component.toJSON();
|
|
258
|
-
expect(json).toMatchSnapshot();
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it('calls onComplete after animation delay when animate is true', () => {
|
|
262
|
-
const onComplete = jest.fn();
|
|
263
|
-
const props = {
|
|
264
|
-
packets: [
|
|
265
|
-
{ ind: 1, obj: { type: PacketType.SEARCH_TOOL_START } },
|
|
266
|
-
{
|
|
267
|
-
ind: 1,
|
|
268
|
-
obj: {
|
|
269
|
-
type: PacketType.SEARCH_TOOL_DELTA,
|
|
270
|
-
queries: ['query'],
|
|
271
|
-
documents: [],
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
{ ind: 1, obj: { type: PacketType.SECTION_END } },
|
|
275
|
-
],
|
|
276
|
-
onComplete,
|
|
277
|
-
animate: true,
|
|
278
|
-
children: mockChildRenderer,
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
act(() => {
|
|
282
|
-
renderer.create(<SearchToolRenderer {...props} />);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// Initially not called
|
|
286
|
-
expect(onComplete).not.toHaveBeenCalled();
|
|
287
|
-
|
|
288
|
-
// Advance timers for the animation duration
|
|
289
|
-
act(() => {
|
|
290
|
-
jest.advanceTimersByTime(3000);
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
expect(onComplete).toHaveBeenCalled();
|
|
294
|
-
});
|
|
295
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { MemoryRouter } from 'react-router-dom';
|
|
2
|
-
import configureStore from 'redux-mock-store';
|
|
3
|
-
import renderer from 'react-test-renderer';
|
|
4
|
-
|
|
5
|
-
import '@testing-library/jest-dom';
|
|
6
|
-
import { Provider } from 'react-intl-redux';
|
|
7
|
-
import SourceDetails from '../components/Source';
|
|
8
|
-
|
|
9
|
-
const mockStore = configureStore();
|
|
10
|
-
|
|
11
|
-
jest.mock('@plone/volto/helpers/Loadable/Loadable', () => ({
|
|
12
|
-
injectLazyLibs: () => (Component) => (props) => (
|
|
13
|
-
<Component {...props} luxon={require('luxon')} />
|
|
14
|
-
),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
describe('SourceDetails', () => {
|
|
18
|
-
it('should render the component with link type', () => {
|
|
19
|
-
const store = mockStore({
|
|
20
|
-
userSession: { token: '1234' },
|
|
21
|
-
intl: {
|
|
22
|
-
locale: 'en',
|
|
23
|
-
messages: {},
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const props = {
|
|
28
|
-
index: '1',
|
|
29
|
-
source: {
|
|
30
|
-
blurb: 'Vestibulum purus quam scelerisque ut',
|
|
31
|
-
link: 'https://www.example.com',
|
|
32
|
-
source_type: 'web',
|
|
33
|
-
semantic_identifier: 'Nam at tortor in tellus',
|
|
34
|
-
updated_at: null,
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const component = renderer.create(
|
|
39
|
-
<Provider store={store}>
|
|
40
|
-
<MemoryRouter>
|
|
41
|
-
<SourceDetails {...props} />
|
|
42
|
-
</MemoryRouter>
|
|
43
|
-
</Provider>,
|
|
44
|
-
);
|
|
45
|
-
const json = component.toJSON();
|
|
46
|
-
expect(json).toMatchSnapshot();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should render the component with doc type', () => {
|
|
50
|
-
const store = mockStore({
|
|
51
|
-
userSession: { token: '1234' },
|
|
52
|
-
intl: {
|
|
53
|
-
locale: 'en',
|
|
54
|
-
messages: {},
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const props = {
|
|
59
|
-
index: '2',
|
|
60
|
-
source: {
|
|
61
|
-
blurb: 'Vestibulum purus quam scelerisque ut',
|
|
62
|
-
link: 'https://www.example.com',
|
|
63
|
-
source_type: 'file',
|
|
64
|
-
semantic_identifier: 'Nam at tortor in tellus',
|
|
65
|
-
updated_at: null,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const component = renderer.create(
|
|
70
|
-
<Provider store={store}>
|
|
71
|
-
<MemoryRouter>
|
|
72
|
-
<SourceDetails {...props} />
|
|
73
|
-
</MemoryRouter>
|
|
74
|
-
</Provider>,
|
|
75
|
-
);
|
|
76
|
-
const json = component.toJSON();
|
|
77
|
-
expect(json).toMatchSnapshot();
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import renderer, { act } from 'react-test-renderer';
|
|
3
|
-
import { render, fireEvent, screen } from '@testing-library/react';
|
|
4
|
-
import '@testing-library/jest-dom';
|
|
5
|
-
import { SourceChip } from '../components/SourceChip';
|
|
6
|
-
|
|
7
|
-
describe('SourceChip', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.useFakeTimers();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
jest.useRealTimers();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('renders with title only', () => {
|
|
17
|
-
const component = renderer.create(<SourceChip title="Test Source" />);
|
|
18
|
-
const json = component.toJSON();
|
|
19
|
-
expect(json).toMatchSnapshot();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('renders with icon', () => {
|
|
23
|
-
const MockIcon = () => <span data-testid="mock-icon">Icon</span>;
|
|
24
|
-
const component = renderer.create(
|
|
25
|
-
<SourceChip title="Test Source" icon={<MockIcon />} />,
|
|
26
|
-
);
|
|
27
|
-
const json = component.toJSON();
|
|
28
|
-
expect(json).toMatchSnapshot();
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('renders with remove button when onRemove is provided', () => {
|
|
32
|
-
const onRemove = jest.fn();
|
|
33
|
-
const component = renderer.create(
|
|
34
|
-
<SourceChip title="Test Source" onRemove={onRemove} />,
|
|
35
|
-
);
|
|
36
|
-
const json = component.toJSON();
|
|
37
|
-
expect(json).toMatchSnapshot();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('calls onClick when clicked', () => {
|
|
41
|
-
const onClick = jest.fn();
|
|
42
|
-
render(<SourceChip title="Test Source" onClick={onClick} />);
|
|
43
|
-
|
|
44
|
-
fireEvent.click(screen.getByRole('button'));
|
|
45
|
-
expect(onClick).toHaveBeenCalledTimes(1);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('calls onRemove when remove button is clicked', () => {
|
|
49
|
-
const onRemove = jest.fn();
|
|
50
|
-
const onClick = jest.fn();
|
|
51
|
-
render(
|
|
52
|
-
<SourceChip title="Test Source" onRemove={onRemove} onClick={onClick} />,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const removeButton = screen.getByRole('button', { name: /Remove/i });
|
|
56
|
-
fireEvent.click(removeButton);
|
|
57
|
-
|
|
58
|
-
expect(onRemove).toHaveBeenCalledTimes(1);
|
|
59
|
-
expect(onClick).not.toHaveBeenCalled(); // stopPropagation should prevent this
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('calls onRemove when Enter key is pressed on remove button', () => {
|
|
63
|
-
const onRemove = jest.fn();
|
|
64
|
-
render(<SourceChip title="Test Source" onRemove={onRemove} />);
|
|
65
|
-
|
|
66
|
-
const removeButton = screen.getByRole('button', { name: /Remove/i });
|
|
67
|
-
fireEvent.keyDown(removeButton, { key: 'Enter' });
|
|
68
|
-
|
|
69
|
-
expect(onRemove).toHaveBeenCalledTimes(1);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('does not call onRemove when other keys are pressed', () => {
|
|
73
|
-
const onRemove = jest.fn();
|
|
74
|
-
render(<SourceChip title="Test Source" onRemove={onRemove} />);
|
|
75
|
-
|
|
76
|
-
const removeButton = screen.getByRole('button', { name: /Remove/i });
|
|
77
|
-
fireEvent.keyDown(removeButton, { key: 'Escape' });
|
|
78
|
-
|
|
79
|
-
expect(onRemove).not.toHaveBeenCalled();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('applies animation class when includeAnimation is true', () => {
|
|
83
|
-
render(<SourceChip title="Test Source" includeAnimation={true} />);
|
|
84
|
-
|
|
85
|
-
const button = screen.getByRole('button');
|
|
86
|
-
expect(button).toHaveClass('animate-in');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('removes animation class after timeout', () => {
|
|
90
|
-
render(<SourceChip title="Test Source" includeAnimation={true} />);
|
|
91
|
-
|
|
92
|
-
const button = screen.getByRole('button');
|
|
93
|
-
expect(button).toHaveClass('animate-in');
|
|
94
|
-
|
|
95
|
-
act(() => {
|
|
96
|
-
jest.advanceTimersByTime(300);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(button).not.toHaveClass('animate-in');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('does not apply animation class when includeAnimation is false', () => {
|
|
103
|
-
render(<SourceChip title="Test Source" includeAnimation={false} />);
|
|
104
|
-
|
|
105
|
-
const button = screen.getByRole('button');
|
|
106
|
-
expect(button).not.toHaveClass('animate-in');
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import renderer from 'react-test-renderer';
|
|
3
|
-
import '@testing-library/jest-dom';
|
|
4
|
-
import Spinner from '../components/Spinner';
|
|
5
|
-
|
|
6
|
-
describe('Spinner', () => {
|
|
7
|
-
it('renders correctly', () => {
|
|
8
|
-
const component = renderer.create(<Spinner />);
|
|
9
|
-
expect(component.toJSON()).toMatchSnapshot();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('renders a div with spinner class', () => {
|
|
13
|
-
const component = renderer.create(<Spinner />);
|
|
14
|
-
const tree = component.toJSON();
|
|
15
|
-
expect(tree.type).toBe('div');
|
|
16
|
-
expect(tree.props.className).toBe('spinner');
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { MemoryRouter } from 'react-router-dom';
|
|
3
|
-
import configureStore from 'redux-mock-store';
|
|
4
|
-
import renderer from 'react-test-renderer';
|
|
5
|
-
import { render, fireEvent, screen } from '@testing-library/react';
|
|
6
|
-
import '@testing-library/jest-dom';
|
|
7
|
-
import { Provider } from 'react-intl-redux';
|
|
8
|
-
import UserActionsToolbar from '../components/UserActionsToolbar';
|
|
9
|
-
|
|
10
|
-
const mockStore = configureStore();
|
|
11
|
-
|
|
12
|
-
// Mock clipboard API
|
|
13
|
-
Object.assign(navigator, {
|
|
14
|
-
clipboard: {
|
|
15
|
-
writeText: jest.fn().mockResolvedValue(undefined),
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
describe('UserActionsToolbar', () => {
|
|
20
|
-
let store;
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
store = mockStore({
|
|
24
|
-
userSession: { token: '1234' },
|
|
25
|
-
intl: { locale: 'en', messages: {} },
|
|
26
|
-
});
|
|
27
|
-
jest.clearAllMocks();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const renderComponent = (props) =>
|
|
31
|
-
renderer.create(
|
|
32
|
-
<Provider store={store}>
|
|
33
|
-
<MemoryRouter>
|
|
34
|
-
<UserActionsToolbar {...props} />
|
|
35
|
-
</MemoryRouter>
|
|
36
|
-
</Provider>,
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const renderWithRTL = (props) =>
|
|
40
|
-
render(
|
|
41
|
-
<Provider store={store}>
|
|
42
|
-
<MemoryRouter>
|
|
43
|
-
<UserActionsToolbar {...props} />
|
|
44
|
-
</MemoryRouter>
|
|
45
|
-
</Provider>,
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
it('renders toolbar with copy button', () => {
|
|
49
|
-
const props = {
|
|
50
|
-
message: {
|
|
51
|
-
messageId: 1,
|
|
52
|
-
message: 'Test message content',
|
|
53
|
-
},
|
|
54
|
-
enableFeedback: false,
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const component = renderComponent(props);
|
|
58
|
-
const json = component.toJSON();
|
|
59
|
-
expect(json).toMatchSnapshot();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('renders toolbar with feedback when enabled', () => {
|
|
63
|
-
const props = {
|
|
64
|
-
message: {
|
|
65
|
-
messageId: 1,
|
|
66
|
-
message: 'Test message content',
|
|
67
|
-
},
|
|
68
|
-
enableFeedback: true,
|
|
69
|
-
feedbackReasons: ['Incorrect', 'Unhelpful'],
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const component = renderComponent(props);
|
|
73
|
-
const json = component.toJSON();
|
|
74
|
-
expect(json).toMatchSnapshot();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('renders with custom className', () => {
|
|
78
|
-
const props = {
|
|
79
|
-
message: {
|
|
80
|
-
messageId: 1,
|
|
81
|
-
message: 'Test message content',
|
|
82
|
-
},
|
|
83
|
-
className: 'custom-toolbar',
|
|
84
|
-
enableFeedback: false,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const component = renderComponent(props);
|
|
88
|
-
const json = component.toJSON();
|
|
89
|
-
expect(json).toMatchSnapshot();
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('copies message to clipboard when copy button is clicked', async () => {
|
|
93
|
-
const props = {
|
|
94
|
-
message: {
|
|
95
|
-
messageId: 1,
|
|
96
|
-
message: 'Test message to copy',
|
|
97
|
-
},
|
|
98
|
-
enableFeedback: false,
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
renderWithRTL(props);
|
|
102
|
-
|
|
103
|
-
const copyButton = screen.getByRole('button', { name: /copy/i });
|
|
104
|
-
fireEvent.click(copyButton);
|
|
105
|
-
|
|
106
|
-
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
|
107
|
-
'Test message to copy',
|
|
108
|
-
);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('handles empty message gracefully', () => {
|
|
112
|
-
const props = {
|
|
113
|
-
message: {
|
|
114
|
-
messageId: 1,
|
|
115
|
-
message: '',
|
|
116
|
-
},
|
|
117
|
-
enableFeedback: false,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const component = renderComponent(props);
|
|
121
|
-
const json = component.toJSON();
|
|
122
|
-
expect(json).toMatchSnapshot();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('handles undefined message gracefully', () => {
|
|
126
|
-
const props = {
|
|
127
|
-
message: null,
|
|
128
|
-
enableFeedback: false,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const component = renderComponent(props);
|
|
132
|
-
const json = component.toJSON();
|
|
133
|
-
expect(json).toMatchSnapshot();
|
|
134
|
-
});
|
|
135
|
-
});
|