mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat(slowmo): only slowmo once per user action (#3012)
This changes the behavior of slowmo to slow down user actions instead of every protocol command. This makes slowmo a lot more predictable. Without this, there is no way to set slowmo to a good value without incurring a huge delay at the start of your test when it sets things up.
This commit is contained in:
parent
b0667e8bc5
commit
97157520a6
@ -30,12 +30,11 @@ export interface BrowserProcess {
|
||||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
export type BrowserOptions = {
|
||||
export type BrowserOptions = types.UIOptions & {
|
||||
name: string,
|
||||
downloadsPath?: string,
|
||||
headful?: boolean,
|
||||
persistent?: types.BrowserContextOptions, // Undefined means no persistent context.
|
||||
slowMo?: number,
|
||||
browserProcess: BrowserProcess,
|
||||
proxy?: ProxySettings,
|
||||
};
|
||||
|
||||
@ -21,7 +21,7 @@ import { Events as CommonEvents } from '../events';
|
||||
import { assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, Worker } from '../page';
|
||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import * as types from '../types';
|
||||
import { ConnectionEvents, CRConnection, CRSession } from './crConnection';
|
||||
import { CRPage } from './crPage';
|
||||
@ -48,7 +48,7 @@ export class CRBrowser extends BrowserBase {
|
||||
private _tracingClient: CRSession | undefined;
|
||||
|
||||
static async connect(transport: ConnectionTransport, options: BrowserOptions, devtools?: CRDevTools): Promise<CRBrowser> {
|
||||
const connection = new CRConnection(SlowMoTransport.wrap(transport, options.slowMo));
|
||||
const connection = new CRConnection(transport);
|
||||
const browser = new CRBrowser(connection, options);
|
||||
browser._devtools = devtools;
|
||||
const session = connection.rootSession;
|
||||
|
||||
13
src/dom.ts
13
src/dom.ts
@ -103,6 +103,10 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||
}
|
||||
return this._debugScriptPromise;
|
||||
}
|
||||
|
||||
async doSlowMo() {
|
||||
return this.frame._page._doSlowMo();
|
||||
}
|
||||
}
|
||||
|
||||
export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
@ -200,6 +204,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
async dispatchEvent(type: string, eventInit: Object = {}) {
|
||||
await this._evaluateInMain(([injected, node, { type, eventInit }]) =>
|
||||
injected.dispatchEvent(node, type, eventInit), { type, eventInit });
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async _scrollRectIntoViewIfNeeded(rect?: types.Rect): Promise<'error:notvisible' | 'error:notconnected' | 'done'> {
|
||||
@ -407,7 +412,9 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
const selectOptions = [...elements, ...values];
|
||||
return this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => {
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
return throwFatalDOMError(await this._evaluateInUtility(([injected, node, selectOptions]) => injected.selectOptions(node, selectOptions), selectOptions));
|
||||
const value = await this._evaluateInUtility(([injected, node, selectOptions]) => injected.selectOptions(node, selectOptions), selectOptions);
|
||||
await this._page._doSlowMo();
|
||||
return throwFatalDOMError(value);
|
||||
});
|
||||
}
|
||||
|
||||
@ -480,12 +487,14 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||
progress.throwIfAborted(); // Avoid action that has side-effects.
|
||||
await this._page._delegate.setInputFiles(this as any as ElementHandle<HTMLInputElement>, files);
|
||||
});
|
||||
await this._page._doSlowMo();
|
||||
return 'done';
|
||||
}
|
||||
|
||||
async focus(): Promise<void> {
|
||||
return this._page._runAbortableTask(async progress => {
|
||||
await this._page._runAbortableTask(async progress => {
|
||||
const result = await this._focus(progress);
|
||||
await this._page._doSlowMo();
|
||||
return assertDone(throwRetargetableDOMError(result));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ import { Events } from '../events';
|
||||
import { assert, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import * as types from '../types';
|
||||
import { ConnectionEvents, FFConnection } from './ffConnection';
|
||||
import { FFPage } from './ffPage';
|
||||
@ -35,7 +35,7 @@ export class FFBrowser extends BrowserBase {
|
||||
private _version = '';
|
||||
|
||||
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<FFBrowser> {
|
||||
const connection = new FFConnection(SlowMoTransport.wrap(transport, options.slowMo));
|
||||
const connection = new FFConnection(transport);
|
||||
const browser = new FFBrowser(connection, options);
|
||||
const promises: Promise<any>[] = [
|
||||
connection.send('Browser.enable', { attachToDefaultContext: !!options.persistent }),
|
||||
|
||||
@ -463,7 +463,9 @@ export class Frame {
|
||||
await helper.waitForEvent(progress, this._eventEmitter, kAddLifecycleEvent, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||
|
||||
const request = event.newDocument ? event.newDocument.request : undefined;
|
||||
return request ? request._finalRequest().response() : null;
|
||||
const response = request ? request._finalRequest().response() : null;
|
||||
await this._page._doSlowMo();
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
@ -521,12 +523,16 @@ export class Frame {
|
||||
|
||||
async _evaluateExpressionHandle(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
const context = await this._mainContext();
|
||||
return context.evaluateExpressionHandleInternal(expression, isFunction, arg);
|
||||
const handle = await context.evaluateExpressionHandleInternal(expression, isFunction, arg);
|
||||
await this._page._doSlowMo();
|
||||
return handle;
|
||||
}
|
||||
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
const context = await this._mainContext();
|
||||
return context.evaluateExpressionInternal(expression, isFunction, arg);
|
||||
const value = await context.evaluateExpressionInternal(expression, isFunction, arg);
|
||||
await this._page._doSlowMo();
|
||||
return value;
|
||||
}
|
||||
|
||||
async $(selector: string): Promise<dom.ElementHandle<Element> | null> {
|
||||
@ -558,11 +564,12 @@ export class Frame {
|
||||
async dispatchEvent(selector: string, type: string, eventInit?: Object, options: types.TimeoutOptions = {}): Promise<void> {
|
||||
const info = selectors._parseSelector(selector);
|
||||
const task = dom.dispatchEventTask(info, type, eventInit || {});
|
||||
return this._page._runAbortableTask(async progress => {
|
||||
await this._page._runAbortableTask(async progress => {
|
||||
progress.log(`Dispatching "${type}" event on selector "${selector}"...`);
|
||||
// Note: we always dispatch events in the main world.
|
||||
await this._scheduleRerunnableTask(progress, 'main', task);
|
||||
}, this._page._timeoutSettings.timeout(options));
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async _$evalExpression(selector: string, expression: string, isFunction: boolean, arg: any): Promise<any> {
|
||||
@ -618,6 +625,7 @@ export class Frame {
|
||||
document.close();
|
||||
}, { html, tag });
|
||||
await Promise.all([contentPromise, lifecyclePromise]);
|
||||
await this._page._doSlowMo();
|
||||
});
|
||||
}
|
||||
|
||||
@ -802,6 +810,7 @@ export class Frame {
|
||||
|
||||
async focus(selector: string, options: types.TimeoutOptions = {}) {
|
||||
await this._retryWithSelectorIfNotConnected(selector, options, (progress, handle) => handle._focus(progress));
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async textContent(selector: string, options: types.TimeoutOptions = {}): Promise<string | null> {
|
||||
|
||||
@ -355,6 +355,11 @@ export function getFromENV(name: string) {
|
||||
return value;
|
||||
}
|
||||
|
||||
export async function doSlowMo(amount?: number) {
|
||||
if (!amount)
|
||||
return;
|
||||
await new Promise(x => setTimeout(x, amount));
|
||||
}
|
||||
|
||||
export async function mkdirIfNeeded(filePath: string) {
|
||||
// This will harmlessly throw on windows if the dirname is the root directory.
|
||||
|
||||
35
src/input.ts
35
src/input.ts
@ -17,6 +17,7 @@
|
||||
import { assert } from './helper';
|
||||
import * as keyboardLayout from './usKeyboardLayout';
|
||||
import * as types from './types';
|
||||
import type { Page } from './page';
|
||||
|
||||
export const keypadLocation = keyboardLayout.keypadLocation;
|
||||
|
||||
@ -39,12 +40,14 @@ export interface RawKeyboard {
|
||||
}
|
||||
|
||||
export class Keyboard {
|
||||
private _raw: RawKeyboard;
|
||||
private _pressedModifiers = new Set<types.KeyboardModifier>();
|
||||
private _pressedKeys = new Set<string>();
|
||||
private _raw: RawKeyboard;
|
||||
private _page: Page;
|
||||
|
||||
constructor(raw: RawKeyboard) {
|
||||
constructor(raw: RawKeyboard, page: Page) {
|
||||
this._raw = raw;
|
||||
this._page = page;
|
||||
}
|
||||
|
||||
async down(key: string) {
|
||||
@ -55,6 +58,7 @@ export class Keyboard {
|
||||
this._pressedModifiers.add(description.key as types.KeyboardModifier);
|
||||
const text = description.text;
|
||||
await this._raw.keydown(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location, autoRepeat, text);
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
private _keyDescriptionForString(keyString: string): KeyDescription {
|
||||
@ -75,10 +79,12 @@ export class Keyboard {
|
||||
this._pressedModifiers.delete(description.key as types.KeyboardModifier);
|
||||
this._pressedKeys.delete(description.code);
|
||||
await this._raw.keyup(this._pressedModifiers, description.code, description.keyCode, description.keyCodeWithoutLocation, description.key, description.location);
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async insertText(text: string) {
|
||||
await this._raw.sendText(text);
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async type(text: string, options?: { delay?: number }) {
|
||||
@ -111,15 +117,19 @@ export class Keyboard {
|
||||
}
|
||||
|
||||
const tokens = split(key);
|
||||
const promises = [];
|
||||
key = tokens[tokens.length - 1];
|
||||
for (let i = 0; i < tokens.length - 1; ++i)
|
||||
await this.down(tokens[i]);
|
||||
await this.down(key);
|
||||
if (options.delay)
|
||||
promises.push(this.down(tokens[i]));
|
||||
promises.push(this.down(key));
|
||||
if (options.delay) {
|
||||
await Promise.all(promises);
|
||||
await new Promise(f => setTimeout(f, options.delay));
|
||||
await this.up(key);
|
||||
}
|
||||
promises.push(this.up(key));
|
||||
for (let i = tokens.length - 2; i >= 0; --i)
|
||||
await this.up(tokens[i]);
|
||||
promises.push(this.up(tokens[i]));
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
async _ensureModifiers(modifiers: types.KeyboardModifier[]): Promise<types.KeyboardModifier[]> {
|
||||
@ -153,16 +163,18 @@ export interface RawMouse {
|
||||
}
|
||||
|
||||
export class Mouse {
|
||||
private _raw: RawMouse;
|
||||
private _keyboard: Keyboard;
|
||||
private _x = 0;
|
||||
private _y = 0;
|
||||
private _lastButton: 'none' | types.MouseButton = 'none';
|
||||
private _buttons = new Set<types.MouseButton>();
|
||||
private _raw: RawMouse;
|
||||
private _page: Page;
|
||||
|
||||
constructor(raw: RawMouse, keyboard: Keyboard) {
|
||||
constructor(raw: RawMouse, page: Page) {
|
||||
this._raw = raw;
|
||||
this._keyboard = keyboard;
|
||||
this._page = page;
|
||||
this._keyboard = this._page.keyboard;
|
||||
}
|
||||
|
||||
async move(x: number, y: number, options: { steps?: number } = {}) {
|
||||
@ -175,6 +187,7 @@ export class Mouse {
|
||||
const middleX = fromX + (x - fromX) * (i / steps);
|
||||
const middleY = fromY + (y - fromY) * (i / steps);
|
||||
await this._raw.move(middleX, middleY, this._lastButton, this._buttons, this._keyboard._modifiers());
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,6 +196,7 @@ export class Mouse {
|
||||
this._lastButton = button;
|
||||
this._buttons.add(button);
|
||||
await this._raw.down(this._x, this._y, this._lastButton, this._buttons, this._keyboard._modifiers(), clickCount);
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async up(options: { button?: types.MouseButton, clickCount?: number } = {}) {
|
||||
@ -190,6 +204,7 @@ export class Mouse {
|
||||
this._lastButton = 'none';
|
||||
this._buttons.delete(button);
|
||||
await this._raw.up(this._x, this._y, button, this._buttons, this._keyboard._modifiers(), clickCount);
|
||||
await this._page._doSlowMo();
|
||||
}
|
||||
|
||||
async click(x: number, y: number, options: { delay?: number, button?: types.MouseButton, clickCount?: number } = {}) {
|
||||
|
||||
@ -73,6 +73,10 @@ export class ExecutionContext {
|
||||
createHandle(remoteObject: RemoteObject): JSHandle {
|
||||
return this._delegate.createHandle(this, remoteObject);
|
||||
}
|
||||
|
||||
async doSlowMo() {
|
||||
// overrided in FrameExecutionContext
|
||||
}
|
||||
}
|
||||
|
||||
export class JSHandle<T = any> {
|
||||
@ -106,8 +110,10 @@ export class JSHandle<T = any> {
|
||||
return evaluate(this._context, false /* returnByValue */, pageFunction, this, arg);
|
||||
}
|
||||
|
||||
_evaluateExpression(expression: string, isFunction: boolean, returnByValue: boolean, arg: any) {
|
||||
return evaluateExpression(this._context, returnByValue, expression, isFunction, this, arg);
|
||||
async _evaluateExpression(expression: string, isFunction: boolean, returnByValue: boolean, arg: any) {
|
||||
const value = await evaluateExpression(this._context, returnByValue, expression, isFunction, this, arg);1;
|
||||
await this._context.doSlowMo();
|
||||
return value;
|
||||
}
|
||||
|
||||
async getProperty(propertyName: string): Promise<JSHandle> {
|
||||
|
||||
25
src/page.ts
25
src/page.ts
@ -133,8 +133,8 @@ export class Page extends EventEmitter {
|
||||
extraHTTPHeaders: null,
|
||||
};
|
||||
this.accessibility = new accessibility.Accessibility(delegate.getAccessibilityTree.bind(delegate));
|
||||
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
||||
this.mouse = new input.Mouse(delegate.rawMouse, this.keyboard);
|
||||
this.keyboard = new input.Keyboard(delegate.rawKeyboard, this);
|
||||
this.mouse = new input.Mouse(delegate.rawMouse, this);
|
||||
this._timeoutSettings = new TimeoutSettings(browserContext._timeoutSettings);
|
||||
this._screenshotter = new Screenshotter(this);
|
||||
this._frameManager = new frames.FrameManager(this);
|
||||
@ -143,6 +143,13 @@ export class Page extends EventEmitter {
|
||||
this.coverage = delegate.coverage ? delegate.coverage() : null;
|
||||
}
|
||||
|
||||
async _doSlowMo() {
|
||||
const slowMo = this._browserContext._browserBase._options.slowMo;
|
||||
if (!slowMo)
|
||||
return;
|
||||
await new Promise(x => setTimeout(x, slowMo));
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
this._frameManager.dispose();
|
||||
assert(this._closedState !== 'closed', 'Page closed twice');
|
||||
@ -237,7 +244,9 @@ export class Page extends EventEmitter {
|
||||
async reload(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
const waitPromise = this.mainFrame().waitForNavigation(options);
|
||||
await this._delegate.reload();
|
||||
return waitPromise;
|
||||
const response = waitPromise;
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async goBack(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
@ -247,7 +256,9 @@ export class Page extends EventEmitter {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
return waitPromise;
|
||||
const response = await waitPromise;
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async goForward(options?: types.NavigateOptions): Promise<network.Response | null> {
|
||||
@ -257,7 +268,9 @@ export class Page extends EventEmitter {
|
||||
waitPromise.catch(() => {});
|
||||
return null;
|
||||
}
|
||||
return waitPromise;
|
||||
const response = await waitPromise;
|
||||
await this._doSlowMo();
|
||||
return response;
|
||||
}
|
||||
|
||||
async emulateMedia(options: { media?: types.MediaType | null, colorScheme?: types.ColorScheme | null }) {
|
||||
@ -270,11 +283,13 @@ export class Page extends EventEmitter {
|
||||
if (options.colorScheme !== undefined)
|
||||
this._state.colorScheme = options.colorScheme;
|
||||
await this._delegate.updateEmulateMedia();
|
||||
await this._doSlowMo();
|
||||
}
|
||||
|
||||
async setViewportSize(viewportSize: types.Size) {
|
||||
this._state.viewportSize = { ...viewportSize };
|
||||
await this._delegate.setViewportSize(this._state.viewportSize);
|
||||
await this._doSlowMo();
|
||||
}
|
||||
|
||||
viewportSize(): types.Size | null {
|
||||
|
||||
@ -44,48 +44,6 @@ export interface ConnectionTransport {
|
||||
onclose?: () => void,
|
||||
}
|
||||
|
||||
export class SlowMoTransport implements ConnectionTransport {
|
||||
private readonly _delay: number;
|
||||
private readonly _delegate: ConnectionTransport;
|
||||
|
||||
onmessage?: (message: ProtocolResponse) => void;
|
||||
onclose?: () => void;
|
||||
|
||||
static wrap(transport: ConnectionTransport, delay?: number): ConnectionTransport {
|
||||
return delay ? new SlowMoTransport(transport, delay) : transport;
|
||||
}
|
||||
|
||||
constructor(transport: ConnectionTransport, delay: number) {
|
||||
this._delay = delay;
|
||||
this._delegate = transport;
|
||||
this._delegate.onmessage = this._onmessage.bind(this);
|
||||
this._delegate.onclose = this._onClose.bind(this);
|
||||
}
|
||||
|
||||
private _onmessage(message: ProtocolResponse) {
|
||||
if (this.onmessage)
|
||||
this.onmessage(message);
|
||||
}
|
||||
|
||||
private _onClose() {
|
||||
if (this.onclose)
|
||||
this.onclose();
|
||||
this._delegate.onmessage = undefined;
|
||||
this._delegate.onclose = undefined;
|
||||
}
|
||||
|
||||
send(s: ProtocolRequest) {
|
||||
setTimeout(() => {
|
||||
if (this._delegate.onmessage)
|
||||
this._delegate.send(s);
|
||||
}, this._delay);
|
||||
}
|
||||
|
||||
close() {
|
||||
this._delegate.close();
|
||||
}
|
||||
}
|
||||
|
||||
export class WebSocketTransport implements ConnectionTransport {
|
||||
private _ws: WebSocket;
|
||||
private _progress: Progress;
|
||||
|
||||
@ -282,7 +282,7 @@ type LaunchOptionsBase = {
|
||||
chromiumSandbox?: boolean,
|
||||
slowMo?: number,
|
||||
};
|
||||
export type LaunchOptions = LaunchOptionsBase & {
|
||||
export type LaunchOptions = LaunchOptionsBase & UIOptions & {
|
||||
firefoxUserPrefs?: { [key: string]: string | number | boolean },
|
||||
};
|
||||
export type LaunchPersistentOptions = LaunchOptionsBase & BrowserContextOptions;
|
||||
@ -333,3 +333,7 @@ export type Error = {
|
||||
name: string,
|
||||
stack?: string,
|
||||
};
|
||||
|
||||
export type UIOptions = {
|
||||
slowMo?: number;
|
||||
};
|
||||
|
||||
@ -21,7 +21,7 @@ import { Events } from '../events';
|
||||
import { helper, RegisteredListener, assert } from '../helper';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding } from '../page';
|
||||
import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
import * as types from '../types';
|
||||
import { Protocol } from './protocol';
|
||||
import { kPageProxyMessageReceived, PageProxyMessageReceivedPayload, WKConnection, WKSession } from './wkConnection';
|
||||
@ -38,7 +38,7 @@ export class WKBrowser extends BrowserBase {
|
||||
private readonly _eventListeners: RegisteredListener[];
|
||||
|
||||
static async connect(transport: ConnectionTransport, options: BrowserOptions): Promise<WKBrowser> {
|
||||
const browser = new WKBrowser(SlowMoTransport.wrap(transport, options.slowMo), options);
|
||||
const browser = new WKBrowser(transport, options);
|
||||
const promises: Promise<any>[] = [
|
||||
browser._browserSession.send('Playwright.enable'),
|
||||
];
|
||||
|
||||
245
test/slowmo.spec.ts
Normal file
245
test/slowmo.spec.ts
Normal file
@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import './base.fixture';
|
||||
|
||||
import { attachFrame } from './utils';
|
||||
|
||||
const {WIRE} = testOptions;
|
||||
|
||||
async function checkSlowMo(toImpl, page, task) {
|
||||
let didSlowMo = false;
|
||||
const orig = toImpl(page)._doSlowMo;
|
||||
toImpl(page)._doSlowMo = async function (...args) {
|
||||
if (didSlowMo)
|
||||
throw new Error('already did slowmo');
|
||||
await new Promise(x => setTimeout(x, 100));
|
||||
didSlowMo = true;
|
||||
return orig.call(this, ...args)
|
||||
}
|
||||
await task();
|
||||
expect(!!didSlowMo).toBe(true);
|
||||
}
|
||||
|
||||
async function checkPageSlowMo(toImpl, page, task) {
|
||||
await page.setContent(`
|
||||
<button>a</button>
|
||||
<input type="checkbox" class="check">
|
||||
<input type="checkbox" checked=true class="uncheck">
|
||||
<input class="fill">
|
||||
<select>
|
||||
<option>foo</option>
|
||||
</select>
|
||||
<input type="file" class="file">
|
||||
`)
|
||||
await checkSlowMo(toImpl, page, task);
|
||||
}
|
||||
|
||||
it.skip(WIRE)('Page SlowMo $$eval', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.$$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo $eval', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo check', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.check('.check'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo click', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.click('button'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo dblclick', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.dblclick('button'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo dispatchEvent', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.dispatchEvent('button', 'click'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo emulateMedia', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.emulateMedia({media: 'print'}));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo evaluate', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.evaluate(() => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo evaluateHandle', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.evaluateHandle(() => window));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo fill', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.fill('.fill', 'foo'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo focus', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.focus('button'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo goto', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.goto('about:blank'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo hover', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.hover('button'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo press', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.press('button', 'Enter'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo reload', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.reload());
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo setContent', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.setContent('hello world'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo selectOption', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.selectOption('select', 'foo'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo setInputFiles', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.setInputFiles('.file', []));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo setViewportSize', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.setViewportSize({height: 400, width: 400}));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo type', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.type('.fill', 'a'));
|
||||
});
|
||||
it.skip(WIRE)('Page SlowMo uncheck', async ({page, toImpl}) => {
|
||||
await checkPageSlowMo(toImpl, page, () => page.uncheck('.uncheck'));
|
||||
});
|
||||
|
||||
async function checkFrameSlowMo(toImpl, page, server, task) {
|
||||
const frame = await attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
await frame.setContent(`
|
||||
<button>a</button>
|
||||
<input type="checkbox" class="check">
|
||||
<input type="checkbox" checked=true class="uncheck">
|
||||
<input class="fill">
|
||||
<select>
|
||||
<option>foo</option>
|
||||
</select>
|
||||
<input type="file" class="file">
|
||||
`)
|
||||
await checkSlowMo(toImpl, page, task.bind(null, frame));
|
||||
}
|
||||
|
||||
it.skip(WIRE)('Frame SlowMo $$eval', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.$$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo $eval', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo check', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.check('.check'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo click', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.click('button'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo dblclick', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.dblclick('button'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo dispatchEvent', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.dispatchEvent('button', 'click'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo evaluate', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluate(() => void 0));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo evaluateHandle', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluateHandle(() => window));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo fill', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.fill('.fill', 'foo'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo focus', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.focus('button'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo goto', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.goto('about:blank'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo hover', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.hover('button'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo press', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.press('button', 'Enter'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo setContent', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.setContent('hello world'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo selectOption', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.selectOption('select', 'foo'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo setInputFiles', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.setInputFiles('.file', []));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo type', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.type('.fill', 'a'));
|
||||
});
|
||||
it.skip(WIRE)('Frame SlowMo uncheck', async({page, server, toImpl}) => {
|
||||
await checkFrameSlowMo(toImpl, page, server, frame => frame.uncheck('.uncheck'));
|
||||
});
|
||||
|
||||
async function checkElementSlowMo(toImpl, page, selector, task) {
|
||||
await page.setContent(`
|
||||
<button>a</button>
|
||||
<input type="checkbox" class="check">
|
||||
<input type="checkbox" checked=true class="uncheck">
|
||||
<input class="fill">
|
||||
<select>
|
||||
<option>foo</option>
|
||||
</select>
|
||||
<input type="file" class="file">
|
||||
`)
|
||||
const element = await page.$(selector);
|
||||
await checkSlowMo(toImpl, page, task.bind(null, element));
|
||||
}
|
||||
it.skip(WIRE)('ElementHandle SlowMo $$eval', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'body', element => element.$$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo $eval', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'body', element => element.$eval('button', () => void 0));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo check', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, '.check', element => element.check());
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo click', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.click());
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo dblclick', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.dblclick());
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo dispatchEvent', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.dispatchEvent('click'));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo evaluate', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.evaluate(() => void 0));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo evaluateHandle', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.evaluateHandle(() => void 0));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo fill', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, '.fill', element => element.fill('foo'));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo focus', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.focus());
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo hover', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.hover());
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo press', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'button', element => element.press('Enter'));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo selectOption', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, 'select', element => element.selectOption('foo'));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo setInputFiles', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, '.file', element => element.setInputFiles([]));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo type', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, '.fill', element => element.type( 'a'));
|
||||
});
|
||||
it.skip(WIRE)('ElementHandle SlowMo uncheck', async ({page, toImpl}) => {
|
||||
await checkElementSlowMo(toImpl, page, '.uncheck', element => element.uncheck());
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user