跳到主要内容

时钟

简介

准确模拟时间依赖行为对于验证应用程序的正确性至关重要。利用 Clock 功能允许开发人员操控和控制测试中的时间,从而在无需实际时间延迟和变化的情况下精确验证诸如渲染时间、超时和计划任务等功能。

Clock API 提供以下方法来控制时间:

  • setFixedTime: 设置 Date.now()new Date() 的固定时间。
  • install: 初始化时钟并允许你:
    • pauseAt: 在特定时间暂停时间。
    • fastForward: 快进时间。
    • runFor: 运行特定时间段。
    • resume: 恢复时间。
  • setSystemTime: 设置当前系统时间。

推荐的方法是使用 setFixedTime 将时间设置为特定值。如果这不适用于你的用例,可以使用 install,它允许你稍后暂停时间、快进等。setSystemTime 仅推荐用于高级用例。

备注

page.clock 覆盖了与时间相关的原生全局类和函数,允许手动控制它们:

  • Date
  • setTimeout
  • clearTimeout
  • setInterval
  • clearInterval
  • requestAnimationFrame
  • cancelAnimationFrame
  • requestIdleCallback
  • cancelIdleCallback
  • performance
  • Event.timeStamp
注意

如果你在测试中的任何地方调用 install,该调用 必须 发生在任何其他与时钟相关调用之前(参见上面笔记中的列表)。调用这些方法顺序错误将导致未定义的行为。例如,不能先调用 setInterval,然后调用 install,再调用 clearInterval,因为 install 会覆盖时钟函数的本机定义。

使用预设时间进行测试

通常只需要伪造 Date.now 同时保持计时器运行。这样时间自然流动,但 Date.now 总是返回一个固定值。

<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>

一致的时间和计时器

有时你的计时器依赖于 Date.now 并且当 Date.now 值不改变时会感到困惑。在这种情况下,你可以安装时钟并在测试时快速前进到感兴趣的时间。

<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
# 在测试时间之前初始化时钟并将页面加载自然地进行。
# 自然流逝,正如计时器触发一样。
page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
page.goto("http://localhost:3333")

# 假设用户关闭了笔记本电脑盖子,然后再次打开时间为上午10点。
# 达到那一点后暂停时间。
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))

# 断言页面状态。
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")

# 再次关闭笔记本电脑盖子,并在10点半打开。
page.clock.fast_forward("30:00")
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")

测试非活动监视

非活动监视是Web应用程序中的一项常见功能,会在一段时间无活动后注销用户。测试此功能可能很棘手,因为你需要等待很长时间才能看到效果。借助时钟的帮助,你可以加速时间并快速测试此功能。

<div id="remaining-time" data-testid="remaining-time"></div>
<script>
const endTime = Date.now() + 5 * 60_000;
const renderTime = () => {
const diffInSeconds = Math.round((endTime - Date.now()) / 1000);
if (diffInSeconds <= 0) {
document.getElementById('remaining-time').textContent =
'由于长时间无操作,您已被自动登出。';
} else {
document.getElementById('remaining-time').textContent =
`您将在 ${diffInSeconds} 秒内被自动登出。`;
}
setTimeout(renderTime, 1000);
};
renderTime();
</script>
<button type="button">交互按钮</button>
# 初始时间对测试无关紧要,因此我们可以选择当前时间。
page.clock.install()
page.goto("http://localhost:3333")
# 与页面互动
page.get_by_role("button").click()

# 快进时间5分钟,就像用户什么也没做一样。
# 快进就像是关闭笔记本电脑盖子,5分钟后再次打开。
# 所有到期的计时器都会立即触发,就像真实的浏览器一样。
page.clock.fast_forward("05:00")

# 检查用户是否因非活动而自动注销。
expect(page.get_by_text("由于长时间无操作,您已被自动登出。")).to_be_visible()

逐步推进时间,一致地触发所有计时器

在极少数情况下,你可能希望手动逐步推进时间,在此过程中触发所有计时器以实现对时间进程的细粒度控制。

<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
# 初始化时钟为特定时间,让页面自然加载。
page.clock.install(
time=datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
page.goto("http://localhost:3333")
locator = page.get_by_test_id("current-time")

# 暂停时间流动,停止计时器,你现在有了对页面时间的手动控制。
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
expect(locator).to_have_text("2/2/2024, 10:00:00 AM")

# 手动推进时间,同时触发所有计时器。
# 在这种情况下,屏幕上显示的时间将更新两次。
page.clock.run_for(2000)
expect(locator).to_have_text("2/2/2024, 10:00:02 AM")

相关视频