Search code examples
angularrxjsangular-test

Angular 7 testing - simulate input event with debounce time


Before starting this question. I'm aware, that there are a lot of similar asked questions liked mine. But no solution was able to help me out.

I created a custom auto-complete with rxjs and want to test if a method gets called on a input event. But the error says the method never got called, like:

 Expected spy CityService.getLocation to have been called with [ 'mun' ] but it was never called.

html

I subscribe to my observable in the HTML via the async pipe.

      <input type="text" [(ngModel)]="location" class="form-control" id="locationSearchInput"/>
      <div class="spacer">
        <p class="invalid-feedBack" *ngIf="searchFailed && location.length > 0">Nothing found.</p>
        <ul id="search" *ngFor="let item of (search | async)">
          <li class="resultItem" type="radio" (click)="location = item">{{item}}</li>
      </ul>
    </div>

component

      ngOnInit(): void {
        this.search = fromEvent(document.getElementById('locationSearchInput'), 'input').pipe(
          debounceTime(750),
          distinctUntilChanged(),
          map((eventObj: Event) => (<HTMLInputElement>eventObj.target).value),
          switchMap((term: string) => this.cityService.getLocation(term)) <== should get called
        );
      }

test

      const cityServiceStub: CityService = jasmine.createSpyObj('CityService', ['getLocation']);
      ...
      it('should search for location on init', async(() => {
        const spy = (<jasmine.Spy>cityServiceStub.getLocation).and.returnValue(['Munich', 'Münster']);

        fixture.detectChanges();

        const rendered: DebugElement = fixture.debugElement.query(By.css('#locationSearchInput'));

        rendered.nativeElement.value = 'mun';
        rendered.nativeElement.dispatchEvent(new Event('input'));

        fixture.detectChanges();

        fixture.whenStable().then(() => {
          console.log(rendered.nativeElement.value);
          expect(spy).toHaveBeenCalledWith('mun');
        });
      }));

I've also tried to use fakeAsync with tick(750). But nothing helped. The console.log within the test also shows an empty string as input value. So maybe I'm simulating a wrong Event.


Solution

  • My test worked with following configuration:

      it('should search for location on init', fakeAsync(() => {
        const spy = (<jasmine.Spy>cityServiceStub.getLocation).and.returnValue(of(['Munich', 'Münster']));
    
        fixture.detectChanges();
    
        const rendered: DebugElement = fixture.debugElement.query(By.css('#locationSearchInput'));
    
        rendered.nativeElement.value = 'mun';
        rendered.nativeElement.dispatchEvent(new Event('input'));
    
        tick(750);
        fixture.detectChanges();
    
        expect(spy).toHaveBeenCalledWith('mun');
      }));
    

    I missed just to return ['Munich', 'Münster'] as an Observable with the of() operator. And I should have used tick(750) to wait the specific amount of debounce time for an event change.