I'm currently struggling on unit testing my canActivate()
method from my Routerguard Service. The Service looks as follows:
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router} from '@angular/router';
import {AuthService} from '../../auth/auth.service';
import {Observable, of} from 'rxjs';
import {NotificationService} from '../../../../shared/services/notification.service';
import {concatMap, map, take, tap} from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class ProfileGuard implements CanActivate {
constructor(private auth: AuthService, private router: Router,
private notification: NotificationService) {
}
canActivate(next: ActivatedRouteSnapshot): Observable<boolean> {
// checks for user if not - page not found
return this.auth.getUserEntity(next.params.uid).pipe(concatMap(user => {
if (user) {
// checks for permission if not - redirect to user overview
return this.auth.currentUser.pipe(
take(1),
map(current => this.auth.canEditProfile(current, next.params)),
tap(canEdit => {
if (!canEdit) {
this.router.navigate([`/profile/${next.params.uid}`]).then(() =>
this.notification.danger('Access denied. Must have permission to edit profile.'));
}
})
);
} else {
this.router.navigate(['/page-not-found']);
return of(false);
}
}));
}
}
It actually looks more complicated than it is: The first observer checks if there is a user in the db with the params value as unique identifier. The second observer checks then for the permission to edit this user. Now on the unit testing part of things:
describe('RouterGuardService', () => {
const routerStub: Router = jasmine.createSpyObj('Router', ['navigate']);
const authStub: AuthService = jasmine.createSpyObj('AuthService', ['getUserEntity', 'currentUser', 'canEditProfile']);
const notificationStub: NotificationService = jasmine.createSpyObj('NotificationService', ['danger']);
function createInputRoute(url: string): ActivatedRouteSnapshot {
const route: ActivatedRouteSnapshot = new ActivatedRouteSnapshot();
const urlSegs: UrlSegment[] = [];
urlSegs.push(new UrlSegment(url, {}));
route.url = urlSegs;
route.params = {
uid: url.replace('/profile/', '')
.replace('/edit', '')
};
return route;
}
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{provide: AuthService, useValue: authStub},
{provide: Router, useValue: routerStub},
{provide: NotificationService, useValue: notificationStub},
ProfileGuard]
});
});
it('should redirect to user overview - if has not permission', inject([ProfileGuard], (service: ProfileGuard) => {
(<jasmine.Spy>authStub.canEditProfile).and.returnValue(false);
authStub.currentUser = of(<any>{uid: 'jdkffdjjfdkls', role: Role.USER});
(<jasmine.Spy>authStub.getUserEntity).and.returnValue(of({uid: 'jdkffdjjfdkls', role: Role.USER}));
const spy = (<jasmine.Spy>routerStub.navigate).and.stub();
const notifySpy = (<jasmine.Spy>notificationStub.danger).and.stub();
const url: ActivatedRouteSnapshot = createInputRoute('/profile/BBB/edit');
service.canActivate(url).subscribe(res => {
console.log(res);
expect(spy).toHaveBeenCalledWith(['/BBB']);
expect(notifySpy).toHaveBeenCalledWith('Access denied. Must have permission to edit profile.');
expect(res).toBe(false);
}, err => console.log(err));
}));
});
But my test does not check my expect methods instead it console logs the error. Can maybe anyone help me on this?