@fluxbase/sdk-react 2026.1.22 → 2026.2.1

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,424 @@
1
+ /**
2
+ * Tests for GraphQL hooks
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
6
+ import { renderHook, waitFor, act } from '@testing-library/react';
7
+ import {
8
+ useGraphQLQuery,
9
+ useGraphQLMutation,
10
+ useGraphQLIntrospection,
11
+ useGraphQL,
12
+ } from './use-graphql';
13
+ import { createMockClient, createWrapper, createTestQueryClient } from './test-utils';
14
+
15
+ describe('useGraphQLQuery', () => {
16
+ it('should execute query and return data', async () => {
17
+ const mockData = { users: [{ id: 1, name: 'Test' }] };
18
+ const executeMock = vi.fn().mockResolvedValue({ data: mockData, errors: null });
19
+
20
+ const client = createMockClient({
21
+ graphql: { execute: executeMock },
22
+ } as any);
23
+
24
+ const { result } = renderHook(
25
+ () => useGraphQLQuery('users', 'query { users { id name } }'),
26
+ { wrapper: createWrapper(client) }
27
+ );
28
+
29
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
30
+ expect(result.current.data).toEqual(mockData);
31
+ expect(executeMock).toHaveBeenCalledWith(
32
+ 'query { users { id name } }',
33
+ undefined,
34
+ undefined,
35
+ undefined
36
+ );
37
+ });
38
+
39
+ it('should pass variables to query', async () => {
40
+ const mockData = { user: { id: 1 } };
41
+ const executeMock = vi.fn().mockResolvedValue({ data: mockData, errors: null });
42
+
43
+ const client = createMockClient({
44
+ graphql: { execute: executeMock },
45
+ } as any);
46
+
47
+ const { result } = renderHook(
48
+ () => useGraphQLQuery(
49
+ ['user', '1'],
50
+ 'query GetUser($id: ID!) { user(id: $id) { id } }',
51
+ { variables: { id: '1' } }
52
+ ),
53
+ { wrapper: createWrapper(client) }
54
+ );
55
+
56
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
57
+ expect(executeMock).toHaveBeenCalledWith(
58
+ 'query GetUser($id: ID!) { user(id: $id) { id } }',
59
+ { id: '1' },
60
+ undefined,
61
+ undefined
62
+ );
63
+ });
64
+
65
+ it('should throw error on GraphQL errors', async () => {
66
+ const graphqlError = { message: 'Query failed' };
67
+ const executeMock = vi.fn().mockResolvedValue({ data: null, errors: [graphqlError] });
68
+
69
+ const client = createMockClient({
70
+ graphql: { execute: executeMock },
71
+ } as any);
72
+
73
+ const { result } = renderHook(
74
+ () => useGraphQLQuery('users', 'query { users { id } }'),
75
+ { wrapper: createWrapper(client) }
76
+ );
77
+
78
+ await waitFor(() => expect(result.current.isError).toBe(true));
79
+ expect(result.current.error).toEqual(graphqlError);
80
+ });
81
+
82
+ it('should not execute when disabled', async () => {
83
+ const executeMock = vi.fn();
84
+
85
+ const client = createMockClient({
86
+ graphql: { execute: executeMock },
87
+ } as any);
88
+
89
+ const { result } = renderHook(
90
+ () => useGraphQLQuery('users', 'query { users { id } }', { enabled: false }),
91
+ { wrapper: createWrapper(client) }
92
+ );
93
+
94
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
95
+ expect(executeMock).not.toHaveBeenCalled();
96
+ });
97
+
98
+ it('should normalize string query key to array', async () => {
99
+ const executeMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
100
+
101
+ const client = createMockClient({
102
+ graphql: { execute: executeMock },
103
+ } as any);
104
+
105
+ const queryClient = createTestQueryClient();
106
+ const { result } = renderHook(
107
+ () => useGraphQLQuery('users', 'query { users { id } }'),
108
+ { wrapper: createWrapper(client, queryClient) }
109
+ );
110
+
111
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
112
+ // Check that data is stored with normalized key
113
+ expect(queryClient.getQueryData(['fluxbase', 'graphql', 'users'])).toEqual({});
114
+ });
115
+
116
+ it('should pass operation name', async () => {
117
+ const executeMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
118
+
119
+ const client = createMockClient({
120
+ graphql: { execute: executeMock },
121
+ } as any);
122
+
123
+ renderHook(
124
+ () => useGraphQLQuery('users', 'query GetUsers { users { id } }', { operationName: 'GetUsers' }),
125
+ { wrapper: createWrapper(client) }
126
+ );
127
+
128
+ await waitFor(() => {
129
+ expect(executeMock).toHaveBeenCalledWith(
130
+ 'query GetUsers { users { id } }',
131
+ undefined,
132
+ 'GetUsers',
133
+ undefined
134
+ );
135
+ });
136
+ });
137
+
138
+ it('should apply select transform', async () => {
139
+ const mockData = { users: [{ id: 1 }, { id: 2 }] };
140
+ const executeMock = vi.fn().mockResolvedValue({ data: mockData, errors: null });
141
+
142
+ const client = createMockClient({
143
+ graphql: { execute: executeMock },
144
+ } as any);
145
+
146
+ const { result } = renderHook(
147
+ () => useGraphQLQuery<{ users: { id: number }[] }>(
148
+ 'users',
149
+ 'query { users { id } }',
150
+ { select: (data) => data ? { users: data.users.filter(u => u.id === 1) } : undefined }
151
+ ),
152
+ { wrapper: createWrapper(client) }
153
+ );
154
+
155
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
156
+ expect(result.current.data?.users).toHaveLength(1);
157
+ });
158
+ });
159
+
160
+ describe('useGraphQLMutation', () => {
161
+ it('should execute mutation and return data', async () => {
162
+ const mockData = { insertUser: { id: 1, email: 'test@example.com' } };
163
+ const executeMock = vi.fn().mockResolvedValue({ data: mockData, errors: null });
164
+
165
+ const client = createMockClient({
166
+ graphql: { execute: executeMock },
167
+ } as any);
168
+
169
+ const { result } = renderHook(
170
+ () => useGraphQLMutation('mutation CreateUser($email: String!) { insertUser(email: $email) { id email } }'),
171
+ { wrapper: createWrapper(client) }
172
+ );
173
+
174
+ await act(async () => {
175
+ await result.current.mutateAsync({ email: 'test@example.com' });
176
+ });
177
+
178
+ expect(executeMock).toHaveBeenCalledWith(
179
+ 'mutation CreateUser($email: String!) { insertUser(email: $email) { id email } }',
180
+ { email: 'test@example.com' },
181
+ undefined,
182
+ undefined
183
+ );
184
+ });
185
+
186
+ it('should throw error on GraphQL errors', async () => {
187
+ const graphqlError = { message: 'Mutation failed' };
188
+ const executeMock = vi.fn().mockResolvedValue({ data: null, errors: [graphqlError] });
189
+
190
+ const client = createMockClient({
191
+ graphql: { execute: executeMock },
192
+ } as any);
193
+
194
+ const { result } = renderHook(
195
+ () => useGraphQLMutation('mutation { deleteUser(id: "1") }'),
196
+ { wrapper: createWrapper(client) }
197
+ );
198
+
199
+ await expect(act(async () => {
200
+ await result.current.mutateAsync({});
201
+ })).rejects.toEqual(graphqlError);
202
+ });
203
+
204
+ it('should invalidate queries on success', async () => {
205
+ const executeMock = vi.fn().mockResolvedValue({ data: { insertUser: { id: 1 } }, errors: null });
206
+
207
+ const client = createMockClient({
208
+ graphql: { execute: executeMock },
209
+ } as any);
210
+
211
+ const queryClient = createTestQueryClient();
212
+ const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
213
+
214
+ const { result } = renderHook(
215
+ () => useGraphQLMutation('mutation { insertUser { id } }', { invalidateQueries: ['users', 'stats'] }),
216
+ { wrapper: createWrapper(client, queryClient) }
217
+ );
218
+
219
+ await act(async () => {
220
+ await result.current.mutateAsync({});
221
+ });
222
+
223
+ expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['fluxbase', 'graphql', 'users'] });
224
+ expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['fluxbase', 'graphql', 'stats'] });
225
+ });
226
+
227
+ it('should call onSuccess callback', async () => {
228
+ const mockData = { insertUser: { id: 1 } };
229
+ const executeMock = vi.fn().mockResolvedValue({ data: mockData, errors: null });
230
+ const onSuccess = vi.fn();
231
+
232
+ const client = createMockClient({
233
+ graphql: { execute: executeMock },
234
+ } as any);
235
+
236
+ const { result } = renderHook(
237
+ () => useGraphQLMutation('mutation { insertUser { id } }', { onSuccess }),
238
+ { wrapper: createWrapper(client) }
239
+ );
240
+
241
+ await act(async () => {
242
+ await result.current.mutateAsync({ name: 'test' });
243
+ });
244
+
245
+ expect(onSuccess).toHaveBeenCalledWith(mockData, { name: 'test' });
246
+ });
247
+
248
+ it('should call onError callback', async () => {
249
+ const graphqlError = { message: 'Mutation failed' };
250
+ const executeMock = vi.fn().mockResolvedValue({ data: null, errors: [graphqlError] });
251
+ const onError = vi.fn();
252
+
253
+ const client = createMockClient({
254
+ graphql: { execute: executeMock },
255
+ } as any);
256
+
257
+ const { result } = renderHook(
258
+ () => useGraphQLMutation('mutation { deleteUser }', { onError }),
259
+ { wrapper: createWrapper(client) }
260
+ );
261
+
262
+ try {
263
+ await act(async () => {
264
+ await result.current.mutateAsync({ id: '1' });
265
+ });
266
+ } catch {
267
+ // Expected error
268
+ }
269
+
270
+ expect(onError).toHaveBeenCalledWith(graphqlError, { id: '1' });
271
+ });
272
+ });
273
+
274
+ describe('useGraphQLIntrospection', () => {
275
+ it('should fetch schema introspection', async () => {
276
+ const mockSchema = { __schema: { types: [], queryType: { name: 'Query' } } };
277
+ const introspectMock = vi.fn().mockResolvedValue({ data: mockSchema, errors: null });
278
+
279
+ const client = createMockClient({
280
+ graphql: { introspect: introspectMock },
281
+ } as any);
282
+
283
+ const { result } = renderHook(
284
+ () => useGraphQLIntrospection(),
285
+ { wrapper: createWrapper(client) }
286
+ );
287
+
288
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
289
+ expect(result.current.data).toEqual(mockSchema);
290
+ });
291
+
292
+ it('should throw error on introspection errors', async () => {
293
+ const graphqlError = { message: 'Introspection disabled' };
294
+ const introspectMock = vi.fn().mockResolvedValue({ data: null, errors: [graphqlError] });
295
+
296
+ const client = createMockClient({
297
+ graphql: { introspect: introspectMock },
298
+ } as any);
299
+
300
+ const { result } = renderHook(
301
+ () => useGraphQLIntrospection(),
302
+ { wrapper: createWrapper(client) }
303
+ );
304
+
305
+ await waitFor(() => expect(result.current.isError).toBe(true));
306
+ expect(result.current.error).toEqual(graphqlError);
307
+ });
308
+
309
+ it('should not fetch when disabled', async () => {
310
+ const introspectMock = vi.fn();
311
+
312
+ const client = createMockClient({
313
+ graphql: { introspect: introspectMock },
314
+ } as any);
315
+
316
+ const { result } = renderHook(
317
+ () => useGraphQLIntrospection({ enabled: false }),
318
+ { wrapper: createWrapper(client) }
319
+ );
320
+
321
+ await waitFor(() => expect(result.current.isLoading).toBe(false));
322
+ expect(introspectMock).not.toHaveBeenCalled();
323
+ });
324
+ });
325
+
326
+ describe('useGraphQL', () => {
327
+ it('should return executeQuery function', () => {
328
+ const queryMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
329
+
330
+ const client = createMockClient({
331
+ graphql: { query: queryMock },
332
+ } as any);
333
+
334
+ const { result } = renderHook(
335
+ () => useGraphQL(),
336
+ { wrapper: createWrapper(client) }
337
+ );
338
+
339
+ expect(result.current.executeQuery).toBeDefined();
340
+ });
341
+
342
+ it('should return executeMutation function', () => {
343
+ const mutationMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
344
+
345
+ const client = createMockClient({
346
+ graphql: { mutation: mutationMock },
347
+ } as any);
348
+
349
+ const { result } = renderHook(
350
+ () => useGraphQL(),
351
+ { wrapper: createWrapper(client) }
352
+ );
353
+
354
+ expect(result.current.executeMutation).toBeDefined();
355
+ });
356
+
357
+ it('should return execute function', () => {
358
+ const executeMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
359
+
360
+ const client = createMockClient({
361
+ graphql: { execute: executeMock },
362
+ } as any);
363
+
364
+ const { result } = renderHook(
365
+ () => useGraphQL(),
366
+ { wrapper: createWrapper(client) }
367
+ );
368
+
369
+ expect(result.current.execute).toBeDefined();
370
+ });
371
+
372
+ it('should return introspect function', () => {
373
+ const introspectMock = vi.fn().mockResolvedValue({ data: {}, errors: null });
374
+
375
+ const client = createMockClient({
376
+ graphql: { introspect: introspectMock },
377
+ } as any);
378
+
379
+ const { result } = renderHook(
380
+ () => useGraphQL(),
381
+ { wrapper: createWrapper(client) }
382
+ );
383
+
384
+ expect(result.current.introspect).toBeDefined();
385
+ });
386
+
387
+ it('should execute query via executeQuery', async () => {
388
+ const queryMock = vi.fn().mockResolvedValue({ data: { users: [] }, errors: null });
389
+
390
+ const client = createMockClient({
391
+ graphql: { query: queryMock },
392
+ } as any);
393
+
394
+ const { result } = renderHook(
395
+ () => useGraphQL(),
396
+ { wrapper: createWrapper(client) }
397
+ );
398
+
399
+ await act(async () => {
400
+ await result.current.executeQuery('query { users { id } }', { limit: 10 });
401
+ });
402
+
403
+ expect(queryMock).toHaveBeenCalledWith('query { users { id } }', { limit: 10 }, undefined);
404
+ });
405
+
406
+ it('should execute mutation via executeMutation', async () => {
407
+ const mutationMock = vi.fn().mockResolvedValue({ data: { insertUser: {} }, errors: null });
408
+
409
+ const client = createMockClient({
410
+ graphql: { mutation: mutationMock },
411
+ } as any);
412
+
413
+ const { result } = renderHook(
414
+ () => useGraphQL(),
415
+ { wrapper: createWrapper(client) }
416
+ );
417
+
418
+ await act(async () => {
419
+ await result.current.executeMutation('mutation { insertUser { id } }', { email: 'test@example.com' });
420
+ });
421
+
422
+ expect(mutationMock).toHaveBeenCalledWith('mutation { insertUser { id } }', { email: 'test@example.com' }, undefined);
423
+ });
424
+ });