简体   繁体   English

带表格的单元测试角度指令

[英]Unit test angular directive with a form

I have a directive which has a form property. 我有一个具有form属性的指令。 The directive is put on the submit button for the form, and listens to click events. 指令放置在表单的“提交”按钮上,并侦听单击事件。 When the submit button is clicked, the directive checks if the form is valid and prevents the click event bubbling up to the ngSubmit handler if it's not. 单击提交按钮后,该指令将检查表单是否有效,如果不是,则阻止click事件冒泡到ngSubmit处理程序。 It also marks each form control as dirty so validation messages show. 它还将每个表单控件标记为脏,以便显示验证消息。

The directive works fine and I'd like to add unit tests, but I can't work out how to set up a form containing a submit button. 该指令可以正常工作,我想添加单元测试,但是我不知道如何设置包含提交按钮的表单。 This is my test as it stands so far, but I can't work out how to relate the NgForm which the directive takes with the fake form I've created which contains the button. 到目前为止,这是我的测试,但是我不知道如何将指令采用的NgForm与我创建的包含按钮的伪造表单相关联。

describe('ValidateBeforeSubmitDirective', () => {

    let fakeSubmitButtonRef: ElementRef<HTMLButtonElement>;
    let fakeForm: HTMLFormElement;

    beforeEach(() => {
        const fakeSubmitButton = document.createElement('BUTTON') as HTMLButtonElement;
        fakeSubmitButton.type = 'submit';
        fakeSubmitButtonRef = new ElementRef(fakeSubmitButton);

        fakeForm = document.createElement('form');
        fakeForm.appendChild(fakeSubmitButton);
    });

    it('should bubble click event to the submit method if form is valid', () => {
        //arrange
        const directive = new ValidateBeforeSubmitDirective(fakeSubmitButtonRef);
        directive.form = new NgForm([], []);
        spyOn(directive.form, 'ngSubmit');
        expect(directive.form.valid).toBe(true, 'Test has been set up incorrectly, the form should be valid for this test.');

        //*** What do I need to do to link my NgForm with fakeForm? ***

        //act
        fakeSubmitButtonRef.nativeElement.click();

        //assert
        expect(directive.form.ngSubmit).toHaveBeenCalled();
    });
});

For reference, this is my directive code: 供参考,这是我的指令代码:

@Directive({
    selector: '[appValidateBeforeSubmit]'
})
export class ValidateBeforeSubmitDirective {

    /**
     * @param element This will be the element on which the directive is being used.
     */
    constructor(private readonly element: ElementRef<HTMLButtonElement>) {  }

    @Input('appValidateBeforeSubmit')
    form: NgForm;

    @HostListener('click', ['$event'])
    private onClick(event: Event) {

        if (!this.form.valid) {
            Object.keys(this.form.controls).forEach(key => {
                this.form.controls[key].markAsDirty();
            });
        }

        return this.form.valid; //if false, this will prevent the event from bubbling up to the ngSubmit handler
    }
}

It is used as follows: 它的用法如下:

<form #componentTypeForm="ngForm" (ngSubmit)="ok()">
    <button type="submit" [appValidateBeforeSubmit]="componentTypeForm">Submit</button>
</form>

Any ideas how I can set up a form within my test so that the button click event bubbles up to the form's ngSubmit handler? 有什么想法可以在测试中设置表单,以便按钮click事件上升到表单的ngSubmit处理程序吗?

Someone did post an answer which put me on the right lines, but they've sadly deleted their answer so I can't give them credit. 有人确实发布了一个答案,使我处于正确的位置,但可悲的是他们删除了答案,所以我不能给他们功劳。 They pointed out that directives should be tested using a dummy component which is defined within the test file. 他们指出, 应该使用测试文件中定义的虚拟组件来测试指令 I have created a component whose template is a form with an input, and I have been able to test the behaviour of the directive by testing the component. 我创建了一个组件,其模板是带有输入的表单,并且已经能够通过测试该组件来测试指令的行为。

This is my working test code: 这是我的工作测试代码:

import { NgForm, FormsModule } from '@angular/forms';
import { Component } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { ValidateBeforeSubmitDirective } from './validate-before-submit.directive';

@Component({
    //The input on this form is required, so we can easily set the form validity by giving it an empty/non-empty string.
    template: `<form #testForm="ngForm">
                   <input name="providedValue" [(ngModel)]="providedValue" type="text" required />
                   <button type="submit" [appValidateBeforeSubmit]="testForm">Submit</button>
               </form>`
})
class TestValidateBeforeSubmitComponent {
    providedValue: string;
}

describe('ValidateBeforeSubmitDirective', () => {
    let form: NgForm;
    let fixture: ComponentFixture<TestValidateBeforeSubmitComponent>;

    beforeEach(() => {
        fixture = TestBed.configureTestingModule({
            declarations: [ValidateBeforeSubmitDirective, TestValidateBeforeSubmitComponent],
            imports: [FormsModule],
            providers: [NgForm, HTMLButtonElement]
        }).createComponent(TestValidateBeforeSubmitComponent);

        const formElement = fixture.debugElement.children[0];
        form = formElement.injector.get(NgForm);

        fixture.detectChanges(); // initial binding
    });

    it('should allow submission of a valid form', async(() => {
        //arrange
        fixture.componentInstance.providedValue = 'Arbitrary content'; //valid value
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(form.valid).toBe(true, 'Test has been set up incorrectly, form should be valid.');

            //act
            const buttonElement = fixture.debugElement.children[0].children[1];
            const button = buttonElement.nativeElement;
            button.click();

            //assert
            expect(form.submitted).toBe(true, 'Clicking the submit button on a valid form should have submitted the form.');
        });
    }));

    it('should prevent submission of an invalid form', async(() => {
        //arrange
        fixture.componentInstance.providedValue = ''; //invalid value
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(form.valid).toBe(false, 'Test has been set up incorrectly, form should be invalid.');

            //act
            const buttonElement = fixture.debugElement.children[0].children[1];
            const button = buttonElement.nativeElement;
            button.click();

            //assert
            expect(form.submitted).toBe(false, 'The directive should prevent an invalid form from submitting when the submit button is clicked.');
        });
    }));
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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