Angular2 +集成单元测试:如何通过"keydown"事件和"input"事件伪造用户在输入中输入内容 [英] Angular2+ integration unit-testing: How to fake user typing something into input, with 'keydown'-event AND 'input'-event

查看:171
本文介绍了Angular2 +集成单元测试:如何通过"keydown"事件和"input"事件伪造用户在输入中输入内容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要测试输入元素对用户的真实"键入.要对我的number.component和我的number-only.directive组合是否仅接受数字输入进行单元测试.
问题是ngModel没有在"keydown"上更新( KeyboardEvent ),但需要触发指令.
输入事件需要在分发nativeElement之前设置其值,这会跳过该指令.

I want to test "real" typing of an user for my input element. To unit-test if my number.component in combination with my number-only.directive only accepts numeric inputs.
The problem is the ngModel isn't updated on 'keydown' (KeyboardEvent), but is needed so the directive is triggered.
The 'input' event requires to set the value of the nativeElement before dispatching it, which would skip the directive.

我已经试验过fakeAsync,tick和whenStable,但没有设法重新创建实际用户在输入字段中键入的流程.

I already experimented with fakeAsync, tick and whenStable, but didn't manage to recreate the flow of an actual user typing into the input field.

number.component.html

number.component.html

<input numberOnly class="number-input ml-2 mr-2" type="text" [(ngModel)]="value">

number-only-directive.ts

number-only-directive.ts

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

@Directive({
    selector: '[NumberOnly]'
})
export class NumberOnlyDirective {

    // Allow decimal numbers. The \. is only allowed once to occur
    private regex: RegExp = new RegExp(/^[0-9]+(\.[0-9]*){0,1}$/g);

    // Allow key codes for special events. Reflect :
    // Backspace, tab, end, home
    private specialKeys: Array<string> = ['Backspace', 'Tab', 'End', 'Home'];

    constructor(private el: ElementRef) {
    }

    @HostListener('keydown', ['$event'])
    onKeyDown(event: KeyboardEvent) {
        // Allow Backspace, tab, end, and home keys
        if (this.specialKeys.indexOf(event.key) !== -1) {
            return;
        }

        // Do not use event.keycode this is deprecated.
        // See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
        const current: string = this.el.nativeElement.value;
        // We need this because the current value on the DOM element
        // is not yet updated with the value from this event
        const next: string = current.concat(event.key);
        if (next && !String(next).match(this.regex)) {
            event.preventDefault();
        }
    }

}

number.component.spec.ts(不仅仅用于了解我想要实现的目标)

number.component.spec.ts (not working just to get an idea what I want to achieve)

it('should prohibit non-numeric input and keep the value 1', fakeAsync(() => {
    const numberDebug = fixture.debugElement.query(By.css('.number-input'));
    const numberInput = numberDebug.nativeElement as HTMLInputElement;
    numberDebug.triggerEventHandler('keydown', { bubbles: true, key: '1' });
    // numberInput.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: '1' }));
    tick();
    fixture.detectChanges();
    expect(component.value).toEqual(1);
    expect(numberInput.value).toEqual('1');

    const eventMock = new KeyboardEvent('keydown', { key: 'a' });
    numberInput.dispatchEvent(eventMock);
    tick();
    // somehow check if event passed the directive      
    // if so fire 'input' event
    fixture.detectChanges();
    expect(component.value).toEqual(1);
    expect(numberInput.value).toEqual('1');
}));

推荐答案

我找到了解决方案.
缺少该事件可以取消的事实(感谢).

I found a solution.
Was missing the fact that the event should be cancelable (thanks to this).

解决此问题后,已为每个KeyboardEvent正确设置了event.defaultPrevented属性,从而导致此正确的功能测试:

After fixing this, the event.defaultPrevented property was correctly set for every KeyboardEvent, resulting in this correct functioning test:

describe('NumberComponent', () => {
    let component: NumberComponent;
    let fixture: ComponentFixture<NumberComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [NumberComponent, NumberOnlyDirective],
            imports: [FormsModule]
        })
            .compileComponents();
    }));

    beforeEach(async(() => {
        fixture = TestBed.createComponent(NumberComponent);
        component = fixture.componentInstance;

        fixture.detectChanges();
    }));

    it('should prohibit non-numeric input', () => {
        let numberDebug = fixture.debugElement.query(By.css('.number-input'));
        let numberInput = numberDebug.nativeElement as HTMLInputElement;

        fakeTyping('12abc34de', numberInput);

        expect(numberInput.value).toBe('1234');
    });

    function fakeTyping(value: string, inputEl: HTMLInputElement) {
        let result: string = '';
        for (let char of value) {
            let eventMock = createKeyDownEvent(char);
            inputEl.dispatchEvent(eventMock);
            if (eventMock.defaultPrevented) {
                // invalid char
            } else {
                result = result.concat(char);
            }
        }

        inputEl.value = result;
        inputEl.dispatchEvent(new Event('input'));
        fixture.detectChanges();
    }
});

export function createKeyDownEvent(value: string, cancelable = true) {  
    return new KeyboardEvent('keydown', { key: value, cancelable })  
}  

这篇关于Angular2 +集成单元测试:如何通过"keydown"事件和"input"事件伪造用户在输入中输入内容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆