Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing directive HostListener not working

I am attempting to unit test some functionality that does work on the front-end flawlessly but the unit test doesn't seem to be taking hold.

Below is my current unit test, it is not finished completely I am just trying to get the bare bones test to work before moving on to more complicated checks.

@Component({
  template : '<input appInputFilter filterType="alphanumericOnly" type="text" minlength="2">'
})
class TestComponent {

}

describe('InputFilterDirective', () => {

  let comp: TestComponent;
  let fixture: ComponentFixture<TestComponent>;
  let de: DebugElement;
 // const el: HTMLElement;

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

    fixture = TestBed.createComponent(TestComponent);

    comp = fixture.componentInstance;

    de = fixture.debugElement.query(By.directive(InputFilterDirective));
  });

  it('should create instance of inputFilter', () => {
    expect(comp).toBeTruthy();
    expect(de).toBeDefined();
  });

  it('should only allow onlyNumbers', async(() => {

    fixture.detectChanges();


    const keyEvent = new KeyboardEvent('keydown', {'code': 'KeyQ'});
    const input = de.nativeElement as HTMLInputElement;
    input.dispatchEvent(keyEvent);
    expect(input.value).toEqual('q');


    expect(true).toBeTruthy();
  }));
});

And here is my full directive:

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

@Directive({
  selector: '[appInputFilter]',

})
export class InputFilterDirective {

  private el : any;
  private specialKeys : Array<string> = ['Backspace', 'Tab', 'End', 'Home'];

  @Input() public filterType : any;

  constructor(el: ElementRef) {
    this.el = el;
   }

   @HostListener('keydown', ['$event'])
   onKeyDown(event: KeyboardEvent) {

    if (this.specialKeys.indexOf(event.key) !== -1) {
      return;
    }

    switch (this.filterType) {
      case 'onlyNumber':
        this.appFilter(event, new RegExp(/\D/g));
      break;
      case 'alphanumericOnly':
        this.appFilter(event, new RegExp(/\W/g));
      break;
      case 'alphabeticAndSpecial':
        this.appFilter(event, new RegExp(/[^a-zA-Z'-]/));
      break;
      case 'ipAddress':
        this.appFilter(event, new RegExp(/[^0-9.]/g));
      break;
    }
   };

  appFilter(event: KeyboardEvent, regex: RegExp) {
    const current: string = this.el.nativeElement.value;
    const next: string = current.concat(event.key);

    if (next && String(next).match(regex)) {
      event.preventDefault();
    }
  };
}

Additional notes:

  • I have attempted many different KeyEvents such as type key: '1' and such like that.
  • I have run the unit test as async and non async.
  • I have followed many different stack overflow instructions on how other people have attempted to unit test HostListeners and unfortunately this is simply not working for me and I have no idea why.

And help is very much appreciated, like I said this is a bare bones test, you will notice in the test component that I am using the filter to filter for alphanumeric (which you can check out in the directive code)

Also I am NOT opposed to listening for "preventDefault" to prove this test so if the issue is how I am detecting change then please let me know and I do not mind adjusting.

like image 608
Stephen Avatar asked Aug 22 '26 22:08

Stephen


1 Answers

I've had luck using triggerEventHandler on the directive element. For example,

it('should add "c2d-dropzone-active" class to element when dragenter has occurred', async done => {
  fixture.detectChanges();
  const el: any = element.query(By.directive(C2dDropzoneDirective));
  expect(el.nativeElement.className).not.toContain('c2d-dropzone-active');
  el.triggerEventHandler('dragenter', mockEvent);
  fixture.detectChanges();
  await fixture.whenStable();
  expect(el.nativeElement.className).toContain('c2d-dropzone-active');
  done();
});
like image 174
Chris Stoy Avatar answered Aug 25 '26 12:08

Chris Stoy