How to test Angular httpClient methods inside a promise in jasmine? - javascript

I know how to test http with a mock backend, as well as promises. Though I am struggling to find a solution to test http methods in a promise. Any advice will be much appreciated. Here's the function which contain the function with the http method inside the promise:
import { Injectable } from '#angular/core';
import { AbstractControl, FormGroup, FormControl, ValidatorFn, AsyncValidatorFn } from '#angular/forms';
import { Headers, RequestOptions } from '#angular/http';
import { Store } from '#ngrx/store';
import { HttpService, IHttpResponse } from '#mystique/mystique-utils/http';
import { IRootState } from '#mystique/mystique-state/root';
#Injectable()
export class ValidatorsService {
regex: { email: string; password: string } = { email: null, password: null };
constructor(private _http: HttpService, private _store: Store<IRootState>) {
this._store.select('config', 'regex').subscribe(regex => (this.regex = regex));
}
recordExistsOnServer(model: string, lookupField: string, savedValue: string, authToken: string): AsyncValidatorFn {
model += 's';
let validationDebounce;
return (control: AbstractControl) => {
const queryParams = [{ key: lookupField, value: control.value }];
clearTimeout(validationDebounce);
return new Promise((resolve, reject) => {
validationDebounce = setTimeout(() => {
if (control.value === '' || control.value === savedValue) {
return resolve(null);
}
this._http.get$(`/${model}`, authToken, queryParams).subscribe((httpResponse: IHttpResponse) => {
if (!httpResponse.data) {
savedValue = control.value;
}
return !httpResponse.data ? resolve(null) : resolve({ recordExistsOnServer: true });
});
}, 400);
});
};
}
Throws this error: Uncaught TypeError:
_this._http.get$ is not a function at localhost:9876/_karma_webpack_/polyfills.bundle.js:2281
Here is my test cases, the last it() fails:
import { TestBed, inject } from '#angular/core/testing';
import { FormGroup, FormControl } from '#angular/forms';
import { StoreModule, Store } from '#ngrx/store';
import { Observable } from 'rxjs/Observable';
import { HttpService } from '#mystique/mystique-utils/http';
import { HttpServiceStub } from '#mystique/mystique-stubs';
import { ValidatorsService } from './validators.service';
import { rootReducer } from '#mystique/mystique-state/root';
describe('ValidatorsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({
config: rootReducer.config
})
],
providers: [{ provide: HttpService, useClass: HttpServiceStub }, ValidatorsService]
});
});
let service, http, store;
beforeEach(() => {
http = TestBed.get(HttpService);
store = TestBed.get(Store);
service = TestBed.get(ValidatorsService);
});
describe('when checking if a record exists on the server', () => {
let control, result, getSpy;
beforeEach(() => {
getSpy = spyOn(http, 'getAll$');
});
it('returns null if the user types the same value', done => {
control = new FormControl('bob');
result = service.recordExistsOnServer('user', 'username', 'bob', 'token');
result(control)['then'](r => {
expect(r).toEqual(null);
done();
});
});
it('returns null if the user types an empty string', done => {
control = new FormControl('');
result = service.recordExistsOnServer('user', 'username', 'bob');
result(control)['then'](r => {
console.log('r: ' + r)
expect(r).toEqual(null);
done();
});
});
it('returns null if the http call cannot find a record', done => {
getSpy.and.returnValue(Observable.of({ data: null }));
control = new FormControl('bobby');
result = service.recordExistsOnServer('user', 'username', 'bob');
result(control)['then'](r => {
expect(r).toEqual(null);
done();
});
});
});
});
Here is my http.service.ts:
import { Injectable } from '#angular/core';
import { Store } from '#ngrx/store';
import { IRootState } from '#mystique/mystique-state/root';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { IBaseModel } from '#spawntech/xmen-core-domain-models';
export interface IHttpQuery {
key: string;
value: string | number;
}
export interface IHttpResponse {
success: boolean;
status: number;
statusText: string;
message: string;
data?: any | any[];
error?: string;
token?: string;
}
#Injectable()
export class HttpService {
apiBaseUrl: string = null;
httpRetries = 3;
constructor(private _http: HttpClient, private _store: Store<IRootState>) {
this._store.select('config', 'apiBaseUrl').subscribe(url => (url ? (this.apiBaseUrl = url) : this.apiBaseUrl));
}
get$(restUrl: string, authToken: string, queryParams?: IHttpQuery[]): Observable<IHttpResponse> {
if (!restUrl) {
throw new Error('A restful url extension must be supplied');
}
const headers = this._prepareAuthHeader(authToken);
const params = this._prepareQueryParams(queryParams);
console.log('in http service---------------')
return this._http
.get<IHttpResponse>(this.apiBaseUrl + restUrl, { headers, params })
.retry(this.httpRetries)
.catch((response: HttpErrorResponse) => this._handleError(response));
}
}

use HttpClientTestingModule for mock http request
TestBed.configureTestingModule({
imports: [..., HttpClientTestingModule],
providers: [...]
})
in the development of applications, I call the service method which returns a query, then I subscribe () and I already consider successful and erroneous queries in the current component, displaying some notifications to the user
then you can take a query from a function to a separate function and do something like this:
spyOn (service, 'confirmEmail').
       .returnValue (Observable.of (new HttpResponse ({body: '', status: 204}))));

Related

TypeError: Cannot read properties of undefined (reading 'subscribe') got error when try to run unit test

I have created a test case for my component with all dependency but when I execute the test case it shows error.
My unit test scenario is when form data is submitted formSubmit() method will be called and it send the data authservice there it makes an HTTP request and return some response. After that it navigate to another page.
Can anyone provide a solution for this test case??
Here are my code
login.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
// Services
import { ActivatedRoute, Router } from '#angular/router';
import { AuthService } from 'src/app/Service/auth.service';
// Material Component
import { MatSnackBar } from '#angular/material/snack-bar';
import { Title } from '#angular/platform-browser';
import { HttpErrorResponse } from '#angular/common/http';
import { LoginResponse } from '../../Model/auth';
import { duration, Notify } from '../../Model/notify';
import { NotificationComponent } from '../../shared/notification/notification.component';
import { NotifyService } from '../../Service/notify.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
})
export class LoginComponent implements OnInit {
constructor(
private documentTitle: Title,
private fb: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private auth: AuthService,
private notify: NotifyService
) {}
passwordHide!: boolean;
returnUrl!: string;
pageTitle!: string;
loginForm!: FormGroup;
res!: LoginResponse;
ngOnInit(): void {
this.passwordHide = true;
this.pageTitle = 'Login | Online Shopping for Men & Women Shoes';
this.documentTitle.setTitle(this.pageTitle);
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '';
this.loginForm = this.fb.group({
username: ['', [Validators.required, Validators.minLength(8)]],
password: ['', [Validators.required, Validators.minLength(6)]],
});
}
/**
* send a request to auth service to make HTTP POST request
* if form is valid
*/
formSubmit() {
if (this.loginForm.valid) {
console.log(this.returnUrl);
this.auth.login(this.loginForm.value).subscribe({
next: (data: LoginResponse) => {
console.log(data);
this.res = data;
localStorage.setItem('access_token', data.accessToken);
this.auth.authStatusToggle(true);
this.router.navigateByUrl(this.returnUrl);
},
error: (error: HttpErrorResponse) => {
console.log(error);
if (error.status == 400) {
this.notify.warning(error.error.message);
} else {
this.notify.warning(error.error.message);
}
},
});
}
}
}
login.component.spec.ts
import { CommonModule } from '#angular/common';
import { ComponentFixture, TestBed, getTestBed } from '#angular/core/testing';
import {
FormBuilder,
FormsModule,
ReactiveFormsModule,
Validators,
} from '#angular/forms';
import { AppModule } from 'src/app/app.module';
import { AuthService } from 'src/app/Service/auth.service';
import { LoginComponent } from './login.component';
import * as Rx from 'rxjs';
import { Title } from '#angular/platform-browser';
import { HttpClientModule } from '#angular/common/http';
import {
HttpClientTestingModule,
HttpTestingController,
} from '#angular/common/http/testing';
import { LoginResponse } from 'src/app/Model/auth';
import { ActivatedRoute, Router } from '#angular/router';
import { RouterTestingModule } from '#angular/router/testing';
import { NotifyService } from 'src/app/Service/notify.service';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let authService = jasmine.createSpyObj('AuthService', ['login']);
let mockRouter = jasmine.createSpyObj('Router', ['navigate']);
let route: ActivatedRoute;
let documentTitle: Title;
let injector: TestBed;
let fb = new FormBuilder();
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
AppModule,
HttpClientModule,
HttpClientTestingModule,
RouterTestingModule,
],
providers: [
{ provide: FormBuilder, useValue: fb },
{ provide: AuthService, useValue: authService },
{ provide: Router, useValue: mockRouter },
{
provide: ActivatedRoute,
useValue: {
snapshot: {
queryParams: {
returnUrl: '',
},
},
},
},
NotifyService,
Title,
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
fixture.detectChanges();
component = fixture.componentInstance;
injector = getTestBed();
route = TestBed.get(ActivatedRoute);
documentTitle = injector.get(Title);
fb = TestBed.inject(FormBuilder);
component.passwordHide = true;
component.pageTitle = 'Login | Online Shopping for Men & Women Shoes';
documentTitle.setTitle(component.pageTitle);
component.returnUrl = route.snapshot.queryParams['returnUrl'] || '';
component.loginForm = fb.group({
username: ['', [Validators.required, Validators.minLength(8)]],
password: ['', [Validators.required, Validators.minLength(6)]],
});
});
it('should form submit successfully', () => {
// console.log(route.snapshot.queryParamMap.get('returnUrl'))
// console.log(component.returnUrl);
component.loginForm.get('username')?.setValue('ramkumar');
component.loginForm.get('password')?.setValue('Ramkumar#45');
expect(component.loginForm.valid).toBeTruthy();
authService.login.and.returnValue(
Rx.of({
accessToken: 'testToken',
username: 'test',
userId: 1,
} as LoginResponse)
);
component.formSubmit();
expect(component.res).toEqual({
accessToken: 'testToken',
username: 'test',
userId: 1,
} as LoginResponse);
expect(mockRouter.navigate).toHaveBeenCalledWith(['']);
});
});
auth.service.ts
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { Observable, Subject } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Login, LoginResponse, Register } from '../Model/auth';
import { JwtHelperService } from '#auth0/angular-jwt';
import { UserResponse } from '../Model/user';
#Injectable({
providedIn: 'root',
})
export class AuthService {
private AUTH_API: string = environment.AUTH_API;
private USER_API: string = environment.USER_API;
public isUserLoggedIn: boolean = this.getAuthStatus();
private isLoggedIn: Subject<boolean> = new Subject<boolean>();
private token: any = localStorage.getItem('access_token')?.toString() || null;
private jwt: JwtHelperService;
/**
* Dependency Injections
* #param http
* #param route
*/
constructor(private http: HttpClient, private route: Router) {
this.jwt = new JwtHelperService();
this.isLoggedIn.subscribe((value) => {
console.log(value);
this.isUserLoggedIn = value;
});
}
/**
* Send user credentials to Auth API for create JWT token
* #param data
*/
login(data: Login): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`${this.AUTH_API}/CreateToken`, data);
}
/**
* Send HTTP request to register new user
* #param data
*/
register(data: Register): Observable<UserResponse> {
return this.http.post<UserResponse>(`${this.USER_API}`, data);
}
/**
* Remove JWT token from localstorage to logout
*/
logout(): void {
localStorage.removeItem('access_token');
this.authStatusToggle(false);
if (this.getUserToken() == null) this.route.navigate(['login']);
}
/**
* get user JWT token from localstorage
* return token as string or null
*/
getUserToken(): string {
if (localStorage.getItem('access_token') != null) {
this.token = localStorage.getItem('access_token')?.toString() || null;
} else {
this.token = null;
}
return this.token;
}
authStatusToggle(value: boolean) {
this.isLoggedIn.next(value);
}
getAuthStatus(): boolean {
let token = localStorage.getItem('access_token')?.toString();
console.log(token);
if (token == null) {
return false;
}
return true;
}
}

Angular - unit test failed for a subscribe function in a component

Hello I spend a lot of time trying to find out a solution without posting something but I don't understand what's going wrong, so I decided to post question.
Here the file I want to test :
home.page.ts
import { Component, OnInit } from '#angular/core';
import { distinctUntilChanged } from 'rxjs/internal/operators/distinctUntilChanged';
import { AlertController, LoadingController } from '#ionic/angular';
import { DataService } from '../service/data.service';
import { League } from '../interfaces/League';
import { LeaguesImageLink } from '../types/leaguesImgLink.enum';
import { ActivatedRoute, Router } from '#angular/router';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
searchTerm: string = '';
leaguesItems: League[] = [];
constructor(
private apiService: ApiService,
private route: ActivatedRoute,
private router: Router,
private dataService: DataService,
private loadingCtrl: LoadingController,
private alertController: AlertController
) { }
ngOnInit() {
this.loadLeagues();
}
async loadLeagues(): Promise<void> {
(await this.showSpinner()).present
this.apiService
.getAllLeagues()
.pipe(distinctUntilChanged())
.subscribe((res: League[]) => {
this.leaguesItems = res;
this.addImgLink();
});
(await this.showSpinner()).dismiss()
}
async showSpinner(): Promise<HTMLIonLoadingElement> {
const loading = await this.loadingCtrl.create({
message: 'Loading..',
spinner: 'bubbles',
});
return loading;
}
addImgLink(): void {
this.leaguesItems = this.leaguesItems.map((item: League) => {
return {
...item,
imgLink:
LeaguesImageLink[
item.name.split(' ').join('') as keyof typeof LeaguesImageLink
],
};
});
}
async showAlert(): Promise<void> {
const alert = await this.alertController.create({
header: "Alert",
message: "Il n'y a pas encore d'équipes !",
buttons: ['OK'],
});
await alert.present();
}
setLeaguesData(league: League): void {
if (league.teams?.length === 0) {
this.showAlert()
return;
} else {
this.dataService.setleaguesData(this.leaguesItems);
this.router.navigate(['../teams', league._id], {
relativeTo: this.route,
});
}
}
}
here my test file home.page.spect.ts
import { MockLoadingController } from './../../mocks/IonicMock';
import { League } from './../interfaces/League';
import { mockLeagueArray, mockLeagueArrayImageLink } from './../../mocks/mockLeagues';
import { ApiService } from 'src/app/service/api.service';
import { FormsModule } from '#angular/forms';
import { Ng2SearchPipeModule } from 'ng2-search-filter/';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import {
ComponentFixture,
fakeAsync,
TestBed,
tick,
waitForAsync,
} from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { HomePage } from './home.page';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { IonicModule } from '#ionic/angular';
import { of } from 'rxjs';
fdescribe('HomePage', () => {
let component: HomePage;
let fixture: ComponentFixture<HomePage>;
let apiService: ApiService;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HomePage],
teardown: { destroyAfterEach: false },
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
IonicModule.forRoot(),
HttpClientTestingModule,
RouterTestingModule,
Ng2SearchPipeModule,
FormsModule,
],
providers: [
ApiService
]
}).compileComponents();
fixture = TestBed.createComponent(HomePage);
component = fixture.componentInstance;
apiService = TestBed.inject(ApiService)
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('should call ngOnInit', () => {
let loadLeagues = spyOn(component, 'loadLeagues');
component.ngOnInit();
expect(loadLeagues).toHaveBeenCalled();
});
it('should call getAllLeagues service and fill leaguesItems array', fakeAsync(() => {
//ARRANGE
spyOn(apiService, 'getAllLeagues').and.returnValue(of(mockLeagueArray))
//ACT
component.loadLeagues()
tick(1000)
fixture.detectChanges()
//ASSERT
expect(component.leaguesItems).toEqual(mockLeagueArray)
}))
});
my mock file :
mockLeagues.ts
import { League } from "src/app/interfaces/League";
export const mockLeagueArray: League[] = [{
_id: "1",
name: "Supercopa de Espana",
sport: "sport1",
teams: ["t1", "t11"],
}, {
_id: "2",
name: "English Premier League",
sport: "sport2",
teams: ["t2", "t22"],
}]
export const mockLeagueArrayImageLink: League[] = [{
_id: "1",
name: "Supercopa de Espana",
sport: "sport1",
teams: ["t1", "t11"],
imgLink: "https://www.thesportsdb.com/images/media/league/badge/sp4q7d1641378531.png",
}, {
_id: "2",
name: "English Premier League",
sport: "sport2",
teams: ["t2", "t22"],
imgLink: "https://www.thesportsdb.com/images/media/league/badge/pdd43f1610891709.png",
}]
Nothing fancy ! And the response I get when I run ng test :
Chrome 107.0.0.0 (Windows 10) HomePage should call getAllLeagues service and fill leaguesItems array FAILED
Expected $.length = 0 to equal 2.
Expected $[0] = undefined to equal Object({ _id: '1', name: 'Supercopa de Espana', sport: 'sport1', teams: [ 't1', 't11' ] }).
Expected $[1] = undefined to equal Object({ _id: '2', name: 'English Premier League', sport: 'sport2', teams: [ 't2', 't22' ] }).
at <Jasmine>
at UserContext.apply (src/app/home/home.page.spec.ts:81:36)
at UserContext.apply (node_modules/zone.js/fesm2015/zone-testing.js:2030:30)
at _ZoneDelegate.invoke (node_modules/zone.js/fesm2015/zone.js:372:26)
at ProxyZoneSpec.onInvoke (node_modules/zone.js/fesm2015/zone-testing.js:287:39)
Chrome 107.0.0.0 (Windows 10): Executed 3 of 12 (1 FAILED) (skipped 9) (0.799 secs / 0.601 secs)
TOTAL: 1 FAILED, 2 SUCCESS
I spend 7 days (reading angular doc, checking on internet..) , now I'm done, someone has a solution ?
Thanks.
Should present not have brackets?
// !! Like this
(await this.showSpinner()).present();
Also, this distinctUntilChanged does nothing because what is being emitted is an array and every time it emits, it will be a new array. It's like saying [] === [], it will always be false so it will always emit. Check out With Object Streams here: https://www.leonelngande.com/an-in-depth-look-at-rxjss-distinctuntilchanged-operator/ but for you it is an array. You can do more research and you can pass a callback in distinctUntilChanged to fine tune when you want it to emit and when you don't.
If I were you, I would change loadLeagues like so:
async loadLeagues(): Promise<void> {
// (await this.showSpinner()).present
this.apiService
.getAllLeagues()
// !! This distinctUntilChanged does nothing since the source is an array
// .pipe(distinctUntilChanged())
.subscribe((res: League[]) => {
this.leaguesItems = res;
this.addImgLink();
});
// (await this.showSpinner()).dismiss()
}
Comment out this.showSpinner dismiss and present and see if the test passes. If it does, you know those 2 lines could be the issue.
This is a good resource in learning unit testing with Angular: https://testing-angular.com/.

Why endpoints not working from imported NESTJS module

tell me please why appController working and itemsController no (from imported module)
I learn nestjs and i did it according to documentation. This controller working its uncomment endpoint.
import {Controller, Get} from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor() {}
// #Get()
// getHome():string {
// return 'Hello world!';
// }
}
itemsController.ts - working if change appController.ts on this
import {Controller, Get, HttpException, HttpStatus, Param, Post, Res} from "#nestjs/common";
import {Response} from "express";
import {ItemsService} from "./items.service";
import {ItemDto} from "./dto/item.dto";
#Controller()
export class ItemsController {
constructor(private readonly itemsService: ItemsService) {}
#Get()
getAll(#Res({passthrough: true}) res: Response):string | object {
const items = this.itemsService.getAll();
if(!!items.length) {
res.status(HttpStatus.OK);
return new HttpException({
items: items
}, HttpStatus.OK).getResponse();
}
res.status(HttpStatus.INTERNAL_SERVER_ERROR);
return new HttpException({
items: 'Items length: ' + items.length,
status: HttpStatus.INTERNAL_SERVER_ERROR
}, HttpStatus.INTERNAL_SERVER_ERROR).getResponse();
}
#Post()
create(#Param() params):ItemDto {
return this.itemsService.create(params);
}
}
Test jest working:
import { Test } from '#nestjs/testing';
import { ItemsController } from './items/items.controller';
import { ItemsService } from './items/items.service';
import * as request from 'supertest';
import { INestApplication } from "#nestjs/common";
describe('ItemsModule', () => {
let itemsController: ItemsController;
let itemsService: ItemsService;
let app: INestApplication;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
controllers: [ItemsController],
providers: [ItemsService],
}).compile();
itemsService = moduleRef.get<ItemsService>(ItemsService);
itemsController = moduleRef.get<ItemsController>(ItemsController);
app = moduleRef.createNestApplication();
await app.init();
});
describe('End-points', () => {
it('/GET Status 200', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect({
"items": [
{
id: '0',
title: '',
message: ''
}
]
});
});
it('/GET Status 500', () => {
return request(app.getHttpServer())
.get('/')
.expect(500)
.expect({
items: 'Items length: 0',
status: 500
});
});
});
});
I pushed all on github, you can see all code
After looking at your source code, you're missing the # for #Module() in your ItemsModule

angular resolver does not have access to angular service properties

i have an anagular service called authservice ,which looks like this:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import { JwtHelperService} from '#auth0/angular-jwt';
#Injectable()
export class AuthService {
public DecdedToken: any;
userToken: any;
public userid: any;
helper: any = new JwtHelperService();
baseUrl = 'http://localhost:5000/api/auth/';
// userToken: any;
constructor(private http: Http , private jwthelpservicee: JwtHelperService) {}
login(model: any) {
return this.http.post(this.baseUrl + 'login', model, this.requestOptions()).map((response: Response) => {
const user = response.json();
if (user && user.stringToken) {
this.userToken = user.stringToken;
localStorage.setItem('token', user.stringToken);
this.DecdedToken = this.helper.decodeToken(user.stringToken);
this.userid = this.DecdedToken.nameid;
// console.log(this.userid);
// onsole.log('so far so good');
}
}).catch(this.HandleError);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model, this.requestOptions()).catch(this.HandleError);
}
private requestOptions() {
const headers = new Headers({ 'Content-type': 'application/json' });
return new RequestOptions({ headers: headers });
}
IsLoggedIn() {
return !this.jwthelpservicee.isTokenExpired();
}
private HandleError(error: any) {
const applicationerror = error.headers.get('Application-Error');
if (applicationerror) {
return Observable.throw(applicationerror);
}
const serverError = error.json();
let modelStateErrors = '';
if (serverError) {
for (const key in serverError) {
if (serverError[key]) {
modelStateErrors += serverError[key] + '\n';
}
}
}
return Observable.throw(modelStateErrors || 'server error');
}
}
in login method DecodedToken gets its value.
i have a resolver too, in which i am trying to get a value of a property from authsercvice
import { Resolve, Router, ActivatedRouteSnapshot } from '../../../node_modules/#angular/router';
import { User } from '../_Models/user';
import { Injectable } from '../../../node_modules/#angular/core';
import { UserService } from '../_services/User.service';
import { AlertifyService } from '../_services/alertify.service';
import { Observable } from '../../../node_modules/rxjs';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/catch';
import { AuthService } from '../_services/auth.service';
#Injectable()
export class MemberEditResolver implements Resolve<User> {
constructor(private userservice: UserService ,
private router: Router ,
private alertify: AlertifyService,
private authservice: AuthService) {
}
resolve(route: ActivatedRouteSnapshot): Observable<User> {
console.log(this.authservice.userid);
return this.userservice.getuser(this.authservice.DecdedToken.nameid).catch(error => {
this.alertify.error('error');
this.router.navigate(['/members']);
return Observable.of(null);
});
}
}
as you can see i have defined DecdedToken as a public property, inside the authservice this property has value, but when i want to access it from resolver ,i get null or undefined...

How to access private method in spec file (Karma - Angular) [duplicate]

I have been going round in circles trying to unit test a Service (AuthService) that depends upon AngularFireAuth.
I am trying to find a way to mock, or highjack the Observable AngularFireAuth.authState instead of the Service actually talking to Firebase.
Here is my test spec:
import { inject, TestBed } from '#angular/core/testing';
import { AngularFireModule } from 'angularfire2';
import { AngularFireAuth, AngularFireAuthModule } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import 'rxjs/add/observable/of';
// import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Rx';
import { AuthService } from './auth.service';
import { environment } from '../../environments/environment';
const authState: firebase.User = null;
const mockAngularFireAuth: any = { authState: Observable.of(authState) };
describe('AuthService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AngularFireModule.initializeApp(environment.firebaseAppConfig)],
providers: [
{ provider: AngularFireAuth, useValue: mockAngularFireAuth },
AuthService
]
});
});
it('should be defined', inject([ AuthService ], (service: AuthService) => {
expect(service).toBeDefined();
}));
it('.authState should be null', inject([ AuthService ], (service: AuthService) => {
expect(service.authState).toBe(null);
}));
});
And here is my (simplified) Service:
import { Injectable } from '#angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class AuthService {
private authState: firebase.User;
constructor(private afAuth: AngularFireAuth) { this.init(); }
private init(): void {
this.afAuth.authState.subscribe((authState) => {
if (authState === null) {
this.afAuth.auth.signInAnonymously()
.then((authState) => {
this.authState = authState;
})
.catch((error) => {
throw new Error(error.message);
});
} else {
this.authState = authState;
}
}, (error) => {
throw new Error(error.message);
});
}
public get currentUser(): firebase.User {
return this.authState ? this.authState : undefined;
}
public get currentUserObservable(): Observable<firebase.User> {
return this.afAuth.authState;
}
public get currentUid(): string {
return this.authState ? this.authState.uid : undefined;
}
public get isAnonymous(): boolean {
return this.authState ? this.authState.isAnonymous : false;
}
public get isAuthenticated(): boolean {
return !!this.authState;
}
public logout(): void {
this.afAuth.auth.signOut();
}
}
I get the error Property 'authState' is private and only accessible within class 'AuthService'.
Of course it is, but I don't want to actually access it — I want to mock or highjack it so I can control it's value from within my test spec. I believe I am way off-course with my code here.
Please note I am using version ^4 of AngularFire2 and there were breaking changes introduced; documented here: https://github.com/angular/angularfire2/blob/master/docs/version-4-upgrade.md
Encapsulated members can be reflected.
The hard way:
expect(Reflect.get(service, 'authState')).toBe(null);
The easy way:
expect(service['authState']).toBe(null);
expect((service as any).authState).toBe(null);

Categories

Resources