I have a canDeactivateGuard which returns from component's MatDialog for unsaved action. My problem is I am unable to test the functional guard and getting error - TypeError: Cannot read properties of undefined (reading 'canUserExitAlertDialog')
Here is the Guard-
import { CanDeactivateFn } from '@angular/router';
import { Observable, map } from 'rxjs';
export const canDeactivateGuard: CanDeactivateFn<any> = (
component,
route,
state
): Observable<boolean> | boolean => {
return component.canUserExitAlertDialog('back').pipe(
map((result) => {
console.log(result);
return result === 'discard';
})
);
};
Here is my component's method which the guard is calling- Note: I have a mat-dialog which have two buttons - 'discard' and 'cancel'. On 'discard' click user is redirected to home page.
canUserExitAlertDialog(key: string): Observable<any> {
//I have a condition in the alert component based on this key
if (this.hasFormSaved) { //if any changes are saved then not considered
return of('discard');
}
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data = { action: key, redirect: 'home' };
if (this.wasFormChanged || this.form.dirty) {
const dialogRef = this.dialog.open(AlertComponent, dialogConfig);
return dialogRef.afterClosed();
} else {
this.dialog.closeAll();
return of('discard');
}
}
Code in Dialog Alert component:
export class AlertComponent {
userAction!: any;
alertMsg = 'Are you sure you want to discard the changes?';
unsavedChanges = 'This will reload the page';
constructor(
@Inject(MAT_DIALOG_DATA) data: any,
@Inject(Window) private window: Window,
private dialogRef: MatDialogRef<AlertComponent>
) {
this.userAction = data;
}
public onCancel(): void {
this.dialogRef.close('cancel');
}
public onDiscard(): void {
this.dialogRef.close('discard');
if (this.userAction.action === 'something') { //key I passed from the main component
console.log('do something');
}
}
}
Finally here is my code in CanDeactivate spec file-
describe('canDeactivateGuard functional guard', () => {
let nextState: RouterStateSnapshot;
let component: MyComponent;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: ActivatedRoute,
useValue: {
snapshot: {},
},
},
],
});
});
it('should be created', fakeAsync(() => {
const activatedRoute = TestBed.inject(ActivatedRoute);
const nextState = {} as RouterStateSnapshot;
const currState = {} as RouterStateSnapshot;
const guardResponse = TestBed.runInInjectionContext(() => {
canDeactivateGuard(
component,
activatedRoute.snapshot,
currState,
nextState
) as Observable<any>;
});
expect(guardResponse).toBeTruthy();
}));
I have tried to create a stub component and define the canUserExitAlertDialog method but didn't help. Is there another way to do this test successfully? AS per angular, class level deactivate guard is deprecated.
Error here- error message
Test Coverage- enter image description here