Unit Testing with Angular 7 - javascript

I am trying to write unit test for this angular script:
export class DataService {
private csrfToken: string = '';
private isContentShow: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor(private http: HttpClient, private cookieService: CookieService) {
this.token = this.cookieService.get('token');
}
public createData(data: Data) {
try {
this.http.post( url,
data,
{
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': this.token
})
})
.subscribe(
data => {
this.isContentShow.next(true);
},
err => {
this.showError();
},
() => console.log('Request Complete')
);
return true;
} catch {
this.showError();
}
}
public getIsContentShow(): Observable<boolean> {
return this.isContentShow.asObservable();
}
}
The test that I had so far and its running as expected.
it('#getIsContentShow should return value from observable',
(done: DoneFn) => {
service.getIsContentShow().subscribe(value => {
expect(value).toBe(true);
done();
});
});
However I am trying to write the test for createData() function
I am able to mock the HttpClient using HttpClientTestingModule however I don't know how to handdle the CookieService and token ?
Thanks

You can use spies to spy on the cookieService get method. This way, you can write your unit test to test the combinations of returns you say the cookieService can provide.
This link says that you can spy on the prototype of the method in order to handle it how you like in the constructor.
it(
"should call #getGeneralStats in the constructor",
inject(
[CookieService, HttpClient],
(cookieService: CookieService, http: HttpClient) => {
let mySpy = spyOn(cookieService, 'get').and.returnValue(<your value>);
dataService = new DataService(http, cookieService);
expect(mySpy).toHaveBeenCalled();
}
)
);
For you, this may depend on how you're writing your tests. The example shows the service being instantiated like new ServiceName, but it's also possible to use dependency injection to get the service. If you're using DI for the service you are testing, I'd have to research more how to do this (others please feel free to add your answer if you know how to do that)!

Related

angular user-idle-service issue

I am using the user-idle-service (https://www.npmjs.com/package/angular-user-idle) for angular. I am using it for my angular 6 front end application, but am running into issues where my users are getting kicked out prematurely when they are active.
I fire the library as soon as a user logs in to the application. It works most of the time, but sometimes users are getting prematurely kicked out when they are active in their browser. I am using this only for browser based usage.
In my service class I fire the below, as well as configuration below that. Is there anything in the library I am using obviously wrong? :
constructor(private http: Http,
private router: Router,
private cookieService: CookieService,
private userIdle: UserIdleService) {
this.userIdle.setCustomActivityEvents(merge(
fromEvent(window, 'mousemove'),
fromEvent(window, 'resize'),
fromEvent(document, 'keydown'),
fromEvent(document, 'touchstart'),
fromEvent(document, 'touchend'),
fromEvent(window, 'scroll')
));
}
login(credentials: Credentials): Observable<any> {
return this.http.post<any>(this.loginUrl, JSON.stringify(credentials), {
headers: new HttpHeaders({'Content-Type': 'application/json'})
})
.pipe(map(userDetails => {
this.setUserDetailsAndExtendExpirationInLocalStorage(userDetails);
this.startUserIdle();
this.goToHome();
}));
}
startUserIdle() {
this.userIdle.startWatching();
this.subscriptions.push((this.userIdle.onTimerStart().subscribe()));
this.subscriptions.push((this.userIdle.onTimeout().subscribe(() => {
this.logout();
})));
this.subscriptions.push(this.userIdle.ping$.subscribe(() => {
this.refreshToken();
}));
}
refreshToken() {
return this.http.get<any>(this.refreshUrl, {
headers: new HttpHeaders({'Content-Type': 'application/json'})
}).subscribe(() => {
// do stuff
}, () => {
this.logout();
});
}
logout() {
this.goToLogin();
this.removeUserDetails();
this.removeSessionToken();
this.userIdle.resetTimer();
this.userIdle.stopTimer();
this.userIdle.stopWatching();
this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.setIsLoggedIn(false);
}
Also in my app.module.ts file I configure it like below:
UserIdleModule.forRoot({idle: 900, timeout: 1, ping: 840})

POST - Angular 5

I'm fairly new to angular and got stuck at getting data from SpringREST which is at backend.
So scenario is:I'll be getting a JSON string from backend as POST(JSON data will be redirected to my hosted link of site as POST) and I need to catch that JSON string and display it on UI.
I'm not sure about the postMethod in dataservice.ts if it should be there.
I googled on stackoverflow and came up with below code which doesn't seem to work in my scenario:
Component.ts
import { MyDataService } from './services/my-data.service';
constructor(private posting: MyDataService
) {}
ngOnInit() {
this.posting.postMethod().subscribe(
(response => {
console.log(response)
}));
}
}
Data-service.ts
#Injectable()
export class MyDataService {
constructor(private http: Http)
{ }
postMethod(model: any ) {
return this.http.post("http ://", model)
.map(res => res.json());
}
}
As the error says, You need to pass the parameter to the service when invoking
this.posting.postMethod(model).subscribe(
(response => {
console.log(response)
}));
As i can see in your component.ts you are not passing the model as a parameter.
you need to pass the model as a parameter.
this.posting.postMethod(anyData).subscribe(
(response => {
console.log(response)
}));
If this is not the issue then please update us with the error you are getting.
This is the right way to define a function in the subscribe method:
ngOnInit() {
this.posting.postMethod(model).subscribe(
(response) => {
console.log(response)
});
}

Angular 2 - Viewing a Single Record of Data

I am new to Angular so I am having trouble figuring out how to form my questions for what I am trying to accomplish, but here it goes.
I have a component that is fetching a single user record from a service. I then want to display those user details on my UI. In other parts of my code, they have always been multiple records so I have used *ngFor and looped over the array of data. However, since this is just a single result, I am not too sure how to accomplish this.
Component:
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { UserRecord } from '../shared/user-record.interface';
import { UserService } from '../shared/user.service';
#Component({
selector: 'app-view-record',
templateUrl: './view-record.component.html',
styleUrls: ['./view-record.component.css']
})
export class ViewRecordComponent implements OnInit {
private record: UserRecord[];
private errorMessage: any = '';
private loaded = false;
private RecordID: number; // User ID of who we are looking at
constructor(private _crudService: UserService,
private activatedRoute: ActivatedRoute) { }
ngOnInit() {
// Get the userID from the activated route
this.activatedRoute.params.subscribe((params: Params) => {
this.RecordID = params['id'];
});
// Call our service and pass the userID
this._crudService.getRecord(this.RecordID)
.then(res => {
this.record = this._crudService.record;
return this._crudService.getRecord(this.RecordID);
})
.then(res => {
console.log(this.record)
this.loaded = true;
})
.catch(err => { console.error(err); });
}
}
Service:
getRecord(userID: number) {
const headers: Headers = new Headers({
"Authorization": this._frameworkService.getSessionInfo().token
});
return new Promise((resolve, rejects) => {
this._http.post(this.baseUrl + '/fetchRecord', { "userID": userID }, { "headers": headers })
.map(res => res.json())
.subscribe((data) => {
if (data) {
this.record = data;
}
resolve(true);
});
});
}
Interface:
export interface UserRecord {
RecordID: number;
QID: string;
FavoriteColor?: string;
FavoriteNumber?: number;
FavoriteActor?: string;
MetaInsertUTC: string;
MetaUpdateUTC: string;
FirstName: string;
LastName: string;
NTID: string;
}
Service Result:
[
{
"RecordID":"55",
"QID":"Q00019204",
"FavoriteColor":"Blue",
"FavoriteNumber":"6",
"FavoriteActor":"Bob",
"MetaInsertUTC":"2017-06-29 18:47:01.750",
"MetaUpdateUTC":null,
"FirstName":"Jim",
"LastName":"Bobs",
"NTID":"bobby"
}
]
In my Component HTML, I have tried {{record.FirstName}} but receive the error of ViewRecordComponent.html:16 ERROR TypeError: Cannot read property 'FirstName' of undefined.
Since this isn't a set of data results, I don't see how *ngFor would be applicable in the use case.
I assumed that since my component is storing the data in the record object, I should be able to access that from the UI? The console.log shows all of the correct data points.
How would I reference the users FirstName in my component HTML? Hopefully I'm on the right path at least.
Your response seems to be an array with an object, so record.FirstName doesn't exist, but record[0].FirstName does.
And when it comes to the view, remember to use either the safe navigation operator or *ngIf so that you do not run into undefined issues like mentioned by DeborahK. Observable type error: cannot read property of undefined
Furthermore just some suggestion on how to handle http in Angular... I would do something like the following...
getRecord(userID: number) {
const headers: Headers = new Headers({
"Authorization": this._frameworkService.getSessionInfo().token
});
return this._http.post(this.baseUrl + '/fetchRecord', { "userID": userID }, { "headers": headers })
.toPromise()
.then(res => res.json()[0]) // get the object only
.catch(err => { console.error(err); });
}
and component:
this._crudService.getRecord(this.RecordID)
.then(res => {
this.record = res;
});
But that's totally up to you :)
Getting data from Http is asynchronous. This means that when the page is first displayed, the data is not yet there.
There are several ways to resolve this:
One option is to use the "?" (safe navigation) operator: {{record?.FirstName}} This better handles nulls. See this link for more information: https://angular.io/guide/template-syntax#the-safe-navigation-operator----and-null-property-paths
Another option is to use *ngIf around your HTML code. *ngIf='record'
So when your page is first displayed, it will not generate an error that record is not yet set. As soon as the data is retrieved, the binding will notice the change and update the UI appropriately.
Here is what one of my service methods look like:
getProducts(): Observable<IProduct[]> {
return this._http.get(this._productUrl)
.map((response: Response) => <IProduct[]> response.json())
.catch(this.handleError);
}
And here is the call to that service:
ngOnInit(): void {
this._productService.getProducts()
.subscribe(products => this.products = products,
error => this.errorMessage = <any>error);
}
Notice that the subscribe is in the component that calls the service, not in the service itself.

ORIGINAL EXCEPTION: Cannot read property 'post' of undefined in angular 2

I had an issue on
Cannot read property 'post' of undefined in angular 2
on submitting the form, a function is called, here is the code
onSubmit() {
console.log("onsubmit->", this.addForm.value);
let addArray = this.addForm.value;
this._employeeservice.addEmployeeCollection(addArray)
.subscribe(sample => {
this.dbemp.push(sample);
});
this.addForm.reset();
this.SubmitToast();
}
addEmployeeCollection() code is here
addEmployeeCollection(addArray: EmployeeSchema) {
let url: string = Constants.DOMAIN + Constants.CREATE_EMPLOYEE_ROUTE;
console.log('addArray at emplyee service', addArray)
var body = addArray;
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let options = {
headers: headers
};
let token = localStorage.getItem('realtoken');
options.headers.set('Authorization', ` ${token}`);
return this.http.post(url, body, options).map(res => res.json()).catch(this._errorHandler);
}
Based on comments, we learned that the HTTP wasn't injected properly, which caused this.http to be undefined.
Instead of marking the http in the service like so:
export class EmployeeService {
public http: Http;
}
it should be injected in the constructor:
export class EmployeeService {
constructor(public http: Http) { }
}
Generally, it's safer to empty your form once you got your success call back. So try this :
onSubmit() {
console.log("onsubmit->", this.addForm.value);
let addArray = this.addForm.value;
this._employeeservice.addEmployeeCollection(addArray)
.subscribe(sample => {
this.dbemp.push(sample);
this.addForm.reset(); //move this in the subscribe area
this.SubmitToast();
});
}

How to consume REST API with Angular2?

This is my demo code:
products.service.ts
getProducts(){
this.headers = new Headers();
this.headers.append("Content-Type", 'application/json');
this.headers.append("Authorization", 'Bearer ' + localStorage.getItem('id_token'));
return this.http.get('http://mydomain.azurewebsites.net/api/products',{headers:headers}).map(res => res.json().data);
}
products.component.ts
constructor(private productsService: ProductsService) { }
ngOnInit() {
this.productsService.getProducts().subscribe((res) => {
console.log(res);
});
}
Is it nescessary to import something in the ngModule decorator to consume a REST API or my code is wrong? I can get the desired data with Postman Chrome Extension but not with Angular 2 code.
I hope to have explained my problem well.
Update
These are the errors i get:
Sorry for making you waste your time.
This was the problem:
app.module.ts
providers: [
ProductsService,
// { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server
// { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data
]
After commenting the in-mem server and and the in-mem server data the problem dissapeared.
You're not setting the headers in the request. You declare the Headers object but you don't actually do anything with it.
You need to set them in the get function like this:
return this.http
.get('http://mydomain.azurewebsites.net/api/products', { headers: headers })
.map(res => res.json().data);
I'd suggest you use ngx-rest-ex: https://www.npmjs.com/package/ngx-rest-ex
npm i -S ngx-rest-ex
It's convenient, you just need to specify the corresponding decorator on top of the method and the return type, it will replace your method body. The return type can be either Promise or Observable depending on the HTTP METHOD annotation that you specified.
My demo code for your case:
import { Injectable, Injector } from '#angular/core';
import { BaseUrl, GET, RESTClient } from 'ngx-rest-ex';
import { Product } from './models';
#Injectable({
providedIn: 'root'
})
#BaseUrl('http://mydomain.azurewebsites.net/api/')
export class ApiService extends RESTClient {
constructor(injector: Injector) { super(injector); }
protected getDefaultHeaders() {
return {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + localStorage.getItem('id_token')
};
}
#GET('products')
getProducts(): Promise<Product[]> {
return;
}
}

Categories

Resources