时钟
简介
准确地模拟时间依赖性行为对于验证应用程序的正确性至关重要。 利用 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>
// 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.InstallAsync(new()
{
TimeDate = new DateTime(2024, 2, 2, 8, 0, 0)
});
await Page.GotoAsync("https://127.0.0.1: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.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
// Assert the page state.
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("2/2/2024, 10:00:00 AM");
// Close the laptop lid again and open it at 10:30am.
await Page.Clock.FastForwardAsync("30:00");
await Expect(Page.GetByTestId("current-time")).ToHaveTextAsync("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.
await Page.Clock.InstallAsync();
await page.GotoAsync("https://127.0.0.1:3333");
// Interact with the page
await page.GetByRole("button").ClickAsync();
// 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.FastForwardAsync("05:00");
// Check that the user was logged out automatically.
await Expect(Page.GetByText("You have been logged out due to inactivity.")).ToBeVisibleAsync();
手动滴答时间,始终如一地触发所有计时器
在极少数情况下,您可能需要手动滴答时间,在此过程中触发所有计时器和动画帧,以实现对时间流逝的精细控制。
<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.
await Page.Clock.InstallAsync(new()
{
TimeDate = new DateTime(2024, 2, 2, 8, 0, 0, DateTimeKind.Pst)
});
await page.GotoAsync("https://127.0.0.1:3333");
var locator = page.GetByTestId("current-time");
// Pause the time flow, stop the timers, you now have manual control
// over the page time.
await Page.Clock.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
await Expect(locator).ToHaveTextAsync("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.RunForAsync(2000);
await Expect(locator).ToHaveTextAsync("2/2/2024, 10:00:02 AM");