chore: add clock.next() (#31097)
This commit is contained in:
parent
a617dd0df8
commit
76e977a934
|
|
@ -4,6 +4,7 @@
|
||||||
Playwright uses [@sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) for clock emulation. Clock is installed for the entire [BrowserContext], so the time
|
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.
|
in all the pages and iframes is controlled by the same clock.
|
||||||
|
|
||||||
|
|
||||||
## async method: Clock.install
|
## async method: Clock.install
|
||||||
* since: v1.45
|
* since: v1.45
|
||||||
|
|
||||||
|
|
@ -42,6 +43,14 @@ Tells `@sinonjs/fake-timers` to increment mocked time automatically based on the
|
||||||
Relevant only when using with [`option: shouldAdvanceTime`]. Increment mocked time by advanceTimeDelta ms every advanceTimeDelta ms change
|
Relevant only when using with [`option: shouldAdvanceTime`]. Increment mocked time by advanceTimeDelta ms every advanceTimeDelta ms change
|
||||||
in the real system time (default: 20).
|
in the real system time (default: 20).
|
||||||
|
|
||||||
|
|
||||||
|
## async method: Clock.next
|
||||||
|
* since: v1.45
|
||||||
|
- returns: <[int]> Fake milliseconds since the unix epoch.
|
||||||
|
|
||||||
|
Advances the clock to the the moment of the first scheduled timer, firing it.
|
||||||
|
|
||||||
|
|
||||||
## async method: Clock.jump
|
## async method: Clock.jump
|
||||||
* since: v1.45
|
* since: v1.45
|
||||||
|
|
||||||
|
|
@ -54,7 +63,6 @@ This can be used to simulate the JS engine (such as a browser) being put to slee
|
||||||
|
|
||||||
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.runAll
|
## async method: Clock.runAll
|
||||||
* since: v1.45
|
* since: v1.45
|
||||||
- returns: <[int]> Fake milliseconds since the unix epoch.
|
- returns: <[int]> Fake milliseconds since the unix epoch.
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,11 @@ export class Clock implements api.Clock {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async next(): Promise<number> {
|
||||||
|
const result = await this._browserContext._channel.clockNext();
|
||||||
|
return result.fakeTime;
|
||||||
|
}
|
||||||
|
|
||||||
async runAll(): Promise<number> {
|
async runAll(): Promise<number> {
|
||||||
const result = await this._browserContext._channel.clockRunAll();
|
const result = await this._browserContext._channel.clockRunAll();
|
||||||
return result.fakeTime;
|
return result.fakeTime;
|
||||||
|
|
|
||||||
|
|
@ -976,6 +976,10 @@ scheme.BrowserContextClockJumpParams = tObject({
|
||||||
timeString: tOptional(tString),
|
timeString: tOptional(tString),
|
||||||
});
|
});
|
||||||
scheme.BrowserContextClockJumpResult = tOptional(tObject({}));
|
scheme.BrowserContextClockJumpResult = tOptional(tObject({}));
|
||||||
|
scheme.BrowserContextClockNextParams = tOptional(tObject({}));
|
||||||
|
scheme.BrowserContextClockNextResult = tObject({
|
||||||
|
fakeTime: tNumber,
|
||||||
|
});
|
||||||
scheme.BrowserContextClockRunAllParams = tOptional(tObject({}));
|
scheme.BrowserContextClockRunAllParams = tOptional(tObject({}));
|
||||||
scheme.BrowserContextClockRunAllResult = tObject({
|
scheme.BrowserContextClockRunAllResult = tObject({
|
||||||
fakeTime: tNumber,
|
fakeTime: tNumber,
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,13 @@ export class Clock {
|
||||||
|
|
||||||
async jump(time: number | string) {
|
async jump(time: number | string) {
|
||||||
this._assertInstalled();
|
this._assertInstalled();
|
||||||
await this._addAndEvaluate(`globalThis.__pwFakeTimers.jump(${JSON.stringify(time)}); 0`);
|
await this._addAndEvaluate(`globalThis.__pwFakeTimers.jump(${JSON.stringify(time)})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async next(): Promise<number> {
|
||||||
|
this._assertInstalled();
|
||||||
|
await this._browserContext.addInitScript(`globalThis.__pwFakeTimers.next()`);
|
||||||
|
return await this._evaluateInFrames(`globalThis.__pwFakeTimers.nextAsync()`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runAll(): Promise<number> {
|
async runAll(): Promise<number> {
|
||||||
|
|
|
||||||
|
|
@ -320,6 +320,10 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
await this._context.clock.jump(params.timeString || params.timeNumber || 0);
|
await this._context.clock.jump(params.timeString || params.timeNumber || 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clockNext(params: channels.BrowserContextClockNextParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockNextResult> {
|
||||||
|
return { fakeTime: await this._context.clock.next() };
|
||||||
|
}
|
||||||
|
|
||||||
async clockRunAll(params: channels.BrowserContextClockRunAllParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockRunAllResult> {
|
async clockRunAll(params: channels.BrowserContextClockRunAllParams, metadata?: CallMetadata | undefined): Promise<channels.BrowserContextClockRunAllResult> {
|
||||||
return { fakeTime: await this._context.clock.runAll() };
|
return { fakeTime: await this._context.clock.runAll() };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1654,7 +1654,6 @@ function withGlobal(_global) {
|
||||||
* @returns {Clock}
|
* @returns {Clock}
|
||||||
*/
|
*/
|
||||||
function install(config) {
|
function install(config) {
|
||||||
console.log('INSTALL', config);
|
|
||||||
if (
|
if (
|
||||||
arguments.length > 1 ||
|
arguments.length > 1 ||
|
||||||
config instanceof Date ||
|
config instanceof Date ||
|
||||||
|
|
|
||||||
5
packages/playwright-core/types/types.d.ts
vendored
5
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -17289,6 +17289,11 @@ export interface Clock {
|
||||||
*/
|
*/
|
||||||
jump(time: number|string): Promise<void>;
|
jump(time: number|string): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances the clock to the the moment of the first scheduled timer, firing it.
|
||||||
|
*/
|
||||||
|
next(): Promise<number>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs all pending timers until there are none remaining. If new timers are added while it is executing they will be
|
* Runs all pending timers until there are none remaining. If new timers are added while it is executing they will be
|
||||||
* run as well. This makes it easier to run asynchronous tests to completion without worrying about the number of
|
* run as well. This makes it easier to run asynchronous tests to completion without worrying about the number of
|
||||||
|
|
|
||||||
|
|
@ -1462,6 +1462,7 @@ export interface BrowserContextChannel extends BrowserContextEventTarget, EventT
|
||||||
updateSubscription(params: BrowserContextUpdateSubscriptionParams, metadata?: CallMetadata): Promise<BrowserContextUpdateSubscriptionResult>;
|
updateSubscription(params: BrowserContextUpdateSubscriptionParams, metadata?: CallMetadata): Promise<BrowserContextUpdateSubscriptionResult>;
|
||||||
clockInstall(params: BrowserContextClockInstallParams, metadata?: CallMetadata): Promise<BrowserContextClockInstallResult>;
|
clockInstall(params: BrowserContextClockInstallParams, metadata?: CallMetadata): Promise<BrowserContextClockInstallResult>;
|
||||||
clockJump(params: BrowserContextClockJumpParams, metadata?: CallMetadata): Promise<BrowserContextClockJumpResult>;
|
clockJump(params: BrowserContextClockJumpParams, metadata?: CallMetadata): Promise<BrowserContextClockJumpResult>;
|
||||||
|
clockNext(params?: BrowserContextClockNextParams, metadata?: CallMetadata): Promise<BrowserContextClockNextResult>;
|
||||||
clockRunAll(params?: BrowserContextClockRunAllParams, metadata?: CallMetadata): Promise<BrowserContextClockRunAllResult>;
|
clockRunAll(params?: BrowserContextClockRunAllParams, metadata?: CallMetadata): Promise<BrowserContextClockRunAllResult>;
|
||||||
clockRunToLast(params?: BrowserContextClockRunToLastParams, metadata?: CallMetadata): Promise<BrowserContextClockRunToLastResult>;
|
clockRunToLast(params?: BrowserContextClockRunToLastParams, metadata?: CallMetadata): Promise<BrowserContextClockRunToLastResult>;
|
||||||
clockTick(params: BrowserContextClockTickParams, metadata?: CallMetadata): Promise<BrowserContextClockTickResult>;
|
clockTick(params: BrowserContextClockTickParams, metadata?: CallMetadata): Promise<BrowserContextClockTickResult>;
|
||||||
|
|
@ -1777,6 +1778,11 @@ export type BrowserContextClockJumpOptions = {
|
||||||
timeString?: string,
|
timeString?: string,
|
||||||
};
|
};
|
||||||
export type BrowserContextClockJumpResult = void;
|
export type BrowserContextClockJumpResult = void;
|
||||||
|
export type BrowserContextClockNextParams = {};
|
||||||
|
export type BrowserContextClockNextOptions = {};
|
||||||
|
export type BrowserContextClockNextResult = {
|
||||||
|
fakeTime: number,
|
||||||
|
};
|
||||||
export type BrowserContextClockRunAllParams = {};
|
export type BrowserContextClockRunAllParams = {};
|
||||||
export type BrowserContextClockRunAllOptions = {};
|
export type BrowserContextClockRunAllOptions = {};
|
||||||
export type BrowserContextClockRunAllResult = {
|
export type BrowserContextClockRunAllResult = {
|
||||||
|
|
|
||||||
|
|
@ -1219,6 +1219,10 @@ BrowserContext:
|
||||||
timeNumber: number?
|
timeNumber: number?
|
||||||
timeString: string?
|
timeString: string?
|
||||||
|
|
||||||
|
clockNext:
|
||||||
|
returns:
|
||||||
|
fakeTime: number
|
||||||
|
|
||||||
clockRunAll:
|
clockRunAll:
|
||||||
returns:
|
returns:
|
||||||
fakeTime: number
|
fakeTime: number
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ it.describe('tick', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.clock.tick(0);
|
await page.clock.tick(0);
|
||||||
expect(calls).toEqual([{ params: [] }]);
|
expect(calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not trigger without sufficient delay', async ({ page, calls }) => {
|
it('does not trigger without sufficient delay', async ({ page, calls }) => {
|
||||||
|
|
@ -58,7 +58,7 @@ it.describe('tick', () => {
|
||||||
setTimeout(window.stub, 100);
|
setTimeout(window.stub, 100);
|
||||||
});
|
});
|
||||||
await page.clock.tick(100);
|
await page.clock.tick(100);
|
||||||
expect(calls).toEqual([{ params: [] }]);
|
expect(calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('triggers simultaneous timers', async ({ page, calls }) => {
|
it('triggers simultaneous timers', async ({ page, calls }) => {
|
||||||
|
|
@ -68,7 +68,7 @@ it.describe('tick', () => {
|
||||||
setTimeout(window.stub, 100);
|
setTimeout(window.stub, 100);
|
||||||
});
|
});
|
||||||
await page.clock.tick(100);
|
await page.clock.tick(100);
|
||||||
expect(calls).toEqual([{ params: [] }, { params: [] }]);
|
expect(calls).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('triggers multiple simultaneous timers', async ({ page, calls }) => {
|
it('triggers multiple simultaneous timers', async ({ page, calls }) => {
|
||||||
|
|
@ -91,7 +91,7 @@ it.describe('tick', () => {
|
||||||
await page.clock.tick(50);
|
await page.clock.tick(50);
|
||||||
expect(calls).toEqual([]);
|
expect(calls).toEqual([]);
|
||||||
await page.clock.tick(100);
|
await page.clock.tick(100);
|
||||||
expect(calls).toEqual([{ params: [] }]);
|
expect(calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('triggers event when some throw', async ({ page, calls }) => {
|
it('triggers event when some throw', async ({ page, calls }) => {
|
||||||
|
|
@ -102,7 +102,7 @@ it.describe('tick', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(page.clock.tick(120)).rejects.toThrow();
|
await expect(page.clock.tick(120)).rejects.toThrow();
|
||||||
expect(calls).toEqual([{ params: [] }]);
|
expect(calls).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates updated Date while ticking', async ({ page, calls }) => {
|
it('creates updated Date while ticking', async ({ page, calls }) => {
|
||||||
|
|
@ -210,7 +210,7 @@ it.describe('jump', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it.describe('runAllAsyn', () => {
|
it.describe('runAll', () => {
|
||||||
it('if there are no timers just return', async ({ page }) => {
|
it('if there are no timers just return', async ({ page }) => {
|
||||||
await page.clock.install();
|
await page.clock.install();
|
||||||
await page.clock.runAll();
|
await page.clock.runAll();
|
||||||
|
|
@ -612,3 +612,113 @@ it.describe('shouldAdvanceTime', () => {
|
||||||
expect(timeDifference).toBe(0);
|
expect(timeDifference).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.describe('popup', () => {
|
||||||
|
it('should tick after popup', async ({ page }) => {
|
||||||
|
const now = new Date('2015-09-25');
|
||||||
|
await page.clock.install({ now });
|
||||||
|
const [popup] = await Promise.all([
|
||||||
|
page.waitForEvent('popup'),
|
||||||
|
page.evaluate(() => window.open('about:blank')),
|
||||||
|
]);
|
||||||
|
const popupTime = await popup.evaluate(() => Date.now());
|
||||||
|
expect(popupTime).toBe(now.getTime());
|
||||||
|
await page.clock.tick(1000);
|
||||||
|
const popupTimeAfter = await popup.evaluate(() => Date.now());
|
||||||
|
expect(popupTimeAfter).toBe(now.getTime() + 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should tick before popup', async ({ page, browserName }) => {
|
||||||
|
it.skip(browserName === 'chromium');
|
||||||
|
const now = new Date('2015-09-25');
|
||||||
|
await page.clock.install({ now });
|
||||||
|
const newNow = await page.clock.tick(1000);
|
||||||
|
expect(newNow).toBe(now.getTime() + 1000);
|
||||||
|
|
||||||
|
const [popup] = await Promise.all([
|
||||||
|
page.waitForEvent('popup'),
|
||||||
|
page.evaluate(() => window.open('about:blank')),
|
||||||
|
]);
|
||||||
|
const popupTime = await popup.evaluate(() => Date.now());
|
||||||
|
expect(popupTime).toBe(now.getTime() + 1000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.describe('next', () => {
|
||||||
|
it('triggers the next timer', async ({ page, calls }) => {
|
||||||
|
await page.clock.install();
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
setTimeout(window.stub, 100);
|
||||||
|
});
|
||||||
|
expect(await page.clock.next()).toBe(100);
|
||||||
|
expect(calls).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not trigger simultaneous timers', async ({ page, calls }) => {
|
||||||
|
await page.clock.install();
|
||||||
|
await page.evaluate(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 100);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subsequent calls trigger simultaneous timers', async ({ page, calls }) => {
|
||||||
|
await page.clock.install();
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 100);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 100);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 99);
|
||||||
|
setTimeout(() => {
|
||||||
|
window.stub();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toHaveLength(1);
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toHaveLength(2);
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toHaveLength(3);
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toHaveLength(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('subsequent calls triggers simultaneous timers with zero callAt', async ({ page, calls }) => {
|
||||||
|
await page.clock.install();
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
window.stub(1);
|
||||||
|
setTimeout(() => {
|
||||||
|
setTimeout(() => window.stub(2), 0);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toEqual([{ params: [1] }]);
|
||||||
|
await page.clock.next();
|
||||||
|
expect(calls).toEqual([{ params: [1] }, { params: [2] }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws exception thrown by timer', async ({ page, calls }) => {
|
||||||
|
await page.clock.install();
|
||||||
|
await page.evaluate(async () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
throw new Error();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(page.clock.next()).rejects.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue