跳到主要內容

時鐘

簡介

精確地模擬與時間相關的行為,對於驗證應用程式的正確性至關重要。利用 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");