'How to unit-test a valuechange of mat-autocomplete filtered search result in Angular?

I use the mat-autocomplete functionality for searching in projects and want to test if entering a search string, which cannot be found in the project list, leads to a zero result.

It's working fine in the browser, but I cannot get the test working.

Template:

<div class="sidebar__header--count" data-test-sidebar-project-count>
  <span>Projects</span> ({{ (filteredProjects | async).length }})
</div>
...
<div class="searchbar">
  <mat-form-field class="searchbar__input">
    <input
      matInput
      data-test-search-input
      [formControl]="searchControl" />
  </mat-form-field>
</div>

<div class="nav__list" *ngIf="(filteredProjects | async).length !== 0">
  <mat-nav-list>...output...</mat-nav-list>
</div>

Component:

...

export class SidebarComponent {
  @Input() projects: ProjectData[];

  searchControl: FormControl = new FormControl();
  filteredProjects: Observable<ProjectData[]>;

  constructor() {
    this.filteredProjects = this.searchControl.valueChanges.pipe(
      startWith(''),
      map(project =>
        project ? this.filterProjects(project) : this.projects.slice()
      )
    );
  }

  private filterProjects(value: string): ProjectData[] {
    return this.projects.filter(project =>
      [project.key, project.name].some(
        str => str?.toLowerCase().indexOf(value?.toLowerCase().trim()) >= 0
      )
    );
  }
}

Test:

...

it('should reduce initial project list based on search criteria', done => {
  /* given */
  component.projects = [
    {
      name: 'FooProject1',
      key: 'FOO1'
    },
    {
      name: 'FooProject2',
      key: 'FOO2'
    }
  ] as any;

  fixture.detectChanges();
  /* when */
  component.searchControl.setValue('bla');
  fixture.detectChanges();
  /* then */
  component.filteredProjects.subscribe(result => {
    expect(result.length).toBe(0);
    done();
  });

In the test the filteredProjects doesn't get reduced to zero and stays at a length of 2.

I also tried a different approach with checking for a string in the HTML like this:

/* then */
const sidebarProjectCountElement = fixture.debugElement.nativeElement.querySelector('[data-test-sidebar-project-count]');
expect(sidebarProjectCountElement).toContainText('(0)');

But this also doesn't work.



Solution 1:[1]

The reason you are not able to get filtered projects is because youare setting the subscription AFTER you are setting searchControl's value.

I was also facing similar challenge and below is how I fixed it.

it('should reduce initial project list based on search criteria', () => {
  /* given */
  component.projects = [
    { name: 'FooProject1', key: 'FOO1' },
    { name: 'FooProject2', key: 'FOO2' }
  ] as any;

  fixture.detectChanges();
  let output;
  /* first of all, set the subscription in test */
  component.filteredProjects.subscribe(result => {
    output = result;
  });

  component.searchControl.setValue('bla');
  fixture.detectChanges();
  /* then */
  expect(output.length).toEqual(0);
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Paritosh