时钟
简介
准确模拟依赖时间的行为对于验证应用程序的正确性至关重要。利用时钟功能,开发人员可以在测试中操作和控制时间,从而能够精确验证诸如渲染时间、超时、计划任务等功能,而无需实时执行的延迟和可变性。
时钟 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
使用预定义时间进行测试
通常您只需要伪造 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")).ToHaveText("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")).ToHaveText("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");