diff --git a/src/chromium/DOMWorld.ts b/src/chromium/DOMWorld.ts index f065aa790cbe3..8096b454a6119 100644 --- a/src/chromium/DOMWorld.ts +++ b/src/chromium/DOMWorld.ts @@ -25,9 +25,10 @@ import { ElementHandle, JSHandle } from './JSHandle'; import { LifecycleWatcher } from './LifecycleWatcher'; import { TimeoutSettings } from '../TimeoutSettings'; import { ClickOptions, MultiClickOptions, PointerActionOptions, SelectOption } from '../input'; +import * as types from '../types'; const readFileAsync = helper.promisify(fs.readFile); -export class DOMWorld { +export class DOMWorld implements types.DOMEvaluationContext { private _frameManager: FrameManager; private _frame: Frame; private _timeoutSettings: TimeoutSettings; @@ -79,14 +80,14 @@ export class DOMWorld { return this._contextPromise; } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { + async evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluateHandle(pageFunction, ...args); + return context.evaluateHandle(pageFunction, ...args as any); } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { + async evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluate(pageFunction, ...args); + return context.evaluate(pageFunction, ...args as any); } async $(selector: string): Promise { @@ -111,15 +112,14 @@ export class DOMWorld { return value; } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - return document.$eval(selector, pageFunction, ...args); + return document.$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - const value = await document.$$eval(selector, pageFunction, ...args); - return value; + return document.$$eval(selector, pageFunction, ...args as any); } async $$(selector: string): Promise { @@ -439,7 +439,7 @@ class WaitTask { } } -async function waitForPredicatePageFunction(predicateBody: string, polling: string, timeout: number, ...args): Promise { +async function waitForPredicatePageFunction(predicateBody: string, polling: string | number, timeout: number, ...args): Promise { const predicate = new Function('...args', predicateBody); let timedOut = false; if (timeout) diff --git a/src/chromium/ExecutionContext.ts b/src/chromium/ExecutionContext.ts index 9f111fef03cc4..c751081f9d76a 100644 --- a/src/chromium/ExecutionContext.ts +++ b/src/chromium/ExecutionContext.ts @@ -25,11 +25,12 @@ import { Protocol } from './protocol'; import * as injectedSource from '../generated/injectedSource'; import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource'; import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource'; +import * as types from '../types'; export const EVALUATION_SCRIPT_URL = '__playwright_evaluation_script__'; const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; -export class ExecutionContext { +export class ExecutionContext implements types.EvaluationContext { _client: CDPSession; _world: DOMWorld; private _injectedPromise: Promise | null = null; @@ -45,11 +46,11 @@ export class ExecutionContext { return this._world ? this._world.frame() : null; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return await this._evaluateInternal(true /* returnByValue */, pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._evaluateInternal(true /* returnByValue */, pageFunction, ...args); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { return this._evaluateInternal(false /* returnByValue */, pageFunction, ...args); } diff --git a/src/chromium/Frame.ts b/src/chromium/Frame.ts index 719d0d7eddc8f..08abb5f6b36f7 100644 --- a/src/chromium/Frame.ts +++ b/src/chromium/Frame.ts @@ -24,8 +24,9 @@ import { FrameManager } from './FrameManager'; import { ElementHandle, JSHandle } from './JSHandle'; import { Response } from './NetworkManager'; import { Protocol } from './protocol'; +import * as types from '../types'; -export class Frame { +export class Frame implements types.DOMEvaluationContext { _id: string; _frameManager: FrameManager; private _client: CDPSession; @@ -68,12 +69,12 @@ export class Frame { return this._mainWorld.executionContext(); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - return this._mainWorld.evaluateHandle(pageFunction, ...args); + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._mainWorld.evaluateHandle(pageFunction, ...args as any); } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return this._mainWorld.evaluate(pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._mainWorld.evaluate(pageFunction, ...args as any); } async $(selector: string): Promise { @@ -84,12 +85,12 @@ export class Frame { return this._mainWorld.$x(expression); } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this._mainWorld.$eval(selector, pageFunction, ...args); + $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._mainWorld.$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this._mainWorld.$$eval(selector, pageFunction, ...args); + $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._mainWorld.$$eval(selector, pageFunction, ...args as any); } async $$(selector: string): Promise { diff --git a/src/chromium/JSHandle.ts b/src/chromium/JSHandle.ts index 370e8691174b4..358ef0acd36ed 100644 --- a/src/chromium/JSHandle.ts +++ b/src/chromium/JSHandle.ts @@ -26,6 +26,7 @@ import { Page } from './Page'; import { Protocol } from './protocol'; import { releaseObject, valueFromRemoteObject } from './protocolHelper'; import Injected from '../injected/injected'; +import * as types from '../types'; type SelectorRoot = Element | ShadowRoot | Document; @@ -43,7 +44,7 @@ export function createJSHandle(context: ExecutionContext, remoteObject: Protocol return new JSHandle(context, context._client, remoteObject); } -export class JSHandle { +export class JSHandle implements types.HandleEvaluationContext { _context: ExecutionContext; protected _client: CDPSession; _remoteObject: Protocol.Runtime.RemoteObject; @@ -59,12 +60,12 @@ export class JSHandle { return this._context; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return await this.executionContext().evaluate(pageFunction, this, ...args); + evaluate(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluate(pageFunction, this, ...args); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - return await this.executionContext().evaluateHandle(pageFunction, this, ...args); + evaluateHandle(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluateHandle(pageFunction, this, ...args); } async getProperty(propertyName: string): Promise { @@ -432,22 +433,22 @@ export class ElementHandle extends JSHandle { return result; } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const elementHandle = await this.$(selector); if (!elementHandle) throw new Error(`Error: failed to find element matching selector "${selector}"`); - const result = await elementHandle.evaluate(pageFunction, ...args); + const result = await elementHandle.evaluate(pageFunction, ...args as any); await elementHandle.dispose(); return result; } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const arrayHandle = await this.evaluateHandle( (root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root), selector, await this._context._injected() ); - const result = await arrayHandle.evaluate(pageFunction, ...args); + const result = await arrayHandle.evaluate(pageFunction, ...args as any); await arrayHandle.dispose(); return result; } diff --git a/src/chromium/Page.ts b/src/chromium/Page.ts index 8256942129fbf..8347dc75f5d6f 100644 --- a/src/chromium/Page.ts +++ b/src/chromium/Page.ts @@ -43,6 +43,7 @@ import { Protocol } from './protocol'; import { getExceptionMessage, releaseObject, valueFromRemoteObject } from './protocolHelper'; import { Target } from './Target'; import { TaskQueue } from './TaskQueue'; +import * as types from '../types'; const writeFileAsync = helper.promisify(fs.writeFile); @@ -55,7 +56,7 @@ export type Viewport = { hasTouch?: boolean; } -export class Page extends EventEmitter { +export class Page extends EventEmitter implements types.DOMEvaluationContext { private _closed = false; _client: CDPSession; private _target: Target; @@ -230,17 +231,16 @@ export class Page extends EventEmitter { return this.mainFrame().$(selector); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - const context = await this.mainFrame().executionContext(); - return context.evaluateHandle(pageFunction, ...args); + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this.mainFrame().evaluateHandle(pageFunction, ...args as any); } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this.mainFrame().$eval(selector, pageFunction, ...args); + $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.mainFrame().$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this.mainFrame().$$eval(selector, pageFunction, ...args); + $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.mainFrame().$$eval(selector, pageFunction, ...args as any); } async $$(selector: string): Promise { @@ -565,8 +565,8 @@ export class Page extends EventEmitter { return this._viewport; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return this._frameManager.mainFrame().evaluate(pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._frameManager.mainFrame().evaluate(pageFunction, ...args as any); } async evaluateOnNewDocument(pageFunction: Function | string, ...args: any[]) { diff --git a/src/chromium/features/workers.ts b/src/chromium/features/workers.ts index e5de3d7276ce3..e7de94e88b53e 100644 --- a/src/chromium/features/workers.ts +++ b/src/chromium/features/workers.ts @@ -21,6 +21,7 @@ import { debugError } from '../../helper'; import { JSHandle } from '../JSHandle'; import { Protocol } from '../protocol'; import { Events } from '../events'; +import * as types from '../../types'; type AddToConsoleCallback = (type: string, args: JSHandle[], stackTrace: Protocol.Runtime.StackTrace | undefined) => void; type HandleExceptionCallback = (exceptionDetails: Protocol.Runtime.ExceptionDetails) => void; @@ -53,7 +54,7 @@ export class Workers extends EventEmitter { } } -export class Worker extends EventEmitter { +export class Worker extends EventEmitter implements types.EvaluationContext { private _client: CDPSession; private _url: string; private _executionContextPromise: Promise; @@ -85,11 +86,11 @@ export class Worker extends EventEmitter { return this._executionContextPromise; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return (await this._executionContextPromise).evaluate(pageFunction, ...args); + async evaluate(pageFunction: string | ((...args: Args) => R | Promise), ...args: types.Boxed): Promise { + return (await this._executionContextPromise).evaluate(pageFunction, ...args as any); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - return (await this._executionContextPromise).evaluateHandle(pageFunction, ...args); + async evaluateHandle(pageFunction: string | ((...args: Args) => any), ...args: types.Boxed): Promise { + return (await this._executionContextPromise).evaluateHandle(pageFunction, ...args as any); } } diff --git a/src/firefox/DOMWorld.ts b/src/firefox/DOMWorld.ts index 2df99d64a8ab9..cfffd2855278a 100644 --- a/src/firefox/DOMWorld.ts +++ b/src/firefox/DOMWorld.ts @@ -4,10 +4,11 @@ import * as fs from 'fs'; import * as util from 'util'; import {ElementHandle, JSHandle} from './JSHandle'; import { ExecutionContext } from './ExecutionContext'; +import * as types from '../types'; const readFileAsync = util.promisify(fs.readFile); -export class DOMWorld { +export class DOMWorld implements types.DOMEvaluationContext { _frame: any; _timeoutSettings: any; _documentPromise: any; @@ -61,14 +62,14 @@ export class DOMWorld { throw new Error('Method not implemented.'); } - async evaluateHandle(pageFunction, ...args): Promise { + async evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluateHandle(pageFunction, ...args); + return context.evaluateHandle(pageFunction, ...args as any); } - async evaluate(pageFunction, ...args): Promise { + async evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluate(pageFunction, ...args); + return context.evaluate(pageFunction, ...args as any); } async $(selector: string): Promise { @@ -87,14 +88,14 @@ export class DOMWorld { return document.$x(expression); } - async $eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - return document.$eval(selector, pageFunction, ...args); + return document.$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - return document.$$eval(selector, pageFunction, ...args); + return document.$$eval(selector, pageFunction, ...args as any); } async $$(selector: string): Promise> { diff --git a/src/firefox/ExecutionContext.ts b/src/firefox/ExecutionContext.ts index 1d55c04cc908e..0cf9d8bf98fdf 100644 --- a/src/firefox/ExecutionContext.ts +++ b/src/firefox/ExecutionContext.ts @@ -21,8 +21,9 @@ import { Frame } from './FrameManager'; import * as injectedSource from '../generated/injectedSource'; import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource'; import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource'; +import * as types from '../types'; -export class ExecutionContext { +export class ExecutionContext implements types.EvaluationContext { _session: any; _frame: Frame; _executionContextId: string; @@ -34,7 +35,7 @@ export class ExecutionContext { this._executionContextId = executionContextId; } - async evaluateHandle(pageFunction, ...args): Promise { + async evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { if (helper.isString(pageFunction)) { const payload = await this._session.send('Runtime.evaluate', { expression: pageFunction.trim(), @@ -62,7 +63,7 @@ export class ExecutionContext { throw new Error('Passed function is not well-serializable!'); } } - args = args.map(arg => { + const protocolArgs = args.map(arg => { if (arg instanceof JSHandle) { if (arg._context !== this) throw new Error('JSHandles can be evaluated only in the context they were created!'); @@ -84,7 +85,7 @@ export class ExecutionContext { try { callFunctionPromise = this._session.send('Runtime.callFunction', { functionDeclaration: functionText, - args, + args: protocolArgs, executionContextId: this._executionContextId }); } catch (err) { @@ -106,9 +107,9 @@ export class ExecutionContext { return this._frame; } - async evaluate(pageFunction, ...args): Promise { + async evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { try { - const handle = await this.evaluateHandle(pageFunction, ...args); + const handle = await this.evaluateHandle(pageFunction, ...args as any); const result = await handle.jsonValue(); await handle.dispose(); return result; diff --git a/src/firefox/FrameManager.ts b/src/firefox/FrameManager.ts index 0a91468262bb3..96fda6029e4c6 100644 --- a/src/firefox/FrameManager.ts +++ b/src/firefox/FrameManager.ts @@ -11,6 +11,7 @@ import { JSHandle, ElementHandle } from './JSHandle'; import { TimeoutSettings } from '../TimeoutSettings'; import { NetworkManager } from './NetworkManager'; import { MultiClickOptions, ClickOptions, SelectOption } from '../input'; +import * as types from '../types'; export const FrameManagerEvents = { FrameNavigated: Symbol('FrameManagerEvents.FrameNavigated'), @@ -139,7 +140,7 @@ export class FrameManager extends EventEmitter { } } -export class Frame { +export class Frame implements types.DOMEvaluationContext { _parentFrame: Frame|null = null; private _session: JugglerSession; _page: Page; @@ -359,8 +360,8 @@ export class Frame { return this._mainWorld.setContent(html); } - async evaluate(pageFunction, ...args): Promise { - return this._mainWorld.evaluate(pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._mainWorld.evaluate(pageFunction, ...args as any); } async $(selector: string): Promise { @@ -371,20 +372,20 @@ export class Frame { return this._mainWorld.$$(selector); } - async $eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { - return this._mainWorld.$eval(selector, pageFunction, ...args); + $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._mainWorld.$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { - return this._mainWorld.$$eval(selector, pageFunction, ...args); + $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._mainWorld.$$eval(selector, pageFunction, ...args as any); } async $x(expression: string): Promise> { return this._mainWorld.$x(expression); } - async evaluateHandle(pageFunction, ...args): Promise { - return this._mainWorld.evaluateHandle(pageFunction, ...args); + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._mainWorld.evaluateHandle(pageFunction, ...args as any); } async addScriptTag(options: { content?: string; path?: string; type?: string; url?: string; }): Promise { diff --git a/src/firefox/JSHandle.ts b/src/firefox/JSHandle.ts index 4962273aeadf6..72157e02f4067 100644 --- a/src/firefox/JSHandle.ts +++ b/src/firefox/JSHandle.ts @@ -20,12 +20,13 @@ import { assert, debugError, helper } from '../helper'; import { ClickOptions, fillFunction, MultiClickOptions, selectFunction, SelectOption } from '../input'; import { JugglerSession } from './Connection'; import Injected from '../injected/injected'; - -type SelectorRoot = Element | ShadowRoot | Document; import { ExecutionContext } from './ExecutionContext'; import { Frame } from './FrameManager'; +import * as types from '../types'; + +type SelectorRoot = Element | ShadowRoot | Document; -export class JSHandle { +export class JSHandle implements types.HandleEvaluationContext { _context: ExecutionContext; protected _session: JugglerSession; private _executionContextId: string; @@ -54,12 +55,12 @@ export class JSHandle { return this._context; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise<(any)> { - return await this.executionContext().evaluate(pageFunction, this, ...args); + evaluate(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluate(pageFunction, this, ...args); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - return await this.executionContext().evaluateHandle(pageFunction, this, ...args); + evaluateHandle(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluateHandle(pageFunction, this, ...args); } toString(): string { @@ -188,7 +189,7 @@ export class ElementHandle extends JSHandle { } isIntersectingViewport(): Promise { - return this._frame.evaluate(async element => { + return this._frame.evaluate(async (element: Element) => { const visibleRatio = await new Promise(resolve => { const observer = new IntersectionObserver(entries => { resolve(entries[0].intersectionRatio); @@ -231,22 +232,22 @@ export class ElementHandle extends JSHandle { return result; } - async $eval(selector: string, pageFunction: Function | string, ...args: Array): Promise { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const elementHandle = await this.$(selector); if (!elementHandle) throw new Error(`Error: failed to find element matching selector "${selector}"`); - const result = await this._frame.evaluate(pageFunction, elementHandle, ...args); + const result = await this._frame.evaluate(pageFunction, elementHandle, ...args as any); await elementHandle.dispose(); return result; } - async $$eval(selector: string, pageFunction: Function | string, ...args: Array): Promise { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const arrayHandle = await this._frame.evaluateHandle( (root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root), this, selector, await this._context._injected() ); - const result = await this._frame.evaluate(pageFunction, arrayHandle, ...args); + const result = await this._frame.evaluate(pageFunction, arrayHandle, ...args as any); await arrayHandle.dispose(); return result; } @@ -268,7 +269,7 @@ export class ElementHandle extends JSHandle { } async _scrollIntoViewIfNeeded() { - const error = await this._frame.evaluate(async element => { + const error = await this._frame.evaluate(async (element: Element) => { if (!element.isConnected) return 'Node is detached from document'; if (element.nodeType !== Node.ELEMENT_NODE) @@ -284,7 +285,7 @@ export class ElementHandle extends JSHandle { requestAnimationFrame(() => {}); }); if (visibleRatio !== 1.0) - element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'}); + element.scrollIntoView({block: 'center', inline: 'center', behavior: ('instant' as ScrollBehavior)}); return false; }, this); if (error) @@ -325,7 +326,7 @@ export class ElementHandle extends JSHandle { } async focus() { - await this._frame.evaluate(element => element.focus(), this); + await this._frame.evaluate((element: HTMLElement) => element.focus(), this); } async type(text: string, options: { delay: (number | undefined); } | undefined) { diff --git a/src/firefox/Page.ts b/src/firefox/Page.ts index cb2490d492239..0a202fe1880d4 100644 --- a/src/firefox/Page.ts +++ b/src/firefox/Page.ts @@ -16,11 +16,12 @@ import { createHandle, ElementHandle, JSHandle } from './JSHandle'; import { NavigationWatchdog } from './NavigationWatchdog'; import { NetworkManager, NetworkManagerEvents, Request, Response } from './NetworkManager'; import { ClickOptions, MultiClickOptions } from '../input'; +import * as types from '../types'; const writeFileAsync = helper.promisify(fs.writeFile); -export class Page extends EventEmitter { +export class Page extends EventEmitter implements types.DOMEvaluationContext { private _timeoutSettings: TimeoutSettings; private _session: JugglerSession; private _target: Target; @@ -460,8 +461,8 @@ export class Page extends EventEmitter { } } - evaluate(pageFunction, ...args) { - return this.mainFrame().evaluate(pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this.mainFrame().evaluate(pageFunction, ...args as any); } addScriptTag(options: { content?: string; path?: string; type?: string; url?: string; }): Promise { @@ -532,20 +533,20 @@ export class Page extends EventEmitter { return this._frameManager.mainFrame().$$(selector); } - $eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { - return this._frameManager.mainFrame().$eval(selector, pageFunction, ...args); + $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._frameManager.mainFrame().$eval(selector, pageFunction, ...args as any); } - $$eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(object | undefined)> { - return this._frameManager.mainFrame().$$eval(selector, pageFunction, ...args); + $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this._frameManager.mainFrame().$$eval(selector, pageFunction, ...args as any); } $x(expression: string): Promise> { return this._frameManager.mainFrame().$x(expression); } - evaluateHandle(pageFunction, ...args) { - return this._frameManager.mainFrame().evaluateHandle(pageFunction, ...args); + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._frameManager.mainFrame().evaluateHandle(pageFunction, ...args as any); } async close(options: any = {}) { diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000000000..4b304710141e1 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export type Boxed = { [Index in keyof Args]: Args[Index] | Handle }; +export type Func = string | ((...args: Args) => R | Promise); +export type FuncOn = string | ((on: On, ...args: Args) => R | Promise); + +export interface EvaluationContext { + evaluate(fn: Func, ...args: Boxed): Promise; + evaluateHandle(fn: Func, ...args: Boxed): Promise; +} + +export interface DOMEvaluationContext extends EvaluationContext { + $eval(selector: string, fn: FuncOn, ...args: Boxed): Promise; + $$eval(selector: string, fn: FuncOn, ...args: Boxed): Promise; +} + +export interface HandleEvaluationContext { + evaluate(fn: FuncOn, ...args: Boxed): Promise; + evaluateHandle(fn: FuncOn, ...args: Boxed): Promise; +} diff --git a/src/webkit/Browser.ts b/src/webkit/Browser.ts index 316232b9952fe..e5e63eded91cb 100644 --- a/src/webkit/Browser.ts +++ b/src/webkit/Browser.ts @@ -75,7 +75,7 @@ export class Browser extends EventEmitter { async userAgent(): Promise { const context = await this.createIncognitoBrowserContext(); const page = await context.newPage(); - const userAgent = await page.evaluate('navigator.userAgent'); + const userAgent = await page.evaluate(() => navigator.userAgent); context.close(); return userAgent; } diff --git a/src/webkit/ExecutionContext.ts b/src/webkit/ExecutionContext.ts index 627454343746f..5ec33f7a63a33 100644 --- a/src/webkit/ExecutionContext.ts +++ b/src/webkit/ExecutionContext.ts @@ -24,11 +24,12 @@ import { Protocol } from './protocol'; import * as injectedSource from '../generated/injectedSource'; import * as cssSelectorEngineSource from '../generated/cssSelectorEngineSource'; import * as xpathSelectorEngineSource from '../generated/xpathSelectorEngineSource'; +import * as types from '../types'; export const EVALUATION_SCRIPT_URL = '__playwright_evaluation_script__'; const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m; -export class ExecutionContext { +export class ExecutionContext implements types.EvaluationContext { _globalObjectId?: string; _session: TargetSession; _frame: Frame; @@ -55,11 +56,11 @@ export class ExecutionContext { return this._frame; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return await this._evaluateInternal(true /* returnByValue */, pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._evaluateInternal(true /* returnByValue */, pageFunction, ...args); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { return this._evaluateInternal(false /* returnByValue */, pageFunction, ...args); } diff --git a/src/webkit/FrameManager.ts b/src/webkit/FrameManager.ts index 2026c911d0e18..a7b8daec167b1 100644 --- a/src/webkit/FrameManager.ts +++ b/src/webkit/FrameManager.ts @@ -27,6 +27,7 @@ import { NetworkManager, NetworkManagerEvents, Request, Response } from './Netwo import { Page } from './Page'; import { Protocol } from './protocol'; import { MultiClickOptions, ClickOptions, SelectOption } from '../input'; +import * as types from '../types'; const readFileAsync = helper.promisify(fs.readFile); export const FrameManagerEvents = { @@ -229,7 +230,7 @@ export class FrameManager extends EventEmitter { } } -export class Frame { +export class Frame implements types.DOMEvaluationContext { _id: string; _frameManager: FrameManager; _session: any; @@ -309,14 +310,14 @@ export class Frame { return this._contextPromise; } - async evaluateHandle(pageFunction: Function | string, ...args: Array): Promise { + async evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluateHandle(pageFunction, ...args); + return context.evaluateHandle(pageFunction, ...args as any); } - async evaluate(pageFunction: Function | string, ...args: Array): Promise { + async evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { const context = await this.executionContext(); - return context.evaluate(pageFunction, ...args); + return context.evaluate(pageFunction, ...args as any); } async $(selector: string): Promise { @@ -341,14 +342,14 @@ export class Frame { return value; } - async $eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(any)> { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - return document.$eval(selector, pageFunction, ...args); + return document.$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: Array): Promise<(any)> { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const document = await this._document(); - const value = await document.$$eval(selector, pageFunction, ...args); + const value = await document.$$eval(selector, pageFunction, ...args as any); return value; } diff --git a/src/webkit/JSHandle.ts b/src/webkit/JSHandle.ts index f4af598f8298d..510238e755e3f 100644 --- a/src/webkit/JSHandle.ts +++ b/src/webkit/JSHandle.ts @@ -24,6 +24,7 @@ import { Page } from './Page'; import { Protocol } from './protocol'; import { releaseObject, valueFromRemoteObject } from './protocolHelper'; import Injected from '../injected/injected'; +import * as types from '../types'; type SelectorRoot = Element | ShadowRoot | Document; @@ -38,7 +39,7 @@ export function createJSHandle(context: ExecutionContext, remoteObject: Protocol return new JSHandle(context, context._session, remoteObject); } -export class JSHandle { +export class JSHandle implements types.HandleEvaluationContext { _context: ExecutionContext; protected _client: TargetSession; _remoteObject: Protocol.Runtime.RemoteObject; @@ -54,12 +55,12 @@ export class JSHandle { return this._context; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return await this.executionContext().evaluate(pageFunction, this, ...args); + evaluate(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluate(pageFunction, this, ...args); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - return await this.executionContext().evaluateHandle(pageFunction, this, ...args); + evaluateHandle(pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.executionContext().evaluateHandle(pageFunction, this, ...args); } async getProperty(propertyName: string): Promise { @@ -311,22 +312,22 @@ export class ElementHandle extends JSHandle { return result; } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const elementHandle = await this.$(selector); if (!elementHandle) throw new Error(`Error: failed to find element matching selector "${selector}"`); - const result = await elementHandle.evaluate(pageFunction, ...args); + const result = await elementHandle.evaluate(pageFunction, ...args as any); await elementHandle.dispose(); return result; } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise { + async $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { const arrayHandle = await this.evaluateHandle( (root: SelectorRoot, selector: string, injected: Injected) => injected.querySelectorAll('css=' + selector, root), selector, await this._context._injected() ); - const result = await arrayHandle.evaluate(pageFunction, ...args); + const result = await arrayHandle.evaluate(pageFunction, ...args as any); await arrayHandle.dispose(); return result; } diff --git a/src/webkit/Page.ts b/src/webkit/Page.ts index 5c87b76f2bda9..271cb19b54861 100644 --- a/src/webkit/Page.ts +++ b/src/webkit/Page.ts @@ -32,6 +32,7 @@ import { Protocol } from './protocol'; import { valueFromRemoteObject } from './protocolHelper'; import { Target } from './Target'; import { TaskQueue } from './TaskQueue'; +import * as types from '../types'; const writeFileAsync = helper.promisify(fs.writeFile); @@ -40,7 +41,7 @@ export type Viewport = { height: number; } -export class Page extends EventEmitter { +export class Page extends EventEmitter implements types.DOMEvaluationContext { private _closed = false; private _session: TargetSession; private _target: Target; @@ -198,17 +199,16 @@ export class Page extends EventEmitter { return this.mainFrame().$(selector); } - async evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise { - const context = await this.mainFrame().executionContext(); - return context.evaluateHandle(pageFunction, ...args); + evaluateHandle(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this.mainFrame().evaluateHandle(pageFunction, ...args as any); } - async $eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this.mainFrame().$eval(selector, pageFunction, ...args); + $eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.mainFrame().$eval(selector, pageFunction, ...args as any); } - async $$eval(selector: string, pageFunction: Function | string, ...args: any[]): Promise<(object | undefined)> { - return this.mainFrame().$$eval(selector, pageFunction, ...args); + $$eval(selector: string, pageFunction: types.FuncOn, ...args: types.Boxed): Promise { + return this.mainFrame().$$eval(selector, pageFunction, ...args as any); } async $$(selector: string): Promise { @@ -342,8 +342,8 @@ export class Page extends EventEmitter { return this._viewport; } - async evaluate(pageFunction: Function | string, ...args: any[]): Promise { - return this._frameManager.mainFrame().evaluate(pageFunction, ...args); + evaluate(pageFunction: types.Func, ...args: types.Boxed): Promise { + return this._frameManager.mainFrame().evaluate(pageFunction, ...args as any); } async evaluateOnNewDocument(pageFunction: Function | string, ...args: Array) {