时钟
简介
准确模拟与时间相关的行为对于验证应用程序的正确性至关重要。利用 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>
- 同步
- 异步
page.clock.set_fixed_time(datetime.datetime(2024, 2, 2, 10, 0, 0))
page.goto("https://:3333")
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
page.clock.set_fixed_time(datetime.datetime(2024, 2, 2, 10, 30, 0))
# We know that the page has a timer that updates the time every second.
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
await page.clock.set_fixed_time(datetime.datetime(2024, 2, 2, 10, 0, 0))
await page.goto("https://:3333")
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
await page.clock.set_fixed_time(datetime.datetime(2024, 2, 2, 10, 30, 0))
# We know that the page has a timer that updates the time every second.
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
一致的时间和计时器
有时你的计时器依赖于 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>
- 同步
- 异步
# Initialize clock with some time before the test time and let the page load
# naturally. `Date.now` will progress as the timers fire.
page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
page.goto("https://:3333")
# Pretend that the user closed the laptop lid and opened it again at 10am.
# Pause the time once reached that point.
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Assert the page state.
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
# Close the laptop lid again and open it at 10:30am.
page.clock.fast_forward("30:00")
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
# Initialize clock with some time before the test time and let the page load
# naturally. `Date.now` will progress as the timers fire.
await page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
await page.goto("https://:3333")
# Pretend that the user closed the laptop lid and opened it again at 10am.
# Pause the time once reached that point.
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Assert the page state.
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
# Close the laptop lid again and open it at 10:30am.
await page.clock.fast_forward("30:00")
await 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 =
'You have been logged out due to inactivity.';
} else {
document.getElementById('remaining-time').textContent =
`You will be logged out in ${diffInSeconds} seconds.`;
}
setTimeout(renderTime, 1000);
};
renderTime();
</script>
<button type="button">Interaction</button>
- 同步
- 异步
# Initial time does not matter for the test, so we can pick current time.
page.clock.install()
page.goto("https://:3333")
# Interact with the page
page.get_by_role("button").click()
# Fast forward time 5 minutes as if the user did not do anything.
# Fast forward is like closing the laptop lid and opening it after 5 minutes.
# All the timers due will fire once immediately, as in the real browser.
page.clock.fast_forward("05:00")
# Check that the user was logged out automatically.
expect(page.get_by_text("You have been logged out due to inactivity.")).to_be_visible()
# Initial time does not matter for the test, so we can pick current time.
await page.clock.install()
await page.goto("https://:3333")
# Interact with the page
await page.get_by_role("button").click()
# Fast forward time 5 minutes as if the user did not do anything.
# Fast forward is like closing the laptop lid and opening it after 5 minutes.
# All the timers due will fire once immediately, as in the real browser.
await page.clock.fast_forward("05:00")
# Check that the user was logged out automatically.
await expect(page.getByText("You have been logged out due to inactivity.")).toBeVisible()
手动按时间滴答,一致地触发所有计时器
在极少数情况下,你可能希望手动按步进时间,在此过程中触发所有计时器和动画帧,以实现对时间流逝的细粒度控制。
<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleString();
};
setInterval(renderTime, 1000);
</script>
- 同步
- 异步
# Initialize clock with a specific time, let the page load naturally.
page.clock.install(
time=datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
page.goto("https://:3333")
locator = page.get_by_test_id("current-time")
# Pause the time flow, stop the timers, you now have manual control
# over the page 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")
# Tick through time manually, firing all timers in the process.
# In this case, time will be updated in the screen 2 times.
page.clock.run_for(2000)
expect(locator).to_have_text("2/2/2024, 10:00:02 AM")
# Initialize clock with a specific time, let the page load naturally.
await page.clock.install(time=
datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
await page.goto("https://:3333")
locator = page.get_by_test_id("current-time")
# Pause the time flow, stop the timers, you now have manual control
# over the page time.
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
await expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
# Tick through time manually, firing all timers in the process.
# In this case, time will be updated in the screen 2 times.
await page.clock.run_for(2000)
await expect(locator).to_have_text("2/2/2024, 10:00:02 AM")