Skip to content

Instantly share code, notes, and snippets.

@LayZeeDK
Created July 16, 2020 11:32
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save LayZeeDK/6d34f5843f123a27950f89c86ab84bbb to your computer and use it in GitHub Desktop.
Save LayZeeDK/6d34f5843f123a27950f89c86ab84bbb to your computer and use it in GitHub Desktop.
AuthGuard: Isolated route guard test suite.
import {
ActivatedRouteSnapshot,
Params,
Route,
Router,
RouterStateSnapshot,
UrlSegment,
} from '@angular/router';
import { AuthGuard } from './auth.guard';
import { AuthService } from './auth.service';
function fakeRouterState(url: string): RouterStateSnapshot {
return {
url,
} as RouterStateSnapshot;
}
describe('AuthGuard (isolated)', () => {
beforeEach(() => {
routerSpy = jasmine.createSpyObj<Router>('Router', ['navigate']);
serviceStub = {};
guard = new AuthGuard(serviceStub as AuthService, routerSpy);
});
const dummyRoute = {} as ActivatedRouteSnapshot;
const fakeUrls = [
'/',
'/admin',
'/crisis-center',
'/a/deep/route',
];
let guard: AuthGuard;
let routerSpy: jasmine.SpyObj<Router>;
let serviceStub: Partial<AuthService>;
describe('when the user is logged in', () => {
beforeEach(() => {
serviceStub.isLoggedIn = true;
});
fakeUrls.forEach(fakeUrl => {
it('grants access', () => {
const isAccessGranted = guard.checkLogin(fakeUrl);
expect(isAccessGranted).toBeTrue();
});
describe('and navigates to a guarded route configuration', () => {
it('grants route access', () => {
const canActivate =
guard.canActivate(dummyRoute, fakeRouterState(fakeUrl));
expect(canActivate).toBeTrue();
});
it('grants child route access', () => {
const canActivateChild =
guard.canActivateChild(dummyRoute, fakeRouterState(fakeUrl));
expect(canActivateChild).toBeTrue();
});
const paths = fakeUrl.split('/').filter(path => path !== '');
paths.forEach(path => {
it('grants feature access', () => {
const fakeRoute: Route = { path };
const fakeUrlSegment = { path } as UrlSegment;
const canLoad = guard.canLoad(fakeRoute, [fakeUrlSegment]);
expect(canLoad).toBeTrue();
});
});
});
});
});
describe('when the user is logged out', () => {
beforeEach(() => {
serviceStub.isLoggedIn = false;
});
fakeUrls.forEach(fakeUrl => {
it('rejects access', () => {
const isAccessGranted = guard.checkLogin(fakeUrl);
expect(isAccessGranted).toBeFalse();
});
it('stores the redirect URL', () => {
guard.checkLogin(fakeUrl);
expect(serviceStub.redirectUrl).toBe(fakeUrl);
});
it('navigates to the login page', () => {
guard.checkLogin(fakeUrl);
expect(routerSpy.navigate)
.toHaveBeenCalledWith(['/login'], jasmine.any(Object));
});
it('adds a token to the login URL', () => {
const expectedToken = 'anchor';
guard.checkLogin(fakeUrl);
expect(routerSpy.navigate).toHaveBeenCalledWith(
jasmine.any(Array),
jasmine.objectContaining({
fragment: expectedToken,
}));
});
it('adds a session ID to the login URL', () => {
const expectedQueryParams: Params = {
session_id: jasmine.any(Number),
};
guard.checkLogin(fakeUrl);
expect(routerSpy.navigate).toHaveBeenCalledWith(
jasmine.any(Array),
jasmine.objectContaining({
queryParams: expectedQueryParams,
}));
});
describe('and navigates to a guarded route configuration', () => {
it('rejects route access', () => {
const canActivate =
guard.canActivate(dummyRoute, fakeRouterState(fakeUrl));
expect(canActivate).toBeFalse();
});
it('rejects child route access', () => {
const canActivateChild =
guard.canActivateChild(dummyRoute, fakeRouterState(fakeUrl));
expect(canActivateChild).toBeFalse();
});
const paths = fakeUrl.split('/').filter(path => path !== '');
paths.forEach(path => {
it('rejects feature access', () => {
const fakeRoute: Route = { path };
const fakeUrlSegment = { path } as UrlSegment;
const canLoad = guard.canLoad(fakeRoute, [fakeUrlSegment]);
expect(canLoad).toBeFalse();
});
});
});
});
});
});
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
CanActivateChild,
CanLoad,
NavigationExtras,
Route,
Router,
RouterStateSnapshot,
UrlSegment,
} from '@angular/router';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root',
})
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
constructor(
private authService: AuthService,
private router: Router,
) { }
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): boolean {
const url = state.url;
return this.checkLogin(url);
}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot,
): boolean {
return this.canActivate(route, state);
}
canLoad(route: Route, segments: UrlSegment[]): boolean {
const url = `/${route.path}`;
return this.checkLogin(url);
}
checkLogin(url: string): boolean {
if (this.authService.isLoggedIn) {
return true;
}
// Store the attempted URL for redirecting
this.authService.redirectUrl = url;
// Create a dummy session id
const sessionId = 123456789;
// Set our navigation extras object
// that contains our global query params and fragment
const navigationExtras: NavigationExtras = {
queryParams: { session_id: sessionId },
fragment: 'anchor',
};
// Navigate to the login page with extras
this.router.navigate(['/login'], navigationExtras);
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment