我正在研究可访问性非常重要的应用程序。作为其一部分,我们希望确保可以通过鼠标事件和键盘事件访问许多不同的组件。我们还尝试围绕这些要求设置单元测试,以确保对这些组件所做的任何更改仍将遵循这些标准。我们遇到了按钮问题。我有一个示例应用程序来演示:
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键一次即可调用,并且将正确设置测试以验证这一点? >
答案 0 :(得分:0)
HTML5 specification具有元素activation
的概念。按下enter
键只是激活按钮的一种方法。用户还可以按space
键或使用其他方法来激活按钮(语音控制等)。通常,activation
最终会导致该元素上的click
事件。问题是:所有浏览器都以相同的方式实现规范吗?是否在每个浏览器中都用enter
键激活了按钮?
如果您要确保按钮对enter
键有反应,可以将两个处理程序都留在按钮上,然后在click
处理程序中检查事件是synthetic
还是{ {1}}。
问题在于您的测试对您不利,并且说一切正确,可能有多种来源。首先,它取决于您要针对哪个浏览器运行测试。 PhantomJS的行为可能与其他浏览器不同。第二个问题可能是您正在测试中创建综合authentic
事件,并且浏览器在激活过程方面对综合事件的行为可能有所不同。我已经在Chrome中对此进行了测试,实际上,当事件是合成事件时,Chrome不会激活该按钮。
我将只使用keyup
处理程序并将激活过程留在浏览器上,然后手动测试该应用程序支持的不同浏览器的行为。在测试中,我只会发出click
事件。