@fluxbase/sdk-react 2026.1.22-rc.9 → 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.
- package/dist/index.d.mts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +33 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -10
- package/src/context.test.tsx +147 -0
- package/src/index.test.ts +255 -0
- package/src/test-setup.ts +22 -0
- package/src/test-utils.tsx +215 -0
- package/src/use-admin-auth.test.ts +175 -0
- package/src/use-admin-auth.ts +10 -2
- package/src/use-admin-hooks.test.ts +457 -0
- package/src/use-auth-config.test.ts +145 -0
- package/src/use-auth.test.ts +313 -0
- package/src/use-auth.ts +2 -1
- package/src/use-captcha.test.ts +273 -0
- package/src/use-client-keys.test.ts +286 -0
- package/src/use-graphql.test.ts +424 -0
- package/src/use-query.test.ts +348 -0
- package/src/use-query.ts +50 -4
- package/src/use-realtime.test.ts +359 -0
- package/src/use-realtime.ts +20 -15
- package/src/use-saml.test.ts +269 -0
- package/src/use-storage.test.ts +549 -0
- package/src/use-storage.ts +10 -2
- package/src/use-users.test.ts +264 -0
- package/vitest.config.ts +22 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for admin authentication hook
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
6
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
7
|
+
import { useAdminAuth } from './use-admin-auth';
|
|
8
|
+
import { createMockClient, createWrapper } from './test-utils';
|
|
9
|
+
|
|
10
|
+
describe('useAdminAuth', () => {
|
|
11
|
+
it('should check auth status on mount when autoCheck is true', async () => {
|
|
12
|
+
const mockUser = { id: '1', email: 'admin@example.com', role: 'admin' };
|
|
13
|
+
const meMock = vi.fn().mockResolvedValue({ data: { user: mockUser }, error: null });
|
|
14
|
+
|
|
15
|
+
const client = createMockClient({
|
|
16
|
+
admin: { me: meMock },
|
|
17
|
+
} as any);
|
|
18
|
+
|
|
19
|
+
const { result } = renderHook(
|
|
20
|
+
() => useAdminAuth({ autoCheck: true }),
|
|
21
|
+
{ wrapper: createWrapper(client) }
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
25
|
+
expect(result.current.user).toEqual(mockUser);
|
|
26
|
+
expect(result.current.isAuthenticated).toBe(true);
|
|
27
|
+
expect(meMock).toHaveBeenCalled();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should not check auth status when autoCheck is false', async () => {
|
|
31
|
+
const meMock = vi.fn();
|
|
32
|
+
|
|
33
|
+
const client = createMockClient({
|
|
34
|
+
admin: { me: meMock },
|
|
35
|
+
} as any);
|
|
36
|
+
|
|
37
|
+
const { result } = renderHook(
|
|
38
|
+
() => useAdminAuth({ autoCheck: false }),
|
|
39
|
+
{ wrapper: createWrapper(client) }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
43
|
+
expect(meMock).not.toHaveBeenCalled();
|
|
44
|
+
expect(result.current.isAuthenticated).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle auth check error', async () => {
|
|
48
|
+
const error = new Error('Not authenticated');
|
|
49
|
+
const meMock = vi.fn().mockResolvedValue({ data: null, error });
|
|
50
|
+
|
|
51
|
+
const client = createMockClient({
|
|
52
|
+
admin: { me: meMock },
|
|
53
|
+
} as any);
|
|
54
|
+
|
|
55
|
+
const { result } = renderHook(
|
|
56
|
+
() => useAdminAuth({ autoCheck: true }),
|
|
57
|
+
{ wrapper: createWrapper(client) }
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
61
|
+
expect(result.current.user).toBeNull();
|
|
62
|
+
expect(result.current.isAuthenticated).toBe(false);
|
|
63
|
+
expect(result.current.error).toBe(error);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should login successfully', async () => {
|
|
67
|
+
const mockUser = { id: '1', email: 'admin@example.com', role: 'admin' };
|
|
68
|
+
const loginMock = vi.fn().mockResolvedValue({
|
|
69
|
+
data: { user: mockUser, token: 'token' },
|
|
70
|
+
error: null,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const client = createMockClient({
|
|
74
|
+
admin: { login: loginMock, me: vi.fn().mockResolvedValue({ data: null, error: null }) },
|
|
75
|
+
} as any);
|
|
76
|
+
|
|
77
|
+
const { result } = renderHook(
|
|
78
|
+
() => useAdminAuth({ autoCheck: false }),
|
|
79
|
+
{ wrapper: createWrapper(client) }
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
await act(async () => {
|
|
83
|
+
await result.current.login('admin@example.com', 'password');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(loginMock).toHaveBeenCalledWith({ email: 'admin@example.com', password: 'password' });
|
|
87
|
+
expect(result.current.user).toEqual(mockUser);
|
|
88
|
+
expect(result.current.isAuthenticated).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle login error', async () => {
|
|
92
|
+
const error = new Error('Invalid credentials');
|
|
93
|
+
const loginMock = vi.fn().mockResolvedValue({ data: null, error });
|
|
94
|
+
|
|
95
|
+
const client = createMockClient({
|
|
96
|
+
admin: { login: loginMock, me: vi.fn().mockResolvedValue({ data: null, error: null }) },
|
|
97
|
+
} as any);
|
|
98
|
+
|
|
99
|
+
const { result } = renderHook(
|
|
100
|
+
() => useAdminAuth({ autoCheck: false }),
|
|
101
|
+
{ wrapper: createWrapper(client) }
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await expect(act(async () => {
|
|
105
|
+
await result.current.login('admin@example.com', 'wrong-password');
|
|
106
|
+
})).rejects.toThrow();
|
|
107
|
+
|
|
108
|
+
// User should remain null after failed login
|
|
109
|
+
expect(result.current.user).toBeNull();
|
|
110
|
+
expect(result.current.isAuthenticated).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should logout', async () => {
|
|
114
|
+
const mockUser = { id: '1', email: 'admin@example.com', role: 'admin' };
|
|
115
|
+
const meMock = vi.fn().mockResolvedValue({ data: { user: mockUser }, error: null });
|
|
116
|
+
|
|
117
|
+
const client = createMockClient({
|
|
118
|
+
admin: { me: meMock },
|
|
119
|
+
} as any);
|
|
120
|
+
|
|
121
|
+
const { result } = renderHook(
|
|
122
|
+
() => useAdminAuth({ autoCheck: true }),
|
|
123
|
+
{ wrapper: createWrapper(client) }
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
await waitFor(() => expect(result.current.isAuthenticated).toBe(true));
|
|
127
|
+
|
|
128
|
+
await act(async () => {
|
|
129
|
+
await result.current.logout();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(result.current.user).toBeNull();
|
|
133
|
+
expect(result.current.isAuthenticated).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should refresh user info', async () => {
|
|
137
|
+
const mockUser = { id: '1', email: 'admin@example.com', role: 'admin' };
|
|
138
|
+
const meMock = vi.fn().mockResolvedValue({ data: { user: mockUser }, error: null });
|
|
139
|
+
|
|
140
|
+
const client = createMockClient({
|
|
141
|
+
admin: { me: meMock },
|
|
142
|
+
} as any);
|
|
143
|
+
|
|
144
|
+
const { result } = renderHook(
|
|
145
|
+
() => useAdminAuth({ autoCheck: false }),
|
|
146
|
+
{ wrapper: createWrapper(client) }
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
await act(async () => {
|
|
150
|
+
await result.current.refresh();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(meMock).toHaveBeenCalledTimes(1);
|
|
154
|
+
expect(result.current.user).toEqual(mockUser);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should show loading state during operations', async () => {
|
|
158
|
+
const meMock = vi.fn().mockImplementation(() => new Promise((resolve) => {
|
|
159
|
+
setTimeout(() => resolve({ data: { user: {} }, error: null }), 100);
|
|
160
|
+
}));
|
|
161
|
+
|
|
162
|
+
const client = createMockClient({
|
|
163
|
+
admin: { me: meMock },
|
|
164
|
+
} as any);
|
|
165
|
+
|
|
166
|
+
const { result } = renderHook(
|
|
167
|
+
() => useAdminAuth({ autoCheck: true }),
|
|
168
|
+
{ wrapper: createWrapper(client) }
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
expect(result.current.isLoading).toBe(true);
|
|
172
|
+
|
|
173
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
174
|
+
});
|
|
175
|
+
});
|
package/src/use-admin-auth.ts
CHANGED
|
@@ -137,14 +137,22 @@ export function useAdminAuth(
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* Logout admin
|
|
140
|
+
*
|
|
141
|
+
* WARNING: Currently only clears local state. The server-side session/token
|
|
142
|
+
* remains valid until it expires. This should call a logout endpoint to
|
|
143
|
+
* invalidate the session on the server for proper security.
|
|
140
144
|
*/
|
|
141
145
|
const logout = useCallback(async (): Promise<void> => {
|
|
142
146
|
try {
|
|
143
147
|
setIsLoading(true);
|
|
144
148
|
setError(null);
|
|
145
|
-
|
|
149
|
+
|
|
150
|
+
// TODO: Call server-side logout endpoint when available
|
|
151
|
+
// This is a security concern - the token remains valid on the server
|
|
152
|
+
// await client.admin.logout();
|
|
153
|
+
|
|
154
|
+
// Clear local user state
|
|
146
155
|
setUser(null);
|
|
147
|
-
// Note: Add logout endpoint call here when available
|
|
148
156
|
} catch (err) {
|
|
149
157
|
setError(err as Error);
|
|
150
158
|
throw err;
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for admin hooks (settings, webhooks)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
6
|
+
import { renderHook, waitFor, act } from "@testing-library/react";
|
|
7
|
+
import {
|
|
8
|
+
useAppSettings,
|
|
9
|
+
useSystemSettings,
|
|
10
|
+
useWebhooks,
|
|
11
|
+
} from "./use-admin-hooks";
|
|
12
|
+
import { createMockClient, createWrapper } from "./test-utils";
|
|
13
|
+
|
|
14
|
+
describe("useAppSettings", () => {
|
|
15
|
+
it("should fetch settings on mount when autoFetch is true", async () => {
|
|
16
|
+
const mockSettings = { features: { darkMode: true } };
|
|
17
|
+
const getMock = vi.fn().mockResolvedValue(mockSettings);
|
|
18
|
+
|
|
19
|
+
const client = createMockClient({
|
|
20
|
+
admin: {
|
|
21
|
+
settings: {
|
|
22
|
+
app: { get: getMock, update: vi.fn() },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
} as any);
|
|
26
|
+
|
|
27
|
+
const { result } = renderHook(() => useAppSettings({ autoFetch: true }), {
|
|
28
|
+
wrapper: createWrapper(client),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
32
|
+
expect(result.current.settings).toEqual(mockSettings);
|
|
33
|
+
expect(getMock).toHaveBeenCalled();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should not fetch settings on mount when autoFetch is false", async () => {
|
|
37
|
+
const getMock = vi.fn();
|
|
38
|
+
|
|
39
|
+
const client = createMockClient({
|
|
40
|
+
admin: {
|
|
41
|
+
settings: {
|
|
42
|
+
app: { get: getMock, update: vi.fn() },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
} as any);
|
|
46
|
+
|
|
47
|
+
const { result } = renderHook(() => useAppSettings({ autoFetch: false }), {
|
|
48
|
+
wrapper: createWrapper(client),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
52
|
+
expect(getMock).not.toHaveBeenCalled();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should update settings", async () => {
|
|
56
|
+
const mockSettings = { features: { darkMode: true } };
|
|
57
|
+
const getMock = vi.fn().mockResolvedValue(mockSettings);
|
|
58
|
+
const updateMock = vi.fn().mockResolvedValue({});
|
|
59
|
+
|
|
60
|
+
const client = createMockClient({
|
|
61
|
+
admin: {
|
|
62
|
+
settings: {
|
|
63
|
+
app: { get: getMock, update: updateMock },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
} as any);
|
|
67
|
+
|
|
68
|
+
const { result } = renderHook(() => useAppSettings({ autoFetch: true }), {
|
|
69
|
+
wrapper: createWrapper(client),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
73
|
+
|
|
74
|
+
await act(async () => {
|
|
75
|
+
await result.current.updateSettings({
|
|
76
|
+
features: { enable_realtime: false },
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(updateMock).toHaveBeenCalledWith({
|
|
81
|
+
features: { enable_realtime: false },
|
|
82
|
+
});
|
|
83
|
+
// Should refetch after update
|
|
84
|
+
expect(getMock).toHaveBeenCalledTimes(2);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should handle errors", async () => {
|
|
88
|
+
const error = new Error("Failed to fetch");
|
|
89
|
+
const getMock = vi.fn().mockRejectedValue(error);
|
|
90
|
+
|
|
91
|
+
const client = createMockClient({
|
|
92
|
+
admin: {
|
|
93
|
+
settings: {
|
|
94
|
+
app: { get: getMock, update: vi.fn() },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
} as any);
|
|
98
|
+
|
|
99
|
+
const { result } = renderHook(() => useAppSettings({ autoFetch: true }), {
|
|
100
|
+
wrapper: createWrapper(client),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
104
|
+
expect(result.current.error).toBe(error);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should refetch on demand", async () => {
|
|
108
|
+
const mockSettings = { features: {} };
|
|
109
|
+
const getMock = vi.fn().mockResolvedValue(mockSettings);
|
|
110
|
+
|
|
111
|
+
const client = createMockClient({
|
|
112
|
+
admin: {
|
|
113
|
+
settings: {
|
|
114
|
+
app: { get: getMock, update: vi.fn() },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
} as any);
|
|
118
|
+
|
|
119
|
+
const { result } = renderHook(() => useAppSettings({ autoFetch: false }), {
|
|
120
|
+
wrapper: createWrapper(client),
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await act(async () => {
|
|
124
|
+
await result.current.refetch();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(getMock).toHaveBeenCalledTimes(1);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("useSystemSettings", () => {
|
|
132
|
+
it("should fetch settings on mount when autoFetch is true", async () => {
|
|
133
|
+
const mockSettings = [{ key: "theme", value: "dark" }];
|
|
134
|
+
const listMock = vi.fn().mockResolvedValue({ settings: mockSettings });
|
|
135
|
+
|
|
136
|
+
const client = createMockClient({
|
|
137
|
+
admin: {
|
|
138
|
+
settings: {
|
|
139
|
+
system: { list: listMock, update: vi.fn(), delete: vi.fn() },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
} as any);
|
|
143
|
+
|
|
144
|
+
const { result } = renderHook(
|
|
145
|
+
() => useSystemSettings({ autoFetch: true }),
|
|
146
|
+
{ wrapper: createWrapper(client) },
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
150
|
+
expect(result.current.settings).toEqual(mockSettings);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should get setting by key", async () => {
|
|
154
|
+
const mockSettings = [
|
|
155
|
+
{ key: "theme", value: "dark" },
|
|
156
|
+
{ key: "language", value: "en" },
|
|
157
|
+
];
|
|
158
|
+
const listMock = vi.fn().mockResolvedValue({ settings: mockSettings });
|
|
159
|
+
|
|
160
|
+
const client = createMockClient({
|
|
161
|
+
admin: {
|
|
162
|
+
settings: {
|
|
163
|
+
system: { list: listMock, update: vi.fn(), delete: vi.fn() },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
} as any);
|
|
167
|
+
|
|
168
|
+
const { result } = renderHook(
|
|
169
|
+
() => useSystemSettings({ autoFetch: true }),
|
|
170
|
+
{ wrapper: createWrapper(client) },
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
174
|
+
|
|
175
|
+
const setting = result.current.getSetting("theme");
|
|
176
|
+
expect(setting).toEqual({ key: "theme", value: "dark" });
|
|
177
|
+
|
|
178
|
+
const notFound = result.current.getSetting("nonexistent");
|
|
179
|
+
expect(notFound).toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should update setting", async () => {
|
|
183
|
+
const mockSettings = [{ key: "theme", value: "dark" }];
|
|
184
|
+
const listMock = vi.fn().mockResolvedValue({ settings: mockSettings });
|
|
185
|
+
const updateMock = vi.fn().mockResolvedValue({});
|
|
186
|
+
|
|
187
|
+
const client = createMockClient({
|
|
188
|
+
admin: {
|
|
189
|
+
settings: {
|
|
190
|
+
system: { list: listMock, update: updateMock, delete: vi.fn() },
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
} as any);
|
|
194
|
+
|
|
195
|
+
const { result } = renderHook(
|
|
196
|
+
() => useSystemSettings({ autoFetch: true }),
|
|
197
|
+
{ wrapper: createWrapper(client) },
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
201
|
+
|
|
202
|
+
await act(async () => {
|
|
203
|
+
await result.current.updateSetting("theme", {
|
|
204
|
+
value: { theme: "light" },
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(updateMock).toHaveBeenCalledWith("theme", {
|
|
209
|
+
value: { theme: "light" },
|
|
210
|
+
});
|
|
211
|
+
// Should refetch after update
|
|
212
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should delete setting", async () => {
|
|
216
|
+
const mockSettings = [{ key: "theme", value: "dark" }];
|
|
217
|
+
const listMock = vi.fn().mockResolvedValue({ settings: mockSettings });
|
|
218
|
+
const deleteMock = vi.fn().mockResolvedValue({});
|
|
219
|
+
|
|
220
|
+
const client = createMockClient({
|
|
221
|
+
admin: {
|
|
222
|
+
settings: {
|
|
223
|
+
system: { list: listMock, update: vi.fn(), delete: deleteMock },
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
} as any);
|
|
227
|
+
|
|
228
|
+
const { result } = renderHook(
|
|
229
|
+
() => useSystemSettings({ autoFetch: true }),
|
|
230
|
+
{ wrapper: createWrapper(client) },
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
234
|
+
|
|
235
|
+
await act(async () => {
|
|
236
|
+
await result.current.deleteSetting("theme");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(deleteMock).toHaveBeenCalledWith("theme");
|
|
240
|
+
// Should refetch after delete
|
|
241
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("useWebhooks", () => {
|
|
246
|
+
it("should fetch webhooks on mount when autoFetch is true", async () => {
|
|
247
|
+
const mockWebhooks = [{ id: "1", url: "https://example.com/webhook" }];
|
|
248
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
249
|
+
|
|
250
|
+
const client = createMockClient({
|
|
251
|
+
admin: {
|
|
252
|
+
management: {
|
|
253
|
+
webhooks: {
|
|
254
|
+
list: listMock,
|
|
255
|
+
create: vi.fn(),
|
|
256
|
+
update: vi.fn(),
|
|
257
|
+
delete: vi.fn(),
|
|
258
|
+
test: vi.fn(),
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
} as any);
|
|
263
|
+
|
|
264
|
+
const { result } = renderHook(() => useWebhooks({ autoFetch: true }), {
|
|
265
|
+
wrapper: createWrapper(client),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
269
|
+
expect(result.current.webhooks).toEqual(mockWebhooks);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("should create webhook", async () => {
|
|
273
|
+
const mockWebhooks: any[] = [];
|
|
274
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
275
|
+
const createMock = vi
|
|
276
|
+
.fn()
|
|
277
|
+
.mockResolvedValue({ id: "1", url: "https://example.com/webhook" });
|
|
278
|
+
|
|
279
|
+
const client = createMockClient({
|
|
280
|
+
admin: {
|
|
281
|
+
management: {
|
|
282
|
+
webhooks: {
|
|
283
|
+
list: listMock,
|
|
284
|
+
create: createMock,
|
|
285
|
+
update: vi.fn(),
|
|
286
|
+
delete: vi.fn(),
|
|
287
|
+
test: vi.fn(),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
} as any);
|
|
292
|
+
|
|
293
|
+
const { result } = renderHook(() => useWebhooks({ autoFetch: true }), {
|
|
294
|
+
wrapper: createWrapper(client),
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
298
|
+
|
|
299
|
+
await act(async () => {
|
|
300
|
+
await result.current.createWebhook({
|
|
301
|
+
url: "https://example.com/webhook",
|
|
302
|
+
events: ["user.created"],
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
expect(createMock).toHaveBeenCalledWith({
|
|
307
|
+
url: "https://example.com/webhook",
|
|
308
|
+
events: ["user.created"],
|
|
309
|
+
});
|
|
310
|
+
// Should refetch after create
|
|
311
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("should update webhook", async () => {
|
|
315
|
+
const mockWebhooks = [{ id: "1", url: "https://example.com/webhook" }];
|
|
316
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
317
|
+
const updateMock = vi
|
|
318
|
+
.fn()
|
|
319
|
+
.mockResolvedValue({ id: "1", url: "https://new.com/webhook" });
|
|
320
|
+
|
|
321
|
+
const client = createMockClient({
|
|
322
|
+
admin: {
|
|
323
|
+
management: {
|
|
324
|
+
webhooks: {
|
|
325
|
+
list: listMock,
|
|
326
|
+
create: vi.fn(),
|
|
327
|
+
update: updateMock,
|
|
328
|
+
delete: vi.fn(),
|
|
329
|
+
test: vi.fn(),
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
} as any);
|
|
334
|
+
|
|
335
|
+
const { result } = renderHook(() => useWebhooks({ autoFetch: true }), {
|
|
336
|
+
wrapper: createWrapper(client),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
340
|
+
|
|
341
|
+
await act(async () => {
|
|
342
|
+
await result.current.updateWebhook("1", {
|
|
343
|
+
url: "https://new.com/webhook",
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(updateMock).toHaveBeenCalledWith("1", {
|
|
348
|
+
url: "https://new.com/webhook",
|
|
349
|
+
});
|
|
350
|
+
// Should refetch after update
|
|
351
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("should delete webhook", async () => {
|
|
355
|
+
const mockWebhooks = [{ id: "1", url: "https://example.com/webhook" }];
|
|
356
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
357
|
+
const deleteMock = vi.fn().mockResolvedValue({});
|
|
358
|
+
|
|
359
|
+
const client = createMockClient({
|
|
360
|
+
admin: {
|
|
361
|
+
management: {
|
|
362
|
+
webhooks: {
|
|
363
|
+
list: listMock,
|
|
364
|
+
create: vi.fn(),
|
|
365
|
+
update: vi.fn(),
|
|
366
|
+
delete: deleteMock,
|
|
367
|
+
test: vi.fn(),
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
} as any);
|
|
372
|
+
|
|
373
|
+
const { result } = renderHook(() => useWebhooks({ autoFetch: true }), {
|
|
374
|
+
wrapper: createWrapper(client),
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
378
|
+
|
|
379
|
+
await act(async () => {
|
|
380
|
+
await result.current.deleteWebhook("1");
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
expect(deleteMock).toHaveBeenCalledWith("1");
|
|
384
|
+
// Should refetch after delete
|
|
385
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it("should test webhook", async () => {
|
|
389
|
+
const mockWebhooks = [{ id: "1", url: "https://example.com/webhook" }];
|
|
390
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
391
|
+
const testMock = vi.fn().mockResolvedValue({});
|
|
392
|
+
|
|
393
|
+
const client = createMockClient({
|
|
394
|
+
admin: {
|
|
395
|
+
management: {
|
|
396
|
+
webhooks: {
|
|
397
|
+
list: listMock,
|
|
398
|
+
create: vi.fn(),
|
|
399
|
+
update: vi.fn(),
|
|
400
|
+
delete: vi.fn(),
|
|
401
|
+
test: testMock,
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
} as any);
|
|
406
|
+
|
|
407
|
+
const { result } = renderHook(() => useWebhooks({ autoFetch: true }), {
|
|
408
|
+
wrapper: createWrapper(client),
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
412
|
+
|
|
413
|
+
await act(async () => {
|
|
414
|
+
await result.current.testWebhook("1");
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
expect(testMock).toHaveBeenCalledWith("1");
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("should set up refetch interval", async () => {
|
|
421
|
+
vi.useFakeTimers();
|
|
422
|
+
const mockWebhooks: any[] = [];
|
|
423
|
+
const listMock = vi.fn().mockResolvedValue({ webhooks: mockWebhooks });
|
|
424
|
+
|
|
425
|
+
const client = createMockClient({
|
|
426
|
+
admin: {
|
|
427
|
+
management: {
|
|
428
|
+
webhooks: {
|
|
429
|
+
list: listMock,
|
|
430
|
+
create: vi.fn(),
|
|
431
|
+
update: vi.fn(),
|
|
432
|
+
delete: vi.fn(),
|
|
433
|
+
test: vi.fn(),
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
} as any);
|
|
438
|
+
|
|
439
|
+
const { unmount } = renderHook(
|
|
440
|
+
() => useWebhooks({ autoFetch: true, refetchInterval: 5000 }),
|
|
441
|
+
{ wrapper: createWrapper(client) },
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
// Initial fetch
|
|
445
|
+
expect(listMock).toHaveBeenCalledTimes(1);
|
|
446
|
+
|
|
447
|
+
// Advance timer
|
|
448
|
+
await act(async () => {
|
|
449
|
+
vi.advanceTimersByTime(5000);
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
expect(listMock).toHaveBeenCalledTimes(2);
|
|
453
|
+
|
|
454
|
+
unmount();
|
|
455
|
+
vi.useRealTimers();
|
|
456
|
+
});
|
|
457
|
+
});
|