Skip to content

Commit

Permalink
feat(cart): add DaffResolveCartGuard (#2506)
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 committed Jul 13, 2023
1 parent a519413 commit 5da2cfc
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 0 deletions.
2 changes: 2 additions & 0 deletions libs/cart/routing/src/cart-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
DaffCartPaymentMethodGuardRedirectUrl,
DaffCartOrderResultGuardRedirectUrl,
DaffCartItemsGuardRedirectUrl,
DaffResolveCartGuardRedirectUrl,
} from './guards/public_api';
import {
DaffEmptyCartResolverRedirectUrl,
Expand All @@ -31,6 +32,7 @@ import {
{ provide: DaffCartOrderResultGuardRedirectUrl, useValue: '/' },
{ provide: DaffEmptyCartResolverRedirectUrl, useValue: '/' },
{ provide: DaffCartResolverRedirectUrl, useValue: '/' },
{ provide: DaffResolveCartGuardRedirectUrl, useValue: '/' },
],
})
export class DaffCartRoutingModule {
Expand Down
2 changes: 2 additions & 0 deletions libs/cart/routing/src/guards/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { DaffBillingAddressGuard } from './billing-address/billing-address.guard';
export { DaffResolvedCartGuard } from './resolved-cart/resolved-cart.guard';
export { DaffResolveCartGuard } from './resolve-cart/resolve-cart.guard';
export { DaffCartItemsGuard } from './cart-items/cart-items.guard';
export { DaffCartInStockItemsGuard } from './in-stock-items/in-stock-items.guard';
export { DaffPaymentMethodGuard } from './payment-method/payment-method.guard';
Expand All @@ -13,3 +14,4 @@ export { DaffCartPaymentMethodGuardRedirectUrl } from './payment-method/payment-
export { DaffCartShippingAddressGuardRedirectUrl } from './shipping-address/shipping-address-guard-redirect.token';
export { DaffCartShippingMethodGuardRedirectUrl } from './shipping-method/shipping-method-guard-redirect.token';
export { DaffCartOrderResultGuardRedirectUrl } from './order-result/order-result-guard-redirect.token';
export { DaffResolveCartGuardRedirectUrl } from './resolve-cart/redirect.token';
3 changes: 3 additions & 0 deletions libs/cart/routing/src/guards/resolve-cart/redirect.token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { InjectionToken } from '@angular/core';

export const DaffResolveCartGuardRedirectUrl = new InjectionToken<string>('DaffResolveCartGuardRedirectUrl');
152 changes: 152 additions & 0 deletions libs/cart/routing/src/guards/resolve-cart/resolve-cart.guard.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import {
TestBed,
waitForAsync,
} from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import {
StoreModule,
combineReducers,
Store,
} from '@ngrx/store';
import { Observable } from 'rxjs';

import {
DaffCart,
DaffCartStorageService,
} from '@daffodil/cart';
import { DaffResolveCartGuardRedirectUrl } from '@daffodil/cart/routing';
import {
daffCartReducers,
DaffCartStateRootSlice,
DaffResolveCartSuccess,
DaffResolveCartFailure,
DaffResolveCartServerSide,
DaffResolveCart,
DAFF_CART_STORE_FEATURE_KEY,
DaffResolveCartPartialSuccess,
} from '@daffodil/cart/state';
import { DaffCartFactory } from '@daffodil/cart/testing';

import { DaffResolveCartGuard } from './resolve-cart.guard';

describe('@daffodil/cart/routing | DaffResolveCartGuard', () => {
const actions$: Observable<any> = null;
let cartResolver: DaffResolveCartGuard;
let store: Store<DaffCartStateRootSlice>;
let cartFactory: DaffCartFactory;

let cartStorageSpy: jasmine.SpyObj<DaffCartStorageService>;

let stubCart: DaffCart;
let router: Router;
const stubUrl = '/cart';

beforeEach(waitForAsync(() => {
cartStorageSpy = jasmine.createSpyObj('DaffCartStorageService', ['getCartId']);

TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({
[DAFF_CART_STORE_FEATURE_KEY]: combineReducers(daffCartReducers),
}),
RouterTestingModule,
],
providers: [
provideMockActions(() => actions$),
{ provide: DaffResolveCartGuardRedirectUrl, useValue: stubUrl },
{
provide: DaffCartStorageService,
useValue: cartStorageSpy,
},
],
});

cartResolver = TestBed.inject(DaffResolveCartGuard);
cartFactory = TestBed.inject(DaffCartFactory);
store = TestBed.inject(Store);
router = TestBed.inject(Router);

stubCart = cartFactory.create();


spyOn(router, 'navigateByUrl');
}));

it('should be created', () => {
expect(cartResolver).toBeTruthy();
});

describe('canActivate', () => {
describe('when there is a cart ID in storage', () => {
beforeEach(() => {
cartStorageSpy.getCartId.and.returnValue(stubCart.id);
});

it('should dispatch a DaffResolveCart action', () => {
spyOn(store, 'dispatch');
cartResolver.canActivate().subscribe();

expect(store.dispatch).toHaveBeenCalledWith(new DaffResolveCart());
});

describe('when DaffResolveCartSuccess is dispatched', () => {
it('should return true', done => {
cartResolver.canActivate().subscribe((returnedValue) => {
expect(returnedValue).toBeTrue();
done();
});

store.dispatch(new DaffResolveCartPartialSuccess(stubCart, []));
});
});

describe('when DaffResolveCartPartialSuccess is dispatched', () => {
it('should return true', done => {
cartResolver.canActivate().subscribe((returnedValue) => {
expect(returnedValue).toBeTrue();
done();
});

store.dispatch(new DaffResolveCartPartialSuccess(stubCart, []));
});
});

describe('when DaffResolveCartFailure is dispatched', () => {
it('should redirect to the provided DaffResolveCartGuardRedirectUrl', done => {
cartResolver.canActivate().subscribe((resp) => {
expect(resp).toEqual(router.parseUrl(stubUrl));
done();
});

store.dispatch(new DaffResolveCartFailure([]));
});
});

describe('when DaffResolveCartServerSide is dispatched', () => {
it('should redirect to the provided DaffResolveCartGuardRedirectUrl', done => {
cartResolver.canActivate().subscribe((resp) => {
expect(resp).toEqual(router.parseUrl(stubUrl));
done();
});

store.dispatch(new DaffResolveCartServerSide(null));
});
});
});

describe('when there is not a cart ID in storage', () => {
beforeEach(() => {
cartStorageSpy.getCartId.and.returnValue(null);
});

it('should redirect', done => {
cartResolver.canActivate().subscribe((resp) => {
expect(resp).toEqual(router.parseUrl(stubUrl));
done();
});
});
});
});
});
87 changes: 87 additions & 0 deletions libs/cart/routing/src/guards/resolve-cart/resolve-cart.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
Injectable,
Inject,
} from '@angular/core';
import {
CanActivate,
Router,
UrlTree,
} from '@angular/router';
import {
ActionsSubject,
Store,
} from '@ngrx/store';
import {
Observable,
of,
} from 'rxjs';
import {
tap,
filter,
take,
map,
} from 'rxjs/operators';

import { DaffCartStorageService } from '@daffodil/cart';
import {
DaffCartActionTypes,
DaffCartActions,
DaffCartFacade,
DaffCartResolveState,
DaffCartStateRootSlice,
DaffResolveCart,
} from '@daffodil/cart/state';

import {
DaffCartRoutingConfiguration,
DAFF_CART_ROUTING_CONFIG,
} from '../../config/config';
import { DaffResolveCartGuardRedirectUrl } from './redirect.token';

/**
* A routing guard that will optionally redirect to a given url if the cart is not properly resolved.
* It will initiate cart resolution.
* The url has no default and the guard will not redirect if no value is specified.
* Specify a redirect path with the {@link DaffResolvedCartGuardRedirectUrl} injection token.
* The guard will wait until the cart has been resolved before performing the check and emitting.
*/
@Injectable({
providedIn: 'root',
})
export class DaffResolveCartGuard implements CanActivate {
constructor(
private store: Store<DaffCartStateRootSlice>,
private dispatcher: ActionsSubject,
private router: Router,
@Inject(DaffResolveCartGuardRedirectUrl) private redirectUrl: string,
private storage: DaffCartStorageService,
) {}

canActivate(): Observable<boolean | UrlTree> {
if (this.storage.getCartId()) {
this.store.dispatch(new DaffResolveCart());

return this.dispatcher.pipe(
filter<DaffCartActions>(action =>
action.type === DaffCartActionTypes.ResolveCartServerSideAction
|| action.type === DaffCartActionTypes.ResolveCartFailureAction
|| action.type === DaffCartActionTypes.ResolveCartSuccessAction
|| action.type === DaffCartActionTypes.ResolveCartPartialSuccessAction,
),
map((action) => {
if (
action.type === DaffCartActionTypes.ResolveCartSuccessAction
|| action.type === DaffCartActionTypes.ResolveCartPartialSuccessAction
) {
return true;
}

return this.router.parseUrl(this.redirectUrl);
}),
take(1),
);
} else {
return of(this.router.parseUrl(this.redirectUrl));
}
}
}

0 comments on commit 5da2cfc

Please sign in to comment.