I am very new to Jasmine and struggle with its concepts. I would like to test if an array 'reduce' has been called.
export class QuestionController implements Controller {
private questionnaireList: Questionnaire[];
private questionList: Question[];
I build a select table that I want to test:
buildSelect(): void {
let options = this.questionnaireList.reduce((result, qset) => result + this.templateForQuestionnaire(qset), '');
let select = this.util.getElementById('quest-select');
select.innerHTML = options;
}
I have tried things like:
beforeEach(() => {
spyOn(dummyQuestionnaireList, "reduce");
spyOn(qc, "buildSelect").and.callThrough();
});
it('should call reduce on this.questionnaireList', () => {
qc.buildSelect();
expect(dummyQuestionnaireList.reduce).toHaveBeenCalled();
});
it('should call util.getElementById', () => {
qc.buildSelect();
expect(u.getElementById).toHaveBeenCalled();
});
but I really have no idea what I am doing, in that I cannot even identify which tools are the right tools from Jasmine from its docs page.
Related
Having the following NgRX effect
loadData$: Observable<any> = createEffect((): Observable<Action> => {
return this.actions$.pipe(
ofType(loadDataStarted),
switchMap(() => {
return timer(0, 10000).pipe(
switchMap(() => this.dataService.loadNewData().pipe(
)),
);
}),
);
});
after all the requirements are mocked try to test if dataService.loadNewData() is getting called, but the test will fail
beforeEach(() => {
service.loadNewData.and.returnValue(of(data));
});
it('should call service', fakeAsync(() => {
tick(10000)
expect(service.loadNewData).toHaveBeenCalledOnceWith();
}));
How do I mock correctly this case
It seems you perform the subscription to the effect outside of the actual test (the it block). This means the subscription lives outside of the fakeAsync timer and because of this, the tick(..) has no effect.
To solve this, make sure to setup everything inside your fakeAsync test block.
A general recommendation would be to use marble testing for this: https://ngrx.io/guide/effects/testing#marble-diagrams
Edit: A concrete solution is found in https://stackoverflow.com/a/65857864/2544163
I would like to know how to call a Firebase array using Angular 2. In my example here, I have an array addnote in my Firebase DB. There are two separate iterations of Do the dishes, and I would like to print them out to my HTML's unordered list.
The [] in my private addsnotes throws errors, and I didn't really expect otherwise. In the absence of understanding how to output the array, I am using it to illustrate what I am trying to achieve. I have also marked the relevant area where the call is being made.
My rainbow.component.html
<div><ul>
<li *ngFor="let addsnote of addsnotes">{{addsnote}}</li>
</ul></div>
My firebase schema:
My rainbow.component.ts
export class Rainbow implements OnInit{
private addsnotes: [];
private username: string;
ngOnInit(){
var self = this;
var user = firebase.auth().currentUser;
var getUserInfo = firebase.database().ref('users/' + user.uid);
setTimeout(acquisition, 1000);
function acquisition(){
if (user){
getUserInfo.once('value', function(snapshot){
self.username = snapshot.val().username;
self.addsnotes = snapshot.val().addnote; //incorrect
});
}
}
}
}
If you want the AngularFire2 makes is easy to tune into the power of Observables so you can detect changes on the firebase end and auto-update your user notes. With AngularFire2, your solution would look more like this...
rainbow.component.html
<div>
<ul>
<li *ngFor="let addsnote of addsnotes$ | async">{{ addsnote.$value }}</li>
</ul>
</div>
rainbow.component.ts
import { Injectable } from '#angular/core';
import { AngularFire } from 'angularfire2';
import { Observable } from 'rxjs/Observable';
export class RainbowComponent implements OnInit {
private addsnotes$: Observable<string[]>;
private username$: Observable<string>;
constructor (
private af: AngularFire
) {}
ngOnInit () {
let user = firebase.auth().currentUser,
userNamePath = `users/${user.uid}/username`,
notesPath = `users/${user.uid}/addnote`;
this.username$ = this.af.database.object(userNamePath);
this.addsnotes$ = this.af.database.list(notesPath);
}
}
You will need the async pipe when using Observables in your template HTML. It will auto subscribe to extract the data. The big difference with this and the previous code is that anytime your addsnotes data changes, it will automatically show the changes on the HTML view. If you want to keep it like the previous code where you are limiting it to one call using once('value'), you can add a .take(1) to the end of this.af.database.list(notesPath) to just take the list values one time.
In addition, I would recommend adding a sub field to your notes such as order so that you can sort your list in an order that you want. You can find info on how to sort with AngularFire2 here.
Hope this helps.
If you wanted to stick with Web Firebase API (no angularfire2), to get the addsnotes to work, it might look something like this.
ngOnInit () {
let user = firebase.auth().currentUser;
// Used ES6 arrow function instead, which is built into Typescript
setTimeout(() => {
// Make sure user and user.uid are defined
if (user && user.uid) {
let userRef = firebase.database().ref(`users/${user.uid}`);
userRef.once('value', (snapshot) => {
let userInfo = snapshot.val() || {};
this.username = userInfo['username'];
this.addsnotes = [];
// Traverse each child for 'addnote'
snapshot.child('addnote').forEach((childSnapshot) => {
let addnoteKey = childSnapshot.key,
addnote = childSnapshot.val();
addnote['id'] = addnoteKey; // Saving the reference key if you want reference it later
self.addsnotes.push(addnote);
});
}
}
}, 1000);
}
Am a newborn in sinon.js, mocha, chai.
Am trying to write unit-test by using above libraries.
Project Front-End is in AngularJS with the combination of ECMASCRIPT6.
From the docs of Sinonjs I understand nothing! It's complicated.
Here is my JS Snippet:
login() {
let loginData = this.loginData;
return this.authService.login(loginData).then(userData => {
let msg = `${this.niceToSeeYouAgain} ${userData.email}!`;
this.userAlertsService.showSuccessToast(msg);
this.navigationService.afterLoggedIn();
}, errorInfo => {
this.userAlertsService.showAlertToast(errorInfo);
});
}
And here is my UNIT-TEST snippet:
it('.login() - should load user data and showSuccessToast and call navigationService afterLoggedIn', sinon.test(() => {
let msg = `Nice to see you again ${userData.email}!`;
let loginData ={
email: "sarfaraz#walkover.in",
password: "designer99",
remember: true
}
let stub = sinon.stub(authService, 'login').resolves(userData);
// let spy1 = sinon.spy(controller.userAlertsService, 'showSuccessToast');
//call function
controller.login();
$timeout.flush();
// expect things
expect(stub.called).to.eq(true);
// restore
stub.restore();
}));
But unfortunately am stucked.
My code not runs after .then lines.
am expecting it should throws error. cause I have not spies on userAlertsService and navigationService.
Please let me know what am doing wrong
My E2E testing is to fill some details in a page, click a button to go to the next page and verify whether we reached the next page.Now I am able to move to the next page and scroll down but after that I couldn't select an element based on id, name or css it fails with the above error.
Why do we get "timeout Aysnc callback was not invoked" error ?
I have seen so many questions asking for same error but none of the answers is working in my case.PFB the code.
beforeEach(() => {
browser.manage().window().setSize(BROWSER_WIDTH, BROWSER_HEIGHT);
browser.get('index.html#/banking');
bankingDetails = require('./banking.page');
});
fit('should navigate to check panel for source type = saving and one ' +
'savings checkbox was selected', () => {
var checkPanelDetails = require('./check.page');
bankingDetails.fillBankingDetails(true, true);
bankingDetails.bankingWeiterButton.click();
browser.executeScript('window.scrollTo(0, 700);');
var isPresent = browser.wait(function () {
return checkPanelDetails.isVisible();
});
expect(isPresent).toBeTruthy();
});
check.page
var CheckPanel = function () {
this.checkPanel = element(by.name('check.confirm'));
this.isVisible = function () {
return this.checkPanel.isPresent();
};
};
module.exports = new CheckPanel();
Note:
I am using jasmine(2.4.1) and protractor(2.3.0)
Here is a link from jasmine Asynchronous_Support that help me understand time out problems. Hope that can help you,
describe("long asynchronous specs", function() {
beforeEach(function(done) {
done();
}, 10000);
});
Basically what i try to do is to hit my API once and save the result inside global variable in my Service, and then share and modify this value in my parent and child component with two helpers functions.
repairs.service.ts
public myItems:any[];
public GetRepairs = ():Observable<any> => {
this.headers = new Headers();
this.headers.set('Authorization', 'Bearer' + ' ' + JSON.parse(window.localStorage.getItem('token')));
return this._http.get(this.actionUrl +'repairs'{headers:this.headers})
.map((res) => {return res.json();
}).map((item) => {
let result:Array<any> = [];
if (item.items) {
item.items.forEach((item) => {
result.push(item);
});
}
this.myItems = result;
return this.myItems;
});
};
public GetItems() {
return this.myItems;
};
public UpdateItems(data:any[]) {
this.myItems = data;
};
And then in my main component i do
repairs.component.ts
export class RepairsComponent implements OnInit {
public myItems:any[];
constructor(private _userService:UserService,
private _RepairsService:RepairsService,
public _GlobalService:GlobalService) {
}
ngOnInit() {
this._userService.userAuthenticate();
this.getAllItems();
}
private getAllItems():void {
this._RepairsService
.GetRepairs()
.subscribe((data) => {
this._RepairsService.UpdateItems(data);
},
error => console.log(error),
() => {
this.myItems = this._RepairsService.GetItems();
});
}
}
This work just fine but when i try to invoke GetItems() in child component i get undefinded. I try to do it inside constructor and ngOnInit with the same result.
child.component.ts
export class ChildComponent {
private items:any[] = [];
constructor(private _RepairsService:RepairsService,
private _Configuration:Configuration) {
this.items = this._RepairsService.GetItems();
// undefinded
}
ngOnInit() {
this.items = this._RepairsService.GetItems();
// undefinded
}
}
From what i can see in the limited amount of code you shared, it would seem you are trying to get the items before the http get call finishes and saves the data. I think a better design pattern would be to make the GetItems() function also an observable or promise, and check if the data is there, if not call the http get call, and once that completes send the data back to the different components that need it.
As #MSwehli mentioned with async code execution you can't rely on the order of code lines. In this code:
ngOnInit() {
this.items = this._RepairsService.GetItems();
// undefinded
}
the async code in GetItems(); is scheduled for later execution into the event queue and then continued with the sync code. The scheduled code will be executed eventually but it's not determined when. It depends on the response of the server in this example.
If you return a Promise you can use .then(...) the chain the execution so that your code is only executed when the async execution is completed.
There are two errors/inconsistencies in your code:
userAuthenticate() call followed with getAllItems() call. These calls are async, user is not yet authenticated by the time getAllItems() is called, getAllItems will fail.
Solution here is to chain calls using rxjs flatMap:
//assuming userAuthenticate returns Observable
userService.userAuthenticate().flatMap(()=>{
return repairsService.GetRepairs();
}).subscribe(..process repairs..);
getAllItems() is called nearly at the same time as GetItems(). In most cases it fails also, because previous http request is not completed when GetItems() is called.
In my opinion early initialization is not necessary here, use service directly:
//ChildComponent
ngOnInit() {
this._RepairsService.GetRepairs().subscribe(..do anything with list of repairs i.e. assign to bindable property..);
}
You could add console.log statements in each part of the code to see the order of events in your app.