in my Angular component I have an object array of companies which is provided as via @Input(): When this is loaded (as it is derived from a HTTP request) I take the value and assign it to another variable called filteredList that I use for an *ngFor.
The *ngFor is used in a select menu and I have a @ViewChild which is an HTML Text Input that when a keyup event happens I take the value of the input and filter the filteredList. I am capturing the keyup event using rxJS fromEvent
To simplify this is my HTML
<input id="companiesInput" #companyInput>
<mat-selection-list (selectionChange)="listBoxChange($event)">
<mat-list-option
*ngFor="let company of filteredList"
[value]="company"
[checkboxPosition]="'before'">
{{company.displayName}}
</mat-list-option>
</mat-selection-list>
and here is some of my component code... all works well!!
@Input() companyList: any[] = [];
private tagsSubscription: Subscription;
filteredList: any[] = [];
ngOnChanges(changes: any): void {
if (changes.companyList.currentValue && changes.companyList.currentValue.length > 0) {
this.filteredList = this.companyList;
}
}
ngAfterViewInit(): void {
const tagsSource = fromEvent(this.companyInput.nativeElement, 'keyup').pipe(
debounceTime(250),
distinctUntilChanged(),
switchMap((ev: any) => {
return of(ev.target.value);
})
);
this.tagsSubscription = tagsSource.subscribe((res: any) => {
res = res.trim();
if (res.length > 0) {
this.filteredList = this.companyList.filter((company) => company.displayName.indexOf(res) > -1);
} else {
this.filteredList = this.companyList;
}
});
}
Everything works great, but I need to test my code and this is where I fall short. Here is my test
describe('tagsSubscription', () => {
it('should filter the available list of companies returned', () => {
const dummyArray = [
{ id: 1, displayName: 'saftey io' },
{ id: 2, displayName: 'msa' },
{ id: 3, displayName: 'acme' }
];
component.companyList = dummyArray;
fixture.detectChanges();
component.ngOnChanges({ companyList: { currentValue: dummyArray } });
const fromEventSpy = spyOn(rxjs, 'fromEvent').and.returnValue(() => rxjs.of({}));
const companiesInput = element.querySelector('#companiesInput');
element.querySelector('#companiesInput').value = 'ac';
companiesInput.dispatchEvent(generateKeyUpEvent('a'));
fixture.detectChanges();
companiesInput.dispatchEvent(generateKeyUpEvent('c'));
fixture.detectChanges();
expect(component.filteredList.length).toEqual(1);
});
});
From logging out I can see that in my test the fromEvent is never raised and thus tagsSource.subscribe is never ran. I am doing something wrong, I thought about mocking the fromEvent in my test by adding the following line to the top of my event, like so
const fromEventSpy = spyOn(rxjs, 'fromEvent').and.returnValue(() => rxjs.of('ac'));
this doesn't work either. Has anyone any experience of how to test fromEvent Observables with Angular and perhaps can tell me how I can get my test working?
Let's assume, if tagSource is a property of the component,
@Input() companyList: any[] = [];
private tagsSubscription: Subscription;
filteredList: any[] = [];
tagSource: any;
ngAfterViewInit(): void {
this.tagsSource = fromEvent(this.companyInput.nativeElement, 'keyup').pipe(
debounceTime(250),
distinctUntilChanged(),
switchMap((ev: any) => {
return of(ev.target.value);
})
);
}
To test this property, create a mock input element and assign it to the nativeElement:
it('ngAfterViewInit():', fakeAsync(() => {
const mockInputElement = document.createElement('input');
component.companyInput = {
nativeElement: mockInputElement
};
component.ngAfterViewInit();
mockInputElement.value = 'example' // pass the input to test
mockInputElement.dispatchEvent(new Event('keyup'));
tick(250); // pass the debounceTime
expect(component.tagSource).toEqual('example');
}));
You can use this logic to test your component.
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