如何测试键盘输入在Angular中的按钮上?

时间:2018-11-06 15:50:25

标签: angular unit-testing

我正在研究可访问性非常重要的应用程序。作为其一部分,我们希望确保可以通过鼠标事件和键盘事件访问许多不同的组件。我们还尝试围绕这些要求设置单元测试,以确保对这些组件所做的任何更改仍将遵循这些标准。我们遇到了按钮问题。我有一个示例应用程序来演示:

app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  console = console;

  actionTaken($event) {
    this.console.log($event);
  }
}

app.component.html

<!-- <button
  (click)="actionTaken($event)"
  (keyup.enter)="actionTaken($event)">
    Take Action
</button> -->

<button
  (click)="actionTaken($event)">
    Take Action
</button>

该html有两个按钮,一个在侦听单击事件,另一个在侦听单击事件和Enter键输入事件。

带有两个事件侦听器的那个是我认为我需要使用的那个,但是当我使用那个侦听器时,我可以看到当我按下按钮上的Enter时,事件被触发两次,一次是作为键另一个点击。

具有一个事件侦听器的事件侦听器,当我使用该事件侦听器时,可以看到单击按钮上的Enter时,该事件仅作为一次单击事件被触发一次。

我可以使用第二种方式,即我不确定该应用程序是否会在浏览器中以这种方式执行。另外,我尝试设置单元测试来验证它是否能够按照我们的合同义务(输入和单击鼠标均应起作用)工作,并且我不知道如何编写测试。

这是我到目前为止尝试过的。

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('clicking on button should take action', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.componentInstance;
    spyOn(component, 'actionTaken');

    fixture.nativeElement
           .querySelector('button')
           .click();

    expect(component.actionTaken).toHaveBeenCalledTimes(1);
  });

  it('clicking on button should log to console', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.componentInstance;
    spyOn(component.console, 'log');

    fixture.nativeElement
           .querySelector('button')
           .click();

    expect(component.console.log).toHaveBeenCalledTimes(1);
  });

  it('pressing enter on button should take action', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.componentInstance;
    spyOn(component, 'actionTaken');

    fixture.nativeElement
           .querySelector('button')
           .dispatchEvent(new KeyboardEvent('keyup', { key: 'enter', bubbles: true }));

    expect(component.actionTaken).toHaveBeenCalledTimes(1);
  });

  it('pressing enter on button should log to console', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.componentInstance;
    spyOn(component.console, 'log');

    fixture.nativeElement
           .querySelector('button')
           .dispatchEvent(new KeyboardEvent('keyup', { key: 'enter', bubbles: true }));

    expect(component.console.log).toHaveBeenCalledTimes(1);
  });
});

利用监听这两个事件的按钮,所有这些测试都通过了,但是当我在浏览器中运行代码时,我可以看到测试不是真实的,因为回车键实际上两次触发了该方法,而不是一次就像测试表明的那样。

尽管使用该按钮仅侦听click事件,但“输入”键测试失败,尽管该功能似乎在浏览器中正常工作。

这似乎只是按钮的问题。其他类型的HTML元素似乎无法以这种方式工作。

编写组件,事件和测试的正确方法是什么,以使所需的方法仅通过单击鼠标和Enter键一次即可调用,并且将正确设置测试以验证这一点? >

1 个答案:

答案 0 :(得分:0)

HTML5 specification具有元素activation的概念。按下enter键只是激活按钮的一种方法。用户还可以按space键或使用其他方法来激活按钮(语音控制等)。通常,activation最终会导致该元素上的click事件。问题是:所有浏览器都以相同的方式实现规范吗?是否在每个浏览器中都用enter键激活了按钮?

如果您要确保按钮对enter键有反应,可以将两个处理程序都留在按钮上,然后在click处理程序中检查事件是synthetic还是{ {1}}。

问题在于您的测试对您不利,并且说一切正确,可能有多种来源。首先,它取决于您要针对哪个浏览器运行测试。 PhantomJS的行为可能与其他浏览器不同。第二个问题可能是您正在测试中创建综合authentic事件,并且浏览器在激活过程方面对综合事件的行为可能有所不同。我已经在Chrome中对此进行了测试,实际上,当事件是合成事件时,Chrome不会激活该按钮。

我将只使用keyup处理程序并将激活过程留在浏览器上,然后手动测试该应用程序支持的不同浏览器的行为。在测试中,我只会发出click事件。