diff --git a/packages/core/core/__tests__/factories/useCartFactory.spec.ts b/packages/core/core/__tests__/factories/useCartFactory.spec.ts index 8116e88353..04e4f72344 100644 --- a/packages/core/core/__tests__/factories/useCartFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useCartFactory.spec.ts @@ -28,6 +28,19 @@ function createComposable() { useCart = useCartFactory(params); } +const factoryParams = { + addItem: jest.fn(() => null), + removeItem: jest.fn(), + updateItemQty: jest.fn(), + load: jest.fn(), + clear: jest.fn(), + applyCoupon: jest.fn(), + removeCoupon: jest.fn(), + isOnCart: jest.fn() +}; + +const useCartMock = useCartFactory(factoryParams); + describe('[CORE - factories] useCartFactory', () => { beforeEach(() => { jest.clearAllMocks(); @@ -80,6 +93,18 @@ describe('[CORE - factories] useCartFactory', () => { expect(params.load).toHaveBeenCalled(); expect(cart.value).toEqual({ id: 'mocked_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.load.mockImplementationOnce(() => { + throw err; + }); + const { load, error } = useCartMock(); + + await load(); + + expect(error.value.load).toBe(err); + }); }); describe('addItem', () => { @@ -93,6 +118,18 @@ describe('[CORE - factories] useCartFactory', () => { }); expect(cart.value).toEqual({ id: 'mocked_added_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.addItem.mockImplementationOnce(() => { + throw err; + }); + const { addItem, error } = useCartMock(); + + await addItem({ product: { id: 'productId' }, quantity: 1 }); + + expect(error.value.addItem).toBe(err); + }); }); describe('removeItem', () => { @@ -105,6 +142,18 @@ describe('[CORE - factories] useCartFactory', () => { }); expect(cart.value).toEqual({ id: 'mocked_removed_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.removeItem.mockImplementationOnce(() => { + throw err; + }); + const { removeItem, error } = useCartMock(); + + await removeItem({ product: { id: 'productId' } }); + + expect(error.value.removeItem).toBe(err); + }); }); describe('updateItemQty', () => { @@ -130,6 +179,18 @@ describe('[CORE - factories] useCartFactory', () => { }); expect(cart.value).toEqual({ id: 'mocked_updated_quantity_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.updateItemQty.mockImplementationOnce(() => { + throw err; + }); + const { updateItemQty, error } = useCartMock(); + + await updateItemQty({ product: { id: 'productId' }, quantity: 1 }); + + expect(error.value.updateItemQty).toBe(err); + }); }); describe('clear', () => { @@ -139,6 +200,18 @@ describe('[CORE - factories] useCartFactory', () => { expect(params.clear).toHaveBeenCalledWith({ context: null }, { currentCart: null }); expect(cart.value).toEqual({ id: 'mocked_cleared_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.clear.mockImplementationOnce(() => { + throw err; + }); + const { clear, error } = useCartMock(); + + await clear(); + + expect(error.value.clear).toBe(err); + }); }); describe('applyCoupon', () => { @@ -151,6 +224,18 @@ describe('[CORE - factories] useCartFactory', () => { }); expect(cart.value).toEqual({ id: 'mocked_apply_coupon_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.applyCoupon.mockImplementationOnce(() => { + throw err; + }); + const { applyCoupon, error } = useCartMock(); + + await applyCoupon({ couponCode: 'qwerty' }); + + expect(error.value.applyCoupon).toBe(err); + }); }); describe('removeCoupon', () => { @@ -165,6 +250,19 @@ describe('[CORE - factories] useCartFactory', () => { expect(cart.value).toEqual({ id: 'mocked_removed_coupon_cart' }); }); + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.removeCoupon.mockImplementationOnce(() => { + throw err; + }); + const { removeCoupon, error } = useCartMock(); + const coupon = 'some-coupon-code-12321231'; + + await removeCoupon({ coupon }); + + expect(error.value.removeCoupon).toBe(err); + }); + // TODO // it('should not invoke removeCoupon method if coupon is not applied', async () => { // }); diff --git a/packages/core/core/__tests__/factories/useCategoryFactory.spec.ts b/packages/core/core/__tests__/factories/useCategoryFactory.spec.ts index a95c52ef43..262fa77bcf 100644 --- a/packages/core/core/__tests__/factories/useCategoryFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useCategoryFactory.spec.ts @@ -4,6 +4,12 @@ import { UseCategory } from '../../src/types'; let useCategory: (cacheId?: string) => UseCategory; let params: UseCategoryFactoryParams; +const factoryParams = { + categorySearch: jest.fn() +}; + +const useCategoryMock = useCategoryFactory(factoryParams); + function createComposable() { params = { categorySearch: jest @@ -37,6 +43,18 @@ describe('[CORE - factories] useCategoryFactory', () => { expect(params.categorySearch).toBeCalledWith({ context: null }, { someparam: 'qwerty' }); expect(categories.value).toEqual({ id: 'mocked_removed_cart' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.categorySearch.mockImplementationOnce(() => { + throw err; + }); + const { search, error } = useCategoryMock('a'); + + await search({ someparam: 'qwerty' }); + + expect(error.value.search).toBe(err); + }); }); }); }); diff --git a/packages/core/core/__tests__/factories/useContentFactory.spec.ts b/packages/core/core/__tests__/factories/useContentFactory.spec.ts index 59d1a897e2..2d8bef6583 100644 --- a/packages/core/core/__tests__/factories/useContentFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useContentFactory.spec.ts @@ -17,6 +17,12 @@ describe('[CORE - factories] useContentFactory', () => { useContent = useContentFactory(params); }; + const factoryParams = { + search: jest.fn() + }; + + const useContentMock = useContentFactory(factoryParams); + beforeEach(() => { jest.clearAllMocks(); createContentFactoryMock(); @@ -27,7 +33,7 @@ describe('[CORE - factories] useContentFactory', () => { expect(content.value).toEqual([]); expect(loading.value).toEqual(false); - expect(error.value).toEqual(null); + expect(error.value).toEqual({}); }); it('invokes content search', async () => { @@ -38,6 +44,18 @@ describe('[CORE - factories] useContentFactory', () => { expect(params.search).toBeCalledWith({ context: null }, searchParams); expect(params.search).toBeCalledTimes(1); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.search.mockImplementationOnce(() => { + throw err; + }); + const { search, error } = useContentMock('a'); + + await search({ someparam: 'qwerty' }); + + expect(error.value.search).toBe(err); + }); }); describe('[CORE - factories] renderContentFactory', () => { diff --git a/packages/core/core/__tests__/factories/useFacetFactory.spec.ts b/packages/core/core/__tests__/factories/useFacetFactory.spec.ts index 6d10c956d7..c54dc20b0f 100644 --- a/packages/core/core/__tests__/factories/useFacetFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useFacetFactory.spec.ts @@ -1,5 +1,11 @@ import { useFacetFactory } from '../../src/factories'; +const factoryParams = { + search: jest.fn() +}; + +const useFacetMock = useFacetFactory(factoryParams); + describe('[CORE - factories] useFacetFactory', () => { it('creates properties', () => { const factorySearch = () => jest.fn(); @@ -21,4 +27,16 @@ describe('[CORE - factories] useFacetFactory', () => { expect(result.value).toEqual({ data: null, input: { param: 'test' } }); expect(loading.value).toEqual(true); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.search.mockImplementationOnce(() => { + throw err; + }); + const { search, error } = useFacetMock('a'); + + await search({ someparam: 'qwerty' }); + + expect(error.value.search).toBe(err); + }); }); diff --git a/packages/core/core/__tests__/factories/useProductFactory.spec.ts b/packages/core/core/__tests__/factories/useProductFactory.spec.ts index 1abb58a3dc..d4f2ab5d53 100644 --- a/packages/core/core/__tests__/factories/useProductFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useProductFactory.spec.ts @@ -5,6 +5,12 @@ const useProduct: (cacheId: string) => UseProduct = useProductFactory< productsSearch: (context, searchParams) => Promise.resolve([{ name: 'product ' + searchParams.slug }]) }); +const factoryParams = { + productsSearch: jest.fn() +}; + +const useProductMock = useProductFactory(factoryParams); + describe('[CORE - factories] useProductFactory', () => { it('creates properties', () => { const { products, loading } = useProduct('test-product'); @@ -28,4 +34,16 @@ describe('[CORE - factories] useProductFactory', () => { expect(products.value).toEqual([{name: 'product product-slug' }]); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.productsSearch.mockImplementationOnce(() => { + throw err; + }); + const { search, error } = useProductMock('a'); + + await search({ someparam: 'qwerty' }); + + expect(error.value.search).toBe(err); + }); }); diff --git a/packages/core/core/__tests__/factories/useReviewFactory.spec.ts b/packages/core/core/__tests__/factories/useReviewFactory.spec.ts index a414a3df21..a40b6dea4e 100644 --- a/packages/core/core/__tests__/factories/useReviewFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useReviewFactory.spec.ts @@ -80,7 +80,7 @@ describe('[CORE - factories] useReviews', () => { expect(reviews.value).toEqual([]); expect(loading.value).toEqual(false); - expect(error.value).toEqual(null); + expect(error.value).toEqual({}); }); it('returns reviews response', async () => { @@ -89,7 +89,7 @@ describe('[CORE - factories] useReviews', () => { await search({}); expect(reviews.value).toEqual(searchReviewResponse); - expect(error.value).toEqual(null); + expect(error.value).toEqual({ search: null }); }); it('can submit new review', async () => { @@ -111,7 +111,7 @@ describe('[CORE - factories] useReviews', () => { expect(reviews.value).toEqual([]); expect(loading.value).toEqual(false); - expect(error.value).toEqual('Error: Couldn\'t retrieve reviews'); + expect(error.value.search.toString()).toEqual('Error: Couldn\'t retrieve reviews'); }); it('returns error when submit fails', async () => { @@ -121,6 +121,6 @@ describe('[CORE - factories] useReviews', () => { expect(reviews.value).toEqual([]); expect(loading.value).toEqual(false); - expect(error.value).toEqual('Error: Couldn\'t submit review'); + expect(error.value.addReview.toString()).toEqual('Error: Couldn\'t submit review'); }); }); diff --git a/packages/core/core/__tests__/factories/useUserBillingFactory.spec.ts b/packages/core/core/__tests__/factories/useUserBillingFactory.spec.ts index 4e626a6507..92f8577d5b 100644 --- a/packages/core/core/__tests__/factories/useUserBillingFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useUserBillingFactory.spec.ts @@ -38,10 +38,12 @@ describe('[CORE - factories] useUserBillingFactory', () => { }); it('throws error', async () => { + const err = new Error('zxczxcx'); factoryParams.addAddress.mockImplementationOnce(() => { - throw new Error; + throw err; }); - await expect(useUserBillingMethods.addAddress('' as any)).rejects.toThrow(); + await useUserBillingMethods.addAddress('' as any); + expect(useUserBillingMethods.error.value.addAddress).toBe(err); }); it('finally loading go to false', () => { @@ -58,10 +60,12 @@ describe('[CORE - factories] useUserBillingFactory', () => { }); it('throws error', async () => { + const err = new Error('87878dfdf'); factoryParams.deleteAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserBillingMethods.deleteAddress('' as any)).rejects.toThrow(); + await useUserBillingMethods.deleteAddress('' as any); + expect(useUserBillingMethods.error.value.deleteAddress).toBe(err); }); it('finally loading go to false', () => { @@ -78,10 +82,12 @@ describe('[CORE - factories] useUserBillingFactory', () => { }); it('throws error', async () => { + const err = new Error('23232323'); factoryParams.updateAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserBillingMethods.updateAddress('' as any)).rejects.toThrow(); + await useUserBillingMethods.updateAddress('' as any); + expect(useUserBillingMethods.error.value.updateAddress).toBe(err); }); it('finally loading go to false', () => { @@ -98,10 +104,12 @@ describe('[CORE - factories] useUserBillingFactory', () => { }); it('throws error', async () => { + const err = new Error('cvcvc'); factoryParams.load.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserBillingMethods.load()).rejects.toThrow(); + await useUserBillingMethods.load(); + expect(useUserBillingMethods.error.value.load).toBe(err); }); it('finally loading go to false', () => { @@ -118,10 +126,12 @@ describe('[CORE - factories] useUserBillingFactory', () => { }); it('throws error', async () => { + const err = new Error('adsd'); factoryParams.setDefaultAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserBillingMethods.setDefaultAddress('' as any)).rejects.toThrow(); + await useUserBillingMethods.setDefaultAddress('' as any); + expect(useUserBillingMethods.error.value.setDefaultAddress).toBe(err); }); it('finally loading go to false', () => { diff --git a/packages/core/core/__tests__/factories/useUserFactory.spec.ts b/packages/core/core/__tests__/factories/useUserFactory.spec.ts index ee6ac0c3b9..90eca2a527 100644 --- a/packages/core/core/__tests__/factories/useUserFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useUserFactory.spec.ts @@ -53,10 +53,12 @@ describe('[CORE - factories] useUserFactory', () => { }); it('throws error', async () => { + const err = new Error('test-568-08989'); factoryParams.updateUser.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.updateUser('' as any)).rejects.toThrow('Error'); + await useUserMethods.updateUser('' as any); + await expect(useUserMethods.error.value.updateUser).toBe(err); }); it('finally loading go to false', () => { @@ -71,10 +73,12 @@ describe('[CORE - factories] useUserFactory', () => { expect(useUserMethods.user.value).toEqual(userToRegister); }); it('throws error', async () => { + const err = new Error('test-568-5687565'); factoryParams.register.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.register('' as any)).rejects.toThrow('Error'); + await useUserMethods.register('' as any); + expect(useUserMethods.error.value.register).toBe(err); }); it('finally loading go to false', () => { expect(useUserMethods.loading.value).toBe(false); @@ -90,10 +94,12 @@ describe('[CORE - factories] useUserFactory', () => { expect(useUserMethods.user.value).toEqual(userToLogin); }); it('throws error', async () => { + const err = new Error('test-568-232332'); factoryParams.logIn.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.login('' as any)).rejects.toThrow('Error'); + await useUserMethods.login('' as any); + expect(useUserMethods.error.value.login).toBe(err); }); it('finally loading go to false', () => { expect(useUserMethods.loading.value).toBe(false); @@ -106,10 +112,12 @@ describe('[CORE - factories] useUserFactory', () => { expect(factoryParams.logOut).toHaveBeenCalled(); }); it('throws error', async () => { + const err = new Error('test-value-568-232332'); factoryParams.logOut.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.logout()).rejects.toThrow('Error'); + await useUserMethods.logout(); + expect(useUserMethods.error.value.logout).toBe(err); }); it('finally loading go to false', () => { expect(useUserMethods.loading.value).toBe(false); @@ -124,10 +132,12 @@ describe('[CORE - factories] useUserFactory', () => { expect(useUserMethods.user.value).toEqual(user); }); it('throws error', async () => { + const err = new Error('test-value-568'); factoryParams.load.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.load()).rejects.toThrow('Error'); + await useUserMethods.load(); + expect(useUserMethods.error.value.load).toBe(err); }); it('finally loading go to false', () => { expect(useUserMethods.loading.value).toBe(false); @@ -141,10 +151,12 @@ describe('[CORE - factories] useUserFactory', () => { expect(useUserMethods.user.value).toEqual(changePasswordData); }); it('throws error', async () => { + const err = new Error('test-value'); factoryParams.changePassword.mockImplementationOnce(() => { - throw new Error('Error'); + throw err; }); - await expect(useUserMethods.changePassword({ current: null as any, new: null as any })).rejects.toThrow('Error'); + await useUserMethods.changePassword({ current: null as any, new: null as any }); + expect(useUserMethods.error.value.changePassword).toBe(err); }); it('finally loading go to false', () => { expect(useUserMethods.loading.value).toBe(false); diff --git a/packages/core/core/__tests__/factories/useUserOrdersFactory.spec.ts b/packages/core/core/__tests__/factories/useUserOrdersFactory.spec.ts index c21bde967f..7d4eb76c73 100644 --- a/packages/core/core/__tests__/factories/useUserOrdersFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useUserOrdersFactory.spec.ts @@ -35,11 +35,13 @@ describe('[CORE - factories] useUserOrderFactory', () => { }); it('should disable loading flag on error', async () => { + const err = new Error('some-error'); params.searchOrders = jest.fn().mockImplementationOnce(() => { - throw new Error(); + throw err; }); - const { search, loading, orders } = useUserOrders(); - await expect(search({})).rejects.toThrow(); + const { search, loading, orders, error } = useUserOrders(); + await search({}); + expect(error.value.search).toBe(err); expect(loading.value).toEqual(false); expect(orders.value).toEqual([]); diff --git a/packages/core/core/__tests__/factories/useUserShippingFactory.spec.ts b/packages/core/core/__tests__/factories/useUserShippingFactory.spec.ts index bd8c077480..0617933731 100644 --- a/packages/core/core/__tests__/factories/useUserShippingFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useUserShippingFactory.spec.ts @@ -38,10 +38,12 @@ describe('[CORE - factories] useUserShippingFactory', () => { }); it('throws error', async () => { + const err = new Error('2323'); factoryParams.addAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserShippingMethods.addAddress('' as any)).rejects.toThrow(); + await useUserShippingMethods.addAddress('' as any); + expect(useUserShippingMethods.error.value.addAddress).toBe(err); }); it('finally loading go to false', () => { @@ -58,10 +60,12 @@ describe('[CORE - factories] useUserShippingFactory', () => { }); it('throws error', async () => { + const err = new Error('2323'); factoryParams.deleteAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserShippingMethods.deleteAddress('' as any)).rejects.toThrow(); + await useUserShippingMethods.deleteAddress('' as any); + expect(useUserShippingMethods.error.value.deleteAddress).toBe(err); }); it('finally loading go to false', () => { @@ -78,10 +82,12 @@ describe('[CORE - factories] useUserShippingFactory', () => { }); it('throws error', async () => { + const err = new Error('2323'); factoryParams.updateAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserShippingMethods.updateAddress('' as any)).rejects.toThrow(); + await useUserShippingMethods.updateAddress('' as any); + expect(useUserShippingMethods.error.value.updateAddress).toBe(err); }); it('finally loading go to false', () => { @@ -98,10 +104,12 @@ describe('[CORE - factories] useUserShippingFactory', () => { }); it('throws error', async () => { + const err = new Error('2323'); factoryParams.load.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserShippingMethods.load()).rejects.toThrow(); + await useUserShippingMethods.load(); + expect(useUserShippingMethods.error.value.load).toBe(err); }); it('finally loading go to false', () => { @@ -118,10 +126,12 @@ describe('[CORE - factories] useUserShippingFactory', () => { }); it('throws error', async () => { + const err = new Error('zxczxcx'); factoryParams.setDefaultAddress.mockImplementationOnce(() => { - throw new Error(); + throw err; }); - await expect(useUserShippingMethods.setDefaultAddress('' as any)).rejects.toThrow(); + await useUserShippingMethods.setDefaultAddress('' as any); + expect(useUserShippingMethods.error.value.setDefaultAddress).toBe(err); }); it('finally loading go to false', () => { diff --git a/packages/core/core/__tests__/factories/useWishlistFactory.spec.ts b/packages/core/core/__tests__/factories/useWishlistFactory.spec.ts index 0478d9d092..ec68ce8710 100644 --- a/packages/core/core/__tests__/factories/useWishlistFactory.spec.ts +++ b/packages/core/core/__tests__/factories/useWishlistFactory.spec.ts @@ -19,6 +19,16 @@ function createComposable() { useWishlist = useWishlistFactory(params); } +const factoryParams = { + addItem: jest.fn(() => null), + removeItem: jest.fn(), + load: jest.fn(), + clear: jest.fn(), + isOnWishlist: jest.fn() +}; + +const useWishlistMock = useWishlistFactory(factoryParams); + describe('[CORE - factories] useWishlistFactory', () => { beforeEach(() => { jest.clearAllMocks(); @@ -70,6 +80,18 @@ describe('[CORE - factories] useWishlistFactory', () => { expect(params.load).toHaveBeenCalledWith({ context: null }, { customQuery }); expect(wishlist.value).toEqual({ id: 'mocked_wishlist' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.load.mockImplementationOnce(() => { + throw err; + }); + const { load, error } = useWishlistMock(); + + await load(); + + expect(error.value.load).toBe(err); + }); }); describe('addItem', () => { @@ -82,6 +104,20 @@ describe('[CORE - factories] useWishlistFactory', () => { }); expect(wishlist.value).toEqual({ id: 'mocked_added_wishlist' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.addItem.mockImplementationOnce(() => { + throw err; + }); + const { addItem, error } = useWishlistMock(); + + await addItem({ + product: { id: 'productId' } + }); + + expect(error.value.addItem).toBe(err); + }); }); describe('removeItem', () => { @@ -94,6 +130,20 @@ describe('[CORE - factories] useWishlistFactory', () => { }); expect(wishlist.value).toEqual({ id: 'mocked_removed_wishlist' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.removeItem.mockImplementationOnce(() => { + throw err; + }); + const { removeItem, error } = useWishlistMock(); + + await removeItem({ + product: { id: 'productId' } + }); + + expect(error.value.removeItem).toBe(err); + }); }); describe('clear', () => { @@ -103,6 +153,18 @@ describe('[CORE - factories] useWishlistFactory', () => { expect(params.clear).toHaveBeenCalledWith({ context: null }, { currentWishlist: null }); expect(wishlist.value).toEqual({ id: 'mocked_cleared_wishlist' }); }); + + it('should set error if factory method throwed', async () => { + const err = new Error('zxczxcx'); + factoryParams.clear.mockImplementationOnce(() => { + throw err; + }); + const { clear, error } = useWishlistMock(); + + await clear(); + + expect(error.value.clear).toBe(err); + }); }); }); }); diff --git a/packages/core/core/src/factories/useCartFactory.ts b/packages/core/core/src/factories/useCartFactory.ts index 14cc21ec93..c7a55d99eb 100644 --- a/packages/core/core/src/factories/useCartFactory.ts +++ b/packages/core/core/src/factories/useCartFactory.ts @@ -1,4 +1,4 @@ -import { CustomQuery, UseCart, Context, FactoryParams } from '../types'; +import { CustomQuery, UseCart, Context, FactoryParams, UseCartErrors } from '../types'; import { Ref, computed } from '@vue/composition-api'; import { sharedRef, Logger, generateContext } from '../utils'; @@ -34,6 +34,7 @@ export const useCartFactory = ( const loading: Ref = sharedRef(false, 'useCart-loading'); const cart: Ref = sharedRef(null, 'useCart-cart'); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, 'useCart-error'); const setCart = (newCart: CART) => { cart.value = newCart; @@ -43,55 +44,76 @@ export const useCartFactory = ( const addItem = async ({ product, quantity, customQuery }) => { Logger.debug('useCart.addItem', { product, quantity }); - loading.value = true; - const updatedCart = await factoryParams.addItem( - context, - { - currentCart: cart.value, - product, - quantity, - customQuery - } - ); - cart.value = updatedCart; - loading.value = false; + try { + loading.value = true; + error.value.addItem = null; + const updatedCart = await factoryParams.addItem( + context, + { + currentCart: cart.value, + product, + quantity, + customQuery + } + ); + cart.value = updatedCart; + } catch (err) { + error.value.addItem = err; + Logger.error('useCart/addItem', err); + } finally { + loading.value = false; + } }; const removeItem = async ({ product, customQuery }) => { Logger.debug('useCart.removeItem', { product }); - loading.value = true; - const updatedCart = await factoryParams.removeItem( - context, - { - currentCart: cart.value, - product, - customQuery - } - ); - cart.value = updatedCart; - loading.value = false; - }; - - const updateItemQty = async ({ product, quantity, customQuery }) => { - Logger.debug('useCart.updateItemQty', { product, quantity }); - - if (quantity && quantity > 0) { + try { loading.value = true; - const updatedCart = await factoryParams.updateItemQty( + error.value.removeItem = null; + const updatedCart = await factoryParams.removeItem( context, { currentCart: cart.value, product, - quantity, customQuery } ); cart.value = updatedCart; + } catch (err) { + error.value.removeItem = err; + Logger.error('useCart/removeItem', err); + } finally { loading.value = false; } }; + const updateItemQty = async ({ product, quantity, customQuery }) => { + Logger.debug('useCart.updateItemQty', { product, quantity }); + + if (quantity && quantity > 0) { + try { + loading.value = true; + error.value.updateItemQty = null; + const updatedCart = await factoryParams.updateItemQty( + context, + { + currentCart: cart.value, + product, + quantity, + customQuery + } + ); + cart.value = updatedCart; + } catch (err) { + error.value.updateItemQty = err; + Logger.error('useCart/updateItemQty', err); + } finally { + loading.value = false; + } + } + }; + const load = async ({ customQuery } = { customQuery: undefined }) => { Logger.debug('useCart.load'); @@ -102,21 +124,36 @@ export const useCartFactory = ( * temporary issue related with cpapi plugin */ loading.value = false; + error.value.load = null; cart.value = { ...cart.value }; return; } - loading.value = true; - cart.value = await factoryParams.load(context, { customQuery }); - loading.value = false; + try { + loading.value = true; + error.value.load = null; + cart.value = await factoryParams.load(context, { customQuery }); + } catch (err) { + error.value.load = err; + Logger.error('useCart/load', err); + } finally { + loading.value = false; + } }; const clear = async () => { Logger.debug('useCart.clear'); - loading.value = true; - const updatedCart = await factoryParams.clear(context, { currentCart: cart.value }); - cart.value = updatedCart; - loading.value = false; + try { + loading.value = true; + error.value.clear = null; + const updatedCart = await factoryParams.clear(context, { currentCart: cart.value }); + cart.value = updatedCart; + } catch (err) { + error.value.clear = err; + Logger.error('useCart/clear', err); + } finally { + loading.value = false; + } }; const isOnCart = ({ product }) => { @@ -131,15 +168,16 @@ export const useCartFactory = ( try { loading.value = true; + error.value.applyCoupon = null; const { updatedCart } = await factoryParams.applyCoupon(context, { currentCart: cart.value, couponCode, customQuery }); cart.value = updatedCart; - } catch (e) { - Logger.error('useCart.applyCoupon', e); - throw e; + } catch (err) { + error.value.applyCoupon = err; + Logger.error('useCart/applyCoupon', err); } finally { loading.value = false; } @@ -150,6 +188,7 @@ export const useCartFactory = ( try { loading.value = true; + error.value.removeCoupon = null; const { updatedCart } = await factoryParams.removeCoupon( context, { @@ -160,9 +199,9 @@ export const useCartFactory = ( ); cart.value = updatedCart; loading.value = false; - } catch (e) { - Logger.error('useCart.applyCoupon', e); - throw e; + } catch (err) { + error.value.removeCoupon = err; + Logger.error('useCart/removeCoupon', err); } finally { loading.value = false; } @@ -179,7 +218,8 @@ export const useCartFactory = ( updateItemQty, applyCoupon, removeCoupon, - loading: computed(() => loading.value) + loading: computed(() => loading.value), + error: computed(() => error.value) }; }; }; diff --git a/packages/core/core/src/factories/useCategoryFactory.ts b/packages/core/core/src/factories/useCategoryFactory.ts index 4ca467f79a..3531e6804c 100644 --- a/packages/core/core/src/factories/useCategoryFactory.ts +++ b/packages/core/core/src/factories/useCategoryFactory.ts @@ -1,4 +1,4 @@ -import { CustomQuery, UseCategory, Context, FactoryParams } from '../types'; +import { CustomQuery, UseCategory, Context, FactoryParams, UseCategoryErrors } from '../types'; import { Ref, computed } from '@vue/composition-api'; import { sharedRef, Logger, generateContext } from '../utils'; @@ -13,19 +13,28 @@ export function useCategoryFactory( const categories: Ref = sharedRef([], `useCategory-categories-${id}`); const loading = sharedRef(false, `useCategory-loading-${id}`); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, `useCategory-error-${id}`); const search = async (searchParams) => { Logger.debug('useCategory.search', searchParams); - loading.value = true; - categories.value = await factoryParams.categorySearch(context, searchParams); - loading.value = false; + try { + loading.value = true; + error.value.search = null; + categories.value = await factoryParams.categorySearch(context, searchParams); + } catch (err) { + error.value.search = err; + Logger.error(`useCategory/${id}/search`, err); + } finally { + loading.value = false; + } }; return { search, loading: computed(() => loading.value), - categories: computed(() => categories.value) + categories: computed(() => categories.value), + error: computed(() => error.value) }; }; } diff --git a/packages/core/core/src/factories/useContentFactory.ts b/packages/core/core/src/factories/useContentFactory.ts index 633ac7f2a8..8772dd90c4 100644 --- a/packages/core/core/src/factories/useContentFactory.ts +++ b/packages/core/core/src/factories/useContentFactory.ts @@ -1,6 +1,6 @@ import { Ref, computed } from '@vue/composition-api'; -import { RenderComponent, UseContent, Context, FactoryParams } from '../types'; -import { sharedRef, generateContext } from '../utils'; +import { RenderComponent, UseContent, Context, FactoryParams, UseContentErrors } from '../types'; +import { sharedRef, Logger, generateContext } from '../utils'; import { PropOptions, VNode } from 'vue'; export interface UseContentFactoryParams extends FactoryParams { @@ -13,15 +13,19 @@ export function useContentFactory( return function useContent(id: string): UseContent { const content: Ref = sharedRef([], `useContent-content-${id}`); const loading: Ref = sharedRef(false, `useContent-loading-${id}`); - const error: Ref = sharedRef(null, `useContent-error-${id}`); + const error: Ref = sharedRef({}, `useContent-error-${id}`); const context = generateContext(factoryParams); const search = async(params: CONTENT_SEARCH_PARAMS): Promise => { + Logger.debug('useContent.search', params); + try { loading.value = true; + error.value.search = null; content.value = await factoryParams.search(context, params); - } catch (searchError) { - error.value = searchError.toString(); + } catch (err) { + error.value.search = err; + Logger.error(`useContent/${id}/search`, err); } finally { loading.value = false; } diff --git a/packages/core/core/src/factories/useFacetFactory.ts b/packages/core/core/src/factories/useFacetFactory.ts index 2367c68299..57e3055b0f 100644 --- a/packages/core/core/src/factories/useFacetFactory.ts +++ b/packages/core/core/src/factories/useFacetFactory.ts @@ -1,6 +1,6 @@ import { Ref, computed } from '@vue/composition-api'; -import { vsfRef, Logger, generateContext } from '../utils'; -import { UseFacet, FacetSearchResult, AgnosticFacetSearchParams, Context, FactoryParams } from '../types'; +import { sharedRef, vsfRef, Logger, generateContext } from '../utils'; +import { UseFacet, FacetSearchResult, AgnosticFacetSearchParams, Context, FactoryParams, UseFacetErrors } from '../types'; interface UseFacetFactoryParams extends FactoryParams { search: (context: Context, params?: FacetSearchResult) => Promise; @@ -13,19 +13,28 @@ const useFacetFactory = (factoryParams: UseFacetFactoryParams = vsfRef(false, `${ssrKey}-loading`); const result: Ref> = vsfRef({ data: null, input: null }, `${ssrKey}-facets`); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, `useFacet-error-${id}`); const search = async (params?: AgnosticFacetSearchParams) => { Logger.debug('useFacet.search', params); result.value.input = params; - loading.value = true; - result.value.data = await factoryParams.search(context, result.value); - loading.value = false; + try { + loading.value = true; + error.value.search = null; + result.value.data = await factoryParams.search(context, result.value); + } catch (err) { + error.value.search = err; + Logger.error(`useFacet/${id}/search`, err); + } finally { + loading.value = false; + } }; return { result: computed(() => result.value), loading: computed(() => loading.value), + error: computed(() => error.value), search }; }; diff --git a/packages/core/core/src/factories/useProductFactory.ts b/packages/core/core/src/factories/useProductFactory.ts index 231b87e407..f398eb959c 100644 --- a/packages/core/core/src/factories/useProductFactory.ts +++ b/packages/core/core/src/factories/useProductFactory.ts @@ -1,4 +1,4 @@ -import { CustomQuery, ProductsSearchParams, UseProduct, Context, FactoryParams } from '../types'; +import { CustomQuery, ProductsSearchParams, UseProduct, Context, FactoryParams, UseProductErrors } from '../types'; import { Ref, computed } from '@vue/composition-api'; import { sharedRef, Logger, generateContext } from '../utils'; export interface UseProductFactoryParams extends FactoryParams { @@ -12,16 +12,18 @@ export function useProductFactory( const products: Ref = sharedRef([], `useProduct-products-${id}`); const loading = sharedRef(false, `useProduct-loading-${id}`); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, `useProduct-error-${id}`); const search = async (searchParams) => { Logger.debug('useProduct.search', searchParams); - loading.value = true; try { + loading.value = true; + error.value.search = null; products.value = await factoryParams.productsSearch(context, searchParams); - } catch (e) { - Logger.error('useProduct.search', e); - throw e; + } catch (err) { + error.value.search = err; + Logger.error(`useProduct/${id}/search`, err); } finally { loading.value = false; } @@ -30,7 +32,8 @@ export function useProductFactory( return { search, products: computed(() => products.value), - loading: computed(() => loading.value) + loading: computed(() => loading.value), + error: computed(() => error.value) }; }; } diff --git a/packages/core/core/src/factories/useReviewFactory.ts b/packages/core/core/src/factories/useReviewFactory.ts index 54870bae88..e2b8ee841a 100644 --- a/packages/core/core/src/factories/useReviewFactory.ts +++ b/packages/core/core/src/factories/useReviewFactory.ts @@ -1,5 +1,5 @@ import { Ref, computed } from '@vue/composition-api'; -import { CustomQuery, UseReview, Context, FactoryParams } from '../types'; +import { CustomQuery, UseReview, Context, FactoryParams, UseReviewErrors } from '../types'; import { sharedRef, Logger, generateContext } from '../utils'; export interface UseReviewFactoryParams extends FactoryParams { @@ -13,7 +13,7 @@ export function useReviewFactory { const reviews: Ref = sharedRef([], `useReviews-reviews-${id}`); const loading: Ref = sharedRef(false, `useReviews-loading-${id}`); - const error: Ref = sharedRef(null, `useReviews-error-${id}`); + const error: Ref = sharedRef({}, `useProduct-error-${id}`); const context = generateContext(factoryParams); const search = async (searchParams): Promise => { @@ -21,11 +21,11 @@ export function useReviewFactory extends FactoryParams{ @@ -42,22 +42,23 @@ export const useUserBillingFactory = ( const loading: Ref = sharedRef(false, 'useUserBilling-loading'); const billing: Ref = sharedRef({}, 'useUserBilling-billing'); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, 'useUserBilling-error'); const readonlyBilling: Readonly = unref(billing); const addAddress = async ({ address }) => { Logger.debug('useUserBilling.addAddress', address); - loading.value = true; try { + loading.value = true; + error.value.addAddress = null; billing.value = await factoryParams.addAddress(context, { address, billing: readonlyBilling }); } catch (err) { - Logger.error('useUserBilling.addAddress', err); - - throw err; + error.value.addAddress = err; + Logger.error('useUserBilling/addAddress', err); } finally { loading.value = false; } @@ -66,16 +67,16 @@ export const useUserBillingFactory = ( const deleteAddress = async ({ address }) => { Logger.debug('useUserBilling.deleteAddress', address); - loading.value = true; try { + loading.value = true; + error.value.deleteAddress = null; billing.value = await factoryParams.deleteAddress(context, { address, billing: readonlyBilling }); } catch (err) { - Logger.error('useUserBilling.deleteAddress', err); - - throw err; + error.value.deleteAddress = err; + Logger.error('useUserBilling/deleteAddress', err); } finally { loading.value = false; } @@ -84,16 +85,16 @@ export const useUserBillingFactory = ( const updateAddress = async ({ address }) => { Logger.debug('useUserBilling.updateAddress', address); - loading.value = true; try { + loading.value = true; + error.value.updateAddress = null; billing.value = await factoryParams.updateAddress(context, { address, billing: readonlyBilling }); } catch (err) { - Logger.error('useUserBilling.updateAddress', err); - - throw err; + error.value.updateAddress = err; + Logger.error('useUserBilling/updateAddress', err); } finally { loading.value = false; } @@ -102,15 +103,15 @@ export const useUserBillingFactory = ( const load = async () => { Logger.debug('useUserBilling.load'); - loading.value = true; try { + loading.value = true; + error.value.load = null; billing.value = await factoryParams.load(context, { billing: readonlyBilling }); } catch (err) { - Logger.error('useUserBilling.load', err); - - throw err; + error.value.load = err; + Logger.error('useUserBilling/load', err); } finally { loading.value = false; } @@ -119,16 +120,16 @@ export const useUserBillingFactory = ( const setDefaultAddress = async ({ address }) => { Logger.debug('useUserBilling.setDefaultAddress'); - loading.value = true; try { + loading.value = true; + error.value.setDefaultAddress = null; billing.value = await factoryParams.setDefaultAddress(context, { address, billing: readonlyBilling }); } catch (err) { - Logger.error('useUserBilling.setDefaultAddress', err); - - throw err; + error.value.setDefaultAddress = err; + Logger.error('useUserBilling/setDefaultAddress', err); } finally { loading.value = false; } @@ -137,6 +138,7 @@ export const useUserBillingFactory = ( return { billing: computed(() => billing.value), loading: computed(() => loading.value), + error: computed(() => error.value), addAddress, deleteAddress, updateAddress, diff --git a/packages/core/core/src/factories/useUserFactory.ts b/packages/core/core/src/factories/useUserFactory.ts index f45c1f4bca..a245d5423e 100644 --- a/packages/core/core/src/factories/useUserFactory.ts +++ b/packages/core/core/src/factories/useUserFactory.ts @@ -1,5 +1,5 @@ import { Ref, computed } from '@vue/composition-api'; -import { UseUser, Context, FactoryParams } from '../types'; +import { UseUser, Context, FactoryParams, UseUserErrors } from '../types'; import { sharedRef, Logger, mask, generateContext } from '../utils'; export interface UseUserFactoryParams extends FactoryParams { @@ -20,6 +20,7 @@ export const useUserFactory = = sharedRef(false, 'useUser-loading'); const isAuthenticated = computed(() => Boolean(user.value)); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, 'useUser-error'); const setUser = (newUser: USER) => { user.value = newUser; @@ -29,13 +30,13 @@ export const useUserFactory = { Logger.debug('useUserFactory.updateUser', providedUser); - loading.value = true; try { + loading.value = true; + error.value.updateUser = null; user.value = await factoryParams.updateUser(context, {currentUser: user.value, updatedUserData: providedUser}); } catch (err) { - Logger.error('useUserFactory.updateUser', err); - - throw err; + error.value.updateUser = err; + Logger.error('useUser/updateUser', err); } finally { loading.value = false; } @@ -44,13 +45,13 @@ export const useUserFactory = { Logger.debug('useUserFactory.register', providedUser); - loading.value = true; try { + loading.value = true; + error.value.register = null; user.value = await factoryParams.register(context, providedUser); } catch (err) { - Logger.error('useUserFactory.register', err); - - throw err; + error.value.register = err; + Logger.error('useUser/register', err); } finally { loading.value = false; } @@ -59,13 +60,13 @@ export const useUserFactory = { Logger.debug('useUserFactory.login', providedUser); - loading.value = true; try { + loading.value = true; + error.value.login = null; user.value = await factoryParams.logIn(context, providedUser); } catch (err) { - Logger.error('useUserFactory.login', err); - - throw err; + error.value.login = err; + Logger.error('useUser/login', err); } finally { loading.value = false; } @@ -75,29 +76,29 @@ export const useUserFactory = { Logger.debug('useUserFactory.changePassword', { currentPassword: mask(params.current), newPassword: mask(params.new) }); - loading.value = true; try { + loading.value = true; + error.value.changePassword = null; user.value = await factoryParams.changePassword(context, { currentUser: user.value, currentPassword: params.current, newPassword: params.new }); } catch (err) { - Logger.error('useUserFactory.changePassword', err); - - throw err; + error.value.changePassword = err; + Logger.error('useUser/changePassword', err); } finally { loading.value = false; } @@ -105,14 +106,14 @@ export const useUserFactory = { Logger.debug('useUserFactory.load'); - loading.value = true; try { + loading.value = true; + error.value.load = null; user.value = await factoryParams.load(context); } catch (err) { - Logger.error('useUserFactory.load', err); - - throw err; + error.value.load = err; + Logger.error('useUser/load', err); } finally { loading.value = false; } @@ -128,7 +129,8 @@ export const useUserFactory = loading.value) + loading: computed(() => loading.value), + error: computed(() => error.value) }; }; }; diff --git a/packages/core/core/src/factories/useUserOrdersFactory.ts b/packages/core/core/src/factories/useUserOrdersFactory.ts index f3aa2e94ad..06dcbc68ef 100644 --- a/packages/core/core/src/factories/useUserOrdersFactory.ts +++ b/packages/core/core/src/factories/useUserOrdersFactory.ts @@ -1,5 +1,5 @@ import { Ref, computed } from '@vue/composition-api'; -import { CustomQuery, UseUserOrders, Context, FactoryParams } from '../types'; +import { CustomQuery, UseUserOrders, Context, FactoryParams, UseUserOrdersErrors } from '../types'; import { sharedRef, Logger, generateContext } from '../utils'; export interface UseUserOrdersFactoryParams extends FactoryParams { @@ -11,17 +11,18 @@ export function useUserOrdersFactory(factoryParams: const orders: Ref = sharedRef([], 'useUserOrders-orders'); const loading: Ref = sharedRef(false, 'useUserOrders-loading'); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, 'useUserOrders-error'); const search = async (searchParams): Promise => { Logger.debug('useUserOrders.search', searchParams); - loading.value = true; try { + loading.value = true; + error.value.search = null; orders.value = await factoryParams.searchOrders(context, searchParams); } catch (err) { - Logger.error('useUserOrders.search', err); - - throw err; + error.value.search = err; + Logger.error('useUserOrders/search', err); } finally { loading.value = false; } @@ -30,7 +31,8 @@ export function useUserOrdersFactory(factoryParams: return { orders: computed(() => orders.value), search, - loading: computed(() => loading.value) + loading: computed(() => loading.value), + error: computed(() => error.value) }; }; } diff --git a/packages/core/core/src/factories/useUserShippingFactory.ts b/packages/core/core/src/factories/useUserShippingFactory.ts index e1506adc44..5f4b6ae0f2 100644 --- a/packages/core/core/src/factories/useUserShippingFactory.ts +++ b/packages/core/core/src/factories/useUserShippingFactory.ts @@ -1,5 +1,5 @@ import { Ref, unref, computed } from '@vue/composition-api'; -import { UseUserShipping, Context, FactoryParams } from '../types'; +import { UseUserShipping, Context, FactoryParams, UseUserShippingErrors } from '../types'; import { sharedRef, Logger, mask, generateContext } from '../utils'; export interface UseUserShippingFactoryParams extends FactoryParams { @@ -43,20 +43,21 @@ export const useUserShippingFactory = ( const shipping: Ref = sharedRef({}, 'useUserShipping-shipping'); const context = generateContext(factoryParams); const readonlyShipping: Readonly = unref(shipping); + const error: Ref = sharedRef({}, 'useUserShipping-error'); const addAddress = async ({ address }) => { Logger.debug('useUserShipping.addAddress', mask(address)); - loading.value = true; try { + loading.value = true; + error.value.addAddress = null; shipping.value = await factoryParams.addAddress(context, { address, shipping: readonlyShipping }); } catch (err) { - Logger.error('useUserShipping.addAddress', err); - - throw err; + error.value.addAddress = err; + Logger.error('useUserShipping/addAddress', err); } finally { loading.value = false; } @@ -65,16 +66,16 @@ export const useUserShippingFactory = ( const deleteAddress = async ({ address }) => { Logger.debug('useUserShipping.deleteAddress', address); - loading.value = true; try { + loading.value = true; + error.value.deleteAddress = null; shipping.value = await factoryParams.deleteAddress(context, { address, shipping: readonlyShipping }); } catch (err) { - Logger.error('useUserShipping.deleteAddress', err); - - throw err; + error.value.deleteAddress = err; + Logger.error('useUserShipping/deleteAddress', err); } finally { loading.value = false; } @@ -83,16 +84,16 @@ export const useUserShippingFactory = ( const updateAddress = async ({ address }) => { Logger.debug('useUserShipping.updateAddress', address); - loading.value = true; try { + loading.value = true; + error.value.updateAddress = null; shipping.value = await factoryParams.updateAddress(context, { address, shipping: readonlyShipping }); } catch (err) { - Logger.error('useUserShipping.updateAddress', address); - - throw err; + error.value.updateAddress = err; + Logger.error('useUserShipping/updateAddress', err); } finally { loading.value = false; } @@ -101,15 +102,15 @@ export const useUserShippingFactory = ( const load = async () => { Logger.debug('useUserShipping.load'); - loading.value = true; try { + loading.value = true; + error.value.load = null; shipping.value = await factoryParams.load(context, { shipping: readonlyShipping }); } catch (err) { - Logger.error('useUserShipping.load', err); - - throw err; + error.value.load = err; + Logger.error('useUserShipping/load', err); } finally { loading.value = false; } @@ -118,16 +119,16 @@ export const useUserShippingFactory = ( const setDefaultAddress = async ({ address }) => { Logger.debug('useUserShipping.setDefaultAddress', address); - loading.value = true; try { + loading.value = true; + error.value.setDefaultAddress = null; shipping.value = await factoryParams.setDefaultAddress(context, { address, shipping: readonlyShipping }); } catch (err) { - Logger.error('useUserShipping.setDefaultAddress', err); - - throw err; + error.value.setDefaultAddress = err; + Logger.error('useUserShipping/setDefaultAddress', err); } finally { loading.value = false; } @@ -136,6 +137,7 @@ export const useUserShippingFactory = ( return { shipping: computed(() => shipping.value), loading: computed(() => loading.value), + error: computed(() => error.value), addAddress, deleteAddress, updateAddress, diff --git a/packages/core/core/src/factories/useWishlistFactory.ts b/packages/core/core/src/factories/useWishlistFactory.ts index 4d6731456a..2d3d606bcf 100644 --- a/packages/core/core/src/factories/useWishlistFactory.ts +++ b/packages/core/core/src/factories/useWishlistFactory.ts @@ -1,4 +1,4 @@ -import { UseWishlist, CustomQuery, Context, FactoryParams } from '../types'; +import { UseWishlist, CustomQuery, Context, FactoryParams, UseWishlistErrors } from '../types'; import { Ref, computed } from '@vue/composition-api'; import { sharedRef, Logger, generateContext } from '../utils'; @@ -29,6 +29,7 @@ export const useWishlistFactory = ( const loading: Ref = sharedRef(false, 'useWishlist-loading'); const wishlist: Ref = sharedRef(null, 'useWishlist-wishlist'); const context = generateContext(factoryParams); + const error: Ref = sharedRef({}, 'useWishlist-error'); const setWishlist = (newWishlist: WISHLIST) => { wishlist.value = newWishlist; @@ -36,35 +37,49 @@ export const useWishlistFactory = ( }; const addItem = async ({ product, customQuery }) => { - Logger.debug('useWishlist.addToWishlist', product); - - loading.value = true; - const updatedWishlist = await factoryParams.addItem( - context, - { - currentWishlist: wishlist.value, - product, - customQuery - } - ); - wishlist.value = updatedWishlist; - loading.value = false; + Logger.debug('useWishlist.addItem', product); + + try { + loading.value = true; + error.value.addItem = null; + const updatedWishlist = await factoryParams.addItem( + context, + { + currentWishlist: wishlist.value, + product, + customQuery + } + ); + wishlist.value = updatedWishlist; + } catch (err) { + error.value.addItem = err; + Logger.error('useWishlist/addItem', err); + } finally { + loading.value = false; + } }; const removeItem = async ({ product, customQuery }) => { - Logger.debug('useWishlist.removeFromWishlist', product); - - loading.value = true; - const updatedWishlist = await factoryParams.removeItem( - context, - { - currentWishlist: wishlist.value, - product, - customQuery - } - ); - wishlist.value = updatedWishlist; - loading.value = false; + Logger.debug('useWishlist.removeItem', product); + + try { + loading.value = true; + error.value.removeItem = null; + const updatedWishlist = await factoryParams.removeItem( + context, + { + currentWishlist: wishlist.value, + product, + customQuery + } + ); + wishlist.value = updatedWishlist; + } catch (err) { + error.value.removeItem = err; + Logger.error('useWishlist/removeItem', err); + } finally { + loading.value = false; + } }; const load = async ({ customQuery } = { customQuery: undefined }) => { @@ -72,20 +87,34 @@ export const useWishlistFactory = ( if (wishlist.value) return; - loading.value = true; - wishlist.value = await factoryParams.load(context, { customQuery }); - loading.value = false; + try { + loading.value = true; + error.value.load = null; + wishlist.value = await factoryParams.load(context, { customQuery }); + } catch (err) { + error.value.load = err; + Logger.error('useWishlist/load', err); + } finally { + loading.value = false; + } }; const clear = async () => { - Logger.debug('useWishlist.clearWishlist'); - - loading.value = true; - const updatedWishlist = await factoryParams.clear(context, { - currentWishlist: wishlist.value - }); - wishlist.value = updatedWishlist; - loading.value = false; + Logger.debug('useWishlist.clear'); + + try { + loading.value = true; + error.value.clear = null; + const updatedWishlist = await factoryParams.clear(context, { + currentWishlist: wishlist.value + }); + wishlist.value = updatedWishlist; + } catch (err) { + error.value.clear = err; + Logger.error('useWishlist/clear', err); + } finally { + loading.value = false; + } }; const isOnWishlist = ({ product }) => { @@ -105,7 +134,8 @@ export const useWishlistFactory = ( removeItem, clear, setWishlist, - loading: computed(() => loading.value) + loading: computed(() => loading.value), + error: computed(() => error.value) }; }; diff --git a/packages/core/core/src/types.ts b/packages/core/core/src/types.ts index 64a9b0f425..f22ad66358 100644 --- a/packages/core/core/src/types.ts +++ b/packages/core/core/src/types.ts @@ -19,10 +19,13 @@ export interface ProductsSearchParams { filters?: any; [x: string]: any; } - +export interface UseProductErrors { + search?: Error; +} export interface UseProduct { products: ComputedProperty; loading: ComputedProperty; + error: ComputedProperty; search(params: ComposableFunctionArgs): Promise; [x: string]: any; } @@ -40,7 +43,14 @@ export interface UseUserLoginParams { password: string; [x: string]: any; } - +export interface UseUserErrors { + updateUser?: Error; + register?: Error; + login?: Error; + logout?: Error; + changePassword?: Error; + load?: Error; +} export interface UseUser < USER, @@ -48,14 +58,15 @@ export interface UseUser > { user: ComputedProperty; setUser: (user: USER) => void; - updateUser: ({ user: UPDATE_USER_PARAMS }) => Promise; - register: ({ user: UseUserRegisterParams }) => Promise; - login: ({ user: UseUserLoginParams }) => Promise; + updateUser: (params: { user: UPDATE_USER_PARAMS }) => Promise; + register: (params: { user: UseUserRegisterParams }) => Promise; + login: (params: { user: UseUserLoginParams }) => Promise; logout: () => Promise; changePassword: (params: { current: string; new: string }) => Promise; load: () => Promise; isAuthenticated: Ref; loading: ComputedProperty; + error: ComputedProperty; } export interface UseUserOrdersSearchParams { @@ -64,11 +75,14 @@ export interface UseUserOrdersSearchParams { perPage?: number; [x: string]: any; } - +export interface UseUserOrdersErrors { + search?: Error; +} export interface UseUserOrders { orders: ComputedProperty; search(params: ComposableFunctionArgs): Promise; loading: ComputedProperty; + error: ComputedProperty; } export interface UseUserAddress
{ @@ -80,15 +94,22 @@ export interface UseUserAddress
{ searchAddresses: (params?: { [x: string]: any }) => Promise; loading: ComputedProperty; } - +export interface UseUserShippingErrors { + addAddress?: Error; + deleteAddress?: Error; + updateAddress?: Error; + load?: Error; + setDefaultAddress?: Error; +} export interface UseUserShipping { shipping: ComputedProperty; - addAddress: ({ address: USER_SHIPPING_ITEM }) => Promise; - deleteAddress: ({ address: USER_SHIPPING_ITEM }) => Promise; - updateAddress: ({ address: USER_SHIPPING_ITEM }) => Promise; + addAddress: (params: { address: USER_SHIPPING_ITEM }) => Promise; + deleteAddress: (params: { address: USER_SHIPPING_ITEM }) => Promise; + updateAddress: (params: { address: USER_SHIPPING_ITEM }) => Promise; load: () => Promise; - setDefaultAddress: ({ address: USER_SHIPPING_ITEM }) => Promise; + setDefaultAddress: (params: { address: USER_SHIPPING_ITEM }) => Promise; loading: ComputedProperty; + error: ComputedProperty; } export interface UserShippingGetters { @@ -112,14 +133,22 @@ export interface UserShippingGetters { isDefault: (address: USER_SHIPPING_ITEM) => boolean; } +export interface UseUserBillingErrors { + addAddress?: Error; + deleteAddress?: Error; + updateAddress?: Error; + load?: Error; + setDefaultAddress?: Error; +} export interface UseUserBilling { billing: ComputedProperty; - addAddress: ({ address: USER_BILLING_ITEM }) => Promise; - deleteAddress: ({ address: USER_BILLING_ITEM }) => Promise; - updateAddress: ({ address: USER_BILLING_ITEM }) => Promise; + addAddress: (params: { address: USER_BILLING_ITEM }) => Promise; + deleteAddress: (params: { address: USER_BILLING_ITEM }) => Promise; + updateAddress: (params: { address: USER_BILLING_ITEM }) => Promise; load: () => Promise; - setDefaultAddress: ({ address: USER_BILLING_ITEM }) => Promise; + setDefaultAddress: (params: { address: USER_BILLING_ITEM }) => Promise; loading: ComputedProperty; + error: ComputedProperty; } export interface UserBillingGetters { @@ -143,12 +172,25 @@ export interface UserBillingGetters { isDefault: (address: USER_BILLING_ITEM) => boolean; } +export interface UseCategoryErrors { + search?: Error; +} export interface UseCategory { categories: ComputedProperty; search(params: ComposableFunctionArgs): Promise; loading: ComputedProperty; + error: ComputedProperty; } +export interface UseCartErrors { + addItem?: Error; + removeItem?: Error; + updateItemQty?: Error; + load?: Error; + clear?: Error; + applyCoupon: Error; + removeCoupon?: Error; +} export interface UseCart < CART, @@ -167,9 +209,15 @@ export interface UseCart removeCoupon(params: { coupon: COUPON; customQuery?: CustomQuery }): Promise; load(): Promise; load(params: { customQuery?: CustomQuery }): Promise; + error: ComputedProperty; loading: ComputedProperty; } - +export interface UseWishlistErrors { + addItem?: Error; + removeItem?: Error; + load?: Error; + clear?: Error; +} export interface UseWishlist < WISHLIST, @@ -185,6 +233,7 @@ export interface UseWishlist clear(): Promise; setWishlist: (wishlist: WISHLIST) => void; isOnWishlist({ product: PRODUCT }): boolean; + error: ComputedProperty; } export interface UseCompare { @@ -216,26 +265,35 @@ export interface UseCheckout placeOrder: PLACE_ORDER; loading: ComputedProperty; } - +export interface UseReviewErrors { + search?: Error; + addReview?: Error; +} export interface UseReview { search(params: ComposableFunctionArgs): Promise; addReview(params: ComposableFunctionArgs): Promise; + error: ComputedProperty; reviews: ComputedProperty; loading: ComputedProperty; [x: string]: any; } - +export interface UseFacetErrors { + search?: Error; +} export interface UseFacet { result: ComputedProperty>; loading: ComputedProperty; search: (params?: AgnosticFacetSearchParams) => Promise; + error: ComputedProperty; +} +export interface UseContentErrors { + search?: Error; } - export interface UseContent { search: (params: CONTENT_SEARCH_PARAMS) => Promise; content: ComputedProperty; loading: ComputedProperty; - error: ComputedProperty; + error: ComputedProperty; } export interface RenderComponent { diff --git a/packages/core/docs/.vuepress/config.js b/packages/core/docs/.vuepress/config.js index 9c9af35faa..d3ae23bf02 100644 --- a/packages/core/docs/.vuepress/config.js +++ b/packages/core/docs/.vuepress/config.js @@ -138,6 +138,8 @@ module.exports = { collapsable: false, children: [ ['/general/architecture', 'Architecture'], + ['/general/i18n', 'i18n'], + ['/general/error-handling', 'Error Handling'], ['/general/logging', 'Logging'], ['/general/performance', 'Performance'], ['/general/context', 'Application Context'] diff --git a/packages/core/docs/contributing/changelog.md b/packages/core/docs/contributing/changelog.md index c01efed563..67943f10ad 100644 --- a/packages/core/docs/contributing/changelog.md +++ b/packages/core/docs/contributing/changelog.md @@ -11,6 +11,7 @@ - optimize loading of fonts and their stylesheets from Google Fonts and introduce lazy hydration to improve performance ([#5326](https://github.com/DivanteLtd/vue-storefront/pull/5326)) - added missing `i18n` tags ([#5337](https://github.com/vuestorefront/vue-storefront/issues/5337)) - fix adding to cart button on product page ([#5375](https://github.com/vuestorefront/vue-storefront/pull/5375)) +- typed error ref for each core's factory ([#4956](https://github.com/vuestorefront/vue-storefront/issues/4956)) ## 2.1.1-rc.1 diff --git a/packages/core/docs/general/error-handling.md b/packages/core/docs/general/error-handling.md new file mode 100644 index 0000000000..fdc8720ed4 --- /dev/null +++ b/packages/core/docs/general/error-handling.md @@ -0,0 +1,87 @@ +# Error handling + +A flexible way of error handling is essential for a framework like Vue Storefront. As composables are hearth of our app - we decided to put there whole error handling mechanism. + +Each composable returns `error` - computed property. It is an object which has names of async functions from composable as keys and Error instance or null as a value. + +Example usage: +```vue + + + +``` + +There is a dedicated interface for each composable. Take a look at this one from `useCart`: +```ts +export interface UseCartErrors { + addItem?: Error; + removeItem?: Error; + updateItemQty?: Error; + load?: Error; + clear?: Error; + applyCoupon: Error; + removeCoupon?: Error; +} +``` + +## How to listen for errors? +Let's imagine you have some global components for error notifications. You want to send information about each new error to this component. But how to know when new error appears? You can observe error object with a simple watcher! + +```ts +const { cart, error } = useCart() + +watch(error, (error, prevError) => { + if (error.value.addItem && error.value.addItem !== prevError.value.addItem) sendInAppNotification('error', error.value.addItem.message) + if (error.value.removeItem && error.value.removeItem !== prevError.value.removeItem) sendInAppNotification('error', error.value.removeItem.message) +}) +``` + +## Where can I find interface of the error property from a certain composable? +When you are writing a code inside a script part of the Vue's component, your IDE should give you hints dedicated for each type of composable. That's why you probably do not need to check these interfaces in the core's code. + +However, if somewhy you still want to do that, you could find them inside [`packages/core/core/src/types.ts`](https://github.com/vuestorefront/vue-storefront/blob/next/packages/core/core/src/types.ts). Just search for `UseCartErrors` with your IDE inside. + +Feel free to replace `UseCartErrors` with other composable name - `UseFacetErrors`, `UseWishlistErrors`, `UseProductErrors` etc. + +## Where does error come from? +To better understand this part you should know what are factories of composables in our core. + +Inside each factory's async method we are clearing the current error before integration's method call and setting it in catch block. +```ts +const addItem = async ({ product, quantity, customQuery }) => { + Logger.debug('useCart.addItem', { product, quantity }); + + try { + loading.value = true; + error.value.addItem = null; // Clearing the current error + const updatedCart = await factoryParams.addItem( + context, + { + currentCart: cart.value, + product, + quantity, + customQuery + } + ); + cart.value = updatedCart; + } catch (err) { + error.value.addItem = err; // Setting a new error + Logger.error('useCart/addItem', err); + } finally { + loading.value = false; + } +}; +```