在 Angular 单元测试中使用回车键提交表单

Posted

技术标签:

【中文标题】在 Angular 单元测试中使用回车键提交表单【英文标题】:Submitting form with enter key in Angular unit test 【发布时间】:2018-02-27 14:32:19 【问题描述】:

我正在为作为登录表单的 Angular 4 组件编写测试。可以通过单击“提交”按钮或在任何输入字段中按 Enter 来提交表单。此行为由 Angular 表单指令规定。

我可以编写一个测试用例来验证按钮单击是否提交表单,但我无法通过按键事件触发提交行为。

模板:

<form (ngSubmit)="onLoginSubmit()" #loginForm="ngForm">
<div class="form-group">
    <label for="userid">User ID</label>
    <input type="text" class="form-control" name="userid" id="userid" required
        [(ngModel)]="model.userId" #userid="ngModel">
    <div [hidden]="userid.valid || userid.untouched" class="alert alert-danger">
        User ID is required
    </div>
</div>
<div class="form-group">
    <label for="password">Password</label>
    <input type="password" class="form-control" name="password" id="password" required
        [(ngModel)]="model.password" #password="ngModel">
    <div [hidden]="password.valid || password.untouched" class="alert alert-danger">
        Password is required
    </div>
</div>
<button type="submit" class="btn btn-success" [disabled]="loginForm.form.invalid">Submit</button>    

规格:

import  ComponentFixture, TestBed  from '@angular/core/testing';
import  By  from '@angular/platform-browser';
import  DebugElement, Component, ViewChild  from '@angular/core';
import  FormsModule, ReactiveFormsModule  from '@angular/forms';
import  Observable  from 'rxjs/Observable';

import  LoginFormComponent  from './login-form.component';
import  ILoginService  from '../../service/ILoginService';
import  IAuthService  from '../../service/IAuthService';


describe('Login Form', () => 
    let comp: LoginFormComponent;
    let fixture: ComponentFixture<LoginFormComponent>;
    let userIdElement: DebugElement;
    let passwordElement: DebugElement;
    let submitElement: DebugElement;

    beforeEach(() => 
        TestBed.configureTestingModule(
            imports: [FormsModule, ReactiveFormsModule],
            declarations: [LoginFormComponent],
            providers: [
                 provide: 'IloginService', useClass: UserServiceMock ,
                 provide: 'IAuthService', useClass: MockAuthService ]
        );
        fixture = TestBed.createComponent(LoginFormComponent);

        comp = fixture.componentInstance;

        userIdElement = fixture.debugElement.query(By.css('input[name=userid]'));
        passwordElement = fixture.debugElement.query(By.css('input[name=password]'));
        submitElement = fixture.debugElement.query(By.css('button'));
    );

    describe('Submit', () => 
        let authService: IAuthService;
        let authServiceSpy: jasmine.Spy;
        let loginService: ILoginService;
        let loginServiceSpy: jasmine.Spy;

        beforeEach(() => 
            comp.model.userId = 'mock user';
            comp.model.password = 'mock password';
            comp.loginUrl = 'mock url';

            authService = fixture.debugElement.injector.get('IAuthService');
            authServiceSpy = spyOn(authService, 'login').and.returnValue(null);

            loginService = fixture.debugElement.injector.get('IloginService');
            loginServiceSpy = spyOn(loginService, 'handleLoginResult');
        );

        it('should invoke the auth and login services when submit is clicked', () => 
            submitElement.nativeElement.click();
        );

        xit('should submit the form on enter key pressed in userId input', () => 
            userIdElement.nativeElement.dispatchEvent(new KeyboardEvent('keydown',  key: 'Enter' ))
        );

        xit('should submit the form on enter key pressed in password input', () => 
            passwordElement.nativeElement.dispatchEvent(new KeyboardEvent('keydown',  key: 'Enter' ))
        );

        afterEach(() => 
            fixture.detectChanges();
            fixture.whenStable().then(() => 
                expect(authService.login).toHaveBeenCalledWith('mock user', 'mock password', 'mock url');
                expect(loginService.handleLoginResult).toHaveBeenCalled();
            );
        );
    );
);

从按钮分派“click”事件的测试通过,但从输入元素分派 keydown 事件的测试(当前已禁用)失败。

我可以分派不同的事件来触发表单的 ngSubmit 处理程序吗?

【问题讨论】:

你找到答案了吗? 【参考方案1】:

(迟到的答案,但我在这里没有看到被接受的答案) 仅调度事件是不够的,您还必须将事件发送到组件。所以在你的例子中,你只有一个表单提交,所以你 需要打电话:

component.onLoginSubmit();

但是要分离出逻辑,也许有一个主机侦听器正在为您工作:

  @HostListener('keypress.enter', ['$event'])
  keypressEnter(event: KeyboardEvent) 
     // Maybe some further logic due to keys? Validation?
     this.onLoginSubmit();
  

那么在你的测试中你还必须调用:

component.keypressEnter(new KeyboardEvent('keypress',  key: 'Enter' ));

最后,fixture.detectChanges() 将完成占空比。

单独调用 dispatch 事件是行不通的。

【讨论】:

【参考方案2】:

尝试使用keypress 而不是'keydown`

new KeyboardEvent('keypress',  key: 'Enter' )

【讨论】:

感谢您的建议,但 'keypress' 并不比 'keydown' 或 'keyup' 好用

以上是关于在 Angular 单元测试中使用回车键提交表单的主要内容,如果未能解决你的问题,请参考以下文章

无法在机器人框架测试中提交表单

麻烦单元测试角度的反应性表单字段

ag-grid-angular 和单元格验证

如何防止HTMLFormElement.submit()调用表单提交[重复]。

Angular 2 - 单元测试绑定到嵌套的自定义表单控件

使用回车键阻止表单提交