I have an Angular 6 component that has input fields. If any of the input fields fail validation if the user tries to navigate away, a guard will trigger on the canDeactivate
function. The guard is generic because this logic needs to happen on more than one component in the app. It works beautifully running it, but when I attempt to unit test this behavior, the canDeactivate
function in the guard is never reached. The guard itself is reached, but never the function. Am I providing the guard in an incorrect way?
Guard Component Interface
export interface GuardComponent {
canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}
CanDeactivateGuard
export class CanDeactivateGuard implements CanDeactivate<GuardComponent> {
constructor() { }
canDeactivate(component: GuardComponent): Observable<boolean> | Promise<boolean> | boolean {
return component.canDeactivate();
}
}
Component
export class MyComponent implements OnInit, GuardComponent {
...
canDeactivate() {
if (!this.form.invalid) {
return true;
}
this.isError = true;
return false;
}
}
spec
const routes = [
{
path: 'my-component',
component: MyComponent,
canDeactivate: [CanDeactivateGuard],
},
{
path: 'my-component-2',
component: MyComponent2
}
];
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ RouterTestingModule.withRoutes(routes) ],
declarations: [ MyComponent, MyComponent2 ],
providers: [ CanDeactivateGuard ],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixutre.componentInstance;
fixture.detectChanges();
});
it('should restrict route change if validation fails', fakeAsync(inject([Router, Location, CanDeactivateGuard], (router: Router, location: Location, guard: CanDeactivateGuard) => {
const textbox = fixture.debugElement.query(By.css('#inputField')).nativeElement;
const canDeactivateSpy = spyOn(guard, 'canDeactivate');
const componentDeactivateSpy = spyOn(component, 'canDeactivate');
// anything lower than 0 will fail the form
textbox.value = '-10';
fixture.detectChanges();
router.navigate(['/my-page-2']).then(() => {
expect(location.path()).toBe('/my-component'); // path doesn't change because guard blocks it due to form being invalid
expect(canDeactivateSpy).toHaveBeenCalled();
expect(componentDeactivateSpy).toHaveBeenCalled();
});
})));
The test fails because the canDeactivate
functions are never reached.