@ai-sdk/react 0.0.51 → 0.0.53

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.
@@ -1,337 +0,0 @@
1
- import { formatStreamPart } from '@ai-sdk/ui-utils';
2
- import {
3
- mockFetchDataStream,
4
- mockFetchDataStreamWithGenerator,
5
- mockFetchError,
6
- } from '@ai-sdk/ui-utils/test';
7
- import '@testing-library/jest-dom/vitest';
8
- import { cleanup, findByText, render, screen } from '@testing-library/react';
9
- import userEvent from '@testing-library/user-event';
10
- import { useAssistant } from './use-assistant';
11
-
12
- describe('stream data stream', () => {
13
- const TestComponent = () => {
14
- const { status, messages, error, append } = useAssistant({
15
- api: '/api/assistant',
16
- });
17
-
18
- return (
19
- <div>
20
- <div data-testid="status">{status}</div>
21
- {error && <div data-testid="error">{error.toString()}</div>}
22
- {messages.map((m, idx) => (
23
- <div data-testid={`message-${idx}`} key={idx}>
24
- {m.role === 'user' ? 'User: ' : 'AI: '}
25
- {m.content}
26
- </div>
27
- ))}
28
-
29
- <button
30
- data-testid="do-append"
31
- onClick={() => {
32
- append({ role: 'user', content: 'hi' });
33
- }}
34
- />
35
- </div>
36
- );
37
- };
38
-
39
- beforeEach(() => {
40
- render(<TestComponent />);
41
- });
42
-
43
- afterEach(() => {
44
- vi.restoreAllMocks();
45
- cleanup();
46
- });
47
-
48
- it('should show streamed response', async () => {
49
- const { requestBody } = mockFetchDataStream({
50
- url: 'https://example.com/api/assistant',
51
- chunks: [
52
- formatStreamPart('assistant_control_data', {
53
- threadId: 't0',
54
- messageId: 'm0',
55
- }),
56
- formatStreamPart('assistant_message', {
57
- id: 'm0',
58
- role: 'assistant',
59
- content: [{ type: 'text', text: { value: '' } }],
60
- }),
61
- // text parts:
62
- '0:"Hello"\n',
63
- '0:","\n',
64
- '0:" world"\n',
65
- '0:"."\n',
66
- ],
67
- });
68
-
69
- await userEvent.click(screen.getByTestId('do-append'));
70
-
71
- await screen.findByTestId('message-0');
72
- expect(screen.getByTestId('message-0')).toHaveTextContent('User: hi');
73
-
74
- await screen.findByTestId('message-1');
75
- expect(screen.getByTestId('message-1')).toHaveTextContent(
76
- 'AI: Hello, world.',
77
- );
78
-
79
- // check that correct information was sent to the server:
80
- expect(await requestBody).toStrictEqual(
81
- JSON.stringify({
82
- threadId: null,
83
- message: 'hi',
84
- }),
85
- );
86
- });
87
-
88
- it('should show error response', async () => {
89
- mockFetchError({ statusCode: 500, errorMessage: 'Internal Error' });
90
-
91
- await userEvent.click(screen.getByTestId('do-append'));
92
-
93
- await screen.findByTestId('error');
94
- expect(screen.getByTestId('error')).toHaveTextContent(
95
- 'Error: Internal Error',
96
- );
97
- });
98
-
99
- describe('loading state', () => {
100
- it('should show loading state', async () => {
101
- let finishGeneration: ((value?: unknown) => void) | undefined;
102
- const finishGenerationPromise = new Promise(resolve => {
103
- finishGeneration = resolve;
104
- });
105
-
106
- mockFetchDataStreamWithGenerator({
107
- url: 'https://example.com/api/chat',
108
- chunkGenerator: (async function* generate() {
109
- const encoder = new TextEncoder();
110
-
111
- yield encoder.encode(
112
- formatStreamPart('assistant_control_data', {
113
- threadId: 't0',
114
- messageId: 'm1',
115
- }),
116
- );
117
-
118
- yield encoder.encode(
119
- formatStreamPart('assistant_message', {
120
- id: 'm1',
121
- role: 'assistant',
122
- content: [{ type: 'text', text: { value: '' } }],
123
- }),
124
- );
125
-
126
- yield encoder.encode('0:"Hello"\n');
127
-
128
- await finishGenerationPromise;
129
- })(),
130
- });
131
-
132
- await userEvent.click(screen.getByTestId('do-append'));
133
-
134
- await screen.findByTestId('status');
135
- expect(screen.getByTestId('status')).toHaveTextContent('in_progress');
136
-
137
- finishGeneration?.();
138
-
139
- await findByText(await screen.findByTestId('status'), 'awaiting_message');
140
- expect(screen.getByTestId('status')).toHaveTextContent(
141
- 'awaiting_message',
142
- );
143
- });
144
- });
145
- });
146
-
147
- describe('thread management', () => {
148
- const TestComponent = () => {
149
- const { status, messages, error, append, setThreadId, threadId } =
150
- useAssistant({
151
- api: '/api/assistant',
152
- });
153
-
154
- return (
155
- <div>
156
- <div data-testid="status">{status}</div>
157
- <div data-testid="thread-id">{threadId || 'undefined'}</div>
158
- {error && <div data-testid="error">{error.toString()}</div>}
159
- {messages.map((m, idx) => (
160
- <div data-testid={`message-${idx}`} key={idx}>
161
- {m.role === 'user' ? 'User: ' : 'AI: '}
162
- {m.content}
163
- </div>
164
- ))}
165
-
166
- <button
167
- data-testid="do-append"
168
- onClick={() => {
169
- append({ role: 'user', content: 'hi' });
170
- }}
171
- />
172
- <button
173
- data-testid="do-new-thread"
174
- onClick={() => {
175
- setThreadId(undefined);
176
- }}
177
- />
178
- <button
179
- data-testid="do-thread-3"
180
- onClick={() => {
181
- setThreadId('t3');
182
- }}
183
- />
184
- </div>
185
- );
186
- };
187
-
188
- beforeEach(() => {
189
- render(<TestComponent />);
190
- });
191
-
192
- afterEach(() => {
193
- vi.restoreAllMocks();
194
- cleanup();
195
- });
196
-
197
- it('create new thread', async () => {
198
- await screen.findByTestId('thread-id');
199
- expect(screen.getByTestId('thread-id')).toHaveTextContent('undefined');
200
- });
201
-
202
- it('should show streamed response', async () => {
203
- const { requestBody } = mockFetchDataStream({
204
- url: 'https://example.com/api/assistant',
205
- chunks: [
206
- formatStreamPart('assistant_control_data', {
207
- threadId: 't0',
208
- messageId: 'm0',
209
- }),
210
- formatStreamPart('assistant_message', {
211
- id: 'm0',
212
- role: 'assistant',
213
- content: [{ type: 'text', text: { value: '' } }],
214
- }),
215
- // text parts:
216
- '0:"Hello"\n',
217
- '0:","\n',
218
- '0:" world"\n',
219
- '0:"."\n',
220
- ],
221
- });
222
-
223
- await userEvent.click(screen.getByTestId('do-append'));
224
-
225
- await screen.findByTestId('message-0');
226
- expect(screen.getByTestId('message-0')).toHaveTextContent('User: hi');
227
-
228
- expect(screen.getByTestId('thread-id')).toHaveTextContent('t0');
229
-
230
- await screen.findByTestId('message-1');
231
- expect(screen.getByTestId('message-1')).toHaveTextContent(
232
- 'AI: Hello, world.',
233
- );
234
-
235
- // check that correct information was sent to the server:
236
- expect(await requestBody).toStrictEqual(
237
- JSON.stringify({
238
- threadId: null,
239
- message: 'hi',
240
- }),
241
- );
242
- });
243
-
244
- it('should switch to new thread on setting undefined threadId', async () => {
245
- await userEvent.click(screen.getByTestId('do-new-thread'));
246
-
247
- expect(screen.queryByTestId('message-0')).toBeNull();
248
- expect(screen.queryByTestId('message-1')).toBeNull();
249
-
250
- const { requestBody } = mockFetchDataStream({
251
- url: 'https://example.com/api/assistant',
252
- chunks: [
253
- formatStreamPart('assistant_control_data', {
254
- threadId: 't1',
255
- messageId: 'm0',
256
- }),
257
- formatStreamPart('assistant_message', {
258
- id: 'm0',
259
- role: 'assistant',
260
- content: [{ type: 'text', text: { value: '' } }],
261
- }),
262
- // text parts:
263
- '0:"Hello"\n',
264
- '0:","\n',
265
- '0:" world"\n',
266
- '0:"."\n',
267
- ],
268
- });
269
-
270
- await userEvent.click(screen.getByTestId('do-append'));
271
-
272
- await screen.findByTestId('message-0');
273
- expect(screen.getByTestId('message-0')).toHaveTextContent('User: hi');
274
-
275
- expect(screen.getByTestId('thread-id')).toHaveTextContent('t1');
276
-
277
- await screen.findByTestId('message-1');
278
- expect(screen.getByTestId('message-1')).toHaveTextContent(
279
- 'AI: Hello, world.',
280
- );
281
-
282
- // check that correct information was sent to the server:
283
- expect(await requestBody).toStrictEqual(
284
- JSON.stringify({
285
- threadId: null,
286
- message: 'hi',
287
- }),
288
- );
289
- });
290
-
291
- it('should switch to thread on setting previously created threadId', async () => {
292
- await userEvent.click(screen.getByTestId('do-thread-3'));
293
-
294
- expect(screen.queryByTestId('message-0')).toBeNull();
295
- expect(screen.queryByTestId('message-1')).toBeNull();
296
-
297
- const { requestBody } = mockFetchDataStream({
298
- url: 'https://example.com/api/assistant',
299
- chunks: [
300
- formatStreamPart('assistant_control_data', {
301
- threadId: 't3',
302
- messageId: 'm0',
303
- }),
304
- formatStreamPart('assistant_message', {
305
- id: 'm0',
306
- role: 'assistant',
307
- content: [{ type: 'text', text: { value: '' } }],
308
- }),
309
- // text parts:
310
- '0:"Hello"\n',
311
- '0:","\n',
312
- '0:" world"\n',
313
- '0:"."\n',
314
- ],
315
- });
316
-
317
- await userEvent.click(screen.getByTestId('do-append'));
318
-
319
- await screen.findByTestId('message-0');
320
- expect(screen.getByTestId('message-0')).toHaveTextContent('User: hi');
321
-
322
- expect(screen.getByTestId('thread-id')).toHaveTextContent('t3');
323
-
324
- await screen.findByTestId('message-1');
325
- expect(screen.getByTestId('message-1')).toHaveTextContent(
326
- 'AI: Hello, world.',
327
- );
328
-
329
- // check that correct information was sent to the server:
330
- expect(await requestBody).toStrictEqual(
331
- JSON.stringify({
332
- threadId: 't3',
333
- message: 'hi',
334
- }),
335
- );
336
- });
337
- });