'How to test keydown keystrokes for a directive on a textbox?

I have a directive that I created that limits the input to be from 1 - 5 and limits the value to only have one decimal point. I am trying to unit test this, but I get a failed test when I try to inject a keydown event--Expected '' to be '1'. So it is as if there were no keys entered for the test. I feel like I am missing something simple. I am new to Angular and unit-testing.

limited-value.directive.spec.ts

import { Component } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LimitedValueDirective } from './limited-value.directive';

@Component({
  selector: 'app-test-container',
  template: `
  <div>
    <label for="limitedValue">Limited Value</label>
    <input id="limitedValue" type="text" class="form-control" formControlName="LimitedValue" appLimitedValue>
  </div>
  `
})
class MyComponent { }
let fixture: ComponentFixture<MyComponent>;

beforeEach(async () => {

  await TestBed.configureTestingModule({
    declarations: [ MyComponent, LimitedValueDirective ],
  }).compileComponents();

  fixture = TestBed.createComponent(MyComponent);
  fixture.detectChanges();
 });

 describe('LimitedValueDirective', () => {
  it('should be empty', () => {
    let inputElement:HTMLInputElement = fixture.debugElement.nativeElement.querySelector('input[type=text]');
    expect(inputElement.value).toBe('');
  });

  it('should be one digit', async () => {
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      let inputElement:HTMLInputElement = fixture.debugElement.nativeElement.querySelector('input[type=text]');

      const event = new KeyboardEvent("keydown", {
        "key": "1"
      });
      inputElement.dispatchEvent(event);

      fixture.detectChanges();
      expect(inputElement.value).toBe('1');
    });
  });
});

limited-value.directive.ts

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

@Directive({
  selector: '[appLimitedValue]'
})
export class LimitedValueDirective {
  @Input() value: string;
  private regex: RegExp = new RegExp(/^[1-4](\.[0-9])?$|^[1-5]\.$|^5$|^5\.0$/g);
  private el: HTMLInputElement;
  private specialKeys: Array<string> = [
    'Backspace',
    'Tab',
    'End',
    'Home',
    'ArrowLeft',
    'ArrowRight',
    'Del',
    'Delete',
  ];

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

  ngOnInit() {}

  @HostListener('keydown', ['$event'])
  onKeyDown(event: KeyboardEvent) {
    if ((this.specialKeys.indexOf(event.key) !== -1) || (event.ctrlKey && (event.key == 'v' || event.key == 'c'))) {
      return;
    }

    let current: string = this.elementRef.nativeElement.value;
    const position = this.elementRef.nativeElement.selectionStart;
    const next: string = [
      current.slice(0, position),
      event.key == 'Decimal' ? '.' : event.key,
      current.slice(position),
    ].join('');

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


Solution 1:[1]

Tim,

In addition to adding the new spec file to the test-files.ts file, it looks like your directive wasn't setting the value of the input. I'm not sure if this is exactly what you are after, but I made a small adjustment here: https://stackblitz.com/edit/angular-ivy-gnwbb9?file=src/app/limited-value.directive.spec.ts

The directive now sets the value of the input and I added another test for pressing 6.

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 Jake Smith