diff --git a/docs/src/api/class-clock.md b/docs/src/api/class-clock.md index d703c07b84..271023e2cb 100644 --- a/docs/src/api/class-clock.md +++ b/docs/src/api/class-clock.md @@ -1,9 +1,10 @@ # class: Clock * since: v1.45 -Playwright uses [@sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) for clock emulation. Clock is installed for the entire [BrowserContext], so the time -in all the pages and iframes is controlled by the same clock. +Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn more about [clock emulation](../clock.md). +Note that clock is installed for the entire [BrowserContext], so the time +in all the pages and iframes is controlled by the same clock. ## async method: Clock.install * since: v1.45 diff --git a/docs/src/clock.md b/docs/src/clock.md index 1b063f11b7..95e8df0e56 100644 --- a/docs/src/clock.md +++ b/docs/src/clock.md @@ -5,24 +5,30 @@ title: "Clock" ## Introduction -[`property: Page.clock`] overrides native global functions related to time allowing them to be manually controlled: +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` - `setTimeout` - `clearTimeout` - `setInterval` - `clearInterval` - - `Date` - `requestAnimationFrame` - `cancelAnimationFrame` - `requestIdleCallback` + - `cancelIdleCallback` -By default, the clock starts at the unix epoch (timestamp of 0), but you can override it using the `now` option. +By default, the clock starts at the unix epoch (timestamp of 0). You can override it using the `now` option. ```js await page.clock.install(); await page.clock.install({ now: new Date('2020-02-02') }); ``` -## Only fake Date.now +## Freeze Date.now + +Sometimes you only need to fake `Date.now` and no other time-related functions. +That way the time flows naturally, but `Date.now` returns a fixed value. ```html @@ -37,7 +43,7 @@ await page.clock.install({ now: new Date('2020-02-02') }); ``` ```js -// Initialize clock with a specific time. +// Initialize clock with a specific time, only fake Date.now. await page.clock.install({ now: new Date('2024-01-01T10:00:00Z'), toFake: ['Date'], @@ -47,7 +53,7 @@ await expect(page.getByTestId('my-time')).toHaveValue('2024-01-01T10:00'); ``` ```python async -# Initialize clock with a specific time. +# Initialize clock with a specific time, only fake Date.now. await page.clock.install( now=datetime.datetime(2024, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), toFake=['Date'], @@ -58,7 +64,7 @@ await expect(locator).to_have_value('2024-01-01T10:00') ``` ```python sync -# Initialize clock with a specific time. +# Initialize clock with a specific time, only fake Date.now. page.clock.install( now=datetime.datetime(2024, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), to_fake=['Date'], @@ -69,7 +75,7 @@ expect(locator).to_have_value('2024-01-01T10:00') ``` ```java -// Initialize clock with a specific time. +// Initialize clock with a specific time, only fake Date.now. page.clock().install( new Clock.InstallOptions() .setNow(Instant.parse("2024-01-01T10:00:00Z")) @@ -81,7 +87,7 @@ assertThat(locator).hasValue("2024-01-01T10:00"); ``` ```csharp -// Initialize clock with a specific time. +// Initialize clock with a specific time, only fake Date.now. await page.Clock.InstallAsync( new ClockInstallOptions { @@ -95,6 +101,9 @@ await Expect(locator).ToHaveValueAsync("2024-01-01T10:00"); ## Assert page at different points in time +More often you need to simulate the passage of time to test time-dependent behavior. +You can jump the clock forward in time to simulate the passage of time without waiting for real-time to pass. + ```html +``` + +```js +// Initialize clock with a specific time, take full control over time. +await page.clock.install({ now: new Date('2024-01-01T10:00:00Z') }); +await page.goto('http://localhost:3333'); + +// 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.tick(2000); +``` + +```python async +# Initialize clock with a specific time, take full control over time. +await page.clock.install( + now=datetime.datetime(2024, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), +) +await page.goto('http://localhost:3333') +locator = page.get_by_test_id('my-time') + +# 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.tick(2000) +``` + +```python sync +# Initialize clock with a specific time, take full control over time. +page.clock.install( + now=datetime.datetime(2024, 1, 1, 10, 0, 0, tzinfo=datetime.timezone.utc), +) +page.goto('http://localhost:3333') +locator = page.get_by_test_id('my-time') + +# Tick through time manually, firing all timers in the process. +# In this case, time will be updated in the screen 2 times. +page.clock.tick(2000) +``` + +```java +// Initialize clock with a specific time, take full control over time. +page.clock().install( + new Clock.InstallOptions() + .setNow(Instant.parse("2024-01-01T10:00:00Z")) +); +page.navigate("http://localhost:3333"); +Locator locator = page.getByTestId("my-time"); + +// Tick through time manually, firing all timers in the process. +// In this case, time will be updated in the screen 2 times. +page.clock().tick(2000); +``` + +```csharp +// Initialize clock with a specific time, take full control over time. +await page.Clock.InstallAsync( + new ClockInstallOptions + { + Now = new DateTime(2024, 1, 1, 10, 0, 0, DateTimeKind.Utc), + }); +await page.GotoAsync("http://localhost:3333"); +var locator = page.GetByTestId("my-time"); + +// 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.TickAsync(2000); +``` diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index a77afd9b5c..dd53b1e890 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -17239,9 +17239,11 @@ export interface BrowserServer { } /** - * Playwright uses [@sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) for clock emulation. Clock is - * installed for the entire {@link BrowserContext}, so the time in all the pages and iframes is controlled by the same - * clock. + * Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn + * more about [clock emulation](https://playwright.dev/docs/clock). + * + * Note that clock is installed for the entire {@link BrowserContext}, so the time in all the pages and iframes is + * controlled by the same clock. */ export interface Clock { /**