chore(clock): introduce pauseAt (#31255)

This commit is contained in:
Pavel Feldman 2024-06-11 12:51:00 -07:00 committed by GitHub
parent 8fd0a56427
commit 2b257ea963
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 157 additions and 201 deletions

View file

@ -45,43 +45,6 @@ await page.Clock.FastForwardAsync("30:00");
Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
## async method: Clock.fastForwardTo
* since: v1.45
Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and
reopening it at the specified time.
**Usage**
```js
await page.clock.fastForwardTo(new Date('2020-02-02'));
await page.clock.fastForwardTo('2020-02-02');
```
```python async
await page.clock.fast_forward_to(datetime.datetime(2020, 2, 2))
await page.clock.fast_forward_to("2020-02-02")
```
```python sync
page.clock.fast_forward_to(datetime.datetime(2020, 2, 2))
page.clock.fast_forward_to("2020-02-02")
```
```java
page.clock().fastForwardTo(Instant.parse("2020-02-02"));
page.clock().fastForwardTo("2020-02-02");
```
```csharp
await page.Clock.FastForwardToAsync(DateTime.Parse("2020-02-02"));
await page.Clock.FastForwardToAsync("2020-02-02");
```
### param: Clock.fastForwardTo.time
* since: v1.45
- `time` <[int]|[string]|[Date]>
## async method: Clock.install ## async method: Clock.install
* since: v1.45 * since: v1.45
@ -145,10 +108,47 @@ await page.Clock.RunForAsync("30:00");
Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds.
## async method: Clock.pause ## async method: Clock.pauseAt
* since: v1.45 * since: v1.45
Pause timers. Once this method is called, no timers are fired unless [`method: Clock.runFor`], [`method: Clock.fastForward`], [`method: Clock.fastForwardTo`] or [`method: Clock.resume`] is called. Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers
are fired unless [`method: Clock.runFor`], [`method: Clock.fastForward`], [`method: Clock.pauseAt`] or [`method: Clock.resume`] is called.
Only fires due timers at most once.
This is equivalent to user closing the laptop lid for a while and reopening it at the specified time and
pausing.
**Usage**
```js
await page.clock.pauseAt(new Date('2020-02-02'));
await page.clock.pauseAt('2020-02-02');
```
```python async
await page.clock.pause_at(datetime.datetime(2020, 2, 2))
await page.clock.pause_at("2020-02-02")
```
```python sync
page.clock.pause_at(datetime.datetime(2020, 2, 2))
page.clock.pause_at("2020-02-02")
```
```java
page.clock().pauseAt(Instant.parse("2020-02-02"));
page.clock().pauseAt("2020-02-02");
```
```csharp
await page.Clock.PauseAtAsync(DateTime.Parse("2020-02-02"));
await page.Clock.PauseAtAsync("2020-02-02");
```
### param: Clock.pauseAt.time
* since: v1.45
- `time` <[int]|[string]|[Date]>
## async method: Clock.resume ## async method: Clock.resume
* since: v1.45 * since: v1.45
@ -202,7 +202,7 @@ Time to be set.
## async method: Clock.setSystemTime ## async method: Clock.setSystemTime
* since: v1.45 * since: v1.45
Sets current system time but does not trigger any timers, unlike [`method: Clock.fastForwardTo`]. Sets current system time but does not trigger any timers.
**Usage** **Usage**

View file

@ -67,10 +67,9 @@ In this case, you can install the clock and fast forward to the time of interest
await page.clock.install({ time: new Date('2024-02-02T08:00:00') }); await page.clock.install({ time: new Date('2024-02-02T08:00:00') });
await page.goto('http://localhost:3333'); await page.goto('http://localhost:3333');
// Take control over time flow. // Pretend that the user closed the laptop lid and opened it again at 10am,
await page.clock.pause(); // Pause the time once reached that point.
// Pretend that the user closed the laptop lid and opened it again at 10am. await page.clock.pauseAt(new Date('2024-02-02T10:00:00'));
await page.clock.fastForwardTo(new Date('2024-02-02T10:00:00'));
// Assert the page state. // Assert the page state.
await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM'); await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM');
@ -86,10 +85,9 @@ await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:30:00 AM
await page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0)) await page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
await page.goto("http://localhost:3333") 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. # 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)) # Pause the time once reached that point.
await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Assert the page state. # Assert the page state.
await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM") await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
@ -105,10 +103,9 @@ await expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:30:
page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0)) page.clock.install(time=datetime.datetime(2024, 2, 2, 8, 0, 0))
page.goto("http://localhost:3333") 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. # 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)) # Pause the time once reached that point.
page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
# Assert the page state. # Assert the page state.
expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM") expect(page.get_by_test_id("current-time")).to_have_text("2/2/2024, 10:00:00 AM")
@ -125,10 +122,9 @@ page.clock().install(new Clock.InstallOptions().setTime(Instant.parse("2024-02-0
page.navigate("http://localhost:3333"); page.navigate("http://localhost:3333");
Locator locator = page.getByTestId("current-time"); Locator locator = page.getByTestId("current-time");
// Take control over time flow.
page.clock().pause();
// Pretend that the user closed the laptop lid and opened it again at 10am. // Pretend that the user closed the laptop lid and opened it again at 10am.
page.clock().fastForwardTo(Instant.parse("2024-02-02T10:00:00")); // Pause the time once reached that point.
page.clock().pauseAt(Instant.parse("2024-02-02T10:00:00"));
// Assert the page state. // Assert the page state.
assertThat(locator).hasText("2/2/2024, 10:00:00 AM"); assertThat(locator).hasText("2/2/2024, 10:00:00 AM");
@ -147,10 +143,9 @@ await Page.Clock.InstallAsync(new
}); });
await Page.GotoAsync("http://localhost:3333"); 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. // 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)); // Pause the time once reached that point.
await Page.Clock.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
// Assert the page state. // Assert the page state.
await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:00:00 AM"); await Expect(Page.GetByTestId("current-time")).ToHaveText("2/2/2024, 10:00:00 AM");
@ -272,8 +267,7 @@ await page.goto('http://localhost:3333');
// Pause the time flow, stop the timers, you now have manual control // Pause the time flow, stop the timers, you now have manual control
// over the page time. // over the page time.
await page.clock.pause(); await page.clock.pauseAt(new Date('2024-02-02T10:00:00'));
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'); await expect(page.getByTestId('current-time')).toHaveText('2/2/2024, 10:00:00 AM');
// Tick through time manually, firing all timers in the process. // Tick through time manually, firing all timers in the process.
@ -292,8 +286,7 @@ locator = page.get_by_test_id("current-time")
# Pause the time flow, stop the timers, you now have manual control # Pause the time flow, stop the timers, you now have manual control
# over the page time. # over the page time.
await page.clock.pause() await page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
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") await expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
# Tick through time manually, firing all timers in the process. # Tick through time manually, firing all timers in the process.
@ -312,8 +305,7 @@ locator = page.get_by_test_id("current-time")
# Pause the time flow, stop the timers, you now have manual control # Pause the time flow, stop the timers, you now have manual control
# over the page time. # over the page time.
page.clock.pause() page.clock.pause_at(datetime.datetime(2024, 2, 2, 10, 0, 0))
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") expect(locator).to_have_text("2/2/2024, 10:00:00 AM")
# Tick through time manually, firing all timers in the process. # Tick through time manually, firing all timers in the process.
@ -331,8 +323,7 @@ Locator locator = page.getByTestId("current-time");
// Pause the time flow, stop the timers, you now have manual control // Pause the time flow, stop the timers, you now have manual control
// over the page time. // over the page time.
page.clock().pause(); page.clock().pauseAt(Instant.parse("2024-02-02T10:00:00"));
page.clock().fastForwardTo(Instant.parse("2024-02-02T10:00:00"));
assertThat(locator).hasText("2/2/2024, 10:00:00 AM"); assertThat(locator).hasText("2/2/2024, 10:00:00 AM");
// Tick through time manually, firing all timers in the process. // Tick through time manually, firing all timers in the process.
@ -352,8 +343,7 @@ var locator = page.GetByTestId("current-time");
// Pause the time flow, stop the timers, you now have manual control // Pause the time flow, stop the timers, you now have manual control
// over the page time. // over the page time.
await Page.Clock.PauseAsync(); await Page.Clock.PauseAtAsync(new DateTime(2024, 2, 2, 10, 0, 0));
await Page.Clock.FastForwardToAsync(new DateTime(2024, 2, 2, 10, 0, 0));
await Expect(locator).ToHaveTextAsync("2/2/2024, 10:00:00 AM"); await Expect(locator).ToHaveTextAsync("2/2/2024, 10:00:00 AM");
// Tick through time manually, firing all timers in the process. // Tick through time manually, firing all timers in the process.

View file

@ -88,7 +88,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
if (!forReuse && !!process.env.PW_FREEZE_TIME) { if (!forReuse && !!process.env.PW_FREEZE_TIME) {
await this._wrapApiCall(async () => { await this._wrapApiCall(async () => {
await context.clock.install({ time: 0 }); await context.clock.install({ time: 0 });
await context.clock.pause(); await context.clock.pauseAt(1000);
}, true); }, true);
} }
return context; return context;

View file

@ -32,12 +32,8 @@ export class Clock implements api.Clock {
await this._browserContext._channel.clockFastForward(parseTicks(ticks)); await this._browserContext._channel.clockFastForward(parseTicks(ticks));
} }
async fastForwardTo(time: number | string | Date) { async pauseAt(time: number | string | Date) {
await this._browserContext._channel.clockFastForwardTo(parseTime(time)); await this._browserContext._channel.clockPauseAt(parseTime(time));
}
async pause() {
await this._browserContext._channel.clockPause({});
} }
async resume() { async resume() {

View file

@ -968,18 +968,16 @@ scheme.BrowserContextClockFastForwardParams = tObject({
ticksString: tOptional(tString), ticksString: tOptional(tString),
}); });
scheme.BrowserContextClockFastForwardResult = tOptional(tObject({})); scheme.BrowserContextClockFastForwardResult = tOptional(tObject({}));
scheme.BrowserContextClockFastForwardToParams = tObject({
timeNumber: tOptional(tNumber),
timeString: tOptional(tString),
});
scheme.BrowserContextClockFastForwardToResult = tOptional(tObject({}));
scheme.BrowserContextClockInstallParams = tObject({ scheme.BrowserContextClockInstallParams = tObject({
timeNumber: tOptional(tNumber), timeNumber: tOptional(tNumber),
timeString: tOptional(tString), timeString: tOptional(tString),
}); });
scheme.BrowserContextClockInstallResult = tOptional(tObject({})); scheme.BrowserContextClockInstallResult = tOptional(tObject({}));
scheme.BrowserContextClockPauseParams = tOptional(tObject({})); scheme.BrowserContextClockPauseAtParams = tObject({
scheme.BrowserContextClockPauseResult = tOptional(tObject({})); timeNumber: tOptional(tNumber),
timeString: tOptional(tString),
});
scheme.BrowserContextClockPauseAtResult = tOptional(tObject({}));
scheme.BrowserContextClockResumeParams = tOptional(tObject({})); scheme.BrowserContextClockResumeParams = tOptional(tObject({}));
scheme.BrowserContextClockResumeResult = tOptional(tObject({})); scheme.BrowserContextClockResumeResult = tOptional(tObject({}));
scheme.BrowserContextClockRunForParams = tObject({ scheme.BrowserContextClockRunForParams = tObject({

View file

@ -33,13 +33,6 @@ export class Clock {
await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForward(${ticksMillis})`); await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForward(${ticksMillis})`);
} }
async fastForwardTo(ticks: number | string) {
await this._installIfNeeded();
const timeMillis = parseTime(ticks);
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('fastForwardTo', ${Date.now()}, ${timeMillis})`);
await this._evaluateInFrames(`globalThis.__pwClock.controller.fastForwardTo(${timeMillis})`);
}
async install(time: number | string | undefined) { async install(time: number | string | undefined) {
await this._installIfNeeded(); await this._installIfNeeded();
const timeMillis = time !== undefined ? parseTime(time) : Date.now(); const timeMillis = time !== undefined ? parseTime(time) : Date.now();
@ -47,10 +40,11 @@ export class Clock {
await this._evaluateInFrames(`globalThis.__pwClock.controller.install(${timeMillis})`); await this._evaluateInFrames(`globalThis.__pwClock.controller.install(${timeMillis})`);
} }
async pause() { async pauseAt(ticks: number | string) {
await this._installIfNeeded(); await this._installIfNeeded();
await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('pause', ${Date.now()})`); const timeMillis = parseTime(ticks);
await this._evaluateInFrames(`globalThis.__pwClock.controller.pause()`); await this._browserContext.addInitScript(`globalThis.__pwClock.controller.log('pauseAt', ${Date.now()}, ${timeMillis})`);
await this._evaluateInFrames(`globalThis.__pwClock.controller.pauseAt(${timeMillis})`);
} }
async resume() { async resume() {

View file

@ -316,16 +316,12 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
await this._context.clock.fastForward(params.ticksString ?? params.ticksNumber ?? 0); await this._context.clock.fastForward(params.ticksString ?? params.ticksNumber ?? 0);
} }
async clockFastForwardTo(params: channels.BrowserContextClockFastForwardToParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockFastForwardToResult> {
await this._context.clock.fastForwardTo(params.timeString ?? params.timeNumber ?? 0);
}
async clockInstall(params: channels.BrowserContextClockInstallParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockInstallResult> { async clockInstall(params: channels.BrowserContextClockInstallParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockInstallResult> {
await this._context.clock.install(params.timeString ?? params.timeNumber ?? undefined); await this._context.clock.install(params.timeString ?? params.timeNumber ?? undefined);
} }
async clockPause(params: channels.BrowserContextClockPauseParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockPauseResult> { async clockPauseAt(params: channels.BrowserContextClockPauseAtParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockPauseAtResult> {
await this._context.clock.pause(); await this._context.clock.pauseAt(params.timeString ?? params.timeNumber ?? 0);
} }
async clockResume(params: channels.BrowserContextClockResumeParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockResumeResult> { async clockResume(params: channels.BrowserContextClockResumeParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockResumeResult> {

View file

@ -69,7 +69,7 @@ type Time = {
origin: number; origin: number;
}; };
type LogEntryType = 'fastForward' | 'fastForwardTo' | 'install' | 'pause' | 'resume' | 'runFor' | 'setFixedTime' | 'setSystemTime'; type LogEntryType = 'fastForward' |'install' | 'pauseAt' | 'resume' | 'runFor' | 'setFixedTime' | 'setSystemTime';
export class ClockController { export class ClockController {
readonly _now: Time; readonly _now: Time;
@ -163,9 +163,10 @@ export class ClockController {
throw firstException; throw firstException;
} }
pause() { async pauseAt(time: number) {
this._replayLogOnce(); this._replayLogOnce();
this._innerPause(); this._innerPause();
await this._innerFastForwardTo(time);
} }
private _innerPause() { private _innerPause() {
@ -218,18 +219,18 @@ export class ClockController {
async fastForward(ticks: number) { async fastForward(ticks: number) {
this._replayLogOnce(); this._replayLogOnce();
const ms = ticks | 0; await this._innerFastForwardTo(this._now.ticks + ticks | 0);
for (const timer of this._timers.values()) {
if (this._now.ticks + ms > timer.callAt)
timer.callAt = this._now.ticks + ms;
}
await this.runFor(ms);
} }
async fastForwardTo(time: number) {
this._replayLogOnce(); private async _innerFastForwardTo(toTicks: number) {
const ticks = time - this._now.time; if (toTicks < this._now.ticks)
await this.fastForward(ticks); throw new Error('Cannot fast-forward to the past');
for (const timer of this._timers.values()) {
if (toTicks > timer.callAt)
timer.callAt = toTicks;
}
await this._runTo(toTicks);
} }
addTimer(options: { func: TimerHandler, type: TimerType, delay?: number | string, args?: any[] }): number { addTimer(options: { func: TimerHandler, type: TimerType, delay?: number | string, args?: any[] }): number {
@ -381,11 +382,10 @@ export class ClockController {
this._innerSetTime(param!); this._innerSetTime(param!);
} else if (type === 'fastForward' || type === 'runFor') { } else if (type === 'fastForward' || type === 'runFor') {
this._advanceNow(this._now.ticks + param!); this._advanceNow(this._now.ticks + param!);
} else if (type === 'fastForwardTo') { } else if (type === 'pauseAt') {
this._innerSetTime(param!);
} else if (type === 'pause') {
this._innerPause();
isPaused = true; isPaused = true;
this._innerPause();
this._innerSetTime(param!);
} else if (type === 'resume') { } else if (type === 'resume') {
this._innerResume(); this._innerResume();
isPaused = false; isPaused = false;

View file

@ -17263,21 +17263,6 @@ export interface Clock {
*/ */
fastForward(ticks: number|string): Promise<void>; fastForward(ticks: number|string): Promise<void>;
/**
* Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user
* closing the laptop lid for a while and reopening it at the specified time.
*
* **Usage**
*
* ```js
* await page.clock.fastForwardTo(new Date('2020-02-02'));
* await page.clock.fastForwardTo('2020-02-02');
* ```
*
* @param time
*/
fastForwardTo(time: number|string|Date): Promise<void>;
/** /**
* Install fake implementations for the following time-related functions: * Install fake implementations for the following time-related functions:
* - `Date` * - `Date`
@ -17305,13 +17290,25 @@ export interface Clock {
}): Promise<void>; }): Promise<void>;
/** /**
* Pause timers. Once this method is called, no timers are fired unless * Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired
* [clock.runFor(ticks)](https://playwright.dev/docs/api/class-clock#clock-run-for), * unless [clock.runFor(ticks)](https://playwright.dev/docs/api/class-clock#clock-run-for),
* [clock.fastForward(ticks)](https://playwright.dev/docs/api/class-clock#clock-fast-forward), * [clock.fastForward(ticks)](https://playwright.dev/docs/api/class-clock#clock-fast-forward),
* [clock.fastForwardTo(time)](https://playwright.dev/docs/api/class-clock#clock-fast-forward-to) or * [clock.pauseAt(time)](https://playwright.dev/docs/api/class-clock#clock-pause-at) or
* [clock.resume()](https://playwright.dev/docs/api/class-clock#clock-resume) is called. * [clock.resume()](https://playwright.dev/docs/api/class-clock#clock-resume) is called.
*
* Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it
* at the specified time and pausing.
*
* **Usage**
*
* ```js
* await page.clock.pauseAt(new Date('2020-02-02'));
* await page.clock.pauseAt('2020-02-02');
* ```
*
* @param time
*/ */
pause(): Promise<void>; pauseAt(time: number|string|Date): Promise<void>;
/** /**
* Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual. * Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual.
@ -17349,8 +17346,7 @@ export interface Clock {
setFixedTime(time: number|string|Date): Promise<void>; setFixedTime(time: number|string|Date): Promise<void>;
/** /**
* Sets current system time but does not trigger any timers, unlike * Sets current system time but does not trigger any timers.
* [clock.fastForwardTo(time)](https://playwright.dev/docs/api/class-clock#clock-fast-forward-to).
* *
* **Usage** * **Usage**
* *

View file

@ -1461,9 +1461,8 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT
createTempFile(params: BrowserContextCreateTempFileParams, metadata?: CallMetadata): Promise<BrowserContextCreateTempFileResult>; createTempFile(params: BrowserContextCreateTempFileParams, metadata?: CallMetadata): Promise<BrowserContextCreateTempFileResult>;
updateSubscription(params: BrowserContextUpdateSubscriptionParams, metadata?: CallMetadata): Promise<BrowserContextUpdateSubscriptionResult>; updateSubscription(params: BrowserContextUpdateSubscriptionParams, metadata?: CallMetadata): Promise<BrowserContextUpdateSubscriptionResult>;
clockFastForward(params: BrowserContextClockFastForwardParams, metadata?: CallMetadata): Promise<BrowserContextClockFastForwardResult>; clockFastForward(params: BrowserContextClockFastForwardParams, metadata?: CallMetadata): Promise<BrowserContextClockFastForwardResult>;
clockFastForwardTo(params: BrowserContextClockFastForwardToParams, metadata?: CallMetadata): Promise<BrowserContextClockFastForwardToResult>;
clockInstall(params: BrowserContextClockInstallParams, metadata?: CallMetadata): Promise<BrowserContextClockInstallResult>; clockInstall(params: BrowserContextClockInstallParams, metadata?: CallMetadata): Promise<BrowserContextClockInstallResult>;
clockPause(params?: BrowserContextClockPauseParams, metadata?: CallMetadata): Promise<BrowserContextClockPauseResult>; clockPauseAt(params: BrowserContextClockPauseAtParams, metadata?: CallMetadata): Promise<BrowserContextClockPauseAtResult>;
clockResume(params?: BrowserContextClockResumeParams, metadata?: CallMetadata): Promise<BrowserContextClockResumeResult>; clockResume(params?: BrowserContextClockResumeParams, metadata?: CallMetadata): Promise<BrowserContextClockResumeResult>;
clockRunFor(params: BrowserContextClockRunForParams, metadata?: CallMetadata): Promise<BrowserContextClockRunForResult>; clockRunFor(params: BrowserContextClockRunForParams, metadata?: CallMetadata): Promise<BrowserContextClockRunForResult>;
clockSetFixedTime(params: BrowserContextClockSetFixedTimeParams, metadata?: CallMetadata): Promise<BrowserContextClockSetFixedTimeResult>; clockSetFixedTime(params: BrowserContextClockSetFixedTimeParams, metadata?: CallMetadata): Promise<BrowserContextClockSetFixedTimeResult>;
@ -1765,15 +1764,6 @@ export type BrowserContextClockFastForwardOptions = {
ticksString?: string, ticksString?: string,
}; };
export type BrowserContextClockFastForwardResult = void; export type BrowserContextClockFastForwardResult = void;
export type BrowserContextClockFastForwardToParams = {
timeNumber?: number,
timeString?: string,
};
export type BrowserContextClockFastForwardToOptions = {
timeNumber?: number,
timeString?: string,
};
export type BrowserContextClockFastForwardToResult = void;
export type BrowserContextClockInstallParams = { export type BrowserContextClockInstallParams = {
timeNumber?: number, timeNumber?: number,
timeString?: string, timeString?: string,
@ -1783,9 +1773,15 @@ export type BrowserContextClockInstallOptions = {
timeString?: string, timeString?: string,
}; };
export type BrowserContextClockInstallResult = void; export type BrowserContextClockInstallResult = void;
export type BrowserContextClockPauseParams = {}; export type BrowserContextClockPauseAtParams = {
export type BrowserContextClockPauseOptions = {}; timeNumber?: number,
export type BrowserContextClockPauseResult = void; timeString?: string,
};
export type BrowserContextClockPauseAtOptions = {
timeNumber?: number,
timeString?: string,
};
export type BrowserContextClockPauseAtResult = void;
export type BrowserContextClockResumeParams = {}; export type BrowserContextClockResumeParams = {};
export type BrowserContextClockResumeOptions = {}; export type BrowserContextClockResumeOptions = {};
export type BrowserContextClockResumeResult = void; export type BrowserContextClockResumeResult = void;

View file

@ -1209,17 +1209,15 @@ BrowserContext:
ticksNumber: number? ticksNumber: number?
ticksString: string? ticksString: string?
clockFastForwardTo:
parameters:
timeNumber: number?
timeString: string?
clockInstall: clockInstall:
parameters: parameters:
timeNumber: number? timeNumber: number?
timeString: string? timeString: string?
clockPause: clockPauseAt:
parameters:
timeNumber: number?
timeString: string?
clockResume: clockResume:

View file

@ -1342,10 +1342,11 @@ it.describe('fastForward', () => {
clock.setInterval(shortTimers[1], 100); clock.setInterval(shortTimers[1], 100);
clock.requestAnimationFrame(shortTimers[2]); clock.requestAnimationFrame(shortTimers[2]);
await clock.fastForward(1500); await clock.fastForward(1500);
for (const stub of longTimers) expect(longTimers[0].called).toBeFalsy();
expect(stub.called).toBeFalsy(); expect(longTimers[1].called).toBeFalsy();
for (const stub of shortTimers) expect(shortTimers[0].callCount).toBe(1);
expect(stub.callCount).toBe(1); expect(shortTimers[1].callCount).toBe(1);
expect(shortTimers[2].callCount).toBe(1);
}); });
}); });

View file

@ -36,8 +36,8 @@ const it = test.extend<{ calls: { params: any[] }[] }>({
it.describe('runFor', () => { it.describe('runFor', () => {
it.beforeEach(async ({ page }) => { it.beforeEach(async ({ page }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.clock.pause(); await page.clock.pauseAt(1000);
}); });
it('triggers immediately without specified delay', async ({ page, calls }) => { it('triggers immediately without specified delay', async ({ page, calls }) => {
@ -171,9 +171,8 @@ it.describe('runFor', () => {
it.describe('fastForward', () => { it.describe('fastForward', () => {
it.beforeEach(async ({ page }) => { it.beforeEach(async ({ page }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.setSystemTime(0);
}); });
it(`ignores timers which wouldn't be run`, async ({ page, calls }) => { it(`ignores timers which wouldn't be run`, async ({ page, calls }) => {
@ -194,7 +193,7 @@ it.describe('fastForward', () => {
}); });
await page.clock.fastForward(2000); await page.clock.fastForward(2000);
expect(calls).toEqual([{ params: [2000] }]); expect(calls).toEqual([{ params: [1000 + 2000] }]);
}); });
it('supports string time arguments', async ({ page, calls }) => { it('supports string time arguments', async ({ page, calls }) => {
@ -204,15 +203,14 @@ it.describe('fastForward', () => {
}, 100000); // 100000 = 1:40 }, 100000); // 100000 = 1:40
}); });
await page.clock.fastForward('01:50'); await page.clock.fastForward('01:50');
expect(calls).toEqual([{ params: [110000] }]); expect(calls).toEqual([{ params: [1000 + 110000] }]);
}); });
}); });
it.describe('fastForwardTo', () => { it.describe('fastForwardTo', () => {
it.beforeEach(async ({ page }) => { it.beforeEach(async ({ page }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.setSystemTime(0);
}); });
it(`ignores timers which wouldn't be run`, async ({ page, calls }) => { it(`ignores timers which wouldn't be run`, async ({ page, calls }) => {
@ -221,7 +219,7 @@ it.describe('fastForwardTo', () => {
window.stub('should not be logged'); window.stub('should not be logged');
}, 1000); }, 1000);
}); });
await page.clock.fastForwardTo(500); await page.clock.fastForward(500);
expect(calls).toEqual([]); expect(calls).toEqual([]);
}); });
@ -232,16 +230,15 @@ it.describe('fastForwardTo', () => {
}, 1000); }, 1000);
}); });
await page.clock.fastForwardTo(2000); await page.clock.fastForward(2000);
expect(calls).toEqual([{ params: [2000] }]); expect(calls).toEqual([{ params: [1000 + 2000] }]);
}); });
}); });
it.describe('stubTimers', () => { it.describe('stubTimers', () => {
it.beforeEach(async ({ page }) => { it.beforeEach(async ({ page }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.setSystemTime(0);
}); });
it('sets initial timestamp', async ({ page, calls }) => { it('sets initial timestamp', async ({ page, calls }) => {
await page.clock.setSystemTime(1400); await page.clock.setSystemTime(1400);
@ -295,20 +292,19 @@ it.describe('stubTimers', () => {
return { prev, next }; return { prev, next };
}); });
await page.clock.runFor(1000); await page.clock.runFor(1000);
expect(await promise).toEqual({ prev: 0, next: 1000 }); expect(await promise).toEqual({ prev: 1000, next: 2000 });
}); });
it('fakes Date constructor', async ({ page }) => { it('fakes Date constructor', async ({ page }) => {
const now = await page.evaluate(() => new Date().getTime()); const now = await page.evaluate(() => new Date().getTime());
expect(now).toBe(0); expect(now).toBe(1000);
}); });
}); });
it.describe('stubTimers', () => { it.describe('stubTimers', () => {
it('replaces global performance.timeOrigin', async ({ page }) => { it('replaces global performance.timeOrigin', async ({ page }) => {
await page.clock.install({ time: 1000 }); await page.clock.install({ time: 1000 });
await page.clock.pause(); await page.clock.pauseAt(2000);
await page.clock.setSystemTime(1000);
const promise = page.evaluate(async () => { const promise = page.evaluate(async () => {
const prev = performance.now(); const prev = performance.now();
await new Promise(f => setTimeout(f, 1000)); await new Promise(f => setTimeout(f, 1000));
@ -317,16 +313,15 @@ it.describe('stubTimers', () => {
}); });
await page.clock.runFor(1000); await page.clock.runFor(1000);
expect(await page.evaluate(() => performance.timeOrigin)).toBe(1000); expect(await page.evaluate(() => performance.timeOrigin)).toBe(1000);
expect(await promise).toEqual({ prev: 0, next: 1000 }); expect(await promise).toEqual({ prev: 2000, next: 3000 });
}); });
}); });
it.describe('popup', () => { it.describe('popup', () => {
it('should tick after popup', async ({ page }) => { it('should tick after popup', async ({ page }) => {
await page.clock.install(); await page.clock.install();
await page.clock.pause();
const now = new Date('2015-09-25'); const now = new Date('2015-09-25');
await page.clock.setSystemTime(now); await page.clock.pauseAt(now);
const [popup] = await Promise.all([ const [popup] = await Promise.all([
page.waitForEvent('popup'), page.waitForEvent('popup'),
page.evaluate(() => window.open('about:blank')), page.evaluate(() => window.open('about:blank')),
@ -340,9 +335,8 @@ it.describe('popup', () => {
it('should tick before popup', async ({ page }) => { it('should tick before popup', async ({ page }) => {
await page.clock.install(); await page.clock.install();
await page.clock.pause();
const now = new Date('2015-09-25'); const now = new Date('2015-09-25');
await page.clock.setSystemTime(now); await page.clock.pauseAt(now);
await page.clock.runFor(1000); await page.clock.runFor(1000);
const [popup] = await Promise.all([ const [popup] = await Promise.all([
@ -358,7 +352,6 @@ it.describe('popup', () => {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.end(`<script>window.time = Date.now()</script>`); res.end(`<script>window.time = Date.now()</script>`);
}); });
await page.clock.setSystemTime(0);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
// Wait for 2 second in real life to check that it is past in popup. // Wait for 2 second in real life to check that it is past in popup.
await page.waitForTimeout(2000); await page.waitForTimeout(2000);
@ -376,8 +369,7 @@ it.describe('popup', () => {
res.end(`<script>window.time = Date.now()</script>`); res.end(`<script>window.time = Date.now()</script>`);
}); });
await page.clock.install(); await page.clock.install();
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.setSystemTime(0);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
// Wait for 2 second in real life to check that it is past in popup. // Wait for 2 second in real life to check that it is past in popup.
await page.waitForTimeout(2000); await page.waitForTimeout(2000);
@ -386,7 +378,7 @@ it.describe('popup', () => {
page.evaluate(url => window.open(url), server.PREFIX + '/popup.html'), page.evaluate(url => window.open(url), server.PREFIX + '/popup.html'),
]); ]);
const popupTime = await popup.evaluate('time'); const popupTime = await popup.evaluate('time');
expect(popupTime).toBe(0); expect(popupTime).toBe(1000);
}); });
}); });
@ -457,7 +449,7 @@ it.describe('while running', () => {
it('should fastForwardTo', async ({ page }) => { it('should fastForwardTo', async ({ page }) => {
await page.clock.install({ time: 0 }); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.fastForwardTo(10000); await page.clock.fastForward(10000);
const now = await page.evaluate(() => Date.now()); const now = await page.evaluate(() => Date.now());
expect(now).toBeGreaterThanOrEqual(10000); expect(now).toBeGreaterThanOrEqual(10000);
expect(now).toBeLessThanOrEqual(11000); expect(now).toBeLessThanOrEqual(11000);
@ -466,7 +458,7 @@ it.describe('while running', () => {
it('should pause', async ({ page }) => { it('should pause', async ({ page }) => {
await page.clock.install({ time: 0 }); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
await page.clock.resume(); await page.clock.resume();
const now = await page.evaluate(() => Date.now()); const now = await page.evaluate(() => Date.now());
@ -474,20 +466,19 @@ it.describe('while running', () => {
expect(now).toBeLessThanOrEqual(1000); expect(now).toBeLessThanOrEqual(1000);
}); });
it('should pause and fastForwardTo', async ({ page }) => { it('should pause and fastForward', async ({ page }) => {
await page.clock.install({ time: 0 }); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.fastForwardTo(1000); await page.clock.fastForward(1000);
const now = await page.evaluate(() => Date.now()); const now = await page.evaluate(() => Date.now());
expect(now).toBe(1000); expect(now).toBe(2000);
}); });
it('should set system time on pause', async ({ page }) => { it('should set system time on pause', async ({ page }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.clock.setSystemTime(1000);
const now = await page.evaluate(() => Date.now()); const now = await page.evaluate(() => Date.now());
expect(now).toBe(1000); expect(now).toBe(1000);
}); });
@ -495,9 +486,9 @@ it.describe('while running', () => {
it.describe('while on pause', () => { it.describe('while on pause', () => {
it('fastForward should not run nested immediate', async ({ page, calls }) => { it('fastForward should not run nested immediate', async ({ page, calls }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.evaluate(() => { await page.evaluate(() => {
setTimeout(() => { setTimeout(() => {
window.stub('outer'); window.stub('outer');
@ -511,9 +502,9 @@ it.describe('while on pause', () => {
}); });
it('runFor should not run nested immediate', async ({ page, calls }) => { it('runFor should not run nested immediate', async ({ page, calls }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.evaluate(() => { await page.evaluate(() => {
setTimeout(() => { setTimeout(() => {
window.stub('outer'); window.stub('outer');
@ -527,9 +518,9 @@ it.describe('while on pause', () => {
}); });
it('runFor should not run nested immediate from microtask', async ({ page, calls }) => { it('runFor should not run nested immediate from microtask', async ({ page, calls }) => {
await page.clock.install(); await page.clock.install({ time: 0 });
await page.goto('data:text/html,'); await page.goto('data:text/html,');
await page.clock.pause(); await page.clock.pauseAt(1000);
await page.evaluate(() => { await page.evaluate(() => {
setTimeout(() => { setTimeout(() => {
window.stub('outer'); window.stub('outer');