playwright/docs/src/clock.md

364 lines
13 KiB
Markdown
Raw Normal View History

2024-06-01 07:30:58 -07:00
---
id: clock
title: "Clock"
---
## Introduction
2024-06-01 08:18:29 -07:00
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.
[`property: Page.clock`] overrides native global classes and functions related to time allowing them to be manually controlled:
- `Date`
2024-06-01 07:30:58 -07:00
- `setTimeout`
- `clearTimeout`
- `setInterval`
- `clearInterval`
- `requestAnimationFrame`
- `cancelAnimationFrame`
- `requestIdleCallback`
2024-06-01 08:18:29 -07:00
- `cancelIdleCallback`
2024-06-11 09:42:15 -07:00
- `performance`
2024-06-01 07:30:58 -07:00
2024-06-11 09:42:15 -07:00
## Test with predefined time
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.
```html
<div id="current-time" data-testid="current-time"></div>
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
new Date().toLocaleTimeString();
};
setInterval(renderTime, 1000);
</script>
```
2024-06-01 07:30:58 -07:00
```js
2024-06-11 09:42:15 -07:00
await page.clock.setFixedTime(new Date('2024-02-02T10:00:00'));
await page.goto('http://localhost:3333');
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM');
await page.clock.setFixedTime(new Date('2024-02-02T10:30:00'));
// We know that the page has a timer that updates the time every second.
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:30:00 AM');
2024-06-01 07:30:58 -07:00
```
2024-06-11 09:42:15 -07:00
## Consistent time and timers
2024-06-01 08:18:29 -07:00
2024-06-11 09:42:15 -07:00
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.
2024-06-01 07:30:58 -07:00
```html
<div id="current-time" data-testid="current-time"></div>
2024-06-01 07:30:58 -07:00
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
2024-06-11 09:42:15 -07:00
new Date().toLocaleTimeString();
2024-06-01 07:30:58 -07:00
};
setInterval(renderTime, 1000);
2024-06-01 07:30:58 -07:00
</script>
```
```js
2024-06-11 09:42:15 -07:00
// 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: new Date('2024-02-02T08:00:00') });
2024-06-01 07:30:58 -07:00
await page.goto('http://localhost:3333');
2024-06-11 09:42:15 -07:00
// Take control over time flow.
await page.clock.pause();
// Pretend that the user closed the laptop lid and opened it again at 10am.
await page.clock.fastForwardTo(new Date('2024-02-02T10:00:00'));
// Assert the page state.
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM');
2024-06-11 09:42:15 -07:00
// Close the laptop lid again and open it at 10:30am.
await page.clock.fastForward('30:00');
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:30:00 AM');
2024-06-01 07:30:58 -07:00
```
```python async
2024-06-11 09:42:15 -07:00
# 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("http://localhost:3333")
# Take control over time flow.
await page.clock.pause()
# Pretend that the user closed the laptop lid and opened it again at 10am.
await page.clock.fast_forward_to(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")
2024-06-01 07:30:58 -07:00
```
```python sync
2024-06-11 09:42:15 -07:00
# 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("http://localhost:3333")
# Take control over time flow.
page.clock.pause()
# Pretend that the user closed the laptop lid and opened it again at 10am.
page.clock.fast_forward_to(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")
2024-06-01 07:30:58 -07:00
```
```java
2024-06-11 09:42:15 -07:00
// 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(new Clock.InstallOptions().setTime(Instant.parse("2024-02-02T08:00:00")));
2024-06-01 07:30:58 -07:00
page.navigate("http://localhost:3333");
Locator locator = page.getByTestId("current-time");
2024-06-11 09:42:15 -07:00
// Take control over time flow.
page.clock().pause();
// Pretend that the user closed the laptop lid and opened it again at 10am.
page.clock().fastForwardTo(Instant.parse("2024-02-02T10:00:00"));
// Assert the page state.
assertThat(locator).hasText("2/2/2024, 10:00:00 AM");
2024-06-11 09:42:15 -07:00
// Close the laptop lid again and open it at 10:30am.
page.clock().fastForward("30:00");
assertThat(locator).hasText("2/2/2024, 10:30:00 AM");
2024-06-01 07:30:58 -07:00
```
```csharp
2024-06-11 09:42:15 -07:00
// 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
{
Time = new DateTime(2024, 2, 2, 8, 0, 0)
});
await Page.GotoAsync("http://localhost:3333");
// Take control over time flow.
await Page.Clock.PauseAsync();
// Pretend that the user closed the laptop lid and opened it again at 10am.
await Page.Clock.FastForwardToAsync(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");
2024-06-01 07:30:58 -07:00
```
2024-06-11 09:42:15 -07:00
## Test inactivity monitoring
2024-06-01 07:30:58 -07:00
2024-06-11 09:42:15 -07:00
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.
2024-06-01 07:30:58 -07:00
```js
2024-06-11 09:42:15 -07:00
// Initial time does not matter for the test, so we can pick current time.
await page.clock.install();
2024-06-01 07:30:58 -07:00
await page.goto('http://localhost:3333');
2024-06-11 09:42:15 -07:00
// Interact with the page
await page.getByRole('button').click();
2024-06-01 07:30:58 -07:00
2024-06-11 09:42:15 -07:00
// 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.fastForward('5:00');
// Check that the user was logged out automatically.
await expect(page.getByText('You have been logged out due to inactivity.')).toBeVisible();
2024-06-01 07:30:58 -07:00
```
```python async
2024-06-11 09:42:15 -07:00
# Initial time does not matter for the test, so we can pick current time.
await page.clock.install()
await page.goto("http://localhost: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("5:00")
# Check that the user was logged out automatically.
await expect(page.getByText("You have been logged out due to inactivity.")).toBeVisible()
2024-06-01 07:30:58 -07:00
```
```python sync
2024-06-11 09:42:15 -07:00
# Initial time does not matter for the test, so we can pick current time.
page.clock.install()
page.goto("http://localhost: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("5: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()
2024-06-01 07:30:58 -07:00
```
```java
2024-06-11 09:42:15 -07:00
// Initial time does not matter for the test, so we can pick current time.
page.clock().install();
2024-06-01 07:30:58 -07:00
page.navigate("http://localhost:3333");
2024-06-11 09:42:15 -07:00
Locator locator = page.getByRole("button");
2024-06-01 07:30:58 -07:00
2024-06-11 09:42:15 -07:00
// Interact with the page
locator.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().fastForward("5:00");
// Check that the user was logged out automatically.
assertThat(page.getByText("You have been logged out due to inactivity.")).isVisible();
2024-06-01 07:30:58 -07:00
```
```csharp
2024-06-11 09:42:15 -07:00
// Initial time does not matter for the test, so we can pick current time.
await Page.Clock.InstallAsync();
2024-06-01 07:30:58 -07:00
await page.GotoAsync("http://localhost:3333");
2024-06-11 09:42:15 -07:00
// 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("5:00");
// Check that the user was logged out automatically.
await Expect(Page.GetByText("You have been logged out due to inactivity.")).ToBeVisibleAsync();
2024-06-01 07:30:58 -07:00
```
2024-06-01 08:18:29 -07:00
2024-06-11 09:42:15 -07:00
## Tick through time manually, firing all the timers consistently
2024-06-01 08:18:29 -07:00
2024-06-11 09:42:15 -07:00
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.
2024-06-01 08:18:29 -07:00
```html
<div id="current-time" data-testid="current-time"></div>
2024-06-01 08:18:29 -07:00
<script>
const renderTime = () => {
document.getElementById('current-time').textContent =
2024-06-11 09:42:15 -07:00
new Date().toLocaleTimeString();
2024-06-01 08:18:29 -07:00
};
setInterval(renderTime, 1000);
2024-06-01 08:18:29 -07:00
</script>
```
```js
2024-06-11 09:42:15 -07:00
// Initialize clock with a specific time, let the page load naturally.
await page.clock.install({ time: new Date('2024-02-02T08:00:00') });
2024-06-01 08:18:29 -07:00
await page.goto('http://localhost:3333');
2024-06-11 09:42:15 -07:00
// Pause the time flow, stop the timers, you now have manual control
// over the page time.
await page.clock.pause();
await page.clock.fastForwardTo(new Date('2024-02-02T10:00:00'));
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM');
2024-06-01 08:18:29 -07:00
// 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.runFor(2000);
2024-06-11 09:42:15 -07:00
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:02 AM');
2024-06-01 08:18:29 -07:00
```
```python async
2024-06-11 09:42:15 -07:00
# 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),
2024-06-01 08:18:29 -07:00
)
2024-06-11 09:42:15 -07:00
await page.goto("http://localhost: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()
await page.clock.fast_forward_to(datetime.datetime(2024, 2, 2, 10, 0, 0))
await expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
2024-06-01 08:18:29 -07:00
# 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)
2024-06-11 09:42:15 -07:00
await expect(locator).to_have_text("2/2/2024, 10:00:02 AM")
2024-06-01 08:18:29 -07:00
```
```python sync
2024-06-11 09:42:15 -07:00
# 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),
2024-06-01 08:18:29 -07:00
)
2024-06-11 09:42:15 -07:00
page.goto("http://localhost: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()
page.clock.fast_forward_to(datetime.datetime(2024, 2, 2, 10, 0, 0))
expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
2024-06-01 08:18:29 -07:00
# 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)
2024-06-11 09:42:15 -07:00
expect(locator).to_have_text("2/2/2024, 10:00:02 AM")
2024-06-01 08:18:29 -07:00
```
```java
2024-06-11 09:42:15 -07:00
// Initialize clock with a specific time, let the page load naturally.
page.clock().install(new Clock.InstallOptions()
.setTime(Instant.parse("2024-02-02T08:00:00")));
2024-06-01 08:18:29 -07:00
page.navigate("http://localhost:3333");
Locator locator = page.getByTestId("current-time");
2024-06-01 08:18:29 -07:00
2024-06-11 09:42:15 -07:00
// Pause the time flow, stop the timers, you now have manual control
// over the page time.
page.clock().pause();
page.clock().fastForwardTo(Instant.parse("2024-02-02T10:00:00"));
assertThat(locator).hasText("2/2/2024, 10:00:00 AM");
2024-06-01 08:18:29 -07:00
// Tick through time manually, firing all timers in the process.
// In this case, time will be updated in the screen 2 times.
page.clock().runFor(2000);
assertThat(locator).hasText("2/2/2024, 10:00:02 AM");
2024-06-01 08:18:29 -07:00
```
```csharp
2024-06-11 09:42:15 -07:00
// Initialize clock with a specific time, let the page load naturally.
await Page.Clock.InstallAsync(new
{
Time = new DateTime(2024, 2, 2, 8, 0, 0, DateTimeKind.Pst)
});
2024-06-01 08:18:29 -07:00
await page.GotoAsync("http://localhost:3333");
var locator = page.GetByTestId("current-time");
2024-06-01 08:18:29 -07:00
2024-06-11 09:42:15 -07:00
// Pause the time flow, stop the timers, you now have manual control
// over the page time.
await Page.Clock.PauseAsync();
await Page.Clock.FastForwardToAsync(new DateTime(2024, 2, 2, 10, 0, 0));
await Expect(locator).ToHaveTextAsync("2/2/2024, 10:00:00 AM");
2024-06-01 08:18:29 -07:00
// Tick through time manually, firing all timers in the process.
// In this case, time will be updated in the screen 2 times.
2024-06-11 09:42:15 -07:00
await Page.Clock.RunForAsync(2000);
await Expect(locator).ToHaveTextAsync("2/2/2024, 10:00:02 AM");
2024-06-01 08:18:29 -07:00
```