Search code examples
angularunit-testingtesting

angular testing click event inside loop ngFor


I'm trying to test the intention of the click event inside an *ngFor but I can't make the test pass.

this is my template:

<div class="container-notification-item" *ngFor="let item of menu.detail">
  <div class="notification-item" (click)="getDetailsMessage(item)">
    <div class="notification-item__icon" [ngClass]="item.iconStyle">
        <span>
          <i class={{item.icon}}></i>
        </span>
    </div>
    <div class="notification-item__text text-muted">
        <h6 [attr.class]="item.textStyle" [ngStyle]="{'font-size':'1.2rem' }">{{item.title}}</h6>
        <div>
            <p *ngIf="item.detail" class="detail-title">{{item.detail}}</p>
            <p *ngIf="item.time" class="detail-time">
              <i class="mdi mdi-clock-outline"></i>
              <span>{{item.time}}</span>
            </p>
        </div>
    </div>
  </div>
</div>

this is my component:

export class AccountMenuComponent implements DropdownMenuComponentInterface {

  @Input() menu: DropdownMenu;
  redirect = false;
  constructor() { }

  getDetailsMessage(item?: DropdownMenuDetail) {
    this.redirect = true;
  }

  getDetailsMessages() {
    console.log(this.menu.detail);
  }

}

and this is my test:

  it('must redirect to view all messages', () => {
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      const elem: DebugElement[] = fixture.debugElement.queryAll(By.css('.notification-item'));
      console.log(elem[0]);

      elem[0].triggerEventHandler('click', null);

      expect(component.redirect).toBeTruthy();
    })
  })

when I do a console.log of the elem variable I have the html element selected but the test fails.


Solution

  • I think the whenStable is throwing off when karma thinks the test has completed. I was able to get the following to run in a test project.

    it('should create the app', (done) => {
      // setup that you probably have in a beforeEach
      const fixture = TestBed.createComponent(AppComponent);
      const component = fixture.componentInstance;
      const menu = {detail: ['a','b','c']};
      // suggest a fake parent component - but for simplicity I'm just assigning
      component.menu = menu;
    
      fixture.detectChanges();
      fixture.whenStable().then(() => {
        const elem: DebugElement[] = fixture.debugElement.queryAll(By.css('.notification-item'));
        console.log(elem[0]);
    
        elem[0].triggerEventHandler('click', null);
    
        // since the value itself is `true`, suggest strongly testing for exactly that 
        // instead of truthy, which allows more passing conditions
        expect(component.redirect).toBe(true);
        // call the function passed in to tell it that this test is now done
        // because your code is in a promise it runs asynchronously
        done();
      })
    });
    

    Normally you only need whenStable for template forms to finish doing their thing. It can also be used to get past a debounceTime from rxjs, but I suggest using fakeAsync and tick in that case.