I just started to learn Jasmine, so I faced with task when is needed to check if the redirecting passing to right page. I am very appreciative for any help. I wanted to check the redirect after click via link and second case - after successful authorization.
In the example below, I tried to implement the first case. For instance, I have a login component:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../auth.service';
import {
AbstractControl,
FormBuilder,
FormGroup,
Validators,
} from '@angular/forms';
import { PushNotificationService } from 'src/app/push-notification.service';
@Component({
selector: 'app-autorization',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent implements OnInit {
isPasswordHidden = true;
form!: FormGroup;
public passwordInput!: AbstractControl;
constructor(
private authService: AuthService,
private fb: FormBuilder,
private pushNotificationService: PushNotificationService
) {}
ngOnInit(): void {
this.form = this.fb.group({
username: ['', [Validators.required, Validators.minLength(3)]],
password: ['', [Validators.required, Validators.minLength(6)]],
});
this.passwordInput = this.form.controls['password'];
}
userLoginClick() {
if (!this.form.valid) {
this.pushNotificationService.createNotification(
'Data format is incorrect'
);
return false;
}
const user = {
username: this.form.controls['username'].value,
password: this.form.controls['password'].value,
};
this.authService.authUser(user);
}
togglePassVisibility(e: Event) {
e.preventDefault();
this.isPasswordHidden = !this.isPasswordHidden;
}
}
function from authService:
authUser(user: User) {
return this.http
.post(
`${environment.domain}${BackendRoutes.Login}`,
user,
this.httpOptions
)
.subscribe((data: any) => {
if (!data.success) {
this.pushNotificationService.createNotification(data.message);
} else {
this.router.navigate([`/${Paths.Lobby}`]);
this.storeUser(data.token, data.user);
}
});
}
I tried to do something like this:
describe('Router redirects to proper pages', () => {
let location: Location;
let router: Router;
let fixture: any;
let redirectLink!: any;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(routes),
HttpClientTestingModule,
],
declarations: [LobbyComponent, SignupComponent],
providers: [FormBuilder, MatSnackBar, Overlay],
});
router = TestBed.inject(Router);
location = TestBed.inject(Location);
fixture = TestBed.createComponent(LoginComponent);
router.initialNavigation();
const compiled = fixture.debugElement;
redirectLink = compiled.query(
By.css('[routerLink="/signup"]')
).nativeElement;
});
it('text', fakeAsync(() => {
redirectLink.dispatchEvent(new Event('click'));
fixture.detectChanges();
expect(router.url).toEqual('/signup');
}));
});
Actually, I didn't get the expected result =) Any help is valuable!
CodePudding user response:
It is suggested that you test only one class at a time, and any dependency you fake out the value and then verify the interaction between your class and that dependency.
In this case you have written a LoginComponent that you want to verify works correctly.
First, let's discuss the TestBed. We need to import any providers and modules that our component needs, but we will fake any that we wrote.
Since your code logs in I will write a basic test over that.
let authService: jasmine.spyObj<AuthService>;
beforeEach(() => {
// create a fake that has the same methods we will want to verify
const fakeAuthService = jasmine.createSpyObj<AuthService>('auth', ['authUser']);
TestBed.configureTestingModule({
imports: [
// the auth service needs these, but we don't here
// RouterTestingModule.withRoutes(routes),
// HttpClientTestingModule,
],
// not necessary unless the html contains them
// declarations: [LobbyComponent, SignupComponent],
declarations: [LoginComponent],
providers: [FormBuilder, MatSnackBar, Overlay,
{ provide: AuthService, useValue: fakeAuthService}
],
});
authService = TestBed.inject<AuthService> as jasmine.spyObj<AuthService>;
fixture = TestBed.createComponent(LoginComponent);
});
it('should log in', () => {
// interact with the html, as that is the public interface of your component
setTextInput('#userName', 'someUser');
setTextInput('#password', 'somePassword');
click('#loginButton');
// verify the interaction with the dependency
// we don't care (from this spec) what the auth service did
// if this function returned a result we would want to verify that we did the correct thing with it
expect(authService.authUser).toHaveBeenCalledWith({ username: 'someUser', password: 'somePassword' })
});
function setTextInput(selector: string, value: string): void {
const input = fixture.debugElement.query(By.css(selector));
input.nativeElement.value = value;
input.nativeElement.dispatchEvent(new Event('input'));
}
click(selector: string): void {
const clicker = fixture.debugElement.query(By.css(selector));
clicker.triggerEventHandler('click', null);
fixture.detectChanges();
}
The closest I have found to the style I use is testing-angular free e-book
You would test that the routing works when you test your auth service. In that case I usually provide a fake object for the Router as I only want to check if the router was called with what I expect.
mockRouter = jasmine.createSpyObj('router', ['navigate']);
...
{ provide: Router, useValue: mockRouter },
To test that your component changes routes, you can do something like. What you had tests that the router does what it is supposed to. What you want is to make sure you interact with the router how you expect.
it('text', () => {
redirectLink.dispatchEvent(new Event('click'));
fixture.detectChanges();
expect(router.navigate).toHaveBeenCalledWith(['/signup']);
});
Hopefully that gets you started. Feel free to post other questions and tag me.