richie-education 2.25.0-b2.dev156 → 2.25.0-b2.dev161
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/js/hooks/useUnionResource/index.spec.tsx +137 -4
- package/js/hooks/useUnionResource/index.ts +11 -3
- package/js/hooks/useUnionResource/utils/fetchEntity.ts +5 -8
- package/js/widgets/Dashboard/components/DashboardBreadcrumbs/index.tsx +2 -2
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +5 -5
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
2
2
|
import fetchMock from 'fetch-mock';
|
|
3
3
|
import queryString from 'query-string';
|
|
4
|
-
import { PaginatedResourceQuery } from 'types/Joanie';
|
|
4
|
+
import { PaginatedResourceQuery, PaginatedResponse } from 'types/Joanie';
|
|
5
5
|
import { Deferred } from 'utils/test/deferred';
|
|
6
6
|
import { noop } from 'utils';
|
|
7
7
|
import { mockPaginatedResponse } from 'utils/test/mockPaginatedResponse';
|
|
@@ -281,7 +281,15 @@ describe('useUnionResource', () => {
|
|
|
281
281
|
});
|
|
282
282
|
|
|
283
283
|
it('should refetch data when filters change', async () => {
|
|
284
|
-
fetchMock.get(
|
|
284
|
+
fetchMock.get(
|
|
285
|
+
'http://data.a/?page=1',
|
|
286
|
+
mockPaginatedResponse([dataAList[0], dataAList[1], dataAList[2]], 7, true),
|
|
287
|
+
);
|
|
288
|
+
fetchMock.get(
|
|
289
|
+
'http://data.a/?page=2',
|
|
290
|
+
mockPaginatedResponse([dataAList[3], dataAList[4], dataAList[5]], 7, true),
|
|
291
|
+
);
|
|
292
|
+
fetchMock.get('http://data.a/?page=3', mockPaginatedResponse([dataAList[6]], 7, false));
|
|
285
293
|
fetchMock.get('http://data.b/?page=1', mockPaginatedResponse([], 0, false));
|
|
286
294
|
|
|
287
295
|
const { result, rerender } = renderHook(
|
|
@@ -297,9 +305,24 @@ describe('useUnionResource', () => {
|
|
|
297
305
|
);
|
|
298
306
|
|
|
299
307
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
308
|
+
// two page of 3 item have been fetch but only 3 item are displayed.
|
|
309
|
+
act(() => {
|
|
310
|
+
// display one more page for a total of 6 item displayed
|
|
311
|
+
result.current.next();
|
|
312
|
+
});
|
|
313
|
+
act(() => {
|
|
314
|
+
// display one more page for a total of 7 item displayed
|
|
315
|
+
result.current.next();
|
|
316
|
+
});
|
|
317
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
318
|
+
expect(result.current.hasMore).toBe(false);
|
|
319
|
+
|
|
300
320
|
let calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
301
|
-
|
|
321
|
+
let expectedQueries = 4;
|
|
322
|
+
expect(calledUrls).toHaveLength(expectedQueries);
|
|
302
323
|
expect(calledUrls).toContain('http://data.a/?page=1');
|
|
324
|
+
expect(calledUrls).toContain('http://data.a/?page=2');
|
|
325
|
+
expect(calledUrls).toContain('http://data.a/?page=3');
|
|
303
326
|
expect(calledUrls).toContain('http://data.b/?page=1');
|
|
304
327
|
|
|
305
328
|
queryAConfig.filters = { isFiltered: true };
|
|
@@ -312,8 +335,10 @@ describe('useUnionResource', () => {
|
|
|
312
335
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
313
336
|
|
|
314
337
|
calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
315
|
-
|
|
338
|
+
expectedQueries += 1; // fetch dataA first page
|
|
339
|
+
expect(calledUrls).toHaveLength(expectedQueries);
|
|
316
340
|
expect(calledUrls).toContain('http://data.a/?isFiltered=true&page=1');
|
|
341
|
+
expect(result.current.hasMore).toBe(false);
|
|
317
342
|
});
|
|
318
343
|
|
|
319
344
|
it.each([
|
|
@@ -366,4 +391,112 @@ describe('useUnionResource', () => {
|
|
|
366
391
|
expect(calledUrls.filter((url) => url === 'http://data.a/?page=1')).toHaveLength(2);
|
|
367
392
|
expect(calledUrls.filter((url) => url === 'http://data.b/?page=1')).toHaveLength(1);
|
|
368
393
|
});
|
|
394
|
+
|
|
395
|
+
it('should adapt eof to the total number of result when it change', async () => {
|
|
396
|
+
fetchMock.get(
|
|
397
|
+
'http://data.a/?page=1',
|
|
398
|
+
mockPaginatedResponse([dataAList[0], dataAList[1], dataAList[2]], 6, true),
|
|
399
|
+
);
|
|
400
|
+
fetchMock.get(
|
|
401
|
+
'http://data.a/?page=2',
|
|
402
|
+
mockPaginatedResponse([dataAList[3], dataAList[4], dataAList[5]], 6, false),
|
|
403
|
+
);
|
|
404
|
+
fetchMock.get('http://data.b/?page=1', mockPaginatedResponse([], 0, false));
|
|
405
|
+
|
|
406
|
+
const queryClient = createTestQueryClient({ user: true });
|
|
407
|
+
const { result } = renderHook(
|
|
408
|
+
(queries: {
|
|
409
|
+
queryA?: QueryConfig<TestDataA, TestDataAFilters>;
|
|
410
|
+
queryB?: QueryConfig<TestDataB, PaginatedResourceQuery>;
|
|
411
|
+
}) =>
|
|
412
|
+
useUnionResource<TestDataA, TestDataB, TestDataAFilters, PaginatedResourceQuery>({
|
|
413
|
+
queryAConfig: queries?.queryA || queryAConfig,
|
|
414
|
+
queryBConfig: queries?.queryB || queryBConfig,
|
|
415
|
+
}),
|
|
416
|
+
{
|
|
417
|
+
wrapper: ({ children }) => (
|
|
418
|
+
<ReactQueryAppWrapper queryOptions={{ client: queryClient }}>
|
|
419
|
+
{children}
|
|
420
|
+
</ReactQueryAppWrapper>
|
|
421
|
+
),
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
426
|
+
// two page of 3 item have been fetch but only 3 item are displayed.
|
|
427
|
+
act(() => {
|
|
428
|
+
// display one more page for a total of 6 item displayed
|
|
429
|
+
result.current.next();
|
|
430
|
+
});
|
|
431
|
+
expect(result.current.hasMore).toBe(false);
|
|
432
|
+
|
|
433
|
+
let calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
434
|
+
expect(calledUrls).toHaveLength(3);
|
|
435
|
+
expect(calledUrls).toContain('http://data.a/?page=1');
|
|
436
|
+
expect(calledUrls).toContain('http://data.a/?page=2');
|
|
437
|
+
expect(calledUrls).toContain('http://data.b/?page=1');
|
|
438
|
+
|
|
439
|
+
// simulate backend adding items, totalResults move from 6 to 7
|
|
440
|
+
// it means that we should request one more page.
|
|
441
|
+
fetchMock.restore();
|
|
442
|
+
fetchMock.get(
|
|
443
|
+
'http://data.a/?page=1',
|
|
444
|
+
mockPaginatedResponse([dataAList[0], dataAList[1], dataAList[2]], 7, true),
|
|
445
|
+
);
|
|
446
|
+
fetchMock.get(
|
|
447
|
+
'http://data.a/?page=2',
|
|
448
|
+
mockPaginatedResponse([dataAList[3], dataAList[4], dataAList[5]], 7, true),
|
|
449
|
+
);
|
|
450
|
+
fetchMock.get('http://data.a/?page=3', mockPaginatedResponse([dataAList[6]], 7, false));
|
|
451
|
+
fetchMock.get('http://data.b/?page=1', mockPaginatedResponse([], 0, false));
|
|
452
|
+
|
|
453
|
+
// simulate cache expire
|
|
454
|
+
queryClient.invalidateQueries({ queryKey: ['resourceA'] });
|
|
455
|
+
queryClient.invalidateQueries({ queryKey: ['resourceB'] });
|
|
456
|
+
|
|
457
|
+
await waitFor(() => {
|
|
458
|
+
expect(
|
|
459
|
+
queryClient.getQueryState<PaginatedResponse<TestDataA>>(['resourceA']),
|
|
460
|
+
).toBeUndefined();
|
|
461
|
+
expect(
|
|
462
|
+
queryClient.getQueryState<PaginatedResponse<TestDataB>>(['resourceB']),
|
|
463
|
+
).toBeUndefined();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
await waitFor(() => {
|
|
467
|
+
expect(result.current.isLoading).toBe(false);
|
|
468
|
+
expect(result.current.data).toHaveLength(3);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// two page of 3 item have been fetch but only 3 item are displayed.
|
|
472
|
+
act(() => {
|
|
473
|
+
// display one more page for a total of 6 item displayed
|
|
474
|
+
result.current.next();
|
|
475
|
+
});
|
|
476
|
+
act(() => {
|
|
477
|
+
expect(result.current.isLoading).toBe(false);
|
|
478
|
+
expect(result.current.data).toHaveLength(6);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
act(() => {
|
|
482
|
+
// display one more page for a total of 7 item displayed
|
|
483
|
+
result.current.next();
|
|
484
|
+
});
|
|
485
|
+
await waitFor(() => {
|
|
486
|
+
expect(result.current.isLoading).toBe(false);
|
|
487
|
+
expect(result.current.data).toHaveLength(7);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
await waitFor(() => {
|
|
491
|
+
expect(result.current.hasMore).toBe(false);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
calledUrls = fetchMock.calls().map((call) => call[0]);
|
|
495
|
+
expect(calledUrls).toHaveLength(4);
|
|
496
|
+
|
|
497
|
+
expect(calledUrls).toContain('http://data.a/?page=1');
|
|
498
|
+
expect(calledUrls).toContain('http://data.a/?page=2');
|
|
499
|
+
expect(calledUrls).toContain('http://data.a/?page=3');
|
|
500
|
+
expect(calledUrls).toContain('http://data.b/?page=1');
|
|
501
|
+
});
|
|
369
502
|
});
|
|
@@ -94,7 +94,11 @@ const useUnionResource = <
|
|
|
94
94
|
|
|
95
95
|
// to force execution of useEffect::fetchNewPage(),
|
|
96
96
|
// reset need to generate a uniq key that is part of it's dependencies.
|
|
97
|
-
const reset = () => {
|
|
97
|
+
const reset = (eofKey?: string) => {
|
|
98
|
+
if (eofKey) {
|
|
99
|
+
delete eofRef.current[eofKey];
|
|
100
|
+
}
|
|
101
|
+
|
|
98
102
|
setStack([]);
|
|
99
103
|
setPage(0);
|
|
100
104
|
setTotalCount(undefined);
|
|
@@ -107,8 +111,12 @@ const useUnionResource = <
|
|
|
107
111
|
// we manualy observe key invalidation to trigger new search
|
|
108
112
|
// by generating a new update key
|
|
109
113
|
if (refetchOnInvalidation) {
|
|
110
|
-
useQueryKeyInvalidateListener(queryAConfig.queryKey,
|
|
111
|
-
|
|
114
|
+
useQueryKeyInvalidateListener(queryAConfig.queryKey, () => {
|
|
115
|
+
reset(queryAConfig.queryKey.join('-'));
|
|
116
|
+
});
|
|
117
|
+
useQueryKeyInvalidateListener(queryBConfig.queryKey, () => {
|
|
118
|
+
reset(queryBConfig.queryKey.join('-'));
|
|
119
|
+
});
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
// filters have changes, new results will be fetch.
|
|
@@ -65,14 +65,11 @@ export const fetchEntity = async <
|
|
|
65
65
|
try {
|
|
66
66
|
const res = await fn(filters);
|
|
67
67
|
queryClient.setQueryData(QUERY_KEY, res);
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// be request to non existing pages after reload.
|
|
74
|
-
queryClient.setQueryData(eofQueryKey, eofRef.current);
|
|
75
|
-
}
|
|
68
|
+
// Eof is cached based, the same way, we cache the fetching data. Otherwise there would
|
|
69
|
+
// be request to non existing pages after reload.
|
|
70
|
+
const totalPages = Math.ceil(res.count / perPage);
|
|
71
|
+
eofRef.current = { ...eofRef.current, [queryKeyString]: totalPages };
|
|
72
|
+
queryClient.setQueryData(eofQueryKey, eofRef.current);
|
|
76
73
|
return res;
|
|
77
74
|
} catch (err) {
|
|
78
75
|
if (isHttpError(err)) {
|
|
@@ -60,7 +60,7 @@ export const DashboardBreadcrumbs = () => {
|
|
|
60
60
|
<RouterButton
|
|
61
61
|
href={backPath}
|
|
62
62
|
size="nano"
|
|
63
|
-
color="tertiary"
|
|
63
|
+
color="tertiary-text"
|
|
64
64
|
icon={<span className="material-icons">chevron_left</span>}
|
|
65
65
|
>
|
|
66
66
|
<FormattedMessage {...messages.back} />
|
|
@@ -69,7 +69,7 @@ export const DashboardBreadcrumbs = () => {
|
|
|
69
69
|
|
|
70
70
|
{breadcrumbs.map((breadcrumb) => (
|
|
71
71
|
<li key={breadcrumb.pathname}>
|
|
72
|
-
<RouterButton href={breadcrumb.pathname} size="nano" color="tertiary">
|
|
72
|
+
<RouterButton href={breadcrumb.pathname} size="nano" color="tertiary-text">
|
|
73
73
|
{breadcrumb.name}
|
|
74
74
|
</RouterButton>
|
|
75
75
|
</li>
|
|
@@ -59,7 +59,7 @@ describe('<DashboardItemOrder/> Contract', () => {
|
|
|
59
59
|
// overwrite useOmniscientOrders call
|
|
60
60
|
fetchMock.get(
|
|
61
61
|
'https://joanie.endpoint/api/v1.0/orders/',
|
|
62
|
-
{ results: [order], next: null, previous: null, count:
|
|
62
|
+
{ results: [order], next: null, previous: null, count: 1 },
|
|
63
63
|
{ overwriteRoutes: true },
|
|
64
64
|
);
|
|
65
65
|
|
|
@@ -68,12 +68,12 @@ describe('<DashboardItemOrder/> Contract', () => {
|
|
|
68
68
|
results: [],
|
|
69
69
|
next: null,
|
|
70
70
|
previous: null,
|
|
71
|
-
count:
|
|
71
|
+
count: 0,
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
fetchMock.get(
|
|
75
75
|
'https://joanie.endpoint/api/v1.0/orders/?product_type=credential&state_exclude=canceled&page=1&page_size=50',
|
|
76
|
-
{ results: [order], next: null, previous: null, count:
|
|
76
|
+
{ results: [order], next: null, previous: null, count: 1 },
|
|
77
77
|
);
|
|
78
78
|
|
|
79
79
|
const submitDeferred = new Deferred();
|
|
@@ -238,7 +238,7 @@ describe('<DashboardItemOrder/> Contract', () => {
|
|
|
238
238
|
results: [signedOrder],
|
|
239
239
|
next: null,
|
|
240
240
|
previous: null,
|
|
241
|
-
count:
|
|
241
|
+
count: 1,
|
|
242
242
|
});
|
|
243
243
|
|
|
244
244
|
// Contract signing is removed.
|
|
@@ -249,7 +249,7 @@ describe('<DashboardItemOrder/> Contract', () => {
|
|
|
249
249
|
// Go back to the list view to make sure the sign button is not shown anymore.
|
|
250
250
|
fetchMock.get(
|
|
251
251
|
'https://joanie.endpoint/api/v1.0/orders/?product_type=credential&state_exclude=canceled&page=1&page_size=50',
|
|
252
|
-
{ results: [signedOrder], next: null, previous: null, count:
|
|
252
|
+
{ results: [signedOrder], next: null, previous: null, count: 1 },
|
|
253
253
|
{ overwriteRoutes: true },
|
|
254
254
|
);
|
|
255
255
|
|