I'm testing a component which is using an observable-based service to get some data and show it for i18n purpose.
The i18n service is custom because of a specific need.
The component works in dev mode (used in some templates, works fine) but the test fails.
@Component({
selector : "i18n",
template : '<span [innerHTML]="text"></span><span #wrapper hidden="true"><ng-content></ng-content><span>',
encapsulation: ViewEncapsulation.None
})
export class I18nComponent implements OnChanges {
constructor(private i18n:I18n) {
}
@ViewChild('wrapper')
content:ElementRef;
@Input('key')
key:string;
@Input('domain')
domain:string;
@Input('variables')
variables:Variables = [];
@Input("plural")
plural:number;
text:string;
ngOnChanges():any {
this.i18n.get(this.key, this.content.nativeElement.innerHTML, this.variables, this.domain).subscribe((res) => {
this.text = res;
});
}
}
public get(key:string,
defaultValue?:string,
variables:Variables = {},
domain?:string):Observable<string>{
const catalog = {
"StackOverflowDomain":
{
"my-key":"my-value"
}
};
return Observable.of(catalog[domain][key]).delay(300);
}
with Variables
:
export interface Variables {
[key:string]:any;
}
describe("I18n component", () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers : [
I18n,
{
provide : I18N_CONFIG,
useValue: {
defaultLocale : "fr_FR",
variable_start: '~',
variable_end : '~'
}
},
{
provide : I18N_LOADERS,
useClass: MockLocaleLoader,
multi : true
}
],
declarations: [
I18nComponent
]
});
fixture = TestBed.createComponent<I18nComponent>(I18nComponent);
comp = fixture.componentInstance;
});
fit("can call I18n.get.", fakeAsync(() => {
comp.content.nativeElement.innerHTML = "nope";
comp.key = "test";
comp.domain = "test domain";
comp.ngOnChanges();
tick();
fixture.detectChanges();
expect(comp.text).toBe("test value");
}));
});
The test fails with message :
Expected undefined to be 'test value'.
Error: 1 periodic timer(s) still in the queue.
Because i18n.get
did not finish its work before assertion has been checked, so comp.text is still undefined
.
tick
method call, changed nothing (tried with 5000).ngOnChanges
return a Promise<void>
that resolves right after this.text = res;
and change fakeAsync
zone for a simple test using a done
method caled in then
of comp.ngOnChanges
. It works, but ngOnChanges
should not return a Promise
and I want a clean solution.Note that async
and fakeasync
are not as powerful and inclusive as jasmine.done
Taking the exact note from (at the time of this writing): https://angular.io/guide/testing#component-fixture
It says:
Writing test functions with done, while more cumbersome than async and fakeAsync, is a viable and occasionally necessary technique. For example, you can't call async or fakeAsync when testing code that involves the intervalTimer, as is common when testing async Observable
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