I'm upgrading our Angular2 app to use rc4 and I started getting an error on my unit tests:
Cannot use setInterval from within an async zone test
My widget makes a request for data from its ngOnInit
method and puts up a loading indicator while that request is made. My mocked service returns some data after 1ms.
Here's a simplified version that exposes the problem
import { inject, async, TestComponentBuilder, ComponentFixture} from '@angular/core/testing';
import {Http, Headers, RequestOptions, Response, HTTP_PROVIDERS} from '@angular/http';
import {provide, Component} from '@angular/core';
import {Observable} from "rxjs/Rx";
class MyService {
constructor(private _http: Http) {}
getData() {
return this._http.get('/some/rule').map(resp => resp.text());
}
}
@Component({
template: `<div>
<div class="loader" *ngIf="_isLoading">Loading</div>
<div class="data" *ngIf="_data">{{_data}}</div>
</div>`
})
class FakeComponent {
private _isLoading: boolean = false;
private _data: string = '';
constructor(private _service: MyService) {}
ngOnInit() {
this._isLoading = true;
this._service.getData().subscribe(data => {
this._isLoading = false;
this._data = data;
});
}
}
describe('FakeComponent', () => {
var service = new MyService(null);
var _fixture:ComponentFixture<FakeComponent>;
beforeEach(async(inject([TestComponentBuilder], (tcb:TestComponentBuilder) => {
return tcb
.overrideProviders(FakeComponent, [
HTTP_PROVIDERS,
provide(MyService, {useValue: service}),
])
.createAsync(FakeComponent)
.then((fixture:ComponentFixture<FakeComponent>) => {
_fixture = fixture;
});
})));
it('Shows loading while fetching data', (cb) => {
// Make the call to getData take one ms so we can verify its state while the request is pending
// Error occurs here, when the widget is initialized and sends out an XHR
spyOn(service, 'getData').and.returnValue(Observable.of('value').delay(1));
_fixture.detectChanges();
expect(_fixture.nativeElement.querySelector('.loader')).toBeTruthy();
// Wait a few ms, should not be loading
// This doesn't seem to be the problem
setTimeout(() => {
_fixture.detectChanges();
expect(_fixture.nativeElement.querySelector('.loader')).toBeFalsy();
cb();
}, 10);
});
});
This was working fine in Angular2 rc1 and it throws an error in rc4, any suggestions?
Also, there are no errors if you use a setTimeout
directly from the test itself
fit('lets you run timeouts', async(() => {
setTimeout(() => {
expect(1).toBe(1);
}, 10);
}));
I've found that for some reason, you cannot use a promise created with Observable.of(anything).delay()
in a test.
My solution for this was to implement that line myself, which almost makes sense considering the other example posted in the question did work.
// This is what we should be doing, but somehow, it isn't working.
// return Observable.of(result).delay(0));
function createDelayedObservable <T>(result:any, time:number = 0):Observable<T> {
return new Observable<T>(observer => {
setTimeout(() => observer.next(result), time);
});
}
However, I still don't understand why the following doesn't fail, so I'm not accepting my own answer in the hopes that someone with a solid understanding of zones can tell me what's happening.
it('should be able to use delay in tests', (cb) => {
var obs = Observable.of(1).delay(0);
obs.subscribe(val => {
expect(val).toBe(1);
cb()
});
});
I've encountered the same issue. And I was able to workaround it using the jasmine done
parameter.
fit('lets you run timeouts', (done) => {
async(() => {
setTimeout(() => {
expect(1).toBe(1);
done();
}, 10);
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With