2020-06-25 16:05:36 -07:00
|
|
|
/**
|
|
|
|
* Copyright 2017 Google Inc. All rights reserved.
|
|
|
|
* Modifications 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.
|
|
|
|
*/
|
|
|
|
|
2020-08-22 15:13:51 -07:00
|
|
|
import { assert } from '../utils/utils';
|
2020-08-24 17:05:16 -07:00
|
|
|
import * as channels from '../protocol/channels';
|
2020-06-25 16:05:36 -07:00
|
|
|
import { BrowserContext } from './browserContext';
|
|
|
|
import { ChannelOwner } from './channelOwner';
|
2020-07-10 15:39:11 -07:00
|
|
|
import { ElementHandle, convertSelectOptionValues, convertInputFiles } from './elementHandle';
|
2020-08-17 14:36:51 -07:00
|
|
|
import { assertMaxArguments, JSHandle, Func1, FuncOn, SmartHandle, serializeArgument, parseResult } from './jsHandle';
|
2020-07-14 10:51:37 -07:00
|
|
|
import * as fs from 'fs';
|
2020-06-25 16:05:36 -07:00
|
|
|
import * as network from './network';
|
2020-07-14 10:51:37 -07:00
|
|
|
import * as util from 'util';
|
2020-06-25 16:05:36 -07:00
|
|
|
import { Page } from './page';
|
2020-07-13 16:03:24 -07:00
|
|
|
import { EventEmitter } from 'events';
|
|
|
|
import { Waiter } from './waiter';
|
2020-07-29 17:26:59 -07:00
|
|
|
import { Events } from './events';
|
|
|
|
import { LifecycleEvent, URLMatch, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions, kLifecycleEvents } from './types';
|
2020-08-19 13:27:58 -07:00
|
|
|
import { urlMatches } from './clientHelper';
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2020-07-14 10:51:37 -07:00
|
|
|
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
|
|
|
|
2020-06-25 16:05:36 -07:00
|
|
|
export type FunctionWithSource = (source: { context: BrowserContext, page: Page, frame: Frame }, ...args: any) => any;
|
2020-07-29 17:26:59 -07:00
|
|
|
export type WaitForNavigationOptions = {
|
|
|
|
timeout?: number,
|
|
|
|
waitUntil?: LifecycleEvent,
|
|
|
|
url?: URLMatch,
|
|
|
|
};
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
export class Frame extends ChannelOwner<channels.FrameChannel, channels.FrameInitializer> {
|
2020-07-13 16:03:24 -07:00
|
|
|
_eventEmitter: EventEmitter;
|
2020-07-29 17:26:59 -07:00
|
|
|
_loadStates: Set<LifecycleEvent>;
|
2020-06-25 16:05:36 -07:00
|
|
|
_parentFrame: Frame | null = null;
|
|
|
|
_url = '';
|
2020-06-26 11:51:47 -07:00
|
|
|
_name = '';
|
2020-06-26 17:24:21 -07:00
|
|
|
_detached = false;
|
2020-06-25 16:05:36 -07:00
|
|
|
_childFrames = new Set<Frame>();
|
2020-06-26 12:28:27 -07:00
|
|
|
_page: Page | undefined;
|
2020-06-25 16:05:36 -07:00
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
static from(frame: channels.FrameChannel): Frame {
|
2020-07-01 18:36:09 -07:00
|
|
|
return (frame as any)._object;
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
static fromNullable(frame: channels.FrameChannel | undefined): Frame | null {
|
2020-06-25 16:05:36 -07:00
|
|
|
return frame ? Frame.from(frame) : null;
|
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.FrameInitializer) {
|
2020-07-10 18:00:10 -07:00
|
|
|
super(parent, type, guid, initializer);
|
2020-07-13 16:03:24 -07:00
|
|
|
this._eventEmitter = new EventEmitter();
|
|
|
|
this._eventEmitter.setMaxListeners(0);
|
2020-06-26 12:28:27 -07:00
|
|
|
this._parentFrame = Frame.fromNullable(initializer.parentFrame);
|
2020-06-25 16:05:36 -07:00
|
|
|
if (this._parentFrame)
|
|
|
|
this._parentFrame._childFrames.add(this);
|
2020-06-26 12:28:27 -07:00
|
|
|
this._name = initializer.name;
|
|
|
|
this._url = initializer.url;
|
2020-07-13 16:03:24 -07:00
|
|
|
this._loadStates = new Set(initializer.loadStates);
|
|
|
|
this._channel.on('loadstate', event => {
|
|
|
|
if (event.add) {
|
|
|
|
this._loadStates.add(event.add);
|
|
|
|
this._eventEmitter.emit('loadstate', event.add);
|
|
|
|
}
|
|
|
|
if (event.remove)
|
|
|
|
this._loadStates.delete(event.remove);
|
|
|
|
});
|
2020-07-15 18:48:19 -07:00
|
|
|
this._channel.on('navigated', event => {
|
|
|
|
this._url = event.url;
|
|
|
|
this._name = event.name;
|
|
|
|
this._eventEmitter.emit('navigated', event);
|
|
|
|
if (!event.error && this._page)
|
|
|
|
this._page.emit(Events.Page.FrameNavigated, this);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-07-15 14:04:39 -07:00
|
|
|
private _apiName(method: string) {
|
|
|
|
return this._page!._isPageCall ? 'page.' + method : 'frame.' + method;
|
|
|
|
}
|
|
|
|
|
2020-08-12 14:45:36 -07:00
|
|
|
page(): Page {
|
|
|
|
return this._page!;
|
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async goto(url: string, options: channels.FrameGotoOptions = {}): Promise<network.Response | null> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('goto'), async () => {
|
2020-07-22 18:05:07 -07:00
|
|
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
|
|
|
return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil })).response);
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
private _setupNavigationWaiter(options: { timeout?: number }): Waiter {
|
2020-07-15 18:48:19 -07:00
|
|
|
const waiter = new Waiter();
|
|
|
|
waiter.rejectOnEvent(this._page!, Events.Page.Close, new Error('Navigation failed because page was closed!'));
|
|
|
|
waiter.rejectOnEvent(this._page!, Events.Page.Crash, new Error('Navigation failed because page crashed!'));
|
|
|
|
waiter.rejectOnEvent<Frame>(this._page!, Events.Page.FrameDetached, new Error('Navigating frame was detached!'), frame => frame === this);
|
2020-07-21 23:48:21 -07:00
|
|
|
const timeout = this._page!._timeoutSettings.navigationTimeout(options);
|
|
|
|
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`);
|
|
|
|
|
2020-07-15 18:48:19 -07:00
|
|
|
return waiter;
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
async waitForNavigation(options: WaitForNavigationOptions = {}): Promise<network.Response | null> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('waitForNavigation'), async () => {
|
2020-07-21 15:25:31 -07:00
|
|
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
2020-07-21 23:48:21 -07:00
|
|
|
const waiter = this._setupNavigationWaiter(options);
|
2020-07-15 18:48:19 -07:00
|
|
|
|
|
|
|
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : '';
|
|
|
|
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
const navigatedEvent = await waiter.waitForEvent<channels.FrameNavigatedEvent>(this._eventEmitter, 'navigated', event => {
|
2020-07-15 18:48:19 -07:00
|
|
|
// Any failed navigation results in a rejection.
|
|
|
|
if (event.error)
|
|
|
|
return true;
|
|
|
|
waiter.log(` navigated to "${event.url}"`);
|
2020-08-19 13:27:58 -07:00
|
|
|
return urlMatches(event.url, options.url);
|
2020-07-15 18:48:19 -07:00
|
|
|
});
|
|
|
|
if (navigatedEvent.error) {
|
|
|
|
const e = new Error(navigatedEvent.error);
|
|
|
|
e.stack = '';
|
|
|
|
await waiter.waitForPromise(Promise.reject(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this._loadStates.has(waitUntil)) {
|
2020-07-29 17:26:59 -07:00
|
|
|
await waiter.waitForEvent<LifecycleEvent>(this._eventEmitter, 'loadstate', s => {
|
2020-07-15 18:48:19 -07:00
|
|
|
waiter.log(` "${s}" event fired`);
|
|
|
|
return s === waitUntil;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-07-20 17:38:06 -07:00
|
|
|
const request = navigatedEvent.newDocument ? network.Request.fromNullable(navigatedEvent.newDocument.request) : null;
|
2020-07-15 18:48:19 -07:00
|
|
|
const response = request ? await waiter.waitForPromise(request._finalRequest().response()) : null;
|
|
|
|
waiter.dispose();
|
|
|
|
return response;
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
async waitForLoadState(state: LifecycleEvent = 'load', options: { timeout?: number } = {}): Promise<void> {
|
2020-07-21 15:25:31 -07:00
|
|
|
state = verifyLoadState('state', state);
|
2020-07-13 16:03:24 -07:00
|
|
|
if (this._loadStates.has(state))
|
|
|
|
return;
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('waitForLoadState'), async () => {
|
2020-07-21 23:48:21 -07:00
|
|
|
const waiter = this._setupNavigationWaiter(options);
|
2020-07-29 17:26:59 -07:00
|
|
|
await waiter.waitForEvent<LifecycleEvent>(this._eventEmitter, 'loadstate', s => {
|
2020-07-15 18:48:19 -07:00
|
|
|
waiter.log(` "${s}" event fired`);
|
|
|
|
return s === state;
|
|
|
|
});
|
2020-07-15 14:04:39 -07:00
|
|
|
waiter.dispose();
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async frameElement(): Promise<ElementHandle> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('frameElement'), async () => {
|
|
|
|
return ElementHandle.from((await this._channel.frameElement()).element);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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>> {
|
|
|
|
assertMaxArguments(arguments.length, 2);
|
2020-07-15 14:04:39 -07:00
|
|
|
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>;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
|
|
|
assertMaxArguments(arguments.length, 2);
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('evaluate'), async () => {
|
|
|
|
const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
|
|
|
|
return parseResult(result.value);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async $(selector: string): Promise<ElementHandle<Element> | null> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('$'), async () => {
|
|
|
|
const result = await this._channel.querySelector({ selector });
|
|
|
|
return ElementHandle.fromNullable(result.element) as ElementHandle<Element> | null;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async waitForSelector(selector: string, options: channels.FrameWaitForSelectorOptions = {}): Promise<ElementHandle<Element> | null> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('waitForSelector'), async () => {
|
2020-07-22 18:05:07 -07:00
|
|
|
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?');
|
2020-07-15 14:04:39 -07:00
|
|
|
const result = await this._channel.waitForSelector({ selector, ...options });
|
|
|
|
return ElementHandle.fromNullable(result.element) as ElementHandle<Element> | null;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async dispatchEvent(selector: string, type: string, eventInit?: any, options: channels.FrameDispatchEventOptions = {}): Promise<void> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('dispatchEvent'), async () => {
|
|
|
|
await this._channel.dispatchEvent({ selector, type, eventInit: serializeArgument(eventInit), ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
|
|
|
assertMaxArguments(arguments.length, 3);
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('$eval'), async () => {
|
|
|
|
const result = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
|
|
|
|
return parseResult(result.value);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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> {
|
|
|
|
assertMaxArguments(arguments.length, 3);
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('$$eval'), async () => {
|
|
|
|
const result = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === 'function', arg: serializeArgument(arg) });
|
|
|
|
return parseResult(result.value);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async $$(selector: string): Promise<ElementHandle<Element>[]> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('$$'), async () => {
|
|
|
|
const result = await this._channel.querySelectorAll({ selector });
|
|
|
|
return result.elements.map(e => ElementHandle.from(e) as ElementHandle<Element>);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async content(): Promise<string> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('content'), async () => {
|
|
|
|
return (await this._channel.content()).value;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async setContent(html: string, options: channels.FrameSetContentOptions = {}): Promise<void> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('setContent'), async () => {
|
2020-07-22 18:05:07 -07:00
|
|
|
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
|
|
|
await this._channel.setContent({ html, ...options, waitUntil });
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
name(): string {
|
|
|
|
return this._name || '';
|
|
|
|
}
|
|
|
|
|
|
|
|
url(): string {
|
|
|
|
return this._url;
|
|
|
|
}
|
|
|
|
|
|
|
|
parentFrame(): Frame | null {
|
|
|
|
return this._parentFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
childFrames(): Frame[] {
|
|
|
|
return Array.from(this._childFrames);
|
|
|
|
}
|
|
|
|
|
|
|
|
isDetached(): boolean {
|
|
|
|
return this._detached;
|
|
|
|
}
|
|
|
|
|
|
|
|
async addScriptTag(options: { url?: string, path?: string, content?: string, type?: string }): Promise<ElementHandle> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('addScriptTag'), async () => {
|
|
|
|
const copy = { ...options };
|
|
|
|
if (copy.path) {
|
|
|
|
copy.content = (await fsReadFileAsync(copy.path)).toString();
|
|
|
|
copy.content += '//# sourceURL=' + copy.path.replace(/\n/g, '');
|
|
|
|
}
|
|
|
|
return ElementHandle.from((await this._channel.addScriptTag({ ...copy })).element);
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async addStyleTag(options: { url?: string; path?: string; content?: string; }): Promise<ElementHandle> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('addStyleTag'), async () => {
|
|
|
|
const copy = { ...options };
|
2020-07-22 18:05:07 -07:00
|
|
|
if (copy.path) {
|
2020-07-15 14:04:39 -07:00
|
|
|
copy.content = (await fsReadFileAsync(copy.path)).toString();
|
2020-07-22 18:05:07 -07:00
|
|
|
copy.content += '/*# sourceURL=' + copy.path.replace(/\n/g, '') + '*/';
|
|
|
|
}
|
|
|
|
return ElementHandle.from((await this._channel.addStyleTag({ ...copy })).element);
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async click(selector: string, options: channels.FrameClickOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('click'), async () => {
|
|
|
|
return await this._channel.click({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async dblclick(selector: string, options: channels.FrameDblclickOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('dblclick'), async () => {
|
|
|
|
return await this._channel.dblclick({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async fill(selector: string, value: string, options: channels.FrameFillOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('fill'), async () => {
|
|
|
|
return await this._channel.fill({ selector, value, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async focus(selector: string, options: channels.FrameFocusOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('focus'), async () => {
|
|
|
|
await this._channel.focus({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async textContent(selector: string, options: channels.FrameTextContentOptions = {}): Promise<null|string> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('textContent'), async () => {
|
2020-07-20 17:38:06 -07:00
|
|
|
const value = (await this._channel.textContent({ selector, ...options })).value;
|
|
|
|
return value === undefined ? null : value;
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async innerText(selector: string, options: channels.FrameInnerTextOptions = {}): Promise<string> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('innerText'), async () => {
|
|
|
|
return (await this._channel.innerText({ selector, ...options })).value;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async innerHTML(selector: string, options: channels.FrameInnerHTMLOptions = {}): Promise<string> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('innerHTML'), async () => {
|
|
|
|
return (await this._channel.innerHTML({ selector, ...options })).value;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async getAttribute(selector: string, name: string, options: channels.FrameGetAttributeOptions = {}): Promise<string | null> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('getAttribute'), async () => {
|
2020-07-20 17:38:06 -07:00
|
|
|
const value = (await this._channel.getAttribute({ selector, name, ...options })).value;
|
|
|
|
return value === undefined ? null : value;
|
2020-07-15 14:04:39 -07:00
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async hover(selector: string, options: channels.FrameHoverOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('hover'), async () => {
|
|
|
|
await this._channel.hover({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
async selectOption(selector: string, values: string | ElementHandle | SelectOption | string[] | ElementHandle[] | SelectOption[] | null, options: SelectOptionOptions = {}): Promise<string[]> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('selectOption'), async () => {
|
|
|
|
return (await this._channel.selectOption({ selector, ...convertSelectOptionValues(values), ...options })).values;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async setInputFiles(selector: string, files: string | FilePayload | string[] | FilePayload[], options: channels.FrameSetInputFilesOptions = {}): Promise<void> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('setInputFiles'), async () => {
|
|
|
|
await this._channel.setInputFiles({ selector, files: await convertInputFiles(files), ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async type(selector: string, text: string, options: channels.FrameTypeOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('type'), async () => {
|
|
|
|
await this._channel.type({ selector, text, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async press(selector: string, key: string, options: channels.FramePressOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('press'), async () => {
|
|
|
|
await this._channel.press({ selector, key, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async check(selector: string, options: channels.FrameCheckOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('check'), async () => {
|
|
|
|
await this._channel.check({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
2020-08-24 17:05:16 -07:00
|
|
|
async uncheck(selector: string, options: channels.FrameUncheckOptions = {}) {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('uncheck'), async () => {
|
|
|
|
await this._channel.uncheck({ selector, ...options });
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async waitForTimeout(timeout: number) {
|
|
|
|
await new Promise(fulfill => setTimeout(fulfill, timeout));
|
|
|
|
}
|
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
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>> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('waitForFunction'), async () => {
|
2020-07-22 18:05:07 -07:00
|
|
|
if (typeof options.polling === 'string')
|
|
|
|
assert(options.polling === 'raf', 'Unknown polling option: ' + options.polling);
|
2020-07-21 08:26:48 -07:00
|
|
|
const result = await this._channel.waitForFunction({
|
|
|
|
...options,
|
|
|
|
pollingInterval: options.polling === 'raf' ? undefined : options.polling,
|
|
|
|
expression: String(pageFunction),
|
|
|
|
isFunction: typeof pageFunction === 'function',
|
|
|
|
arg: serializeArgument(arg),
|
|
|
|
});
|
2020-07-15 14:04:39 -07:00
|
|
|
return JSHandle.from(result.handle) as SmartHandle<R>;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
async title(): Promise<string> {
|
2020-07-15 14:04:39 -07:00
|
|
|
return this._wrapApiCall(this._apiName('title'), async () => {
|
|
|
|
return (await this._channel.title()).value;
|
|
|
|
});
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
2020-09-07 15:50:25 -07:00
|
|
|
|
|
|
|
async _extendInjectedScript<Arg>(source: string, arg?: Arg): Promise<JSHandle> {
|
|
|
|
const result = await this._channel.extendInjectedScript({ source, arg: serializeArgument(arg) });
|
|
|
|
return JSHandle.from(result.handle);
|
|
|
|
}
|
2020-06-25 16:05:36 -07:00
|
|
|
}
|
2020-07-13 16:03:24 -07:00
|
|
|
|
2020-07-29 17:26:59 -07:00
|
|
|
export function verifyLoadState(name: string, waitUntil: LifecycleEvent): LifecycleEvent {
|
2020-07-13 16:03:24 -07:00
|
|
|
if (waitUntil as unknown === 'networkidle0')
|
|
|
|
waitUntil = 'networkidle';
|
2020-07-29 17:26:59 -07:00
|
|
|
if (!kLifecycleEvents.has(waitUntil))
|
2020-07-21 15:25:31 -07:00
|
|
|
throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle)`);
|
2020-07-13 16:03:24 -07:00
|
|
|
return waitUntil;
|
|
|
|
}
|