feat(types): make our client classes implement public types (#4817)

This patch:
- introduces non-exported but used in api/impl struct types (e.g. Point);
- makes all client classes implement respective public api interface.

Pros:
- Typescript is now responsible for type checking.
  We can remove our doclint checker (not removed yet).
- Electron and Android types can be defined in the same way
  (this is not implemented yet).
- We can move most of the type structs like Point to the public api
  and make some of them available.

Cons:
- Any cons?
This commit is contained in:
Dmitry Gozman 2020-12-26 17:05:57 -08:00 committed by GitHub
parent dc25173ae3
commit 34c1b338be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 196 additions and 210 deletions

View file

@ -17,6 +17,7 @@
import * as channels from '../protocol/channels';
import { ElementHandle } from './elementHandle';
import * as api from '../../types/types';
type SerializedAXNode = Omit<channels.AXNode, 'valueString' | 'valueNumber' | 'children' | 'checked' | 'pressed'> & {
value?: string|number,
@ -38,7 +39,7 @@ function axNodeFromProtocol(axNode: channels.AXNode): SerializedAXNode {
return result;
}
export class Accessibility {
export class Accessibility implements api.Accessibility {
private _channel: channels.PageChannel;
constructor(channel: channels.PageChannel) {

View file

@ -21,8 +21,9 @@ import { ChannelOwner } from './channelOwner';
import { Events } from './events';
import { BrowserContextOptions } from './types';
import { isSafeCloseError } from '../utils/errors';
import * as api from '../../types/types';
export class Browser extends ChannelOwner<channels.BrowserChannel, channels.BrowserInitializer> {
export class Browser extends ChannelOwner<channels.BrowserChannel, channels.BrowserInitializer> implements api.Browser {
readonly _contexts = new Set<BrowserContext>();
private _isConnected = true;
private _closedPromise: Promise<void>;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Page, BindingCall, FunctionWithSource } from './page';
import { Page, BindingCall } from './page';
import * as network from './network';
import * as channels from '../protocol/channels';
import * as util from 'util';
@ -30,16 +30,18 @@ import { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageS
import { isUnderTest, headersObjectToArray, mkdirIfNeeded } from '../utils/utils';
import { isSafeCloseError } from '../utils/errors';
import { serializeArgument } from './jsHandle';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel, channels.BrowserContextInitializer> {
export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel, channels.BrowserContextInitializer> implements api.BrowserContext {
_pages = new Set<Page>();
private _routes: { url: URLMatch, handler: network.RouteHandler }[] = [];
readonly _browser: Browser | null = null;
readonly _browserName: string;
readonly _bindings = new Map<string, FunctionWithSource>();
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
_timeoutSettings = new TimeoutSettings();
_ownerPage: Page | undefined;
private _closedPromise: Promise<void>;
@ -182,7 +184,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
});
}
async exposeBinding(name: string, playwrightBinding: FunctionWithSource, options: { handle?: boolean } = {}): Promise<void> {
async exposeBinding(name: string, playwrightBinding: (source: structs.BindingSource, ...args: any[]) => any, options: { handle?: boolean } = {}): Promise<void> {
return this._wrapApiCall('browserContext.exposeBinding', async () => {
await this._channel.exposeBinding({ name, needsHandle: options.handle });
this._bindings.set(name, playwrightBinding);
@ -192,7 +194,7 @@ export class BrowserContext extends ChannelOwner<channels.BrowserContextChannel,
async exposeFunction(name: string, playwrightFunction: Function): Promise<void> {
return this._wrapApiCall('browserContext.exposeFunction', async () => {
await this._channel.exposeBinding({ name });
const binding: FunctionWithSource = (source, ...args) => playwrightFunction(...args);
const binding = (source: structs.BindingSource, ...args: any[]) => playwrightFunction(...args);
this._bindings.set(name, binding);
});
}

View file

@ -32,19 +32,21 @@ import { assert, makeWaitForNextTask, mkdirIfNeeded } from '../utils/utils';
import { SelectorsOwner, sharedSelectors } from './selectors';
import { kBrowserClosedError } from '../utils/errors';
import { Stream } from './stream';
import * as api from '../../types/types';
export interface BrowserServerLauncher {
launchServer(options?: LaunchServerOptions): Promise<BrowserServer>;
launchServer(options?: LaunchServerOptions): Promise<api.BrowserServer>;
}
export interface BrowserServer {
// This is here just for api generation and checking.
export interface BrowserServer extends api.BrowserServer {
process(): ChildProcess;
wsEndpoint(): string;
close(): Promise<void>;
kill(): Promise<void>;
}
export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, channels.BrowserTypeInitializer> {
export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, channels.BrowserTypeInitializer> implements api.BrowserType<api.Browser> {
private _timeoutSettings = new TimeoutSettings();
_serverLauncher?: BrowserServerLauncher;
@ -83,7 +85,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
}, logger);
}
async launchServer(options: LaunchServerOptions = {}): Promise<BrowserServer> {
async launchServer(options: LaunchServerOptions = {}): Promise<api.BrowserServer> {
if (!this._serverLauncher)
throw new Error('Launching server is not supported');
return this._serverLauncher.launchServer(options);

View file

@ -17,8 +17,9 @@
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import { Protocol } from '../server/chromium/protocol';
import * as api from '../../types/types';
export class CDPSession extends ChannelOwner<channels.CDPSessionChannel, channels.CDPSessionInitializer> {
export class CDPSession extends ChannelOwner<channels.CDPSessionChannel, channels.CDPSessionInitializer> implements api.CDPSession {
static from(cdpSession: channels.CDPSessionChannel): CDPSession {
return (cdpSession as any)._object;
}

View file

@ -17,8 +17,19 @@
import { Page } from './page';
import { CDPSession } from './cdpSession';
import { Browser } from './browser';
import * as api from '../../types/types';
import { ChromiumBrowserContext } from './chromiumBrowserContext';
import { BrowserContextOptions } from './types';
export class ChromiumBrowser extends Browser implements api.ChromiumBrowser {
contexts(): ChromiumBrowserContext[] {
return super.contexts() as ChromiumBrowserContext[];
}
newContext(options?: BrowserContextOptions): Promise<ChromiumBrowserContext> {
return super.newContext(options) as Promise<ChromiumBrowserContext>;
}
export class ChromiumBrowser extends Browser {
async newBrowserCDPSession(): Promise<CDPSession> {
return this._wrapApiCall('chromiumBrowser.newBrowserCDPSession', async () => {
return CDPSession.from((await this._channel.crNewBrowserCDPSession()).session);

View file

@ -22,8 +22,9 @@ import { CDPSession } from './cdpSession';
import { Events } from './events';
import { Worker } from './worker';
import { BrowserContext } from './browserContext';
import * as api from '../../types/types';
export class ChromiumBrowserContext extends BrowserContext {
export class ChromiumBrowserContext extends BrowserContext implements api.ChromiumBrowserContext {
_backgroundPages = new Set<Page>();
_serviceWorkers = new Set<Worker>();

View file

@ -15,8 +15,9 @@
*/
import * as channels from '../protocol/channels';
import * as api from '../../types/types';
export class ChromiumCoverage {
export class ChromiumCoverage implements api.ChromiumCoverage {
private _channel: channels.PageChannel;
constructor(channel: channels.PageChannel) {

View file

@ -18,10 +18,11 @@ import * as util from 'util';
import { JSHandle } from './jsHandle';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import * as api from '../../types/types';
type ConsoleMessageLocation = channels.ConsoleMessageInitializer['location'];
export class ConsoleMessage extends ChannelOwner<channels.ConsoleMessageChannel, channels.ConsoleMessageInitializer> {
export class ConsoleMessage extends ChannelOwner<channels.ConsoleMessageChannel, channels.ConsoleMessageInitializer> implements api.ConsoleMessage {
static from(message: channels.ConsoleMessageChannel): ConsoleMessage {
return (message as any)._object;
}

View file

@ -16,8 +16,9 @@
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import * as api from '../../types/types';
export class Dialog extends ChannelOwner<channels.DialogChannel, channels.DialogInitializer> {
export class Dialog extends ChannelOwner<channels.DialogChannel, channels.DialogInitializer> implements api.Dialog {
static from(dialog: channels.DialogChannel): Dialog {
return (dialog as any)._object;
}

View file

@ -22,8 +22,9 @@ import { Browser } from './browser';
import { BrowserContext } from './browserContext';
import * as fs from 'fs';
import { mkdirIfNeeded } from '../utils/utils';
import * as api from '../../types/types';
export class Download extends ChannelOwner<channels.DownloadChannel, channels.DownloadInitializer> {
export class Download extends ChannelOwner<channels.DownloadChannel, channels.DownloadInitializer> implements api.Download {
private _browser: Browser | null;
static from(download: channels.DownloadChannel): Download {

View file

@ -18,12 +18,13 @@ import * as channels from '../protocol/channels';
import { BrowserContext } from './browserContext';
import { ChannelOwner } from './channelOwner';
import { Page } from './page';
import { serializeArgument, FuncOn, parseResult, SmartHandle, JSHandle } from './jsHandle';
import { serializeArgument, parseResult, JSHandle } from './jsHandle';
import { TimeoutSettings } from '../utils/timeoutSettings';
import { Waiter } from './waiter';
import { Events } from './events';
import { WaitForEventOptions, Env, Logger } from './types';
import { envObjectToArray } from './clientHelper';
import * as structs from '../../types/structs';
type ElectronOptions = Omit<channels.ElectronLaunchOptions, 'env'> & {
env?: Env,
@ -110,17 +111,13 @@ export class ElectronApplication extends ChannelOwner<channels.ElectronApplicati
return result;
}
async evaluate<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: FuncOn<any, void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<any, Arg, R>, arg: Arg): Promise<R> {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
}
async evaluateHandle<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: FuncOn<any, void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: FuncOn<any, Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<any, Arg, R>, arg: Arg): Promise<structs.SmartHandle<R>> {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
}
}

View file

@ -16,7 +16,7 @@
import * as channels from '../protocol/channels';
import { Frame } from './frame';
import { FuncOn, JSHandle, serializeArgument, parseResult } from './jsHandle';
import { JSHandle, serializeArgument, parseResult } from './jsHandle';
import { ChannelOwner } from './channelOwner';
import { SelectOption, FilePayload, Rect, SelectOptionOptions } from './types';
import * as fs from 'fs';
@ -24,10 +24,12 @@ import * as mime from 'mime';
import * as path from 'path';
import * as util from 'util';
import { assert, isString, mkdirIfNeeded } from '../utils/utils';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
const fsWriteFileAsync = util.promisify(fs.writeFile.bind(fs));
export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements api.ElementHandle {
readonly _elementChannel: channels.ElementHandleChannel;
static from(handle: channels.ElementHandleChannel): ElementHandle {
@ -43,8 +45,8 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
this._elementChannel = this._channel as channels.ElementHandleChannel;
}
asElement(): ElementHandle<T> | null {
return this;
asElement(): T extends Node ? ElementHandle<T> : null {
return this as any;
}
async ownerFrame(): Promise<Frame | null> {
@ -121,7 +123,7 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
});
}
async selectOption(values: string | ElementHandle | SelectOption | string[] | ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
async selectOption(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
return this._wrapApiCall('elementHandle.selectOption', async () => {
const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options });
return result.values;
@ -198,31 +200,27 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
});
}
async $(selector: string): Promise<ElementHandle<Element> | null> {
async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall('elementHandle.$', async () => {
return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element) as ElementHandle<Element> | null;
return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element) as ElementHandle<SVGElement | HTMLElement> | null;
});
}
async $$(selector: string): Promise<ElementHandle<Element>[]> {
async $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> {
return this._wrapApiCall('elementHandle.$$', async () => {
const result = await this._elementChannel.querySelectorAll({ selector });
return result.elements.map(h => ElementHandle.from(h) as ElementHandle<Element>);
return result.elements.map(h => ElementHandle.from(h) as ElementHandle<SVGElement | HTMLElement>);
});
}
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
async $eval<R>(selector: string, pageFunction: FuncOn<Element, void, R>, arg?: any): Promise<R>;
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
async $eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element, Arg, R>, arg?: Arg): Promise<R> {
return this._wrapApiCall('elementHandle.$eval', async () => {
const result = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
});
}
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
async $$eval<R>(selector: string, pageFunction: FuncOn<Element[], void, R>, arg?: any): Promise<R>;
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
async $$eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> {
return this._wrapApiCall('elementHandle.$$eval', async () => {
const result = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
@ -235,15 +233,17 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> {
});
}
async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions = {}): Promise<ElementHandle<Element> | null> {
waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<ElementHandle<SVGElement | HTMLElement>>;
waitForSelector(selector: string, options?: channels.ElementHandleWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null>;
async waitForSelector(selector: string, options: channels.ElementHandleWaitForSelectorOptions = {}): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall('elementHandle.waitForSelector', async () => {
const result = await this._elementChannel.waitForSelector({ selector, ...options });
return ElementHandle.fromNullable(result.element) as ElementHandle<Element> | null;
return ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
});
}
}
export function convertSelectOptionValues(values: string | ElementHandle | SelectOption | string[] | ElementHandle[] | SelectOption[] | null): { elements?: channels.ElementHandleChannel[], options?: SelectOption[] } {
export function convertSelectOptionValues(values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null): { elements?: channels.ElementHandleChannel[], options?: SelectOption[] } {
if (values === null)
return {};
if (!Array.isArray(values))

View file

@ -18,8 +18,9 @@ import { ElementHandle } from './elementHandle';
import { Page } from './page';
import { FilePayload } from './types';
import * as channels from '../protocol/channels';
import * as api from '../../types/types';
export class FileChooser {
export class FileChooser implements api.FileChooser {
private _page: Page;
private _elementHandle: ElementHandle<Node>;
private _isMultiple: boolean;

View file

@ -15,6 +15,7 @@
*/
import { Browser } from './browser';
import * as api from '../../types/types';
export class FirefoxBrowser extends Browser {
export class FirefoxBrowser extends Browser implements api.FirefoxBrowser {
}

View file

@ -19,7 +19,7 @@ import { assert } from '../utils/utils';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import { ElementHandle, convertSelectOptionValues, convertInputFiles } from './elementHandle';
import { assertMaxArguments, JSHandle, Func1, FuncOn, SmartHandle, serializeArgument, parseResult } from './jsHandle';
import { assertMaxArguments, JSHandle, serializeArgument, parseResult } from './jsHandle';
import * as fs from 'fs';
import * as network from './network';
import * as util from 'util';
@ -29,6 +29,8 @@ import { Waiter } from './waiter';
import { Events } from './events';
import { LifecycleEvent, URLMatch, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, kLifecycleEvents } from './types';
import { urlMatches } from './clientHelper';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
@ -38,7 +40,7 @@ export type WaitForNavigationOptions = {
url?: URLMatch,
};
export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameInitializer> {
export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameInitializer> implements api.Frame {
_eventEmitter: EventEmitter;
_loadStates: Set<LifecycleEvent>;
_parentFrame: Frame | null = null;
@ -164,29 +166,25 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(this._apiName('evaluateHandle'), async () => {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
}
async _evaluateHandleInUtility<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async _evaluateHandleInUtility<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
async _evaluateHandleInUtility<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async _evaluateHandleInUtility<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg: Arg): Promise<structs.SmartHandle<R>>;
async _evaluateHandleInUtility<R>(pageFunction: structs.PageFunction<void, R>, arg?: any): Promise<structs.SmartHandle<R>>;
async _evaluateHandleInUtility<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(this._apiName('_evaluateHandleInUtility'), async () => {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg), world: 'utility' });
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
}
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(this._apiName('evaluate'), async () => {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
@ -194,9 +192,9 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async _evaluateInUtility<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
async _evaluateInUtility<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
async _evaluateInUtility<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
async _evaluateInUtility<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg: Arg): Promise<R>;
async _evaluateInUtility<R>(pageFunction: structs.PageFunction<void, R>, arg?: any): Promise<R>;
async _evaluateInUtility<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
return this._wrapApiCall(this._apiName('evaluate'), async () => {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg), world: 'utility' });
@ -204,21 +202,23 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async $(selector: string): Promise<ElementHandle<Element> | null> {
async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall(this._apiName('$'), async () => {
const result = await this._channel.querySelector({ selector });
return ElementHandle.fromNullable(result.element) as ElementHandle<Element> | null;
return ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
});
}
async waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions = {}): Promise<ElementHandle<Element> | null> {
waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<ElementHandle<SVGElement | HTMLElement>>;
waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null>;
async waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions = {}): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._wrapApiCall(this._apiName('waitForSelector'), async () => {
if ((options as any).visibility)
throw new Error('options.visibility is not supported, did you mean options.state?');
if ((options as any).waitFor && (options as any).waitFor !== 'visible')
throw new Error('options.waitFor is not supported, did you mean options.state?');
const result = await this._channel.waitForSelector({ selector, ...options });
return ElementHandle.fromNullable(result.element) as ElementHandle<Element> | null;
return ElementHandle.fromNullable(result.element) as ElementHandle<SVGElement | HTMLElement> | null;
});
}
@ -228,9 +228,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
async $eval<R>(selector: string, pageFunction: FuncOn<Element, void, R>, arg?: any): Promise<R>;
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
async $eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element, Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._wrapApiCall(this._apiName('$eval'), async () => {
const result = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
@ -238,9 +236,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
async $$eval<R>(selector: string, pageFunction: FuncOn<Element[], void, R>, arg?: any): Promise<R>;
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
async $$eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._wrapApiCall(this._apiName('$$eval'), async () => {
const result = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
@ -248,10 +244,10 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async $$(selector: string): Promise<ElementHandle<Element>[]> {
async $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> {
return this._wrapApiCall(this._apiName('$$'), async () => {
const result = await this._channel.querySelectorAll({ selector });
return result.elements.map(e => ElementHandle.from(e) as ElementHandle<Element>);
return result.elements.map(e => ElementHandle.from(e) as ElementHandle<SVGElement | HTMLElement>);
});
}
@ -372,7 +368,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
});
}
async selectOption(selector: string, values: string | ElementHandle | SelectOption | string[] | ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
async selectOption(selector: string, values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
return this._wrapApiCall(this._apiName('selectOption'), async () => {
return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options })).values;
});
@ -412,9 +408,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
await new Promise(fulfill => setTimeout(fulfill, timeout));
}
async waitForFunction<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg, options?: WaitForFunctionOptions): Promise<SmartHandle<R>>;
async waitForFunction<R>(pageFunction: Func1<void, R>, arg?: any, options?: WaitForFunctionOptions): Promise<SmartHandle<R>>;
async waitForFunction<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg, options: WaitForFunctionOptions = {}): Promise<SmartHandle<R>> {
async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options: WaitForFunctionOptions = {}): Promise<structs.SmartHandle<R>> {
return this._wrapApiCall(this._apiName('waitForFunction'), async () => {
if (typeof options.polling === 'string')
assert(options.polling === 'raf', 'Unknown polling option: ' + options.polling);
@ -425,7 +419,7 @@ export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameIni
isFunction: typeof pageFunction === 'function',
arg: serializeArgument(arg),
});
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
});
}

View file

@ -16,8 +16,9 @@
*/
import * as channels from '../protocol/channels';
import * as api from '../../types/types';
export class Keyboard {
export class Keyboard implements api.Keyboard {
private _channel: channels.PageChannel;
constructor(channel: channels.PageChannel) {
@ -45,7 +46,7 @@ export class Keyboard {
}
}
export class Mouse {
export class Mouse implements api.Mouse {
private _channel: channels.PageChannel;
constructor(channel: channels.PageChannel) {
@ -73,7 +74,7 @@ export class Mouse {
}
}
export class Touchscreen {
export class Touchscreen implements api.Touchscreen {
private _channel: channels.PageChannel;
constructor(channel: channels.PageChannel) {

View file

@ -15,27 +15,12 @@
*/
import * as channels from '../protocol/channels';
import { ElementHandle } from './elementHandle';
import { ChannelOwner } from './channelOwner';
import { parseSerializedValue, serializeValue } from '../protocol/serializers';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
type Unboxed<Arg> =
Arg extends ElementHandle<infer T> ? T :
Arg extends JSHandle<infer T> ? T :
Arg extends NoHandles<Arg> ? Arg :
Arg extends [infer A0] ? [Unboxed<A0>] :
Arg extends [infer A0, infer A1] ? [Unboxed<A0>, Unboxed<A1>] :
Arg extends [infer A0, infer A1, infer A2] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>] :
Arg extends Array<infer T> ? Array<Unboxed<T>> :
Arg extends object ? { [Key in keyof Arg]: Unboxed<Arg[Key]> } :
Arg;
export type Func0<R> = string | (() => R | Promise<R>);
export type Func1<Arg, R> = string | ((arg: Unboxed<Arg>) => R | Promise<R>);
export type FuncOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
export type SmartHandle<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel, channels.JSHandleInitializer> {
export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel, channels.JSHandleInitializer> implements api.JSHandle {
private _preview: string;
static from(handle: channels.JSHandleChannel): JSHandle {
@ -48,18 +33,14 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel, ch
this._channel.on('previewUpdated', ({preview}) => this._preview = preview);
}
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: FuncOn<T, void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<R> {
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
}
async evaluateHandle<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: FuncOn<T, void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: FuncOn<T, Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunctionOn<T, Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
}
async getProperty(propertyName: string): Promise<JSHandle> {
@ -78,8 +59,8 @@ export class JSHandle<T = any> extends ChannelOwner<channels.JSHandleChannel, ch
return parseResult((await this._channel.jsonValue()).value);
}
asElement(): ElementHandle | null {
return null;
asElement(): T extends Node ? api.ElementHandle<T> : null {
return null as any;
}
async dispose() {

View file

@ -26,6 +26,7 @@ import { isString, headersObjectToArray, headersArrayToObject } from '../utils/u
import { Events } from './events';
import { Page } from './page';
import { Waiter } from './waiter';
import * as api from '../../types/types';
export type NetworkCookie = {
name: string,
@ -50,7 +51,7 @@ export type SetNetworkCookieParam = {
sameSite?: 'Strict' | 'Lax' | 'None'
};
export class Request extends ChannelOwner<channels.RequestChannel, channels.RequestInitializer> {
export class Request extends ChannelOwner<channels.RequestChannel, channels.RequestInitializer> implements api.Request {
private _redirectedFrom: Request | null = null;
private _redirectedTo: Request | null = null;
_failureText: string | null = null;
@ -167,7 +168,7 @@ export class Request extends ChannelOwner<channels.RequestChannel, channels.Requ
}
}
export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteInitializer> {
export class Route extends ChannelOwner<channels.RouteChannel, channels.RouteInitializer> implements api.Route {
static from(route: channels.RouteChannel): Route {
return (route as any)._object;
}
@ -246,7 +247,7 @@ export type ResourceTiming = {
responseEnd: number;
};
export class Response extends ChannelOwner<channels.ResponseChannel, channels.ResponseInitializer> {
export class Response extends ChannelOwner<channels.ResponseChannel, channels.ResponseInitializer> implements api.Response {
private _headers: Headers;
private _request: Request;
@ -316,7 +317,7 @@ export class Response extends ChannelOwner<channels.ResponseChannel, channels.Re
}
}
export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.WebSocketInitializer> {
export class WebSocket extends ChannelOwner<channels.WebSocketChannel, channels.WebSocketInitializer> implements api.WebSocket {
private _page: Page;
private _isClosed: boolean;

View file

@ -30,13 +30,14 @@ import { ElementHandle, determineScreenshotType } from './elementHandle';
import { Worker } from './worker';
import { Frame, verifyLoadState, WaitForNavigationOptions } from './frame';
import { Keyboard, Mouse, Touchscreen } from './input';
import { assertMaxArguments, Func1, FuncOn, SmartHandle, serializeArgument, parseResult, JSHandle } from './jsHandle';
import { assertMaxArguments, serializeArgument, parseResult, JSHandle } from './jsHandle';
import { Request, Response, Route, RouteHandler, WebSocket, validateHeaders } from './network';
import { FileChooser } from './fileChooser';
import { Buffer } from 'buffer';
import { ChromiumCoverage } from './chromiumCoverage';
import { Waiter } from './waiter';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
import * as fs from 'fs';
import * as path from 'path';
import * as util from 'util';
@ -61,9 +62,8 @@ type PDFOptions = Omit<channels.PagePdfParams, 'width' | 'height' | 'margin'> &
path?: string,
};
type Listener = (...args: any[]) => void;
export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame }, ...args: any) => any;
export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitializer> {
export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitializer> implements api.Page {
private _browserContext: BrowserContext;
_ownedContext: BrowserContext | undefined;
@ -79,9 +79,9 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
readonly mouse: Mouse;
readonly touchscreen: Touchscreen;
coverage: ChromiumCoverage | null = null;
pdf?: (options?: PDFOptions) => Promise<Buffer>;
pdf: (options?: PDFOptions) => Promise<Buffer>;
readonly _bindings = new Map<string, FunctionWithSource>();
readonly _bindings = new Map<string, (source: structs.BindingSource, ...args: any[]) => any>();
readonly _timeoutSettings: TimeoutSettings;
_isPageCall = false;
private _video: Video | null = null;
@ -136,6 +136,8 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
if (this._browserContext._browserName === 'chromium') {
this.coverage = new ChromiumCoverage(this._channel);
this.pdf = options => this._pdf(options);
} else {
this.pdf = undefined as any;
}
}
@ -261,11 +263,13 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
}
}
async $(selector: string): Promise<ElementHandle<Element> | null> {
async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._attributeToPage(() => this._mainFrame.$(selector));
}
async waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise<ElementHandle<Element> | null> {
waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions & { state: 'attached' | 'visible' }): Promise<ElementHandle<SVGElement | HTMLElement>>;
waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null>;
async waitForSelector(selector: string, options?: channels.FrameWaitForSelectorOptions): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
return this._attributeToPage(() => this._mainFrame.waitForSelector(selector, options));
}
@ -273,28 +277,22 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
return this._attributeToPage(() => this._mainFrame.dispatchEvent(selector, type, eventInit, options));
}
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this._mainFrame.evaluateHandle(pageFunction, arg));
}
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R>;
async $eval<R>(selector: string, pageFunction: FuncOn<Element, void, R>, arg?: any): Promise<R>;
async $eval<R, Arg>(selector: string, pageFunction: FuncOn<Element, Arg, R>, arg: Arg): Promise<R> {
async $eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element, Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this._mainFrame.$eval(selector, pageFunction, arg));
}
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R>;
async $$eval<R>(selector: string, pageFunction: FuncOn<Element[], void, R>, arg?: any): Promise<R>;
async $$eval<R, Arg>(selector: string, pageFunction: FuncOn<Element[], Arg, R>, arg: Arg): Promise<R> {
async $$eval<R, Arg>(selector: string, pageFunction: structs.PageFunctionOn<Element[], Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 3);
return this._attributeToPage(() => this._mainFrame.$$eval(selector, pageFunction, arg));
}
async $$(selector: string): Promise<ElementHandle<Element>[]> {
async $$(selector: string): Promise<ElementHandle<SVGElement | HTMLElement>[]> {
return this._attributeToPage(() => this._mainFrame.$$(selector));
}
@ -309,12 +307,12 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
async exposeFunction(name: string, playwrightFunction: Function) {
return this._wrapApiCall('page.exposeFunction', async () => {
await this._channel.exposeBinding({ name });
const binding: FunctionWithSource = (source, ...args) => playwrightFunction(...args);
const binding = (source: structs.BindingSource, ...args: any[]) => playwrightFunction(...args);
this._bindings.set(name, binding);
});
}
async exposeBinding(name: string, playwrightBinding: FunctionWithSource, options: { handle?: boolean } = {}) {
async exposeBinding(name: string, playwrightBinding: (source: structs.BindingSource, ...args: any[]) => any, options: { handle?: boolean } = {}) {
return this._wrapApiCall('page.exposeBinding', async () => {
await this._channel.exposeBinding({ name, needsHandle: options.handle });
this._bindings.set(name, playwrightBinding);
@ -425,9 +423,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
return this._viewportSize;
}
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
return this._attributeToPage(() => this._mainFrame.evaluate(pageFunction, arg));
}
@ -538,7 +534,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
return this._attributeToPage(() => this._mainFrame.hover(selector, options));
}
async selectOption(selector: string, values: string | ElementHandle | SelectOption | string[] | ElementHandle[] | SelectOption[] | null, options?: SelectOptionOptions): Promise<string[]> {
async selectOption(selector: string, values: string | api.ElementHandle | SelectOption | string[] | api.ElementHandle[] | SelectOption[] | null, options?: SelectOptionOptions): Promise<string[]> {
return this._attributeToPage(() => this._mainFrame.selectOption(selector, values, options));
}
@ -566,9 +562,7 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
await this._mainFrame.waitForTimeout(timeout);
}
async waitForFunction<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg, options?: WaitForFunctionOptions): Promise<SmartHandle<R>>;
async waitForFunction<R>(pageFunction: Func1<void, R>, arg?: any, options?: WaitForFunctionOptions): Promise<SmartHandle<R>>;
async waitForFunction<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg, options?: WaitForFunctionOptions): Promise<SmartHandle<R>> {
async waitForFunction<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg, options?: WaitForFunctionOptions): Promise<structs.SmartHandle<R>> {
return this._attributeToPage(() => this._mainFrame.waitForFunction(pageFunction, arg, options));
}
@ -636,7 +630,7 @@ export class BindingCall extends ChannelOwner<channels.BindingCallChannel, chann
super(parent, type, guid, initializer);
}
async call(func: FunctionWithSource) {
async call(func: (source: structs.BindingSource, ...args: any[]) => any) {
try {
const frame = Frame.from(this._initializer.frame);
const source = {

View file

@ -18,8 +18,9 @@ import { evaluationScript } from './clientHelper';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import { SelectorEngine } from './types';
import * as api from '../../types/types';
export class Selectors {
export class Selectors implements api.Selectors {
private _channels = new Set<SelectorsOwner>();
private _registrations: channels.SelectorsRegisterParams[] = [];

View file

@ -16,8 +16,9 @@
import * as path from 'path';
import { Page } from './page';
import * as api from '../../types/types';
export class Video {
export class Video implements api.Video {
private _page: Page;
private _pathCallback: ((path: string) => void) | undefined;
private _pathPromise: Promise<string>;

View file

@ -15,6 +15,7 @@
*/
import { Browser } from './browser';
import * as api from '../../types/types';
export class WebKitBrowser extends Browser {
export class WebKitBrowser extends Browser implements api.WebKitBrowser {
}

View file

@ -17,12 +17,14 @@
import { Events } from './events';
import * as channels from '../protocol/channels';
import { ChannelOwner } from './channelOwner';
import { assertMaxArguments, Func1, JSHandle, parseResult, serializeArgument, SmartHandle } from './jsHandle';
import { assertMaxArguments, JSHandle, parseResult, serializeArgument } from './jsHandle';
import { Page } from './page';
import { BrowserContext } from './browserContext';
import { ChromiumBrowserContext } from './chromiumBrowserContext';
import * as api from '../../types/types';
import * as structs from '../../types/structs';
export class Worker extends ChannelOwner<channels.WorkerChannel, channels.WorkerInitializer> {
export class Worker extends ChannelOwner<channels.WorkerChannel, channels.WorkerInitializer> implements api.Worker {
_page: Page | undefined; // Set for web workers.
_context: BrowserContext | undefined; // Set for service workers.
@ -45,19 +47,15 @@ export class Worker extends ChannelOwner<channels.WorkerChannel, channels.Worker
return this._initializer.url;
}
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R>;
async evaluate<R>(pageFunction: Func1<void, R>, arg?: any): Promise<R>;
async evaluate<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<R> {
async evaluate<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<R> {
assertMaxArguments(arguments.length, 2);
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return parseResult(result.value);
}
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>>;
async evaluateHandle<R>(pageFunction: Func1<void, R>, arg?: any): Promise<SmartHandle<R>>;
async evaluateHandle<R, Arg>(pageFunction: Func1<Arg, R>, arg: Arg): Promise<SmartHandle<R>> {
async evaluateHandle<R, Arg>(pageFunction: structs.PageFunction<Arg, R>, arg?: Arg): Promise<structs.SmartHandle<R>> {
assertMaxArguments(arguments.length, 2);
const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
return JSHandle.from(result.handle) as SmartHandle<R>;
return JSHandle.from(result.handle) as any as structs.SmartHandle<R>;
}
}

45
types/structs.d.ts vendored Normal file
View file

@ -0,0 +1,45 @@
/**
* 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 { JSHandle, ElementHandle, Frame, Page, BrowserContext } from './types';
/**
* Can be converted to JSON
*/
export type Serializable = {};
/**
* Can be converted to JSON, but may also contain JSHandles.
*/
export type EvaluationArgument = {};
export type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
export type Unboxed<Arg> =
Arg extends ElementHandle<infer T> ? T :
Arg extends JSHandle<infer T> ? T :
Arg extends NoHandles<Arg> ? Arg :
Arg extends [infer A0] ? [Unboxed<A0>] :
Arg extends [infer A0, infer A1] ? [Unboxed<A0>, Unboxed<A1>] :
Arg extends [infer A0, infer A1, infer A2] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>] :
Arg extends [infer A0, infer A1, infer A2, infer A3] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>, Unboxed<A3>] :
Arg extends Array<infer T> ? Array<Unboxed<T>> :
Arg extends object ? { [Key in keyof Arg]: Unboxed<Arg[Key]> } :
Arg;
export type PageFunction0<R> = string | (() => R | Promise<R>);
export type PageFunction<Arg, R> = string | ((arg: Unboxed<Arg>) => R | Promise<R>);
export type PageFunctionOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
export type SmartHandle<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
export type ElementHandleForTag<K extends keyof HTMLElementTagNameMap> = ElementHandle<HTMLElementTagNameMap[K]>;
export type BindingSource = { context: BrowserContext, page: Page, frame: Frame };

29
types/types.d.ts vendored
View file

@ -18,32 +18,7 @@ import { Protocol } from './protocol';
import { ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
import { Readable } from 'stream';
/**
* Can be converted to JSON
*/
type Serializable = {};
/**
* Can be converted to JSON, but may also contain JSHandles.
*/
type EvaluationArgument = {};
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
type Unboxed<Arg> =
Arg extends ElementHandle<infer T> ? T :
Arg extends JSHandle<infer T> ? T :
Arg extends NoHandles<Arg> ? Arg :
Arg extends [infer A0] ? [Unboxed<A0>] :
Arg extends [infer A0, infer A1] ? [Unboxed<A0>, Unboxed<A1>] :
Arg extends [infer A0, infer A1, infer A2] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>] :
Arg extends [infer A0, infer A1, infer A2, infer A3] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>, Unboxed<A3>] :
Arg extends Array<infer T> ? Array<Unboxed<T>> :
Arg extends object ? { [Key in keyof Arg]: Unboxed<Arg[Key]> } :
Arg;
type PageFunction<Arg, R> = string | ((arg: Unboxed<Arg>) => R | Promise<R>);
type PageFunctionOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
type SmartHandle<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
type ElementHandleForTag<K extends keyof HTMLElementTagNameMap> = ElementHandle<HTMLElementTagNameMap[K]>;
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
state: 'visible'|'attached';
@ -52,8 +27,6 @@ type ElementHandleWaitForSelectorOptionsNotHidden = ElementHandleWaitForSelector
state: 'visible'|'attached';
};
type BindingSource = { context: BrowserContext, page: Page, frame: Frame };
/**
* Page provides methods to interact with a single tab in a Browser, or an extension background page in Chromium. One Browser instance might have multiple Page instances.
* This example creates a page, navigates it to a URL, and then saves a screenshot:

View file

@ -200,7 +200,8 @@ function compareDocumentations(actual, expected) {
[/Handle\<R\>/g, 'JSHandle'],
[/JSHandle\<Object\>/g, 'JSHandle'],
[/object/g, 'Object'],
[/Promise\<T\>/, 'Promise<Object>']
[/Promise\<T\>/, 'Promise<Object>'],
[/TextendsNode\?ElementHandle:null/, 'null|ElementHandle']
]
let actualName = actual.name;
for (const replacer of mdReplacers)

View file

@ -17,32 +17,7 @@ import { Protocol } from './protocol';
import { ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
import { Readable } from 'stream';
/**
* Can be converted to JSON
*/
type Serializable = {};
/**
* Can be converted to JSON, but may also contain JSHandles.
*/
type EvaluationArgument = {};
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
type Unboxed<Arg> =
Arg extends ElementHandle<infer T> ? T :
Arg extends JSHandle<infer T> ? T :
Arg extends NoHandles<Arg> ? Arg :
Arg extends [infer A0] ? [Unboxed<A0>] :
Arg extends [infer A0, infer A1] ? [Unboxed<A0>, Unboxed<A1>] :
Arg extends [infer A0, infer A1, infer A2] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>] :
Arg extends [infer A0, infer A1, infer A2, infer A3] ? [Unboxed<A0>, Unboxed<A1>, Unboxed<A2>, Unboxed<A3>] :
Arg extends Array<infer T> ? Array<Unboxed<T>> :
Arg extends object ? { [Key in keyof Arg]: Unboxed<Arg[Key]> } :
Arg;
type PageFunction<Arg, R> = string | ((arg: Unboxed<Arg>) => R | Promise<R>);
type PageFunctionOn<On, Arg2, R> = string | ((on: On, arg2: Unboxed<Arg2>) => R | Promise<R>);
type SmartHandle<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
type ElementHandleForTag<K extends keyof HTMLElementTagNameMap> = ElementHandle<HTMLElementTagNameMap[K]>;
import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';
type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & {
state: 'visible'|'attached';
@ -51,8 +26,6 @@ type ElementHandleWaitForSelectorOptionsNotHidden = ElementHandleWaitForSelector
state: 'visible'|'attached';
};
type BindingSource = { context: BrowserContext, page: Page, frame: Frame };
export interface Page {
evaluate<R, Arg>(pageFunction: PageFunction<Arg, R>, arg: Arg): Promise<R>;
evaluate<R>(pageFunction: PageFunction<void, R>, arg?: any): Promise<R>;