@shopgate/pwa-common-commerce 7.31.5 → 7.31.6

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.
@@ -1,702 +0,0 @@
1
- import { subscribe, invoke, getSubscriptionCount, resetSubscriptions } from '@shopgate/pwa-common/subscriptions/mock';
2
- import pipelineDependencies from '@shopgate/pwa-core/classes/PipelineDependencies';
3
- import showModal from '@shopgate/pwa-common/actions/modal/showModal';
4
- import { appDidStart$ } from '@shopgate/pwa-common/streams';
5
- import { favoritesWillEnter$, shouldFetchFreshFavorites$, addProductToFavoritesDebounced$, removeProductFromFavoritesDebounced$, didReceiveFlushFavoritesBuffer$, errorFavoritesLimit$, refreshFavorites$, updateProductInFavoritesDebounced$ } from "../streams";
6
- import favorites from "./index";
7
- import fetchFavorites from "../actions/fetchFavorites";
8
- import addFavorites from "../actions/addFavorites";
9
- import removeFavorites from "../actions/removeFavorites";
10
- import updateFavorites from "../actions/updateFavorites";
11
- import { FAVORITES_LIMIT_ERROR } from "../constants";
12
- import { SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES } from "../constants/Pipelines";
13
- import { addProductToFavorites, removeProductFromFavorites, requestAddFavorites, requestRemoveFavorites, cancelRequestSyncFavorites, errorFavorites, idleSyncFavorites, updateProductInFavorites, requestUpdateFavorites } from "../action-creators";
14
- import { getFavoritesProducts, getFavoritesCount, makeGetProductRelativesOnFavorites } from "../selectors";
15
- import fetchFavoritesListsWithItems from "../actions/fetchFavoritesListsWithItems";
16
-
17
- // Required for custom runner without env-setup
18
- jest.mock('@shopgate/pwa-core', () => {});
19
- jest.mock('@shopgate/pwa-common/actions/modal/showModal', () => jest.fn().mockReturnValue('showModal'));
20
- jest.mock('@shopgate/pwa-common-commerce/product', () => ({
21
- fetchProductsById: jest.fn()
22
- }));
23
- jest.mock('@shopgate/pwa-common/providers', () => ({
24
- LoadingProvider: {
25
- setLoading: jest.fn(),
26
- unsetLoading: jest.fn()
27
- }
28
- }));
29
- const mockedDefaultListId = 'DEFAULT';
30
- jest.mock("../actions/fetchFavorites", () => jest.fn().mockReturnValue('fetchFavoritesResult'));
31
- jest.mock("../actions/fetchFavoritesList", () => jest.fn().mockResolvedValue([{
32
- id: 'DEFAULT',
33
- name: 'Wish List'
34
- }]));
35
- jest.mock("../actions/fetchFavoritesListsWithItems", () => {
36
- const original = jest.requireActual("../actions/fetchFavoritesListsWithItems");
37
- return {
38
- __esModule: true,
39
- default: jest.fn(original.default)
40
- };
41
- });
42
-
43
- // Mock all used selectors to avoid mocking the store
44
- let mockedGetFavoritesProductsIdsReturnValue;
45
- let mockedGetFavoritesCountReturnValue;
46
- let mockedGetFavoritesCountByListReturnValue;
47
- let mockedGetFavoritesProductsReturnValue;
48
- let mockedGetProductRelativesOnFavoritesReturnValue;
49
- let mockedMakeGetFavoritesReturnValue;
50
- const mockedMakeGetFavoritesCountByList = jest.fn(() => mockedGetFavoritesCountByListReturnValue || 0);
51
- jest.mock("../selectors", () => ({
52
- getFavoritesProductsIds: jest.fn(() => mockedGetFavoritesProductsIdsReturnValue),
53
- getFavoritesProducts: jest.fn(() => mockedGetFavoritesProductsReturnValue),
54
- getFavoritesCount: jest.fn(() => mockedGetFavoritesCountReturnValue),
55
- makeGetFavoritesCountByList: jest.fn(() => mockedMakeGetFavoritesCountByList),
56
- makeGetFavorites: jest.fn(() => mockedMakeGetFavoritesReturnValue),
57
- makeGetProductRelativesOnFavorites: jest.fn(() => jest.fn().mockReturnValue(mockedGetProductRelativesOnFavoritesReturnValue)),
58
- getUseGetFavoriteIdsPipeline: jest.fn().mockReturnValue(false)
59
- }));
60
- jest.mock("../actions/addFavorites", () => jest.fn().mockReturnValue('addFavoritesResult'));
61
- jest.mock("../actions/removeFavorites", () => jest.fn().mockReturnValue('removeFavoritesResult'));
62
- jest.mock("../actions/updateFavorites", () => jest.fn().mockReturnValue('updateFavoritesResult'));
63
- let mockedHasFavorites = true;
64
- let mockedFavoritesLimit = 100;
65
- jest.mock('@shopgate/pwa-common/helpers/config', () => ({
66
- get hasFavorites() {
67
- return mockedHasFavorites;
68
- },
69
- get favorites() {
70
- return {
71
- limit: mockedFavoritesLimit
72
- };
73
- },
74
- get themeConfig() {
75
- return {
76
- colors: {}
77
- };
78
- }
79
- }));
80
-
81
- /**
82
- * Flushes the promise queue.
83
- * @returns {Promise}
84
- */
85
- const flushPromises = () => new Promise(resolve => queueMicrotask(resolve));
86
- describe('Favorites - subscriptions', () => {
87
- describe('Favorites enabled', () => {
88
- const pipelineDependenciesSet = pipelineDependencies.set;
89
- beforeAll(() => {
90
- // Make sure no conflicting subscriptions exist
91
- resetSubscriptions();
92
-
93
- // Replace singleton object property with a mock
94
- pipelineDependencies.set = jest.fn();
95
-
96
- // Subscribe all streams
97
- favorites(subscribe);
98
- });
99
- afterAll(() => {
100
- // Clean up subscriptions
101
- resetSubscriptions();
102
-
103
- // Restore singleton
104
- pipelineDependencies.set = pipelineDependenciesSet;
105
- });
106
- const getState = jest.fn().mockReturnValue(() => {});
107
- // Dispatch always resolves it's promise by default
108
- const dispatch = jest.fn(result => {
109
- if (typeof result === 'function') {
110
- return result(dispatch, getState);
111
- }
112
- return result;
113
- });
114
- const productIds = ['prod1', 'prod2'];
115
- const defaultListItems = [{
116
- quantity: 1,
117
- notes: null,
118
- productId: productIds[0]
119
- }, {
120
- quantity: 1,
121
- notes: null,
122
- productId: productIds[1]
123
- }];
124
- beforeEach(() => {
125
- jest.clearAllMocks();
126
-
127
- // Reset selector return values
128
- mockedGetFavoritesProductsIdsReturnValue = productIds;
129
- mockedGetFavoritesCountReturnValue = productIds.length;
130
- mockedGetProductRelativesOnFavoritesReturnValue = [];
131
- mockedGetFavoritesProductsReturnValue = {
132
- byList: {
133
- DEFAULT: {
134
- items: defaultListItems,
135
- isFetching: false
136
- }
137
- }
138
- };
139
- mockedMakeGetFavoritesReturnValue = jest.fn().mockReturnValue(defaultListItems);
140
- });
141
- describe('appDidStart$', () => {
142
- it('should subscribe to the stream', () => {
143
- expect(getSubscriptionCount(appDidStart$)).toBe(1);
144
- });
145
- it('should not return any value', async () => {
146
- await expect(invoke(appDidStart$, {
147
- dispatch,
148
- getState
149
- })).resolves.toBeUndefined();
150
- });
151
- it('should set up pipeline dependencies correctly', () => {
152
- invoke(appDidStart$, {
153
- dispatch,
154
- getState
155
- });
156
-
157
- // Expect pipeline dependencies to be set correctly
158
- expect(pipelineDependencies.set).toHaveBeenCalledTimes(2);
159
- expect(pipelineDependencies.set.mock.calls[0][0]).toBe(SHOPGATE_USER_ADD_FAVORITES);
160
- expect(pipelineDependencies.set.mock.calls[0][1]).toEqual([SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES]);
161
- expect(pipelineDependencies.set.mock.calls[1][0]).toBe(SHOPGATE_USER_DELETE_FAVORITES);
162
- expect(pipelineDependencies.set.mock.calls[1][1]).toEqual([SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES]);
163
- });
164
- it('should fetch favorites', async () => {
165
- await invoke(appDidStart$, {
166
- dispatch,
167
- getState
168
- });
169
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1);
170
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(false);
171
- expect(dispatch).toHaveBeenCalledTimes(3);
172
- });
173
- });
174
- describe('favoritesWillEnter$', () => {
175
- it('should subscribe to the stream', () => {
176
- expect(getSubscriptionCount(favoritesWillEnter$)).toBe(1);
177
- });
178
- it('should not return any value', async () => {
179
- expect(await invoke(favoritesWillEnter$, {
180
- dispatch,
181
- getState
182
- })).toBeUndefined();
183
- });
184
- it('should handle fetch and map favorites', async () => {
185
- // eslint-disable-next-line extra-rules/no-single-line-objects
186
- await invoke(favoritesWillEnter$, {
187
- dispatch,
188
- getState
189
- });
190
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1);
191
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(true);
192
- expect(dispatch).toHaveBeenCalledTimes(3);
193
- });
194
- });
195
- describe('shouldFetchFreshFavorites$', () => {
196
- it('should subscribe to the stream', () => {
197
- expect(getSubscriptionCount(shouldFetchFreshFavorites$)).toBe(1);
198
- });
199
- it('should not return any value', async () => {
200
- await expect(invoke(shouldFetchFreshFavorites$, {
201
- dispatch
202
- })).resolves.toBeUndefined();
203
- });
204
- it('should fetch fresh favorites without using the cache', async () => {
205
- await invoke(shouldFetchFreshFavorites$, {
206
- dispatch,
207
- getState
208
- });
209
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1);
210
- expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(true);
211
- expect(dispatch).toHaveBeenCalledTimes(3);
212
- });
213
- });
214
- describe('addProductToFavoritesDebounced$', () => {
215
- it('should subscribe to the stream', () => {
216
- expect(getSubscriptionCount(addProductToFavoritesDebounced$)).toBe(1);
217
- });
218
- it('should not return any value', () => {
219
- expect(invoke(addProductToFavoritesDebounced$, {
220
- action: addProductToFavorites('prod3'),
221
- dispatch,
222
- getState
223
- })).toBeUndefined();
224
- });
225
- it('should cancel the request when the product is already there', () => {
226
- invoke(addProductToFavoritesDebounced$, {
227
- action: addProductToFavorites('prod1', mockedDefaultListId),
228
- dispatch,
229
- getState
230
- });
231
- expect(getState).toHaveBeenCalledTimes(1);
232
- expect(getFavoritesProducts).toHaveBeenCalledWith(getState());
233
- expect(getFavoritesProducts).toHaveBeenCalledTimes(1);
234
- expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(0, mockedDefaultListId));
235
- expect(dispatch).toHaveBeenCalledTimes(1);
236
- });
237
- it('should proceed and dispatch an error if the product is not yet on the list and the limit is reached', () => {
238
- const favoritesLimit = mockedFavoritesLimit;
239
- const productId = 'prod3';
240
- mockedFavoritesLimit = 2;
241
- mockedGetFavoritesCountByListReturnValue = 2;
242
- invoke(addProductToFavoritesDebounced$, {
243
- action: addProductToFavorites(productId, 'DEFAULT'),
244
- dispatch,
245
- getState
246
- });
247
- expect(getState).toHaveBeenCalledTimes(1);
248
- expect(getFavoritesProducts).toHaveBeenCalledWith(getState());
249
- expect(getFavoritesProducts).toHaveBeenCalledTimes(1);
250
- expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledWith(getState());
251
- expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledTimes(1);
252
- expect(mockedMakeGetFavoritesCountByList).toHaveReturnedWith(mockedGetFavoritesCountByListReturnValue);
253
- const error = new Error('Limit exceeded');
254
- error.code = FAVORITES_LIMIT_ERROR;
255
- expect(dispatch).toHaveBeenCalledWith(errorFavorites(productId, error));
256
- expect(dispatch).toHaveBeenCalledTimes(1);
257
- mockedFavoritesLimit = favoritesLimit;
258
- });
259
- it('should proceed and dispatch an add request if all is good', () => {
260
- const productId = 'prod3';
261
- invoke(addProductToFavoritesDebounced$, {
262
- action: addProductToFavorites(productId),
263
- dispatch,
264
- getState
265
- });
266
- expect(getState).toHaveBeenCalledTimes(1);
267
- expect(getFavoritesProducts).toHaveBeenCalledWith(getState());
268
- expect(getFavoritesProducts).toHaveBeenCalledTimes(1);
269
- expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledWith(getState());
270
- expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledTimes(1);
271
- expect(dispatch).toHaveBeenCalledWith(requestAddFavorites(productId));
272
- expect(dispatch).toHaveBeenCalledTimes(1);
273
- });
274
- });
275
- describe('updateProductInFavoritesDebounced$', () => {
276
- it('should subscribe to the stream', () => {
277
- expect(getSubscriptionCount(updateProductInFavoritesDebounced$)).toBe(1);
278
- });
279
- it('should not return any value', () => {
280
- expect(invoke(updateProductInFavoritesDebounced$, {
281
- action: updateProductInFavorites('prod3'),
282
- dispatch,
283
- getState
284
- })).toBeUndefined();
285
- });
286
- it('should proceed and dispatch an add request if all is good', () => {
287
- const productId = 'prod3';
288
- invoke(updateProductInFavoritesDebounced$, {
289
- action: updateProductInFavorites(productId),
290
- dispatch,
291
- getState
292
- });
293
- expect(dispatch).toHaveBeenCalledWith(requestUpdateFavorites(productId));
294
- expect(dispatch).toHaveBeenCalledTimes(1);
295
- });
296
- });
297
- describe('removeProductFromFavoritesDebounced$', () => {
298
- it('should subscribe to the stream', () => {
299
- expect(getSubscriptionCount(removeProductFromFavoritesDebounced$)).toBe(1);
300
- });
301
- it('should not return any value', () => {
302
- expect(invoke(removeProductFromFavoritesDebounced$, {
303
- action: removeProductFromFavorites('prod1', true, mockedDefaultListId),
304
- dispatch,
305
- getState
306
- })).toBeUndefined();
307
- expect(invoke(removeProductFromFavoritesDebounced$, {
308
- action: removeProductFromFavorites('prod1', false, mockedDefaultListId),
309
- dispatch,
310
- getState
311
- })).toBeUndefined();
312
- });
313
- it('should not do anything if no products are on the list and currently fetching', () => {
314
- mockedGetFavoritesCountReturnValue = 0;
315
- mockedGetFavoritesProductsReturnValue.isFetching = true;
316
- invoke(removeProductFromFavoritesDebounced$, {
317
- action: removeProductFromFavorites('prod1', false, mockedDefaultListId),
318
- dispatch,
319
- getState
320
- });
321
- expect(getFavoritesCount).toHaveBeenCalledTimes(1);
322
- expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0);
323
- expect(getFavoritesProducts).toHaveBeenCalledTimes(1);
324
- expect(getState).toHaveBeenCalledTimes(2);
325
- expect(dispatch).toHaveBeenCalledTimes(1);
326
- });
327
- it('should idle-sync when no products are on the list and not fetching', () => {
328
- mockedGetFavoritesCountReturnValue = 0;
329
- invoke(removeProductFromFavoritesDebounced$, {
330
- action: removeProductFromFavorites('prod1', false, mockedDefaultListId),
331
- dispatch,
332
- getState
333
- });
334
- expect(getFavoritesCount).toHaveBeenCalledTimes(1);
335
- expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0);
336
- expect(getFavoritesProducts).toHaveBeenCalledTimes(1);
337
- expect(getState).toHaveBeenCalledTimes(2);
338
- expect(dispatch).toHaveBeenCalledTimes(1);
339
- expect(dispatch).toHaveBeenCalledWith(idleSyncFavorites(mockedDefaultListId));
340
- });
341
- it('should dispatch a remove request for each relative', () => {
342
- const productId = 'prod0';
343
- const mock = jest.fn(() => productIds);
344
- makeGetProductRelativesOnFavorites.mockReturnValueOnce(mock);
345
- invoke(removeProductFromFavoritesDebounced$, {
346
- action: removeProductFromFavorites(productId, true, mockedDefaultListId),
347
- dispatch,
348
- getState
349
- });
350
-
351
- // Expect multiple remove requests to be dispatched
352
- expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(1);
353
- expect(dispatch).toHaveBeenCalledTimes(4);
354
- expect(dispatch.mock.calls[1][0]).toEqual(requestRemoveFavorites(productIds[0], mockedDefaultListId)(dispatch, getState));
355
- expect(dispatch.mock.calls[3][0]).toEqual(requestRemoveFavorites(productIds[1], mockedDefaultListId)(dispatch, getState));
356
- });
357
- it('should cancel the remove request if the product is not on the list', () => {
358
- const productId = 'prod0';
359
- mockedGetProductRelativesOnFavoritesReturnValue = productIds;
360
- invoke(removeProductFromFavoritesDebounced$, {
361
- action: removeProductFromFavorites(productId, false, mockedDefaultListId),
362
- dispatch,
363
- getState
364
- });
365
-
366
- // Don't expect any remove requests to be triggered
367
- expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0);
368
- expect(dispatch).toHaveBeenCalledTimes(1);
369
- expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(0, mockedDefaultListId));
370
- });
371
- it('should request the removal of the single favorite item', () => {
372
- const productId = 'prod0';
373
- mockedGetFavoritesProductsReturnValue = {
374
- byList: {
375
- DEFAULT: {
376
- items: [].concat(mockedGetFavoritesProductsReturnValue.byList.DEFAULT.items, [{
377
- productId,
378
- quantity: 1,
379
- notes: null
380
- }])
381
- }
382
- }
383
- };
384
- const mock = jest.fn(() => productIds);
385
- makeGetProductRelativesOnFavorites.mockReturnValueOnce(mock);
386
- invoke(removeProductFromFavoritesDebounced$, {
387
- action: removeProductFromFavorites(productId, false, mockedDefaultListId),
388
- dispatch,
389
- getState
390
- });
391
-
392
- // Only one removal request is expected
393
- expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0);
394
- expect(dispatch).toHaveBeenCalledTimes(2);
395
- expect(dispatch.mock.calls[1][0]).toEqual(requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState));
396
- });
397
- });
398
- describe('errorFavoritesLimit$', () => {
399
- it('should subscribe to the stream', () => {
400
- expect(getSubscriptionCount(errorFavoritesLimit$)).toBe(1);
401
- });
402
- it('should not return any value', () => {
403
- expect(invoke(errorFavoritesLimit$, {
404
- dispatch
405
- })).toBeUndefined();
406
- });
407
- it('should dispatch an action to show a modal', () => {
408
- invoke(errorFavoritesLimit$, {
409
- dispatch
410
- });
411
- expect(showModal).toHaveBeenCalledTimes(1);
412
- expect(dispatch).toHaveBeenCalledTimes(1);
413
- expect(dispatch).toHaveBeenCalledWith(showModal({
414
- confirm: null,
415
- dismiss: 'modal.ok',
416
- title: 'modal.title_error',
417
- message: 'favorites.error_limit'
418
- }));
419
- });
420
- });
421
- describe('refreshFavorites$', () => {
422
- it('should subscribe to the stream', () => {
423
- expect(getSubscriptionCount(refreshFavorites$)).toBe(1);
424
- });
425
- it('should not return any value', async () => {
426
- await expect(invoke(refreshFavorites$, {
427
- dispatch,
428
- action: {
429
- listId: mockedDefaultListId
430
- },
431
- getState
432
- })).resolves.toBeUndefined();
433
- });
434
- it('should refresh favorites by fetching them without using the cache', async () => {
435
- await invoke(refreshFavorites$, {
436
- dispatch,
437
- action: {
438
- listId: mockedDefaultListId
439
- },
440
- getState
441
- });
442
- expect(fetchFavorites).toHaveBeenCalledTimes(1);
443
- expect(dispatch).toHaveBeenCalledTimes(1);
444
- expect(dispatch).toHaveBeenCalledWith(fetchFavorites(true));
445
- });
446
- });
447
- describe('didReceiveFlushFavoritesBuffer$', () => {
448
- it('should subscribe to the stream', () => {
449
- expect(getSubscriptionCount(didReceiveFlushFavoritesBuffer$)).toBe(1);
450
- });
451
- it('should not return any value', async () => {
452
- const productId = 'prod1';
453
- expect(await invoke(didReceiveFlushFavoritesBuffer$, null)).toBeUndefined();
454
- expect(await invoke(didReceiveFlushFavoritesBuffer$, [])).toBeUndefined();
455
- expect(await invoke(didReceiveFlushFavoritesBuffer$, [{
456
- action: requestAddFavorites(productId, mockedDefaultListId),
457
- dispatch,
458
- getState
459
- }])).toBeUndefined();
460
- expect(await invoke(didReceiveFlushFavoritesBuffer$, [{
461
- action: requestAddFavorites(productId, mockedDefaultListId),
462
- dispatch,
463
- getState
464
- }, {
465
- action: requestUpdateFavorites(productId, mockedDefaultListId),
466
- dispatch,
467
- getState
468
- }, {
469
- action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState),
470
- dispatch,
471
- getState
472
- }])).toBeUndefined();
473
- });
474
- it('should cancel all redundant requests', async () => {
475
- const productId = 'prod1';
476
-
477
- // Add and remove the same product to make both redundant
478
- await invoke(didReceiveFlushFavoritesBuffer$, [{
479
- action: requestAddFavorites(productId, mockedDefaultListId),
480
- dispatch,
481
- getState
482
- }, {
483
- action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState),
484
- dispatch,
485
- getState
486
- }]);
487
- expect(dispatch).toHaveBeenCalledTimes(2);
488
- expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(2, mockedDefaultListId));
489
- });
490
- it('should filter out duplicated add requests', async () => {
491
- const productId = 'prod1';
492
-
493
- // Add the same product twice
494
- await invoke(didReceiveFlushFavoritesBuffer$, [{
495
- action: requestAddFavorites(productId, mockedDefaultListId),
496
- dispatch,
497
- getState
498
- }, {
499
- action: requestAddFavorites(productId, mockedDefaultListId),
500
- dispatch,
501
- getState
502
- }]);
503
- await flushPromises();
504
-
505
- // One cancel and one add action call as well as one idle-sync afterwards
506
- expect(addFavorites).toHaveBeenCalledTimes(1);
507
- expect(addFavorites.mock.calls[0][0]).toBe(productId);
508
- expect(dispatch).toHaveBeenCalledTimes(2);
509
- expect(dispatch.mock.calls[0][0]).toEqual(addFavorites());
510
- expect(dispatch.mock.calls[1][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
511
- });
512
- it('should filter out duplicated remove requests', async () => {
513
- const productId = 'prod1';
514
-
515
- // Remove the same product twice
516
- await invoke(didReceiveFlushFavoritesBuffer$, [{
517
- action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState),
518
- dispatch,
519
- getState
520
- }, {
521
- action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState),
522
- dispatch,
523
- getState
524
- }]);
525
- await flushPromises();
526
-
527
- // One cancel and one add action call as well as one idle-sync afterwards
528
- expect(removeFavorites).toHaveBeenCalledTimes(1);
529
- expect(removeFavorites.mock.calls[0][0]).toBe(productId);
530
- expect(dispatch).toHaveBeenCalledTimes(4);
531
- expect(dispatch.mock.calls[2][0]).toEqual(removeFavorites());
532
- expect(dispatch.mock.calls[3][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
533
- });
534
- it('should not filter out unique add or remove requests', async () => {
535
- // Remove the same product twice
536
- await invoke(didReceiveFlushFavoritesBuffer$, [{
537
- action: requestAddFavorites('prod3', mockedDefaultListId),
538
- dispatch,
539
- getState
540
- }, {
541
- action: requestAddFavorites('prod4', mockedDefaultListId),
542
- dispatch,
543
- getState
544
- }, {
545
- action: requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState),
546
- dispatch,
547
- getState
548
- }, {
549
- action: requestRemoveFavorites('prod2', mockedDefaultListId)(dispatch, getState),
550
- dispatch,
551
- getState
552
- }]);
553
- await flushPromises();
554
-
555
- // Only dispatch add and remove actions, no cancellation
556
- expect(addFavorites).toHaveBeenCalledTimes(2);
557
- expect(addFavorites.mock.calls[0][0]).toBe('prod3');
558
- expect(addFavorites.mock.calls[1][0]).toBe('prod4');
559
- expect(removeFavorites).toHaveBeenCalledTimes(2);
560
- expect(removeFavorites.mock.calls[0][0]).toBe('prod1');
561
- expect(removeFavorites.mock.calls[1][0]).toBe('prod2');
562
- expect(dispatch).toHaveBeenCalledTimes(7);
563
- expect(dispatch.mock.calls[2][0]).toEqual(addFavorites());
564
- expect(dispatch.mock.calls[3][0]).toEqual(addFavorites());
565
- expect(dispatch.mock.calls[4][0]).toEqual(removeFavorites());
566
- expect(dispatch.mock.calls[5][0]).toEqual(removeFavorites());
567
- expect(dispatch.mock.calls[6][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
568
- });
569
- it('should filter out duplicated update requests', async () => {
570
- const productId = 'prod1';
571
-
572
- // Add the same product twice
573
- await invoke(didReceiveFlushFavoritesBuffer$, [{
574
- action: requestUpdateFavorites(productId, mockedDefaultListId, 2),
575
- dispatch,
576
- getState
577
- }, {
578
- action: requestUpdateFavorites(productId, mockedDefaultListId, 3),
579
- dispatch,
580
- getState
581
- }]);
582
- await flushPromises();
583
-
584
- // One cancel and one add action call as well as one idle-sync afterwards
585
- expect(updateFavorites).toHaveBeenCalledTimes(1);
586
- expect(updateFavorites.mock.calls[0][0]).toBe(productId);
587
- expect(updateFavorites.mock.calls[0][1]).toBe(mockedDefaultListId);
588
- expect(updateFavorites.mock.calls[0][2]).toBe(3);
589
- expect(dispatch).toHaveBeenCalledTimes(2);
590
- expect(dispatch.mock.calls[0][0]).toEqual(updateFavorites());
591
- expect(dispatch.mock.calls[1][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
592
- });
593
- it('should properly handle conflicts and duplicates when they occur at once', async () => {
594
- // Remove the same product twice
595
- await invoke(didReceiveFlushFavoritesBuffer$, [{
596
- action: requestAddFavorites('prod3', mockedDefaultListId),
597
- dispatch,
598
- getState
599
- }, {
600
- action: requestAddFavorites('prod4', mockedDefaultListId),
601
- dispatch,
602
- getState
603
- }, {
604
- action: requestAddFavorites('prod4', mockedDefaultListId),
605
- // duplicated
606
- dispatch,
607
- getState
608
- }, {
609
- action: requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState),
610
- dispatch,
611
- getState
612
- }, {
613
- action: requestAddFavorites('prod1', mockedDefaultListId),
614
- // conflict
615
- dispatch,
616
- getState
617
- }, {
618
- action: requestRemoveFavorites('prod2', mockedDefaultListId)(dispatch, getState),
619
- dispatch,
620
- getState
621
- }]);
622
- await flushPromises();
623
-
624
- // Cancel three calls (conflict -> 2 + duplicate -> 1 => 3), handle the rest
625
- expect(addFavorites).toHaveBeenCalledTimes(2);
626
- expect(addFavorites.mock.calls[0][0]).toBe('prod3');
627
- expect(addFavorites.mock.calls[1][0]).toBe('prod4');
628
- expect(removeFavorites).toHaveBeenCalledTimes(1);
629
- expect(removeFavorites.mock.calls[0][0]).toBe('prod2');
630
- expect(dispatch).toHaveBeenCalledTimes(7);
631
- expect(dispatch.mock.calls[2][0]).toEqual(addFavorites());
632
- expect(dispatch.mock.calls[3][0]).toEqual(addFavorites());
633
- expect(dispatch.mock.calls[4][0]).toEqual(cancelRequestSyncFavorites(2, mockedDefaultListId));
634
- expect(dispatch.mock.calls[5][0]).toEqual(removeFavorites());
635
- expect(dispatch.mock.calls[6][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
636
- });
637
- it('should not break on any failing request', async () => {
638
- dispatch.mockImplementation(async action => {
639
- if (action === addFavorites() || action === removeFavorites() || action === updateFavorites()) {
640
- throw new Error('Failed to add/remove favorite');
641
- }
642
- });
643
-
644
- // Remove the same product twice
645
- await expect(invoke(didReceiveFlushFavoritesBuffer$, [{
646
- action: requestAddFavorites('prod3'),
647
- dispatch,
648
- getState
649
- }, {
650
- action: requestUpdateFavorites('prod3'),
651
- dispatch,
652
- getState
653
- }, {
654
- action: requestRemoveFavorites('prod1')(dispatch, getState),
655
- dispatch,
656
- getState
657
- }])).resolves.toBeUndefined();
658
- });
659
- it('should dispatch only the requests and one idle-sync if any error occurs', async () => {
660
- dispatch.mockImplementation(async action => {
661
- if (action === addFavorites() || action === updateFavorites() || action === removeFavorites()) {
662
- throw new Error('Failed to add/remove favorite');
663
- }
664
- if (typeof action === 'function') {
665
- return action(dispatch, getState);
666
- }
667
- return action;
668
- });
669
-
670
- // Remove the same product twice
671
- await invoke(didReceiveFlushFavoritesBuffer$, [{
672
- action: requestAddFavorites('prod3', mockedDefaultListId),
673
- dispatch,
674
- getState
675
- }, {
676
- action: requestUpdateFavorites('prod2', mockedDefaultListId, 3),
677
- dispatch,
678
- getState
679
- }, {
680
- action: await requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState),
681
- dispatch,
682
- getState
683
- }]);
684
- await flushPromises();
685
- expect(dispatch).toHaveBeenCalledTimes(5);
686
- expect(dispatch.mock.calls[1][0]).toEqual(addFavorites());
687
- expect(dispatch.mock.calls[2][0]).toEqual(updateFavorites());
688
- expect(dispatch.mock.calls[3][0]).toEqual(removeFavorites());
689
- expect(dispatch.mock.calls[4][0]).toEqual(idleSyncFavorites(mockedDefaultListId));
690
- });
691
- });
692
- });
693
- describe('Favorites disabled', () => {
694
- it('should not subscribe to anything', () => {
695
- const hasFavorites = mockedHasFavorites;
696
- mockedHasFavorites = false;
697
- favorites(subscribe);
698
- expect(getSubscriptionCount()).toBe(0);
699
- mockedHasFavorites = hasFavorites;
700
- });
701
- });
702
- });