I have an Angular authenticated
guard
@Injectable({
providedIn: 'root'
})
export class AuthenticatedGuard implements CanActivate, CanActivateChild {
constructor(@Inject('Window') private window: Window,
private readonly authenticationService: AuthenticationService) {}
canActivate(): boolean {
if (!this.authenticationService.isAuthenticated) {
this.window.location.href = '/login';
}
return allowed;
}
canActivateChild(): boolean {
return this.canActivate();
}
}
And I have these tests with a mock window I am injecting so that my test windoe doesn't redirect and so I can check to see if the href
is set
const MockWindow = {
location: {
_href: '',
set href(url: string) {
//console.log('set!', url)
this._href = url;
},
get href(): string {
//console.log('get!', this._href)
return this._href;
}
}
};
describe('Guard: authenticated', () => {
let redirectSpy: jasmine.Spy;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthenticatedGuard,
AuthenticationService,
{provide: 'Window', useValue: MockWindow}
]
});
redirectSpy = spyOnProperty(MockWindow.location, 'href', 'set');
});
afterEach(() => {
sessionStorage.removeItem('token');
});
it('should allow route activation when both the token is set', inject([AuthenticatedGuard], (guard: AuthenticatedGuard) => {
sessionStorage.setItem('token', 'foo');
expect(guard.canActivate()).toEqual(true);
expect(guard.canActivateChild()).toEqual(true);
expect(redirectSpy).not.toHaveBeenCalled();
}));
it('should disallow route activation when the token is not set', inject([AuthenticatedGuard], (guard: AuthenticatedGuard) => {
expect(guard.canActivate()).toEqual(false);
expect(guard.canActivateChild()).toEqual(false);
expect(redirectSpy).toHaveBeenCalledWith('/login'); //<-- this fails!
}));
...
The expect(redirectSpy).toHaveBeenCalledWith('/login');
always fails saying it was never called at all. Same with just expect(redirectSpy).toHaveBeenCalled();
- What am I doing wrong here? I want to be able to test this redirection, but I don't want my karma test browser to actually redirect.
(FYI: I need to use window.location.href
instead of the angular router so we can direct users to a different non-angular application that handles the login)
I have found something that works which removes the need for a spy. now I just get
the injected window provider from the guard by calling TestBed.get('Window')
which seems to work great!
Please let me know if this could be done better!
const MockWindow = {
location: {
href: '',
}
};
describe('Guard: authenticated', () => {
let guard: AuthenticatedGuard;
let guardWindow: Window;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
AuthenticatedGuard,
AuthenticationService,
{provide: 'Window', useValue: MockWindow}
]
});
guardWindow = TestBed.get('Window');
MockWindow.location.href = '/some/page';
});
afterEach(() => {
sessionStorage.removeItem('token');
});
it('should allow route activation when the token is set', () => {
sessionStorage.setItem('token', 'foo');
expect(guard.canActivate()).toEqual(true);
expect(guard.canActivateChild()).toEqual(true);
expect(guardWindow.location.href).toEqual('/some/page');
});
it('should disallow route activation when the token is not set', () => {
sessionStorage.setItem('token', 'foo');
expect(guard.canActivate()).toEqual(false);
expect(guard.canActivateChild()).toEqual(false);
expect(guardWindow.location.href).toEqual('/login');
});
...