@aiao/rxdb-react 0.0.9 → 0.0.10
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/package.json +3 -3
- package/src/hooks.spec.ts +276 -2
- package/src/rxdb-react.spec.tsx +41 -1
- package/vite.config.mts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiao/rxdb-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@aiao/rxdb": "0.0.
|
|
19
|
-
"@aiao/utils": "0.0.
|
|
18
|
+
"@aiao/rxdb": "0.0.10",
|
|
19
|
+
"@aiao/utils": "0.0.10"
|
|
20
20
|
},
|
|
21
21
|
"nx": {
|
|
22
22
|
"name": "rxdb-react",
|
package/src/hooks.spec.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import { of } from 'rxjs';
|
|
1
|
+
import { of, Subject, throwError } from 'rxjs';
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
useCount,
|
|
5
|
+
useCountAncestors,
|
|
6
|
+
useCountDescendants,
|
|
7
|
+
useCountNeighbors,
|
|
8
|
+
useFind,
|
|
9
|
+
useFindAll,
|
|
10
|
+
useFindAncestors,
|
|
11
|
+
useFindDescendants,
|
|
12
|
+
useFindOne,
|
|
13
|
+
useFindOneOrFail,
|
|
14
|
+
useGet,
|
|
15
|
+
useGraphNeighbors,
|
|
16
|
+
useGraphPaths
|
|
17
|
+
} from './hooks';
|
|
4
18
|
|
|
5
19
|
// Mock React hooks
|
|
6
20
|
let mockState: any[] = [];
|
|
@@ -32,7 +46,17 @@ class MockEntity {
|
|
|
32
46
|
name!: string;
|
|
33
47
|
static get = vi.fn();
|
|
34
48
|
static findOne = vi.fn();
|
|
49
|
+
static findOneOrFail = vi.fn();
|
|
35
50
|
static find = vi.fn();
|
|
51
|
+
static findAll = vi.fn();
|
|
52
|
+
static count = vi.fn();
|
|
53
|
+
static findDescendants = vi.fn();
|
|
54
|
+
static countDescendants = vi.fn();
|
|
55
|
+
static findAncestors = vi.fn();
|
|
56
|
+
static countAncestors = vi.fn();
|
|
57
|
+
static findNeighbors = vi.fn();
|
|
58
|
+
static countNeighbors = vi.fn();
|
|
59
|
+
static findPaths = vi.fn();
|
|
36
60
|
}
|
|
37
61
|
|
|
38
62
|
describe('useGet', () => {
|
|
@@ -124,4 +148,254 @@ describe('Hook cleanup', () => {
|
|
|
124
148
|
|
|
125
149
|
expect(mockEffectCleanups.length).toBeGreaterThan(0);
|
|
126
150
|
});
|
|
151
|
+
|
|
152
|
+
it('should unsubscribe on cleanup', () => {
|
|
153
|
+
const mockUnsubscribe = vi.fn();
|
|
154
|
+
const mockSubscription = { unsubscribe: mockUnsubscribe };
|
|
155
|
+
const subject = new Subject();
|
|
156
|
+
MockEntity.get.mockReturnValue(subject.asObservable());
|
|
157
|
+
(subject.subscribe as any) = vi.fn(() => mockSubscription);
|
|
158
|
+
|
|
159
|
+
MockEntity.get.mockReturnValue({
|
|
160
|
+
subscribe: vi.fn(() => mockSubscription)
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
useGet(MockEntity as any, '1');
|
|
164
|
+
|
|
165
|
+
// Execute cleanup
|
|
166
|
+
mockEffectCleanups.forEach(cleanup => cleanup());
|
|
167
|
+
|
|
168
|
+
expect(mockUnsubscribe).toHaveBeenCalled();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should clean up previous subscription on re-render', () => {
|
|
172
|
+
const mockUnsubscribe = vi.fn();
|
|
173
|
+
const mockSubscription = { unsubscribe: mockUnsubscribe };
|
|
174
|
+
|
|
175
|
+
MockEntity.get.mockReturnValue({
|
|
176
|
+
subscribe: vi.fn(() => mockSubscription)
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// First render
|
|
180
|
+
useGet(MockEntity as any, '1');
|
|
181
|
+
|
|
182
|
+
// Simulate re-render by calling effect again (the effect will clean up previous subscription)
|
|
183
|
+
mockState = [];
|
|
184
|
+
mockStateIndex = 0;
|
|
185
|
+
|
|
186
|
+
// Second render - this should trigger cleanup of previous subscription
|
|
187
|
+
useGet(MockEntity as any, '2');
|
|
188
|
+
|
|
189
|
+
// The cleanup from first render should have been called
|
|
190
|
+
expect(mockEffectCleanups.length).toBeGreaterThan(0);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('Error handling', () => {
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
vi.clearAllMocks();
|
|
197
|
+
mockState = [];
|
|
198
|
+
mockStateIndex = 0;
|
|
199
|
+
mockEffectCleanups = [];
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should handle method not found error', async () => {
|
|
203
|
+
// Use a mock entity without the method
|
|
204
|
+
const NoMethodEntity = {} as any;
|
|
205
|
+
|
|
206
|
+
const result = useGet(NoMethodEntity, '1');
|
|
207
|
+
|
|
208
|
+
// Wait for Promise.resolve to complete
|
|
209
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
210
|
+
|
|
211
|
+
expect(result).toBeDefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should handle subscription error', () => {
|
|
215
|
+
const testError = new Error('Test error');
|
|
216
|
+
MockEntity.get.mockReturnValue(throwError(() => testError));
|
|
217
|
+
|
|
218
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
219
|
+
|
|
220
|
+
const result = useGet(MockEntity as any, '1');
|
|
221
|
+
|
|
222
|
+
expect(result).toBeDefined();
|
|
223
|
+
consoleSpy.mockRestore();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should handle exception during query execution', () => {
|
|
227
|
+
MockEntity.get.mockImplementation(() => {
|
|
228
|
+
throw new Error('Sync error');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const result = useGet(MockEntity as any, '1');
|
|
232
|
+
|
|
233
|
+
expect(result).toBeDefined();
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('isEmpty behavior', () => {
|
|
238
|
+
beforeEach(() => {
|
|
239
|
+
vi.clearAllMocks();
|
|
240
|
+
mockState = [];
|
|
241
|
+
mockStateIndex = 0;
|
|
242
|
+
mockEffectCleanups = [];
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('should set isEmpty true for empty array', () => {
|
|
246
|
+
MockEntity.find.mockReturnValue(of([]));
|
|
247
|
+
|
|
248
|
+
const result = useFind(MockEntity as any, {});
|
|
249
|
+
|
|
250
|
+
expect(result).toBeDefined();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should set isEmpty false for non-empty array', () => {
|
|
254
|
+
MockEntity.find.mockReturnValue(of([{ id: '1' }]));
|
|
255
|
+
|
|
256
|
+
const result = useFind(MockEntity as any, {});
|
|
257
|
+
|
|
258
|
+
expect(result).toBeDefined();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('should set isEmpty true for null value', () => {
|
|
262
|
+
MockEntity.findOne.mockReturnValue(of(null));
|
|
263
|
+
|
|
264
|
+
const result = useFindOne(MockEntity as any, {});
|
|
265
|
+
|
|
266
|
+
expect(result).toBeDefined();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should set isEmpty false for non-null value', () => {
|
|
270
|
+
MockEntity.findOne.mockReturnValue(of({ id: '1' }));
|
|
271
|
+
|
|
272
|
+
const result = useFindOne(MockEntity as any, {});
|
|
273
|
+
|
|
274
|
+
expect(result).toBeDefined();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
describe('useFindOneOrFail', () => {
|
|
279
|
+
beforeEach(() => {
|
|
280
|
+
vi.clearAllMocks();
|
|
281
|
+
mockState = [];
|
|
282
|
+
mockStateIndex = 0;
|
|
283
|
+
mockEffectCleanups = [];
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('should call findOneOrFail method', () => {
|
|
287
|
+
MockEntity.findOneOrFail.mockReturnValue(of({ id: '1', name: 'Found' }));
|
|
288
|
+
|
|
289
|
+
useFindOneOrFail(MockEntity as any, { where: { id: '1' } });
|
|
290
|
+
|
|
291
|
+
expect(MockEntity.findOneOrFail).toHaveBeenCalledWith({ where: { id: '1' } });
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('useFindAll', () => {
|
|
296
|
+
beforeEach(() => {
|
|
297
|
+
vi.clearAllMocks();
|
|
298
|
+
mockState = [];
|
|
299
|
+
mockStateIndex = 0;
|
|
300
|
+
mockEffectCleanups = [];
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should call findAll method', () => {
|
|
304
|
+
MockEntity.findAll.mockReturnValue(of([{ id: '1' }, { id: '2' }]));
|
|
305
|
+
|
|
306
|
+
useFindAll(MockEntity as any, {});
|
|
307
|
+
|
|
308
|
+
expect(MockEntity.findAll).toHaveBeenCalled();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('useCount', () => {
|
|
313
|
+
beforeEach(() => {
|
|
314
|
+
vi.clearAllMocks();
|
|
315
|
+
mockState = [];
|
|
316
|
+
mockStateIndex = 0;
|
|
317
|
+
mockEffectCleanups = [];
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should call count method', () => {
|
|
321
|
+
MockEntity.count.mockReturnValue(of(5));
|
|
322
|
+
|
|
323
|
+
useCount(MockEntity as any, {});
|
|
324
|
+
|
|
325
|
+
expect(MockEntity.count).toHaveBeenCalled();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('Tree Repository Hooks', () => {
|
|
330
|
+
beforeEach(() => {
|
|
331
|
+
vi.clearAllMocks();
|
|
332
|
+
mockState = [];
|
|
333
|
+
mockStateIndex = 0;
|
|
334
|
+
mockEffectCleanups = [];
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('useFindDescendants should call findDescendants', () => {
|
|
338
|
+
MockEntity.findDescendants.mockReturnValue(of([{ id: '2' }]));
|
|
339
|
+
|
|
340
|
+
useFindDescendants(MockEntity as any, { entityId: '1' });
|
|
341
|
+
|
|
342
|
+
expect(MockEntity.findDescendants).toHaveBeenCalledWith({ entityId: '1' });
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('useCountDescendants should call countDescendants', () => {
|
|
346
|
+
MockEntity.countDescendants.mockReturnValue(of(3));
|
|
347
|
+
|
|
348
|
+
useCountDescendants(MockEntity as any, { entityId: '1' });
|
|
349
|
+
|
|
350
|
+
expect(MockEntity.countDescendants).toHaveBeenCalledWith({ entityId: '1' });
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('useFindAncestors should call findAncestors', () => {
|
|
354
|
+
MockEntity.findAncestors.mockReturnValue(of([{ id: '0' }]));
|
|
355
|
+
|
|
356
|
+
useFindAncestors(MockEntity as any, { entityId: '1' });
|
|
357
|
+
|
|
358
|
+
expect(MockEntity.findAncestors).toHaveBeenCalledWith({ entityId: '1' });
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('useCountAncestors should call countAncestors', () => {
|
|
362
|
+
MockEntity.countAncestors.mockReturnValue(of(2));
|
|
363
|
+
|
|
364
|
+
useCountAncestors(MockEntity as any, { entityId: '1' });
|
|
365
|
+
|
|
366
|
+
expect(MockEntity.countAncestors).toHaveBeenCalledWith({ entityId: '1' });
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('Graph Repository Hooks', () => {
|
|
371
|
+
beforeEach(() => {
|
|
372
|
+
vi.clearAllMocks();
|
|
373
|
+
mockState = [];
|
|
374
|
+
mockStateIndex = 0;
|
|
375
|
+
mockEffectCleanups = [];
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('useGraphNeighbors should call findNeighbors', () => {
|
|
379
|
+
MockEntity.findNeighbors.mockReturnValue(of([{ id: '2' }]));
|
|
380
|
+
|
|
381
|
+
useGraphNeighbors(MockEntity as any, { entityId: '1', direction: 'out', level: 1 });
|
|
382
|
+
|
|
383
|
+
expect(MockEntity.findNeighbors).toHaveBeenCalledWith({ entityId: '1', direction: 'out', level: 1 });
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it('useCountNeighbors should call countNeighbors', () => {
|
|
387
|
+
MockEntity.countNeighbors.mockReturnValue(of(5));
|
|
388
|
+
|
|
389
|
+
useCountNeighbors(MockEntity as any, { entityId: '1', direction: 'out', level: 1 });
|
|
390
|
+
|
|
391
|
+
expect(MockEntity.countNeighbors).toHaveBeenCalledWith({ entityId: '1', direction: 'out', level: 1 });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('useGraphPaths should call findPaths', () => {
|
|
395
|
+
MockEntity.findPaths.mockReturnValue(of([['1', '2', '3']]));
|
|
396
|
+
|
|
397
|
+
useGraphPaths(MockEntity as any, { fromId: '1', toId: '3', maxDepth: 5 });
|
|
398
|
+
|
|
399
|
+
expect(MockEntity.findPaths).toHaveBeenCalledWith({ fromId: '1', toId: '3', maxDepth: 5 });
|
|
400
|
+
});
|
|
127
401
|
});
|
package/src/rxdb-react.spec.tsx
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
import { makeRxDBProvider, RxDBProvider, useRxDB } from './rxdb-react';
|
|
4
4
|
|
|
5
|
+
// Mock React hooks for testing useRxDB behavior
|
|
6
|
+
let mockContextValue: any = undefined;
|
|
7
|
+
vi.mock('react', async () => {
|
|
8
|
+
const actual = await vi.importActual<typeof React>('react');
|
|
9
|
+
return {
|
|
10
|
+
...actual,
|
|
11
|
+
useContext: () => mockContextValue,
|
|
12
|
+
createContext: actual.createContext
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
5
16
|
describe('makeRxDBProvider', () => {
|
|
6
17
|
it('should create RxDBProvider and useRxDB hook', () => {
|
|
7
18
|
const { RxDBProvider, useRxDB } = makeRxDBProvider();
|
|
@@ -39,3 +50,32 @@ describe('Default exports', () => {
|
|
|
39
50
|
expect(typeof makeRxDBProvider).toBe('function');
|
|
40
51
|
});
|
|
41
52
|
});
|
|
53
|
+
|
|
54
|
+
describe('useRxDB behavior', () => {
|
|
55
|
+
it('should return provided db parameter if given', () => {
|
|
56
|
+
const { useRxDB } = makeRxDBProvider();
|
|
57
|
+
const directDB = { name: 'direct-db' } as any;
|
|
58
|
+
mockContextValue = { name: 'context-db' };
|
|
59
|
+
|
|
60
|
+
const result = useRxDB(directDB);
|
|
61
|
+
|
|
62
|
+
expect(result).toBe(directDB);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return context db when no parameter given', () => {
|
|
66
|
+
const { useRxDB } = makeRxDBProvider();
|
|
67
|
+
const contextDB = { name: 'context-db' };
|
|
68
|
+
mockContextValue = contextDB;
|
|
69
|
+
|
|
70
|
+
const result = useRxDB();
|
|
71
|
+
|
|
72
|
+
expect(result).toBe(contextDB);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should throw error when no db in context and no parameter', () => {
|
|
76
|
+
const { useRxDB } = makeRxDBProvider();
|
|
77
|
+
mockContextValue = undefined;
|
|
78
|
+
|
|
79
|
+
expect(() => useRxDB()).toThrow('No RxDB instance found, use RxDBProvider to provide one');
|
|
80
|
+
});
|
|
81
|
+
});
|
package/vite.config.mts
CHANGED
|
@@ -72,9 +72,10 @@ export default defineConfig({
|
|
|
72
72
|
coverage: {
|
|
73
73
|
enabled: true,
|
|
74
74
|
reportsDirectory: '../../coverage/packages/rxdb-react',
|
|
75
|
-
provider: 'v8',
|
|
75
|
+
provider: 'v8' as const,
|
|
76
76
|
include: ['src/**/*'],
|
|
77
|
-
exclude: ['**/index.ts', '**/dist/**']
|
|
77
|
+
exclude: ['**/index.ts', '**/dist/**'],
|
|
78
|
+
all: false
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
});
|