这篇博文是关于在浏览器中测试焦点行为的一个概述。
在 Azure Portal 工作中,我花了相当多的时间来确保丰富的键盘支持。这就需要对焦点进行适当的管理。当用户按下一些快捷键时,焦点应该移动到相应的元素上。焦点行为还应当非常便于测试,因为它很容易在 HTML 或 JavaScript 进行了细微变化时 被改变(打断)。
标准测试流程如下:
准备:打开一些页面
进行:执行一些应该能打开一个指定页面并(或)在相应元素上设置焦点的快捷键
断言:检测预期的元素是否获得焦点
最简单的方式是使用 jQuery:
expect($(element).is(':focus')).equals(true);
然而,这可能不是在任何时候都好使,特别是当你并行运行你的单元测试的话,因为 当窗口没有获得焦点时 $element.is(‘:focus’) 将无法正常工作 。
更好(正确)的方式是使用 document.activeElement :
expect(document.activeElement).equals(element);
就算浏览器窗口没有获得焦点时其也能正常工作。
有时,键盘调用异步操作最终会改变焦点。这可能会导致:
漏报(false negative):断言在焦点被设置之前执行
误报(false positives):最终聚焦的元素,是在断言执行之后才获得焦点的
对于第一个问题最简单的方案就是延迟进行断言:
setTimeout(() => { expect(document.activeElement).equals(element); done(); }, 100);
这种方法的难点在于选择合适的延迟时间。因此,最好避免使用原生的 setTimeout, 而是使用我在 setTimeout 被认为是有害的 一文中写的这种轮询方法:
poll( () => document.activeElement === element, () => { assert(true); start(); }, () => { assert(false); start(); } );
轮询函数也可以用来解决第二个问题(通过改变回调函数中的断言顺序)。然而,对于误报的问题,简单的 setTimeout 已经足够了,因为相比于等待某些特定的时间执行断言我们没有其他选择。
有下列 3 种可以让我们用来模拟键盘操作的事件:
keydown
keyup
keypress
最安全的方式是使用 keydown。为啥呢? 一个示例中可能使用了特殊的功能键。而在使用 keypress 的情况下 —— 各个浏览器对其进行的处理是不同的(例如,不触发事件),而 keydown 的行为则是相当一致的。如果你对细节感兴趣的话,我建议你看看 这篇文章 。
为了检测 键盘操作, 你可以在 捕获和冒泡阶段 处理事件。你应该选择符合你需求的模型,但通常冒泡阶段是最佳的解决方案。此外,它是被 jQuery 支持的,可以为你兼容浏览器。
为了在测试中调用 keydown 事件,你首先需要在元素上设置焦点:
$(element) .focus() .trigger("keydown", { which: keyCode });
你可以在 这里 找到 JavaScript 键码。
使用 document.activeElement 检查哪个元素获得了焦点。
使用轮询的方法来测试异步操作。
使用 keydown 事件,以及冒泡阶段调用(处理)键盘操作。