Search code examples
angularunit-testingkarma-jasmineangular2-directivesmouseenter

Unit testing Directive with arguments in Angular 2+


I have a directive which changes background color of an element during mouse over as below.

import {Directive, ElementRef, HostListener, Input} from '@angular/core';

@Directive({
  selector: '[appHighlightme]'
})
export class HighlightmeDirective {

  @Input('appHighlightme') color : string
  constructor(private el:ElementRef) { }

  @HostListener('mouseenter') onMouseEnter(){
    this.highlight(this.color || 'yellow');
  }

  @HostListener('mouseleave') onMouseLeave(){
    this.highlight('inherit');
  }

  highlight(color){
    this.el.nativeElement.style.backgroundColor = color;
  }
}

I was trying to write a unit test case for this directive as below

import { HighlightmeDirective } from './highlightme.directive';
import {ChangeDetectionStrategy, Component, DebugElement, ViewChild} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';

@Component({
  selector: 'my-test-component',
  template: '<a [appHighlightme]="color" >test</a>'
})
export class TestComponent {
  color:string = 'blue';
}

describe('HighlightmeDirective', () => {

  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;
  let inputEl: DebugElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestComponent,
        HighlightmeDirective
      ]
    })

    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    inputEl = fixture.debugElement.query(By.directive(HighlightmeDirective));

  });

  it('detect hover changes', () => {
    inputEl.triggerEventHandler('mouseenter', {});
    fixture.detectChanges();
    expect(inputEl.nativeElement.style.backgroundColor).toBe(component.color);
    inputEl.triggerEventHandler('mouseleave', {});
    fixture.detectChanges();
    expect(inputEl.nativeElement.style.backgroundColor).toBe('inherit');
  });

  it('should create an instance', () => {
    const directive = new HighlightmeDirective(inputEl);
    expect(directive).toBeTruthy();
  });
});

The directive is working fine in other components. it accepts a color argument variable defined in the ts file and use it as the bg color when hovering the element. But when I'm trying to unit test the same, the color argument passed form the TestComponent is not getting detected by the directive and the test case is failing with the following error message.

Error: Expected 'yellow' to be 'blue'. - As yellow is set as the default hover color


Solution

  • You do not need to track event handlers over the directive. You need to emit an event on the native element (anchor tag in your case) and the directive handlers will be called automatically. This is the actual way of testing a directive.

    Let say we have an anchor tag with a class, so we need to create an event on this tag.

    @Component({
      selector: 'my-test-component',
      template: '<a class="mytag" [appHighlightme]="color" >test</a>'
    })
    export class TestComponent {
      color:string = 'blue';
    }
    
    describe('HighlightmeDirective', () => {
    
      let component: TestComponent;
      let fixture: ComponentFixture<TestComponent>;
      let inputEl: DebugElement;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          declarations: [
            TestComponent,
            HighlightmeDirective
          ]
        })
    
        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();   // trigger change detection so that UI renders and you can access element in next step.
        inputEl = fixture.debugElement.query(By.css('.mytag'));
    
      });
    
      it('detect hover changes', () => {
        inputEl.triggerEventHandler('mouseenter', {});
        fixture.detectChanges();
        expect(inputEl.nativeElement.style.backgroundColor).toBe(component.color);
        inputEl.triggerEventHandler('mouseleave', {});
        fixture.detectChanges();
        expect(inputEl.nativeElement.style.backgroundColor).toBe('inherit');
      });
    
      it('should create an instance', () => {
        const directive = new HighlightmeDirective(inputEl);
        expect(directive).toBeTruthy();
      });
    });
    

    I hope it will help you.