@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,147 @@
1
+ /**
2
+ * Tests for FluxbaseProvider and useFluxbaseClient
3
+ */
4
+
5
+ import { describe, it, expect, vi } from 'vitest';
6
+ import { render, screen, renderHook } from '@testing-library/react';
7
+ import React from 'react';
8
+ import { FluxbaseProvider, useFluxbaseClient } from './context';
9
+ import { createMockClient } from './test-utils';
10
+ import type { FluxbaseClient } from '@fluxbase/sdk';
11
+
12
+ describe('FluxbaseProvider', () => {
13
+ it('should render children', () => {
14
+ const client = createMockClient();
15
+ render(
16
+ <FluxbaseProvider client={client}>
17
+ <div data-testid="child">Hello</div>
18
+ </FluxbaseProvider>
19
+ );
20
+
21
+ expect(screen.getByTestId('child')).toHaveTextContent('Hello');
22
+ });
23
+
24
+ it('should provide client to children', () => {
25
+ const client = createMockClient();
26
+ let receivedClient: FluxbaseClient | null = null;
27
+
28
+ function TestComponent() {
29
+ receivedClient = useFluxbaseClient();
30
+ return null;
31
+ }
32
+
33
+ render(
34
+ <FluxbaseProvider client={client}>
35
+ <TestComponent />
36
+ </FluxbaseProvider>
37
+ );
38
+
39
+ expect(receivedClient).toBe(client);
40
+ });
41
+
42
+ it('should support nested children', () => {
43
+ const client = createMockClient();
44
+ render(
45
+ <FluxbaseProvider client={client}>
46
+ <div>
47
+ <span>
48
+ <p data-testid="nested">Nested content</p>
49
+ </span>
50
+ </div>
51
+ </FluxbaseProvider>
52
+ );
53
+
54
+ expect(screen.getByTestId('nested')).toHaveTextContent('Nested content');
55
+ });
56
+ });
57
+
58
+ describe('useFluxbaseClient', () => {
59
+ it('should return the client from context', () => {
60
+ const client = createMockClient();
61
+
62
+ const { result } = renderHook(() => useFluxbaseClient(), {
63
+ wrapper: ({ children }) => (
64
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
65
+ ),
66
+ });
67
+
68
+ expect(result.current).toBe(client);
69
+ });
70
+
71
+ it('should throw error when used outside provider', () => {
72
+ // Suppress console.error for this test
73
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
74
+
75
+ expect(() => {
76
+ renderHook(() => useFluxbaseClient());
77
+ }).toThrow('useFluxbaseClient must be used within a FluxbaseProvider');
78
+
79
+ consoleSpy.mockRestore();
80
+ });
81
+
82
+ it('should provide access to auth methods', () => {
83
+ const client = createMockClient();
84
+
85
+ const { result } = renderHook(() => useFluxbaseClient(), {
86
+ wrapper: ({ children }) => (
87
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
88
+ ),
89
+ });
90
+
91
+ expect(result.current.auth).toBeDefined();
92
+ expect(result.current.auth.getSession).toBeDefined();
93
+ expect(result.current.auth.signIn).toBeDefined();
94
+ });
95
+
96
+ it('should provide access to storage methods', () => {
97
+ const client = createMockClient();
98
+
99
+ const { result } = renderHook(() => useFluxbaseClient(), {
100
+ wrapper: ({ children }) => (
101
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
102
+ ),
103
+ });
104
+
105
+ expect(result.current.storage).toBeDefined();
106
+ expect(result.current.storage.from).toBeDefined();
107
+ });
108
+
109
+ it('should provide access to realtime methods', () => {
110
+ const client = createMockClient();
111
+
112
+ const { result } = renderHook(() => useFluxbaseClient(), {
113
+ wrapper: ({ children }) => (
114
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
115
+ ),
116
+ });
117
+
118
+ expect(result.current.realtime).toBeDefined();
119
+ expect(result.current.realtime.channel).toBeDefined();
120
+ });
121
+
122
+ it('should provide access to graphql methods', () => {
123
+ const client = createMockClient();
124
+
125
+ const { result } = renderHook(() => useFluxbaseClient(), {
126
+ wrapper: ({ children }) => (
127
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
128
+ ),
129
+ });
130
+
131
+ expect(result.current.graphql).toBeDefined();
132
+ expect(result.current.graphql.execute).toBeDefined();
133
+ });
134
+
135
+ it('should provide access to admin methods', () => {
136
+ const client = createMockClient();
137
+
138
+ const { result } = renderHook(() => useFluxbaseClient(), {
139
+ wrapper: ({ children }) => (
140
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
141
+ ),
142
+ });
143
+
144
+ expect(result.current.admin).toBeDefined();
145
+ expect(result.current.admin.me).toBeDefined();
146
+ });
147
+ });
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Tests for module exports
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import * as exports from './index';
7
+
8
+ describe('Module Exports', () => {
9
+ describe('Context exports', () => {
10
+ it('should export FluxbaseProvider', () => {
11
+ expect(exports.FluxbaseProvider).toBeDefined();
12
+ });
13
+
14
+ it('should export useFluxbaseClient', () => {
15
+ expect(exports.useFluxbaseClient).toBeDefined();
16
+ });
17
+ });
18
+
19
+ describe('Auth hook exports', () => {
20
+ it('should export useAuth', () => {
21
+ expect(exports.useAuth).toBeDefined();
22
+ });
23
+
24
+ it('should export useUser', () => {
25
+ expect(exports.useUser).toBeDefined();
26
+ });
27
+
28
+ it('should export useSession', () => {
29
+ expect(exports.useSession).toBeDefined();
30
+ });
31
+
32
+ it('should export useSignIn', () => {
33
+ expect(exports.useSignIn).toBeDefined();
34
+ });
35
+
36
+ it('should export useSignUp', () => {
37
+ expect(exports.useSignUp).toBeDefined();
38
+ });
39
+
40
+ it('should export useSignOut', () => {
41
+ expect(exports.useSignOut).toBeDefined();
42
+ });
43
+
44
+ it('should export useUpdateUser', () => {
45
+ expect(exports.useUpdateUser).toBeDefined();
46
+ });
47
+ });
48
+
49
+ describe('CAPTCHA hook exports', () => {
50
+ it('should export useCaptchaConfig', () => {
51
+ expect(exports.useCaptchaConfig).toBeDefined();
52
+ });
53
+
54
+ it('should export useCaptcha', () => {
55
+ expect(exports.useCaptcha).toBeDefined();
56
+ });
57
+
58
+ it('should export isCaptchaRequiredForEndpoint', () => {
59
+ expect(exports.isCaptchaRequiredForEndpoint).toBeDefined();
60
+ });
61
+ });
62
+
63
+ describe('Auth config exports', () => {
64
+ it('should export useAuthConfig', () => {
65
+ expect(exports.useAuthConfig).toBeDefined();
66
+ });
67
+ });
68
+
69
+ describe('SAML hook exports', () => {
70
+ it('should export useSAMLProviders', () => {
71
+ expect(exports.useSAMLProviders).toBeDefined();
72
+ });
73
+
74
+ it('should export useGetSAMLLoginUrl', () => {
75
+ expect(exports.useGetSAMLLoginUrl).toBeDefined();
76
+ });
77
+
78
+ it('should export useSignInWithSAML', () => {
79
+ expect(exports.useSignInWithSAML).toBeDefined();
80
+ });
81
+
82
+ it('should export useHandleSAMLCallback', () => {
83
+ expect(exports.useHandleSAMLCallback).toBeDefined();
84
+ });
85
+
86
+ it('should export useSAMLMetadataUrl', () => {
87
+ expect(exports.useSAMLMetadataUrl).toBeDefined();
88
+ });
89
+ });
90
+
91
+ describe('GraphQL hook exports', () => {
92
+ it('should export useGraphQLQuery', () => {
93
+ expect(exports.useGraphQLQuery).toBeDefined();
94
+ });
95
+
96
+ it('should export useGraphQLMutation', () => {
97
+ expect(exports.useGraphQLMutation).toBeDefined();
98
+ });
99
+
100
+ it('should export useGraphQLIntrospection', () => {
101
+ expect(exports.useGraphQLIntrospection).toBeDefined();
102
+ });
103
+
104
+ it('should export useGraphQL', () => {
105
+ expect(exports.useGraphQL).toBeDefined();
106
+ });
107
+ });
108
+
109
+ describe('Database query hook exports', () => {
110
+ it('should export useFluxbaseQuery', () => {
111
+ expect(exports.useFluxbaseQuery).toBeDefined();
112
+ });
113
+
114
+ it('should export useTable', () => {
115
+ expect(exports.useTable).toBeDefined();
116
+ });
117
+
118
+ it('should export useInsert', () => {
119
+ expect(exports.useInsert).toBeDefined();
120
+ });
121
+
122
+ it('should export useUpdate', () => {
123
+ expect(exports.useUpdate).toBeDefined();
124
+ });
125
+
126
+ it('should export useUpsert', () => {
127
+ expect(exports.useUpsert).toBeDefined();
128
+ });
129
+
130
+ it('should export useDelete', () => {
131
+ expect(exports.useDelete).toBeDefined();
132
+ });
133
+ });
134
+
135
+ describe('Realtime hook exports', () => {
136
+ it('should export useRealtime', () => {
137
+ expect(exports.useRealtime).toBeDefined();
138
+ });
139
+
140
+ it('should export useTableSubscription', () => {
141
+ expect(exports.useTableSubscription).toBeDefined();
142
+ });
143
+
144
+ it('should export useTableInserts', () => {
145
+ expect(exports.useTableInserts).toBeDefined();
146
+ });
147
+
148
+ it('should export useTableUpdates', () => {
149
+ expect(exports.useTableUpdates).toBeDefined();
150
+ });
151
+
152
+ it('should export useTableDeletes', () => {
153
+ expect(exports.useTableDeletes).toBeDefined();
154
+ });
155
+ });
156
+
157
+ describe('Storage hook exports', () => {
158
+ it('should export useStorageList', () => {
159
+ expect(exports.useStorageList).toBeDefined();
160
+ });
161
+
162
+ it('should export useStorageUpload', () => {
163
+ expect(exports.useStorageUpload).toBeDefined();
164
+ });
165
+
166
+ it('should export useStorageUploadWithProgress', () => {
167
+ expect(exports.useStorageUploadWithProgress).toBeDefined();
168
+ });
169
+
170
+ it('should export useStorageDownload', () => {
171
+ expect(exports.useStorageDownload).toBeDefined();
172
+ });
173
+
174
+ it('should export useStorageDelete', () => {
175
+ expect(exports.useStorageDelete).toBeDefined();
176
+ });
177
+
178
+ it('should export useStoragePublicUrl', () => {
179
+ expect(exports.useStoragePublicUrl).toBeDefined();
180
+ });
181
+
182
+ it('should export useStorageTransformUrl', () => {
183
+ expect(exports.useStorageTransformUrl).toBeDefined();
184
+ });
185
+
186
+ it('should export useStorageSignedUrl', () => {
187
+ expect(exports.useStorageSignedUrl).toBeDefined();
188
+ });
189
+
190
+ it('should export useStorageSignedUrlWithOptions', () => {
191
+ expect(exports.useStorageSignedUrlWithOptions).toBeDefined();
192
+ });
193
+
194
+ it('should export useStorageMove', () => {
195
+ expect(exports.useStorageMove).toBeDefined();
196
+ });
197
+
198
+ it('should export useStorageCopy', () => {
199
+ expect(exports.useStorageCopy).toBeDefined();
200
+ });
201
+
202
+ it('should export useStorageBuckets', () => {
203
+ expect(exports.useStorageBuckets).toBeDefined();
204
+ });
205
+
206
+ it('should export useCreateBucket', () => {
207
+ expect(exports.useCreateBucket).toBeDefined();
208
+ });
209
+
210
+ it('should export useDeleteBucket', () => {
211
+ expect(exports.useDeleteBucket).toBeDefined();
212
+ });
213
+ });
214
+
215
+ describe('Admin hook exports', () => {
216
+ it('should export useAdminAuth', () => {
217
+ expect(exports.useAdminAuth).toBeDefined();
218
+ });
219
+
220
+ it('should export useUsers', () => {
221
+ expect(exports.useUsers).toBeDefined();
222
+ });
223
+
224
+ it('should export useClientKeys', () => {
225
+ expect(exports.useClientKeys).toBeDefined();
226
+ });
227
+
228
+ it('should export useAPIKeys (deprecated alias)', () => {
229
+ expect(exports.useAPIKeys).toBeDefined();
230
+ expect(exports.useAPIKeys).toBe(exports.useClientKeys);
231
+ });
232
+
233
+ it('should export useWebhooks', () => {
234
+ expect(exports.useWebhooks).toBeDefined();
235
+ });
236
+
237
+ it('should export useAppSettings', () => {
238
+ expect(exports.useAppSettings).toBeDefined();
239
+ });
240
+
241
+ it('should export useSystemSettings', () => {
242
+ expect(exports.useSystemSettings).toBeDefined();
243
+ });
244
+ });
245
+
246
+ describe('Total export count', () => {
247
+ it('should export the expected number of items', () => {
248
+ // Count the exports (functions and types are counted)
249
+ const exportKeys = Object.keys(exports);
250
+
251
+ // We expect at least 50 exports (hooks, types, etc.)
252
+ expect(exportKeys.length).toBeGreaterThanOrEqual(50);
253
+ });
254
+ });
255
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Test setup file for Fluxbase React SDK
3
+ */
4
+
5
+ import { vi } from 'vitest';
6
+ import '@testing-library/react';
7
+ import '@testing-library/jest-dom/vitest';
8
+
9
+ // Mock window.location for tests that need it
10
+ Object.defineProperty(window, 'location', {
11
+ value: {
12
+ href: 'http://localhost:3000',
13
+ origin: 'http://localhost:3000',
14
+ pathname: '/',
15
+ search: '',
16
+ hash: '',
17
+ assign: vi.fn(),
18
+ replace: vi.fn(),
19
+ reload: vi.fn(),
20
+ },
21
+ writable: true,
22
+ });
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Test utilities for Fluxbase React SDK
3
+ */
4
+
5
+ import React, { ReactElement } from "react";
6
+ import { render, RenderOptions } from "@testing-library/react";
7
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8
+ import { FluxbaseProvider } from "./context";
9
+ import type { FluxbaseClient } from "@fluxbase/sdk";
10
+ import { vi } from "vitest";
11
+
12
+ /**
13
+ * Create a mock FluxbaseClient for testing
14
+ */
15
+ export function createMockClient(
16
+ overrides: Partial<FluxbaseClient> = {},
17
+ ): FluxbaseClient {
18
+ return {
19
+ auth: {
20
+ getSession: vi.fn().mockResolvedValue({ data: null, error: null }),
21
+ getCurrentUser: vi.fn().mockResolvedValue({ data: null, error: null }),
22
+ signIn: vi.fn().mockResolvedValue({ user: null, session: null }),
23
+ signUp: vi.fn().mockResolvedValue({ data: null, error: null }),
24
+ signOut: vi.fn().mockResolvedValue(undefined),
25
+ updateUser: vi
26
+ .fn()
27
+ .mockResolvedValue({ id: "1", email: "test@example.com" }),
28
+ getAuthConfig: vi.fn().mockResolvedValue({ data: {}, error: null }),
29
+ getCaptchaConfig: vi.fn().mockResolvedValue({ data: {}, error: null }),
30
+ getSAMLProviders: vi
31
+ .fn()
32
+ .mockResolvedValue({ data: { providers: [] }, error: null }),
33
+ getSAMLLoginUrl: vi
34
+ .fn()
35
+ .mockResolvedValue({ data: { url: "" }, error: null }),
36
+ signInWithSAML: vi.fn().mockResolvedValue({ data: null, error: null }),
37
+ handleSAMLCallback: vi
38
+ .fn()
39
+ .mockResolvedValue({ data: null, error: null }),
40
+ getSAMLMetadataUrl: vi
41
+ .fn()
42
+ .mockReturnValue("http://localhost/saml/metadata"),
43
+ ...overrides.auth,
44
+ },
45
+ from: vi.fn().mockReturnValue({
46
+ select: vi.fn().mockReturnThis(),
47
+ insert: vi.fn().mockResolvedValue({ data: null, error: null }),
48
+ update: vi.fn().mockResolvedValue({ data: null, error: null }),
49
+ upsert: vi.fn().mockResolvedValue({ data: null, error: null }),
50
+ delete: vi.fn().mockResolvedValue({ data: null, error: null }),
51
+ execute: vi.fn().mockResolvedValue({ data: [], error: null }),
52
+ eq: vi.fn().mockReturnThis(),
53
+ }),
54
+ storage: {
55
+ from: vi.fn().mockReturnValue({
56
+ list: vi.fn().mockResolvedValue({ data: [], error: null }),
57
+ upload: vi
58
+ .fn()
59
+ .mockResolvedValue({ data: { path: "test.txt" }, error: null }),
60
+ download: vi.fn().mockResolvedValue({ data: new Blob(), error: null }),
61
+ remove: vi.fn().mockResolvedValue({ data: null, error: null }),
62
+ getPublicUrl: vi
63
+ .fn()
64
+ .mockReturnValue({ data: { publicUrl: "http://localhost/file" } }),
65
+ getTransformUrl: vi
66
+ .fn()
67
+ .mockReturnValue("http://localhost/transform/file"),
68
+ createSignedUrl: vi
69
+ .fn()
70
+ .mockResolvedValue({
71
+ data: { signedUrl: "http://localhost/signed" },
72
+ error: null,
73
+ }),
74
+ move: vi
75
+ .fn()
76
+ .mockResolvedValue({ data: { path: "new.txt" }, error: null }),
77
+ copy: vi
78
+ .fn()
79
+ .mockResolvedValue({ data: { path: "copy.txt" }, error: null }),
80
+ }),
81
+ listBuckets: vi.fn().mockResolvedValue({ data: [], error: null }),
82
+ createBucket: vi.fn().mockResolvedValue({ error: null }),
83
+ deleteBucket: vi.fn().mockResolvedValue({ error: null }),
84
+ ...overrides.storage,
85
+ },
86
+ realtime: {
87
+ channel: vi.fn().mockReturnValue({
88
+ on: vi.fn().mockReturnThis(),
89
+ subscribe: vi.fn().mockReturnThis(),
90
+ unsubscribe: vi.fn(),
91
+ }),
92
+ ...overrides.realtime,
93
+ },
94
+ graphql: {
95
+ execute: vi.fn().mockResolvedValue({ data: null, errors: null }),
96
+ query: vi.fn().mockResolvedValue({ data: null, errors: null }),
97
+ mutation: vi.fn().mockResolvedValue({ data: null, errors: null }),
98
+ introspect: vi
99
+ .fn()
100
+ .mockResolvedValue({ data: { __schema: {} }, errors: null }),
101
+ ...overrides.graphql,
102
+ },
103
+ admin: {
104
+ me: vi.fn().mockResolvedValue({ data: null, error: null }),
105
+ login: vi.fn().mockResolvedValue({ data: null, error: null }),
106
+ listUsers: vi
107
+ .fn()
108
+ .mockResolvedValue({ data: { users: [], total: 0 }, error: null }),
109
+ inviteUser: vi.fn().mockResolvedValue({ data: null, error: null }),
110
+ updateUserRole: vi.fn().mockResolvedValue({ data: null, error: null }),
111
+ deleteUser: vi.fn().mockResolvedValue({ data: null, error: null }),
112
+ resetUserPassword: vi
113
+ .fn()
114
+ .mockResolvedValue({
115
+ data: { message: "Password reset" },
116
+ error: null,
117
+ }),
118
+ settings: {
119
+ app: {
120
+ get: vi.fn().mockResolvedValue({}),
121
+ update: vi.fn().mockResolvedValue({}),
122
+ },
123
+ system: {
124
+ list: vi.fn().mockResolvedValue({ settings: [] }),
125
+ update: vi.fn().mockResolvedValue({}),
126
+ delete: vi.fn().mockResolvedValue({}),
127
+ },
128
+ },
129
+ management: {
130
+ clientKeys: {
131
+ list: vi.fn().mockResolvedValue({ client_keys: [] }),
132
+ create: vi.fn().mockResolvedValue({ key: "new-key", client_key: {} }),
133
+ update: vi.fn().mockResolvedValue({}),
134
+ revoke: vi.fn().mockResolvedValue({}),
135
+ delete: vi.fn().mockResolvedValue({}),
136
+ },
137
+ webhooks: {
138
+ list: vi.fn().mockResolvedValue({ webhooks: [] }),
139
+ create: vi.fn().mockResolvedValue({}),
140
+ update: vi.fn().mockResolvedValue({}),
141
+ delete: vi.fn().mockResolvedValue({}),
142
+ test: vi.fn().mockResolvedValue({}),
143
+ },
144
+ },
145
+ ...overrides.admin,
146
+ },
147
+ ...overrides,
148
+ } as unknown as FluxbaseClient;
149
+ }
150
+
151
+ /**
152
+ * Create a fresh QueryClient for testing
153
+ */
154
+ export function createTestQueryClient(): QueryClient {
155
+ return new QueryClient({
156
+ defaultOptions: {
157
+ queries: {
158
+ retry: false,
159
+ gcTime: 0,
160
+ },
161
+ mutations: {
162
+ retry: false,
163
+ },
164
+ },
165
+ });
166
+ }
167
+
168
+ interface WrapperProps {
169
+ children: React.ReactNode;
170
+ }
171
+
172
+ /**
173
+ * Create a wrapper component with all providers
174
+ */
175
+ export function createWrapper(
176
+ client: FluxbaseClient,
177
+ queryClient?: QueryClient,
178
+ ) {
179
+ const qc = queryClient || createTestQueryClient();
180
+
181
+ return function Wrapper({ children }: WrapperProps) {
182
+ return (
183
+ <QueryClientProvider client={qc}>
184
+ <FluxbaseProvider client={client}>{children}</FluxbaseProvider>
185
+ </QueryClientProvider>
186
+ );
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Custom render function that includes all providers
192
+ */
193
+ export function renderWithProviders(
194
+ ui: ReactElement,
195
+ options?: Omit<RenderOptions, "wrapper"> & {
196
+ client?: FluxbaseClient;
197
+ queryClient?: QueryClient;
198
+ },
199
+ ) {
200
+ const {
201
+ client = createMockClient(),
202
+ queryClient,
203
+ ...renderOptions
204
+ } = options || {};
205
+ const wrapper = createWrapper(client, queryClient);
206
+
207
+ return {
208
+ ...render(ui, { wrapper, ...renderOptions }),
209
+ client,
210
+ queryClient: queryClient || createTestQueryClient(),
211
+ };
212
+ }
213
+
214
+ // Re-export testing library utilities
215
+ export * from "@testing-library/react";