UNPKG

@shopgate/pwa-common-commerce

Version:

Commerce library for the Shopgate Connect PWA.

702 lines (680 loc) • 29.7 kB
import { subscribe, invoke, getSubscriptionCount, resetSubscriptions } from '@shopgate/pwa-common/subscriptions/mock'; import pipelineDependencies from '@shopgate/pwa-core/classes/PipelineDependencies'; import showModal from '@shopgate/pwa-common/actions/modal/showModal'; import { appDidStart$ } from '@shopgate/pwa-common/streams'; import { favoritesWillEnter$, shouldFetchFreshFavorites$, addProductToFavoritesDebounced$, removeProductFromFavoritesDebounced$, didReceiveFlushFavoritesBuffer$, errorFavoritesLimit$, refreshFavorites$, updateProductInFavoritesDebounced$ } from "../streams"; import favorites from "./index"; import fetchFavorites from "../actions/fetchFavorites"; import addFavorites from "../actions/addFavorites"; import removeFavorites from "../actions/removeFavorites"; import updateFavorites from "../actions/updateFavorites"; import { FAVORITES_LIMIT_ERROR } from "../constants"; import { SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES } from "../constants/Pipelines"; import { addProductToFavorites, removeProductFromFavorites, requestAddFavorites, requestRemoveFavorites, cancelRequestSyncFavorites, errorFavorites, idleSyncFavorites, updateProductInFavorites, requestUpdateFavorites } from "../action-creators"; import { getFavoritesProducts, getFavoritesCount, makeGetProductRelativesOnFavorites } from "../selectors"; import fetchFavoritesListsWithItems from "../actions/fetchFavoritesListsWithItems"; // Required for custom runner without env-setup jest.mock('@shopgate/pwa-core', () => {}); jest.mock('@shopgate/pwa-common/actions/modal/showModal', () => jest.fn().mockReturnValue('showModal')); jest.mock('@shopgate/pwa-common-commerce/product', () => ({ fetchProductsById: jest.fn() })); jest.mock('@shopgate/pwa-common/providers', () => ({ LoadingProvider: { setLoading: jest.fn(), unsetLoading: jest.fn() } })); const mockedDefaultListId = 'DEFAULT'; jest.mock("../actions/fetchFavorites", () => jest.fn().mockReturnValue('fetchFavoritesResult')); jest.mock("../actions/fetchFavoritesList", () => jest.fn().mockResolvedValue([{ id: 'DEFAULT', name: 'Wish List' }])); jest.mock("../actions/fetchFavoritesListsWithItems", () => { const original = jest.requireActual("../actions/fetchFavoritesListsWithItems"); return { __esModule: true, default: jest.fn(original.default) }; }); // Mock all used selectors to avoid mocking the store let mockedGetFavoritesProductsIdsReturnValue; let mockedGetFavoritesCountReturnValue; let mockedGetFavoritesCountByListReturnValue; let mockedGetFavoritesProductsReturnValue; let mockedGetProductRelativesOnFavoritesReturnValue; let mockedMakeGetFavoritesReturnValue; const mockedMakeGetFavoritesCountByList = jest.fn(() => mockedGetFavoritesCountByListReturnValue || 0); jest.mock("../selectors", () => ({ getFavoritesProductsIds: jest.fn(() => mockedGetFavoritesProductsIdsReturnValue), getFavoritesProducts: jest.fn(() => mockedGetFavoritesProductsReturnValue), getFavoritesCount: jest.fn(() => mockedGetFavoritesCountReturnValue), makeGetFavoritesCountByList: jest.fn(() => mockedMakeGetFavoritesCountByList), makeGetFavorites: jest.fn(() => mockedMakeGetFavoritesReturnValue), makeGetProductRelativesOnFavorites: jest.fn(() => jest.fn().mockReturnValue(mockedGetProductRelativesOnFavoritesReturnValue)), getUseGetFavoriteIdsPipeline: jest.fn().mockReturnValue(false) })); jest.mock("../actions/addFavorites", () => jest.fn().mockReturnValue('addFavoritesResult')); jest.mock("../actions/removeFavorites", () => jest.fn().mockReturnValue('removeFavoritesResult')); jest.mock("../actions/updateFavorites", () => jest.fn().mockReturnValue('updateFavoritesResult')); let mockedHasFavorites = true; let mockedFavoritesLimit = 100; jest.mock('@shopgate/pwa-common/helpers/config', () => ({ get hasFavorites() { return mockedHasFavorites; }, get favorites() { return { limit: mockedFavoritesLimit }; }, get themeConfig() { return { colors: {} }; } })); /** * Flushes the promise queue. * @returns {Promise} */ const flushPromises = () => new Promise(resolve => queueMicrotask(resolve)); describe('Favorites - subscriptions', () => { describe('Favorites enabled', () => { const pipelineDependenciesSet = pipelineDependencies.set; beforeAll(() => { // Make sure no conflicting subscriptions exist resetSubscriptions(); // Replace singleton object property with a mock pipelineDependencies.set = jest.fn(); // Subscribe all streams favorites(subscribe); }); afterAll(() => { // Clean up subscriptions resetSubscriptions(); // Restore singleton pipelineDependencies.set = pipelineDependenciesSet; }); const getState = jest.fn().mockReturnValue(() => {}); // Dispatch always resolves it's promise by default const dispatch = jest.fn(result => { if (typeof result === 'function') { return result(dispatch, getState); } return result; }); const productIds = ['prod1', 'prod2']; const defaultListItems = [{ quantity: 1, notes: null, productId: productIds[0] }, { quantity: 1, notes: null, productId: productIds[1] }]; beforeEach(() => { jest.clearAllMocks(); // Reset selector return values mockedGetFavoritesProductsIdsReturnValue = productIds; mockedGetFavoritesCountReturnValue = productIds.length; mockedGetProductRelativesOnFavoritesReturnValue = []; mockedGetFavoritesProductsReturnValue = { byList: { DEFAULT: { items: defaultListItems, isFetching: false } } }; mockedMakeGetFavoritesReturnValue = jest.fn().mockReturnValue(defaultListItems); }); describe('appDidStart$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(appDidStart$)).toBe(1); }); it('should not return any value', async () => { await expect(invoke(appDidStart$, { dispatch, getState })).resolves.toBeUndefined(); }); it('should set up pipeline dependencies correctly', () => { invoke(appDidStart$, { dispatch, getState }); // Expect pipeline dependencies to be set correctly expect(pipelineDependencies.set).toHaveBeenCalledTimes(2); expect(pipelineDependencies.set.mock.calls[0][0]).toBe(SHOPGATE_USER_ADD_FAVORITES); expect(pipelineDependencies.set.mock.calls[0][1]).toEqual([SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES]); expect(pipelineDependencies.set.mock.calls[1][0]).toBe(SHOPGATE_USER_DELETE_FAVORITES); expect(pipelineDependencies.set.mock.calls[1][1]).toEqual([SHOPGATE_USER_ADD_FAVORITES, SHOPGATE_USER_DELETE_FAVORITES]); }); it('should fetch favorites', async () => { await invoke(appDidStart$, { dispatch, getState }); expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1); expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(false); expect(dispatch).toHaveBeenCalledTimes(3); }); }); describe('favoritesWillEnter$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(favoritesWillEnter$)).toBe(1); }); it('should not return any value', async () => { expect(await invoke(favoritesWillEnter$, { dispatch, getState })).toBeUndefined(); }); it('should handle fetch and map favorites', async () => { // eslint-disable-next-line extra-rules/no-single-line-objects await invoke(favoritesWillEnter$, { dispatch, getState }); expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1); expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(true); expect(dispatch).toHaveBeenCalledTimes(3); }); }); describe('shouldFetchFreshFavorites$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(shouldFetchFreshFavorites$)).toBe(1); }); it('should not return any value', async () => { await expect(invoke(shouldFetchFreshFavorites$, { dispatch })).resolves.toBeUndefined(); }); it('should fetch fresh favorites without using the cache', async () => { await invoke(shouldFetchFreshFavorites$, { dispatch, getState }); expect(fetchFavoritesListsWithItems).toHaveBeenCalledTimes(1); expect(fetchFavoritesListsWithItems).toHaveBeenCalledWith(true); expect(dispatch).toHaveBeenCalledTimes(3); }); }); describe('addProductToFavoritesDebounced$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(addProductToFavoritesDebounced$)).toBe(1); }); it('should not return any value', () => { expect(invoke(addProductToFavoritesDebounced$, { action: addProductToFavorites('prod3'), dispatch, getState })).toBeUndefined(); }); it('should cancel the request when the product is already there', () => { invoke(addProductToFavoritesDebounced$, { action: addProductToFavorites('prod1', mockedDefaultListId), dispatch, getState }); expect(getState).toHaveBeenCalledTimes(1); expect(getFavoritesProducts).toHaveBeenCalledWith(getState()); expect(getFavoritesProducts).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(0, mockedDefaultListId)); expect(dispatch).toHaveBeenCalledTimes(1); }); it('should proceed and dispatch an error if the product is not yet on the list and the limit is reached', () => { const favoritesLimit = mockedFavoritesLimit; const productId = 'prod3'; mockedFavoritesLimit = 2; mockedGetFavoritesCountByListReturnValue = 2; invoke(addProductToFavoritesDebounced$, { action: addProductToFavorites(productId, 'DEFAULT'), dispatch, getState }); expect(getState).toHaveBeenCalledTimes(1); expect(getFavoritesProducts).toHaveBeenCalledWith(getState()); expect(getFavoritesProducts).toHaveBeenCalledTimes(1); expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledWith(getState()); expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledTimes(1); expect(mockedMakeGetFavoritesCountByList).toHaveReturnedWith(mockedGetFavoritesCountByListReturnValue); const error = new Error('Limit exceeded'); error.code = FAVORITES_LIMIT_ERROR; expect(dispatch).toHaveBeenCalledWith(errorFavorites(productId, error)); expect(dispatch).toHaveBeenCalledTimes(1); mockedFavoritesLimit = favoritesLimit; }); it('should proceed and dispatch an add request if all is good', () => { const productId = 'prod3'; invoke(addProductToFavoritesDebounced$, { action: addProductToFavorites(productId), dispatch, getState }); expect(getState).toHaveBeenCalledTimes(1); expect(getFavoritesProducts).toHaveBeenCalledWith(getState()); expect(getFavoritesProducts).toHaveBeenCalledTimes(1); expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledWith(getState()); expect(mockedMakeGetFavoritesCountByList).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(requestAddFavorites(productId)); expect(dispatch).toHaveBeenCalledTimes(1); }); }); describe('updateProductInFavoritesDebounced$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(updateProductInFavoritesDebounced$)).toBe(1); }); it('should not return any value', () => { expect(invoke(updateProductInFavoritesDebounced$, { action: updateProductInFavorites('prod3'), dispatch, getState })).toBeUndefined(); }); it('should proceed and dispatch an add request if all is good', () => { const productId = 'prod3'; invoke(updateProductInFavoritesDebounced$, { action: updateProductInFavorites(productId), dispatch, getState }); expect(dispatch).toHaveBeenCalledWith(requestUpdateFavorites(productId)); expect(dispatch).toHaveBeenCalledTimes(1); }); }); describe('removeProductFromFavoritesDebounced$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(removeProductFromFavoritesDebounced$)).toBe(1); }); it('should not return any value', () => { expect(invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites('prod1', true, mockedDefaultListId), dispatch, getState })).toBeUndefined(); expect(invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites('prod1', false, mockedDefaultListId), dispatch, getState })).toBeUndefined(); }); it('should not do anything if no products are on the list and currently fetching', () => { mockedGetFavoritesCountReturnValue = 0; mockedGetFavoritesProductsReturnValue.isFetching = true; invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites('prod1', false, mockedDefaultListId), dispatch, getState }); expect(getFavoritesCount).toHaveBeenCalledTimes(1); expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0); expect(getFavoritesProducts).toHaveBeenCalledTimes(1); expect(getState).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(1); }); it('should idle-sync when no products are on the list and not fetching', () => { mockedGetFavoritesCountReturnValue = 0; invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites('prod1', false, mockedDefaultListId), dispatch, getState }); expect(getFavoritesCount).toHaveBeenCalledTimes(1); expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0); expect(getFavoritesProducts).toHaveBeenCalledTimes(1); expect(getState).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(idleSyncFavorites(mockedDefaultListId)); }); it('should dispatch a remove request for each relative', () => { const productId = 'prod0'; const mock = jest.fn(() => productIds); makeGetProductRelativesOnFavorites.mockReturnValueOnce(mock); invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites(productId, true, mockedDefaultListId), dispatch, getState }); // Expect multiple remove requests to be dispatched expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(4); expect(dispatch.mock.calls[1][0]).toEqual(requestRemoveFavorites(productIds[0], mockedDefaultListId)(dispatch, getState)); expect(dispatch.mock.calls[3][0]).toEqual(requestRemoveFavorites(productIds[1], mockedDefaultListId)(dispatch, getState)); }); it('should cancel the remove request if the product is not on the list', () => { const productId = 'prod0'; mockedGetProductRelativesOnFavoritesReturnValue = productIds; invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites(productId, false, mockedDefaultListId), dispatch, getState }); // Don't expect any remove requests to be triggered expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0); expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(0, mockedDefaultListId)); }); it('should request the removal of the single favorite item', () => { const productId = 'prod0'; mockedGetFavoritesProductsReturnValue = { byList: { DEFAULT: { items: [].concat(mockedGetFavoritesProductsReturnValue.byList.DEFAULT.items, [{ productId, quantity: 1, notes: null }]) } } }; const mock = jest.fn(() => productIds); makeGetProductRelativesOnFavorites.mockReturnValueOnce(mock); invoke(removeProductFromFavoritesDebounced$, { action: removeProductFromFavorites(productId, false, mockedDefaultListId), dispatch, getState }); // Only one removal request is expected expect(makeGetProductRelativesOnFavorites).toHaveBeenCalledTimes(0); expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch.mock.calls[1][0]).toEqual(requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState)); }); }); describe('errorFavoritesLimit$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(errorFavoritesLimit$)).toBe(1); }); it('should not return any value', () => { expect(invoke(errorFavoritesLimit$, { dispatch })).toBeUndefined(); }); it('should dispatch an action to show a modal', () => { invoke(errorFavoritesLimit$, { dispatch }); expect(showModal).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(showModal({ confirm: null, dismiss: 'modal.ok', title: 'modal.title_error', message: 'favorites.error_limit' })); }); }); describe('refreshFavorites$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(refreshFavorites$)).toBe(1); }); it('should not return any value', async () => { await expect(invoke(refreshFavorites$, { dispatch, action: { listId: mockedDefaultListId }, getState })).resolves.toBeUndefined(); }); it('should refresh favorites by fetching them without using the cache', async () => { await invoke(refreshFavorites$, { dispatch, action: { listId: mockedDefaultListId }, getState }); expect(fetchFavorites).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledWith(fetchFavorites(true)); }); }); describe('didReceiveFlushFavoritesBuffer$', () => { it('should subscribe to the stream', () => { expect(getSubscriptionCount(didReceiveFlushFavoritesBuffer$)).toBe(1); }); it('should not return any value', async () => { const productId = 'prod1'; expect(await invoke(didReceiveFlushFavoritesBuffer$, null)).toBeUndefined(); expect(await invoke(didReceiveFlushFavoritesBuffer$, [])).toBeUndefined(); expect(await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites(productId, mockedDefaultListId), dispatch, getState }])).toBeUndefined(); expect(await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites(productId, mockedDefaultListId), dispatch, getState }, { action: requestUpdateFavorites(productId, mockedDefaultListId), dispatch, getState }, { action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState), dispatch, getState }])).toBeUndefined(); }); it('should cancel all redundant requests', async () => { const productId = 'prod1'; // Add and remove the same product to make both redundant await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites(productId, mockedDefaultListId), dispatch, getState }, { action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState), dispatch, getState }]); expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenCalledWith(cancelRequestSyncFavorites(2, mockedDefaultListId)); }); it('should filter out duplicated add requests', async () => { const productId = 'prod1'; // Add the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites(productId, mockedDefaultListId), dispatch, getState }, { action: requestAddFavorites(productId, mockedDefaultListId), dispatch, getState }]); await flushPromises(); // One cancel and one add action call as well as one idle-sync afterwards expect(addFavorites).toHaveBeenCalledTimes(1); expect(addFavorites.mock.calls[0][0]).toBe(productId); expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch.mock.calls[0][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[1][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); it('should filter out duplicated remove requests', async () => { const productId = 'prod1'; // Remove the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState), dispatch, getState }, { action: requestRemoveFavorites(productId, mockedDefaultListId)(dispatch, getState), dispatch, getState }]); await flushPromises(); // One cancel and one add action call as well as one idle-sync afterwards expect(removeFavorites).toHaveBeenCalledTimes(1); expect(removeFavorites.mock.calls[0][0]).toBe(productId); expect(dispatch).toHaveBeenCalledTimes(4); expect(dispatch.mock.calls[2][0]).toEqual(removeFavorites()); expect(dispatch.mock.calls[3][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); it('should not filter out unique add or remove requests', async () => { // Remove the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites('prod3', mockedDefaultListId), dispatch, getState }, { action: requestAddFavorites('prod4', mockedDefaultListId), dispatch, getState }, { action: requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState), dispatch, getState }, { action: requestRemoveFavorites('prod2', mockedDefaultListId)(dispatch, getState), dispatch, getState }]); await flushPromises(); // Only dispatch add and remove actions, no cancellation expect(addFavorites).toHaveBeenCalledTimes(2); expect(addFavorites.mock.calls[0][0]).toBe('prod3'); expect(addFavorites.mock.calls[1][0]).toBe('prod4'); expect(removeFavorites).toHaveBeenCalledTimes(2); expect(removeFavorites.mock.calls[0][0]).toBe('prod1'); expect(removeFavorites.mock.calls[1][0]).toBe('prod2'); expect(dispatch).toHaveBeenCalledTimes(7); expect(dispatch.mock.calls[2][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[3][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[4][0]).toEqual(removeFavorites()); expect(dispatch.mock.calls[5][0]).toEqual(removeFavorites()); expect(dispatch.mock.calls[6][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); it('should filter out duplicated update requests', async () => { const productId = 'prod1'; // Add the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestUpdateFavorites(productId, mockedDefaultListId, 2), dispatch, getState }, { action: requestUpdateFavorites(productId, mockedDefaultListId, 3), dispatch, getState }]); await flushPromises(); // One cancel and one add action call as well as one idle-sync afterwards expect(updateFavorites).toHaveBeenCalledTimes(1); expect(updateFavorites.mock.calls[0][0]).toBe(productId); expect(updateFavorites.mock.calls[0][1]).toBe(mockedDefaultListId); expect(updateFavorites.mock.calls[0][2]).toBe(3); expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch.mock.calls[0][0]).toEqual(updateFavorites()); expect(dispatch.mock.calls[1][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); it('should properly handle conflicts and duplicates when they occur at once', async () => { // Remove the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites('prod3', mockedDefaultListId), dispatch, getState }, { action: requestAddFavorites('prod4', mockedDefaultListId), dispatch, getState }, { action: requestAddFavorites('prod4', mockedDefaultListId), // duplicated dispatch, getState }, { action: requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState), dispatch, getState }, { action: requestAddFavorites('prod1', mockedDefaultListId), // conflict dispatch, getState }, { action: requestRemoveFavorites('prod2', mockedDefaultListId)(dispatch, getState), dispatch, getState }]); await flushPromises(); // Cancel three calls (conflict -> 2 + duplicate -> 1 => 3), handle the rest expect(addFavorites).toHaveBeenCalledTimes(2); expect(addFavorites.mock.calls[0][0]).toBe('prod3'); expect(addFavorites.mock.calls[1][0]).toBe('prod4'); expect(removeFavorites).toHaveBeenCalledTimes(1); expect(removeFavorites.mock.calls[0][0]).toBe('prod2'); expect(dispatch).toHaveBeenCalledTimes(7); expect(dispatch.mock.calls[2][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[3][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[4][0]).toEqual(cancelRequestSyncFavorites(2, mockedDefaultListId)); expect(dispatch.mock.calls[5][0]).toEqual(removeFavorites()); expect(dispatch.mock.calls[6][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); it('should not break on any failing request', async () => { dispatch.mockImplementation(async action => { if (action === addFavorites() || action === removeFavorites() || action === updateFavorites()) { throw new Error('Failed to add/remove favorite'); } }); // Remove the same product twice await expect(invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites('prod3'), dispatch, getState }, { action: requestUpdateFavorites('prod3'), dispatch, getState }, { action: requestRemoveFavorites('prod1')(dispatch, getState), dispatch, getState }])).resolves.toBeUndefined(); }); it('should dispatch only the requests and one idle-sync if any error occurs', async () => { dispatch.mockImplementation(async action => { if (action === addFavorites() || action === updateFavorites() || action === removeFavorites()) { throw new Error('Failed to add/remove favorite'); } if (typeof action === 'function') { return action(dispatch, getState); } return action; }); // Remove the same product twice await invoke(didReceiveFlushFavoritesBuffer$, [{ action: requestAddFavorites('prod3', mockedDefaultListId), dispatch, getState }, { action: requestUpdateFavorites('prod2', mockedDefaultListId, 3), dispatch, getState }, { action: await requestRemoveFavorites('prod1', mockedDefaultListId)(dispatch, getState), dispatch, getState }]); await flushPromises(); expect(dispatch).toHaveBeenCalledTimes(5); expect(dispatch.mock.calls[1][0]).toEqual(addFavorites()); expect(dispatch.mock.calls[2][0]).toEqual(updateFavorites()); expect(dispatch.mock.calls[3][0]).toEqual(removeFavorites()); expect(dispatch.mock.calls[4][0]).toEqual(idleSyncFavorites(mockedDefaultListId)); }); }); }); describe('Favorites disabled', () => { it('should not subscribe to anything', () => { const hasFavorites = mockedHasFavorites; mockedHasFavorites = false; favorites(subscribe); expect(getSubscriptionCount()).toBe(0); mockedHasFavorites = hasFavorites; }); }); });