简体   繁体   中英

Angular4 - how to unit test a directive using Karma and Jasmine

I have created a very simple directive to use on input elements, that should only allow entry of a decimal number (numeric with single decimal point).

The directive is defined as follows:

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

// Directive attribute to stop any input, other than a decimal number.
@Directive({
    selector: '[decimalinput]'
})
export class DecimalInputDirective {

    constructor(private element : ElementRef) { }

    // Hook into the key press event.
    @HostListener('keypress', ['$event']) onkeypress( keyEvent : KeyboardEvent ) : boolean {

        // Check if a full stop already exists in the input.
        var alreadyHasFullStop = this.element.nativeElement.value.indexOf('.') != -1;

        // Get the key that was pressed in order to check it against the regEx.
        let input = String.fromCharCode(keyEvent.which);

        // Test for allowed character using regEx. Allowed is number or decimal.
        var isAllowed = /^(\d+)?([.]?\d{0,2})?$/.test( input );

        // If this is an invlid character (i.e. alpha or symbol) OR we already have a full stop, prevent key press.
        if (!isAllowed || (isAllowed && input == '.' && alreadyHasFullStop)){
            keyEvent.preventDefault();
            return false;
        }

        return true;
    }
}

This directive should allow "123.123" not "abc" , nor "1.2.1" . Now I want to test this directive, reading online, I've come up with this so far:

import { Component, OnInit, TemplateRef,DebugElement, ComponentFactory, ViewChild, ViewContainerRef } from '@angular/core';
import { TestBed, ComponentFixture } from '@angular/core/testing';
import { DecimalInputDirective } from './decimalinput.directive';
import { By } from '@angular/platform-browser';

@Component({
    template: `<input type="text" name="txtDecimalTest" decimalinput>` 
})
class TestDecimalComponent { }


describe('Directive: DecimalInputDirective', () => {

    let component: TestDecimalComponent;
    let fixture: ComponentFixture<TestDecimalComponent>;
    let decimalInput: DebugElement;

    beforeEach(() => {

      TestBed.configureTestingModule({
        declarations: [TestDecimalComponent]
      });

      fixture = TestBed.createComponent(TestDecimalComponent);
      component = fixture.componentInstance;
      decimalInput = fixture.debugElement.query(By.css('input[name=txtDecimalTest]'));
    });

    it('Entering email and password emits loggedIn event', () => {

        // This sets the value (I can even put "abc" here and it will work.
        decimalInput.nativeElement.value = "12345";

        // But I am trying to initialize the keypress event, so the character is tested in a real world way when the user is using.
        decimalInput.nativeElement.dispatchEvent(new KeyboardEvent("keypress", { key: "a" })); // Nothing happens here!  This was my attempt...

        // This 
        expect(decimalInput.nativeElement.value).toBe("12345");
    });
});

You can see from the code, the line:

decimalInput.nativeElement.dispatchEvent(new KeyboardEvent...

Is my attempt to simulate keypresses, as if the user was inputting. If I simulated a, then b, then c, then 1, then 2, then 3, I'd expect the test to make sure the value is only "123" and its ignored "abc" in the way the directive works.

Two questions - 1) is this the correct test I should be doing? 2) Whats wrong with my code - why is the simulated key press doing nothing?

Thanks for any pointers in advance! :)

Normally directive are tested in such a way that its being used in real component. So you can create a fake component which will use your directive and you can test that component to handle your directive.

This is the most people suggest.

So in your test file create a fake directive

// tslint:disable-next-line:data
@Component({
  selector: 'sd-test-layout',
  template: `
    <div sdAuthorized [permission]="'data_objects'">test</div>`
})
export class TestDecimalInputDirectiveComponent {

  @Input() permission;

  constructor() {
  }
}

Then in your before each using TestBed create the component instance, and now You are ready to apply mouse events and test them in real situation

TestBed.configureTestingModule({
  imports: [
    HttpModule,
    SharedModule
  ],
  declarations: [
    TestDecimalInputDirectiveComponent,
  ],
  providers: [
    {
      provide: ElementRef,
      useClass: MockElementRef
    },
    AuthorizedDirective,
  ]
}).compileComponents();

Just given you the hint. you can follow this link to get more information

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM