5

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.

J-man
  • 1,743
  • 3
  • 26
  • 50

0 Answers0