diff --git a/libs/cart/state/src/effects/cart-item.effects.spec.ts b/libs/cart/state/src/effects/cart-item.effects.spec.ts index bc5c224392..b14558c44a 100644 --- a/libs/cart/state/src/effects/cart-item.effects.spec.ts +++ b/libs/cart/state/src/effects/cart-item.effects.spec.ts @@ -12,6 +12,7 @@ import { import { Observable, of, + throwError, } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; @@ -24,6 +25,7 @@ import { import { DaffCartItemServiceInterface, DaffCartItemDriver, + DaffCartDriverResolveService, } from '@daffodil/cart/driver'; import { DaffTestingCartDriverModule } from '@daffodil/cart/driver/testing'; import { @@ -82,8 +84,11 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { let driverUpdateSpy: jasmine.Spy; let driverDeleteSpy: jasmine.Spy; let getCartIdSpy: jasmine.Spy; + let cartResolverSpy: jasmine.SpyObj; beforeEach(() => { + cartResolverSpy = jasmine.createSpyObj('DaffCartDriverResolveService', ['getCartIdOrFail']); + TestBed.configureTestingModule({ imports: [ DaffTestingCartDriverModule.forRoot(), @@ -98,6 +103,10 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { provide: DaffCartItemStateDebounceTime, useValue: 4000, }, + { + provide: DaffCartDriverResolveService, + useValue: cartResolverSpy, + }, ], }); @@ -216,28 +225,52 @@ describe('@daffodil/cart/state | DaffCartItemEffects', () => { mockCart.items = []; }); - describe('and the call to CartItemService is successful', () => { + describe('and the cart ID retrieval succeeds', () => { beforeEach(() => { - mockCart.items.push(mockCartItem); - driverAddSpy.and.returnValue(of(mockCart)); - const cartItemAddSuccessAction = new DaffCartItemAddSuccess(mockCart); - actions$ = hot('--a', { a: cartItemAddAction }); - expected = cold('--b', { b: cartItemAddSuccessAction }); + cartResolverSpy.getCartIdOrFail.and.returnValue(of(mockCart.id)); }); - it('should dispatch a CartItemAddSuccess action', () => { - expect(effects.add$).toBeObservable(expected); + describe('and the call to CartItemService is successful', () => { + beforeEach(() => { + mockCart.items.push(mockCartItem); + driverAddSpy.and.returnValue(of(mockCart)); + const cartItemAddSuccessAction = new DaffCartItemAddSuccess(mockCart); + actions$ = hot('--a', { a: cartItemAddAction }); + expected = cold('--b', { b: cartItemAddSuccessAction }); + }); + + it('should dispatch a CartItemAddSuccess action', () => { + expect(effects.add$).toBeObservable(expected); + }); + }); + + describe('and the call to CartItemService fails', () => { + beforeEach(() => { + const error: DaffStateError = { code: 'code', recoverable: false, message: 'Failed to add cart item' }; + const response = cold('#', {}, error); + driverAddSpy.and.returnValue(response); + const cartItemAddFailureAction = new DaffCartItemAddFailure(error); + actions$ = hot('--a', { a: cartItemAddAction }); + expected = cold('--b', { b: cartItemAddFailureAction }); + }); + + it('should dispatch a CartItemAddFailure action', () => { + expect(effects.add$).toBeObservable(expected); + }); }); }); - describe('and the call to CartItemService fails', () => { + describe('and the cart ID retrieval fails', () => { beforeEach(() => { const error: DaffStateError = { code: 'code', recoverable: false, message: 'Failed to add cart item' }; - const response = cold('#', {}, error); - driverAddSpy.and.returnValue(response); const cartItemAddFailureAction = new DaffCartItemAddFailure(error); actions$ = hot('--a', { a: cartItemAddAction }); - expected = cold('--b', { b: cartItemAddFailureAction }); + expected = cold('--(b|)', { b: cartItemAddFailureAction }); + cartResolverSpy.getCartIdOrFail.and.returnValue(throwError(() => error)); + }); + + it('should not try to add the item', () => { + expect(driverAddSpy).not.toHaveBeenCalled(); }); it('should dispatch a CartItemAddFailure action', () => { diff --git a/libs/cart/state/src/effects/cart-item.effects.ts b/libs/cart/state/src/effects/cart-item.effects.ts index b699cd587a..12a1055be0 100644 --- a/libs/cart/state/src/effects/cart-item.effects.ts +++ b/libs/cart/state/src/effects/cart-item.effects.ts @@ -22,6 +22,7 @@ import { debounceTime, mergeMap, take, + concatMap, } from 'rxjs/operators'; import { @@ -31,8 +32,10 @@ import { DAFF_CART_ERROR_MATCHER, } from '@daffodil/cart'; import { + DaffCartDriverResolveService, DaffCartItemDriver, DaffCartItemServiceInterface, + daffCartDriverHandleCartNotFound, } from '@daffodil/cart/driver'; import { ErrorTransformer } from '@daffodil/core/state'; @@ -50,13 +53,13 @@ import { DaffCartItemList, DaffCartItemListSuccess, DaffCartItemListFailure, - DaffCartItemAdd, DaffCartItemAddSuccess, DaffCartItemAddFailure, DaffCartItemStateReset, DaffCartItemDeleteOutOfStock, DaffCartItemDeleteOutOfStockSuccess, DaffCartItemDeleteOutOfStockFailure, + DaffCartItemActions, } from '../actions/public_api'; import { DaffCartItemStateDebounceTime } from '../injection-tokens/cart-item-state-debounce-time'; import { DaffStatefulCartItem } from '../models/public_api'; @@ -69,12 +72,13 @@ export class DaffCartItemEffects< V extends DaffCart, > { constructor( - private actions$: Actions, + private actions$: Actions>, @Inject(DAFF_CART_ERROR_MATCHER) private errorMatcher: ErrorTransformer, @Inject(DaffCartItemDriver) private driver: DaffCartItemServiceInterface, private storage: DaffCartStorageService, @Inject(DaffCartItemStateDebounceTime) private cartItemStateDebounceTime: number, private store: Store, + private cartResolver: DaffCartDriverResolveService, ) {} @@ -99,18 +103,33 @@ export class DaffCartItemEffects< ), )); + private addCartItem$( + cartId: DaffCart['id'], + input: U, + ) { + return this.driver.add( + cartId, + input, + ).pipe( + map((resp: V) => new DaffCartItemAddSuccess(resp)), + catchError(error => of(new DaffCartItemAddFailure(this.errorMatcher(error)))), + ); + } add$ = createEffect(() => this.actions$.pipe( ofType(DaffCartItemActionTypes.CartItemAddAction), - switchMap((action: DaffCartItemAdd) => - this.driver.add( - this.storage.getCartId(), + concatMap((action) => combineLatest([ + of(action), + this.cartResolver.getCartIdOrFail(), + ])), + mergeMap(([action, id]) => + this.addCartItem$( + id, action.input, - ).pipe( - map((resp: V) => new DaffCartItemAddSuccess(resp)), - catchError(error => of(new DaffCartItemAddFailure(this.errorMatcher(error)))), ), ), + daffCartDriverHandleCartNotFound(this.storage), + catchError(error => of(new DaffCartItemAddFailure(this.errorMatcher(error)))), ));