I have a slide-show component that has an Input array of slide objects and shows each one as long as it's been defined in slide.time
of itself. also there are two buttons that clicking them has to slide to the next item and reset the timer. in order to make this work, I'm using Observables like this:
/**
* a "SUBJECT" for pausing(restarting) the slider's auto-slide on user's click on left and right arrows
* @type {Subject}
*/
private pauser = new Subject();
/**
* the main observable for timer (before adding the pause/reset option)
* @type {Observable<T>}
*/
private source = Observable
.interval(1000)
.timeInterval()
.map(function (x) { /*return x.value + ':' + x.interval;*/ return x })
.share();
/**
* the final timer, which can be paused
* @type {Observable<R>}
*/
private pausableSource = this.pauser.switchMap(paused => paused ? Observable.never() : this.source);
/**
* the subscription to the timer which is assigned at OnInit hook , and destroyed at OnDestroy
*/
private subscription;
ngOnInit(){
this.subscription = this.pausableSource.subscribe(() => {
//doing changes to the template and changing between slides
});
this.pauser.next(false);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
and it's working properly. now to test this component, I'm giving it some data within a test-host component and want to check it's functionality like this:
it("should show the second (.slidingImg img) element after testHost.data[0].time seconds
have passed (which here, is 2 seconds)", () => {
//testing
});
i tried many things that i found in docs or anywhere on the internet, but none of them work for me. the problem is that I can't mock the passage of time in a way that the observable timer would perform next actions, and it's like no time has passed whatsoever. two of the ways that haven't worked for me are these:
it("should show the second (.slidingImg img) element after testHost.data[0].time seconds
have passed (which here, is 2 seconds)", fakeAsync(() => {
fixture.detectChanges();
tick(2500);
flushMicrotasks();
fixture.detectChanges();
let secondSlidingImg = fixture.debugElement.queryAll(By.css('.slidingImg'))[1].query(By.css('img'));
expect(secondSlidingImg).toBeTruthy();
//error: expected null to be truthy
}));
i got this from angular2 docs.
and:
beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
testHost = fixture.componentInstance;
scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
const originalTimer = Observable.interval;
spyOn(Observable, 'interval').and.callFake(function(initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, scheduler);
});
// or:
// const originalTimer = Observable.timer;
// spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) {
// return originalTimer.call(this, initialDelay, dueTime, scheduler);
// });
scheduler.maxFrames = 5000;
});
it("should show the second (.slidingImg img) element after testHost.data[0].time seconds
have passed (which here, is 2 seconds)", async(() => {
scheduler.schedule(() => {
fixture.detectChanges();
let secondSlidingImg = fixture.debugElement.queryAll(By.css('.slidingImg'))[1].query(By.css('img'));
expect(secondSlidingImg).toBeTruthy();
//error: expected null to be truthy
}, 2500, null);
scheduler.flush();
}));
i got this approach from this question.
so I desperately need to know how exactly should I simulate time passage in my unit test so that the component's observable time interval would really trigger...
versions:
angular: "2.4.5"
"rxjs": "5.0.1"
"jasmine": "~2.4.1"
"karma": "^1.3.0"
"typescript": "~2.0.10"
"webpack": "2.2.1"
fakeAsync
andtick(/*some second*/)
which is the first solution that i mentioned and is not working for me... – Sell