wip
This commit is contained in:
parent
8bc139b27c
commit
294f0523c7
|
|
@ -35,6 +35,10 @@ Install fake timers with the specified base time.
|
|||
|
||||
The maximum number of timers that will be run in [`method: Clock.runAllTimers`]. Defaults to `1000`.
|
||||
|
||||
### option: Clock.installFakeTimers.speed
|
||||
* since: v1.45
|
||||
- `speed` <[float]>
|
||||
|
||||
## async method: Clock.runAllTimers
|
||||
* since: v1.45
|
||||
- returns: <[int]>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ export class Clock implements api.Clock {
|
|||
this._browserContext = browserContext;
|
||||
}
|
||||
|
||||
async installFakeTimers(time: number | Date, options: { loopLimit?: number } = {}) {
|
||||
async installFakeTimers(time: number | Date, options: { loopLimit?: number, speed?: number } = {}) {
|
||||
const timeMs = time instanceof Date ? time.getTime() : time;
|
||||
await this._browserContext._channel.clockInstallFakeTimers({ time: timeMs, loopLimit: options.loopLimit });
|
||||
await this._browserContext._channel.clockInstallFakeTimers({ time: timeMs, loopLimit: options.loopLimit, speed: options.speed });
|
||||
}
|
||||
|
||||
async runAllTimers(): Promise<number> {
|
||||
|
|
|
|||
|
|
@ -966,6 +966,7 @@ scheme.BrowserContextUpdateSubscriptionResult = tOptional(tObject({}));
|
|||
scheme.BrowserContextClockInstallFakeTimersParams = tObject({
|
||||
time: tNumber,
|
||||
loopLimit: tOptional(tNumber),
|
||||
speed: tOptional(tNumber),
|
||||
});
|
||||
scheme.BrowserContextClockInstallFakeTimersResult = tOptional(tObject({}));
|
||||
scheme.BrowserContextClockRunAllTimersParams = tOptional(tObject({}));
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ export class Clock {
|
|||
this._browserContext = browserContext;
|
||||
}
|
||||
|
||||
async installFakeTimers(time: number, loopLimit: number | undefined) {
|
||||
async installFakeTimers(time: number, loopLimit: number | undefined, speed: number | undefined) {
|
||||
await this._injectScriptIfNeeded();
|
||||
await this._addAndEvaluate(`(() => {
|
||||
globalThis.__pwClock.clock?.uninstall();
|
||||
globalThis.__pwClock.clock = globalThis.__pwClock.install(${JSON.stringify({ now: time, loopLimit })});
|
||||
globalThis.__pwClock.clock = globalThis.__pwClock.install(${JSON.stringify({ now: time, loopLimit, speed })});
|
||||
})();`);
|
||||
this._now = time;
|
||||
this._clockInstalled = true;
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
}
|
||||
|
||||
async clockInstallFakeTimers(params: channels.BrowserContextClockInstallFakeTimersParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockInstallFakeTimersResult> {
|
||||
await this._context.clock.installFakeTimers(params.time, params.loopLimit);
|
||||
await this._context.clock.installFakeTimers(params.time, params.loopLimit, params.speed);
|
||||
}
|
||||
|
||||
async clockRunAllTimers(params: channels.BrowserContextClockRunAllTimersParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockRunAllTimersResult> {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export type ClockConfig = {
|
|||
|
||||
export type InstallConfig = ClockConfig & {
|
||||
toFake?: (keyof ClockMethods)[];
|
||||
speed?: number;
|
||||
};
|
||||
|
||||
enum TimerType {
|
||||
|
|
@ -53,7 +54,8 @@ type Timer = {
|
|||
};
|
||||
|
||||
interface Embedder {
|
||||
postTask(task: () => void): void;
|
||||
performanceNow(): DOMHighResTimeStamp;
|
||||
postTask(task: () => void, timeout?: number): () => void;
|
||||
postTaskPeriodically(task: () => void, delay: number): () => void;
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +68,8 @@ export class ClockController {
|
|||
private _uniqueTimerId = idCounterStart;
|
||||
private _embedder: Embedder;
|
||||
readonly disposables: (() => void)[] = [];
|
||||
private _realTime: { startTicks: number, speed: number, lastSyncTicks: number } | undefined;
|
||||
private _currentRealTimeTimer: { callAt: number, dispose: () => void } | undefined;
|
||||
|
||||
constructor(embedder: Embedder, startDate: Date | number | undefined, loopLimit: number = 1000) {
|
||||
const start = Math.floor(getEpoch(startDate));
|
||||
|
|
@ -145,6 +149,37 @@ export class ClockController {
|
|||
return this.tick(this.getTimeToNextFrame());
|
||||
}
|
||||
|
||||
startRealTime(options: { speed?: number } = {}) {
|
||||
const now = Math.ceil(this._embedder.performanceNow());
|
||||
this._realTime = { startTicks: now, speed: options.speed || 1, lastSyncTicks: now };
|
||||
}
|
||||
|
||||
private _ensureTimer() {
|
||||
const firstTimer = this._firstTimer();
|
||||
if (!firstTimer)
|
||||
return;
|
||||
const callAt = firstTimer.callAt;
|
||||
|
||||
if (this._currentRealTimeTimer && this._currentRealTimeTimer.callAt < callAt)
|
||||
return;
|
||||
|
||||
if (this._currentRealTimeTimer) {
|
||||
this._currentRealTimeTimer.dispose();
|
||||
this._currentRealTimeTimer = undefined;
|
||||
}
|
||||
|
||||
this._currentRealTimeTimer = {
|
||||
callAt,
|
||||
dispose: this._embedder.postTask(() => {
|
||||
const now = Math.ceil(this._embedder.performanceNow());
|
||||
const sinceLastSync = now - this._realTime!.lastSyncTicks;
|
||||
this._realTime!.lastSyncTicks = now;
|
||||
this._currentRealTimeTimer = undefined;
|
||||
this.tick(sinceLastSync * this._realTime!.speed).then(() => this._ensureTimer());
|
||||
}, (callAt - this._now.ticks) / this._realTime!.speed),
|
||||
};
|
||||
}
|
||||
|
||||
async runAll(): Promise<number> {
|
||||
for (let i = 0; i < this._loopLimit; i++) {
|
||||
const numTimers = this._timers.size;
|
||||
|
|
@ -204,6 +239,8 @@ export class ClockController {
|
|||
error: new Error(),
|
||||
};
|
||||
this._timers.set(timer.id, timer);
|
||||
if (this._realTime)
|
||||
this._ensureTimer();
|
||||
return timer.id;
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +449,7 @@ function parseTime(value: number | string): number {
|
|||
|
||||
function mirrorDateProperties(target: any, source: typeof Date): DateConstructor & Date {
|
||||
let prop;
|
||||
for (prop of Object.keys(source) as (keyof DateConstructor)[])
|
||||
for (prop of Object.getOwnPropertyNames(source) as (keyof DateConstructor)[])
|
||||
target[prop] = source[prop];
|
||||
target.toString = () => source.toString();
|
||||
target.prototype = source.prototype;
|
||||
|
|
@ -485,7 +522,7 @@ function createIntl(clock: ClockController, NativeIntl: typeof Intl): typeof Int
|
|||
* All properties of Intl are non-enumerable, so we need
|
||||
* to do a bit of work to get them out.
|
||||
*/
|
||||
for (const key of Object.keys(NativeIntl) as (keyof typeof Intl)[])
|
||||
for (const key of Object.getOwnPropertyNames(NativeIntl) as (keyof typeof Intl)[])
|
||||
ClockIntl[key] = NativeIntl[key];
|
||||
|
||||
ClockIntl.DateTimeFormat = function(...args: any[]) {
|
||||
|
|
@ -661,8 +698,10 @@ function fakePerformance(clock: ClockController, performance: Performance): Perf
|
|||
export function createClock(globalObject: WindowOrWorkerGlobalScope, config: ClockConfig = {}): { clock: ClockController, api: ClockMethods, originals: ClockMethods } {
|
||||
const originals = platformOriginals(globalObject);
|
||||
const embedder = {
|
||||
postTask: (task: () => void) => {
|
||||
originals.bound.setTimeout(task, 0);
|
||||
performanceNow: () => originals.raw.performance!.now(),
|
||||
postTask: (task: () => void, timeout?: number) => {
|
||||
const timerId = originals.bound.setTimeout(task, timeout);
|
||||
return () => originals.bound.clearTimeout(timerId);
|
||||
},
|
||||
postTaskPeriodically: (task: () => void, delay: number) => {
|
||||
const intervalId = globalObject.setInterval(task, delay);
|
||||
|
|
@ -702,6 +741,9 @@ export function install(globalObject: WindowOrWorkerGlobalScope, config: Install
|
|||
});
|
||||
}
|
||||
|
||||
if (typeof config.speed === 'number')
|
||||
clock.startRealTime({ speed: config.speed });
|
||||
|
||||
return { clock, api, originals };
|
||||
}
|
||||
|
||||
|
|
|
|||
2
packages/playwright-core/types/types.d.ts
vendored
2
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -17271,6 +17271,8 @@ export interface Clock {
|
|||
* [clock.runAllTimers()](https://playwright.dev/docs/api/class-clock#clock-run-all-timers). Defaults to `1000`.
|
||||
*/
|
||||
loopLimit?: number;
|
||||
|
||||
speed?: number;
|
||||
}): Promise<void>;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1758,9 +1758,11 @@ export type BrowserContextUpdateSubscriptionResult = void;
|
|||
export type BrowserContextClockInstallFakeTimersParams = {
|
||||
time: number,
|
||||
loopLimit?: number,
|
||||
speed?: number,
|
||||
};
|
||||
export type BrowserContextClockInstallFakeTimersOptions = {
|
||||
loopLimit?: number,
|
||||
speed?: number,
|
||||
};
|
||||
export type BrowserContextClockInstallFakeTimersResult = void;
|
||||
export type BrowserContextClockRunAllTimersParams = {};
|
||||
|
|
|
|||
|
|
@ -1208,6 +1208,7 @@ BrowserContext:
|
|||
parameters:
|
||||
time: number
|
||||
loopLimit: number?
|
||||
speed: number?
|
||||
|
||||
clockRunAllTimers:
|
||||
returns:
|
||||
|
|
|
|||
|
|
@ -2326,6 +2326,40 @@ it.describe('Intl API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it.describe('startRealTime', () => {
|
||||
it.only('should start real time', async ({ clock }) => {
|
||||
clock.startRealTime({ speed: 2 });
|
||||
const start = Date.now();
|
||||
clock.setTimeout(() => {
|
||||
const end = Date.now();
|
||||
console.log('A', clock.Date.now(), end - start);
|
||||
}, 1000);
|
||||
clock.setTimeout(() => {
|
||||
const end = Date.now();
|
||||
console.log('B', clock.Date.now(), end - start);
|
||||
}, 1100);
|
||||
clock.setTimeout(() => {
|
||||
const end = Date.now();
|
||||
console.log('C', clock.Date.now(), end - start);
|
||||
clock.setTimeout(() => {
|
||||
const end = Date.now();
|
||||
console.log('D', clock.Date.now(), end - start);
|
||||
}, 400);
|
||||
}, 300);
|
||||
await new Promise(f => setTimeout(f, 2000));
|
||||
});
|
||||
|
||||
it.only('should start real time interval', async ({ clock }) => {
|
||||
clock.startRealTime({ speed: 2 });
|
||||
const start = Date.now();
|
||||
clock.setInterval(() => {
|
||||
const end = Date.now();
|
||||
console.log('A', clock.Date.now(), end - start);
|
||||
}, 100);
|
||||
await new Promise(f => setTimeout(f, 2000));
|
||||
});
|
||||
});
|
||||
|
||||
interface Stub {
|
||||
called: boolean;
|
||||
callCount: number;
|
||||
|
|
|
|||
|
|
@ -738,4 +738,12 @@ it.describe('setTime', () => {
|
|||
await page.clock.setTime(200);
|
||||
expect(calls).toHaveLength(2);
|
||||
});
|
||||
|
||||
it.only('my clock', async ({ page }) => {
|
||||
await page.clock.installFakeTimers(Date.now(), { speed: 0.5 });
|
||||
await page.goto('https://www.online-stopwatch.com/large-online-clock/');
|
||||
// await page.goto('https://www.example.com');
|
||||
await new Promise(() => {});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue