@ai-sdk/vue 3.0.44 → 3.0.46

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.
@@ -0,0 +1,250 @@
1
+ import {
2
+ createTestServer,
3
+ TestResponseController,
4
+ } from '@ai-sdk/test-server/with-vitest';
5
+ import '@testing-library/jest-dom/vitest';
6
+ import { cleanup, screen, waitFor } from '@testing-library/vue';
7
+ import userEvent from '@testing-library/user-event';
8
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
9
+ import { setupTestComponent } from './setup-test-component';
10
+ import TestUseObjectComponent from './TestUseObjectComponent.vue';
11
+ import TestUseObjectCustomTransportComponent from './TestUseObjectCustomTransportComponent.vue';
12
+
13
+ const server = createTestServer({
14
+ '/api/use-object': {},
15
+ });
16
+
17
+ describe('text stream', () => {
18
+ afterEach(() => {
19
+ vi.restoreAllMocks();
20
+ cleanup();
21
+ });
22
+
23
+ describe('basic component', () => {
24
+ setupTestComponent(TestUseObjectComponent);
25
+
26
+ describe("when the API returns 'Hello, world!'", () => {
27
+ beforeEach(async () => {
28
+ server.urls['/api/use-object'].response = {
29
+ type: 'stream-chunks',
30
+ chunks: ['{ ', '"content": "Hello, ', 'world', '!"'],
31
+ };
32
+ await userEvent.click(screen.getByTestId('submit-button'));
33
+ });
34
+
35
+ it('should render stream', async () => {
36
+ await screen.findByTestId('object');
37
+ expect(screen.getByTestId('object')).toHaveTextContent(
38
+ JSON.stringify({ content: 'Hello, world!' }),
39
+ );
40
+ });
41
+
42
+ it("should send 'test' to the API", async () => {
43
+ expect(await server.calls[0].requestBodyJson).toBe('test-input');
44
+ });
45
+
46
+ it('should not have an error', async () => {
47
+ await screen.findByTestId('error');
48
+ expect(screen.getByTestId('error')).toBeEmptyDOMElement();
49
+ expect(screen.getByTestId('on-error-result')).toBeEmptyDOMElement();
50
+ });
51
+ });
52
+
53
+ describe('isLoading', () => {
54
+ it('should be true while loading', async () => {
55
+ const controller = new TestResponseController();
56
+ server.urls['/api/use-object'].response = {
57
+ type: 'controlled-stream',
58
+ controller,
59
+ };
60
+
61
+ controller.write('{"content": ');
62
+ await userEvent.click(screen.getByTestId('submit-button'));
63
+
64
+ // wait for element "loading" to have text content "true":
65
+ await waitFor(() => {
66
+ expect(screen.getByTestId('loading')).toHaveTextContent('true');
67
+ });
68
+
69
+ controller.write('"Hello, world!"}');
70
+ controller.close();
71
+
72
+ // wait for element "loading" to have text content "false":
73
+ await waitFor(() => {
74
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
75
+ });
76
+ });
77
+ });
78
+
79
+ it('should abort the stream and not consume any more data', async () => {
80
+ const controller = new TestResponseController();
81
+ server.urls['/api/use-object'].response = {
82
+ type: 'controlled-stream',
83
+ controller,
84
+ };
85
+
86
+ controller.write('{"content": "h');
87
+ await userEvent.click(screen.getByTestId('submit-button'));
88
+
89
+ // wait for element "loading" and "object" to have text content:
90
+ await waitFor(() => {
91
+ expect(screen.getByTestId('loading')).toHaveTextContent('true');
92
+ });
93
+ await waitFor(() => {
94
+ expect(screen.getByTestId('object')).toHaveTextContent(
95
+ '{"content":"h"}',
96
+ );
97
+ });
98
+
99
+ // click stop button:
100
+ await userEvent.click(screen.getByTestId('stop-button'));
101
+
102
+ // wait for element "loading" to have text content "false":
103
+ await waitFor(() => {
104
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
105
+ });
106
+
107
+ // this should not be consumed any more:
108
+ await expect(controller.write('ello, world!"}')).rejects.toThrow();
109
+ await expect(controller.close()).rejects.toThrow();
110
+
111
+ // should only show start of object:
112
+ await waitFor(() => {
113
+ expect(screen.getByTestId('object')).toHaveTextContent(
114
+ '{"content":"h"}',
115
+ );
116
+ });
117
+ });
118
+
119
+ it('should stop and clear the object state after a call to submit then clear', async () => {
120
+ const controller = new TestResponseController();
121
+ server.urls['/api/use-object'].response = {
122
+ type: 'controlled-stream',
123
+ controller,
124
+ };
125
+
126
+ controller.write('{"content": "h');
127
+ await userEvent.click(screen.getByTestId('submit-button'));
128
+
129
+ await waitFor(() => {
130
+ expect(screen.getByTestId('loading')).toHaveTextContent('true');
131
+ });
132
+ await waitFor(() => {
133
+ expect(screen.getByTestId('object')).toHaveTextContent(
134
+ '{"content":"h"}',
135
+ );
136
+ });
137
+
138
+ await userEvent.click(screen.getByTestId('clear-button'));
139
+
140
+ await expect(controller.write('ello, world!"}')).rejects.toThrow();
141
+ await expect(controller.close()).rejects.toThrow();
142
+
143
+ await waitFor(() => {
144
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
145
+ expect(screen.getByTestId('error')).toBeEmptyDOMElement();
146
+ expect(screen.getByTestId('object')).toBeEmptyDOMElement();
147
+ });
148
+ });
149
+
150
+ describe('when the API returns a 404', () => {
151
+ it('should render error', async () => {
152
+ server.urls['/api/use-object'].response = {
153
+ type: 'error',
154
+ status: 404,
155
+ body: 'Not found',
156
+ };
157
+
158
+ await userEvent.click(screen.getByTestId('submit-button'));
159
+
160
+ await screen.findByTestId('error');
161
+ expect(screen.getByTestId('error')).toHaveTextContent('Not found');
162
+ expect(screen.getByTestId('on-error-result')).toHaveTextContent(
163
+ 'Not found',
164
+ );
165
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
166
+ });
167
+ });
168
+
169
+ describe('onFinish', () => {
170
+ it('should be called with an object when the stream finishes and the object matches the schema', async () => {
171
+ server.urls['/api/use-object'].response = {
172
+ type: 'stream-chunks',
173
+ chunks: ['{ ', '"content": "Hello, ', 'world', '!"', '}'],
174
+ };
175
+
176
+ await userEvent.click(screen.getByTestId('submit-button'));
177
+
178
+ expect(screen.getByTestId('on-finish-calls')).toHaveTextContent(
179
+ JSON.stringify([
180
+ { object: { content: 'Hello, world!' }, error: undefined },
181
+ ]),
182
+ );
183
+ });
184
+
185
+ it('should be called with an error when the stream finishes and the object does not match the schema', async () => {
186
+ server.urls['/api/use-object'].response = {
187
+ type: 'stream-chunks',
188
+ chunks: ['{ ', '"content-wrong": "Hello, ', 'world', '!"', '}'],
189
+ };
190
+
191
+ await userEvent.click(screen.getByTestId('submit-button'));
192
+
193
+ expect(screen.getByTestId('on-finish-calls')).toHaveTextContent(
194
+ 'ZodError',
195
+ );
196
+ });
197
+ });
198
+
199
+ it('should clear the object state after a call to clear', async () => {
200
+ server.urls['/api/use-object'].response = {
201
+ type: 'stream-chunks',
202
+ chunks: ['{ ', '"content": "Hello, ', 'world', '!"', '}'],
203
+ };
204
+
205
+ await userEvent.click(screen.getByTestId('submit-button'));
206
+
207
+ await screen.findByTestId('object');
208
+ expect(screen.getByTestId('object')).toHaveTextContent(
209
+ JSON.stringify({ content: 'Hello, world!' }),
210
+ );
211
+
212
+ await userEvent.click(screen.getByTestId('clear-button'));
213
+
214
+ await waitFor(() => {
215
+ expect(screen.getByTestId('object')).toBeEmptyDOMElement();
216
+ expect(screen.getByTestId('error')).toBeEmptyDOMElement();
217
+ expect(screen.getByTestId('loading')).toHaveTextContent('false');
218
+ });
219
+ });
220
+ });
221
+
222
+ describe('custom transport', () => {
223
+ setupTestComponent(TestUseObjectCustomTransportComponent);
224
+
225
+ it('should send custom headers', async () => {
226
+ server.urls['/api/use-object'].response = {
227
+ type: 'stream-chunks',
228
+ chunks: ['{ ', '"content": "Hello, ', 'world', '!"', '}'],
229
+ };
230
+
231
+ await userEvent.click(screen.getByTestId('submit-button'));
232
+
233
+ expect(server.calls[0].requestHeaders).toStrictEqual({
234
+ 'content-type': 'application/json',
235
+ authorization: 'Bearer TEST_TOKEN',
236
+ 'x-custom-header': 'CustomValue',
237
+ });
238
+ });
239
+
240
+ it('should send custom credentials', async () => {
241
+ server.urls['/api/use-object'].response = {
242
+ type: 'stream-chunks',
243
+ chunks: ['{ ', '"content": "Authenticated ', 'content', '!"', '}'],
244
+ };
245
+
246
+ await userEvent.click(screen.getByTestId('submit-button'));
247
+ expect(server.calls[0].requestCredentials).toBe('include');
248
+ });
249
+ });
250
+ });