Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Utilizing Clock functionality allows developers to manipulate and control time within tests, enabling the precise validation of features such as rendering time, timeouts, scheduled tasks without the delays and variability of real-time execution.
The Clock API provides the following methods to control time:
setFixedTime
: Sets the fixed time for Date.now()
and new Date()
.install
: initializes the clock and allows you to:
pauseAt
: Pauses the time at a specific time.fastForward
: Fast forwards the time.runFor
: Runs the time for a specific duration.resume
: Resumes the time.setSystemTime
: Sets the current system time.The recommended approach is to use setFixedTime
to set the time to a specific value. If that doesn't work for your use case, you can use install
which allows you to pause time later on, fast forward it, tick it, etc. setSystemTime
is only recommended for advanced use cases.
note
page.clock overrides native global classes and functions related to time allowing them to be manually controlled:
Date
setTimeout
clearTimeout
setInterval
clearInterval
requestAnimationFrame
cancelAnimationFrame
requestIdleCallback
cancelIdleCallback
performance
Event.timeStamp
warning
If you call install
at any point in your test, the call MUST occur before any other clock related calls (see note above for list). Calling these methods out of order will result in undefined behavior. For example, you cannot call setInterval
, followed by install
, then clearInterval
, as install
overrides the native definition of the clock functions.
Often you only need to fake Date.now
while keeping the timers going. That way the time flows naturally, but Date.now
always returns a fixed value.
<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("http://localhost: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))
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("http://localhost: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))
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
Consistent time and timers
Sometimes your timers depend on Date.now
and are confused when the Date.now
value does not change over time. In this case, you can install the clock and fast forward to the time of interest when testing.
<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")
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")
page.clock.fast_forward("30:00")
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:00 AM")
await page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
await page.goto("http://localhost:3333")
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
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")
Test inactivity monitoring
Inactivity monitoring is a common feature in web applications that logs out users after a period of inactivity. Testing this feature can be tricky because you need to wait for a long time to see the effect. With the help of the clock, you can speed up time and test this feature quickly.
<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>
page.clock.install()
page.goto("http://localhost:3333")
page.get_by_role("button").click()
page.clock.fast_forward("05:00")
expect(page.get_by_text("You have been logged out due to inactivity.")).to_be_visible()
await page.clock.install()
await page.goto("http://localhost:3333")
await page.get_by_role("button").click()
await page.clock.fast_forward("05:00")
await expect(page.getByText("You have been logged out due to inactivity.")).toBeVisible()
Tick through time manually, firing all the timers consistently
In rare cases, you may want to tick through time manually, firing all timers and animation frames in the process to achieve a fine-grained control over the passage of time.
<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")
await page.clock.install(time=
datetime.datetime(2024, 2, 2, 8, 0, 0, tzinfo=datetime.timezone.pst),
)
await page.goto("http://localhost:3333")
locator = page.get_by_test_id("current-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")
await page.clock.run_for(2000)
await expect(locator).to_have_text("2/2/2024, 10:00:02 AM")
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4