mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat!: type inference for evaluation types (#8547)
This PR greatly improves the types within Puppeteer: - **Almost everything** is auto-deduced. - Parameters don't need to be specified in the function. They are deduced from the spread. - Return types don't need to be specified. They are deduced from the function. (More on this below) - Selections based on tag names correctly deduce element type, similar to TypeScript's mechanism for `getElementByTagName`. - [**BREAKING CHANGE**] We've removed the ability to declare return types in type arguments for the following reasons: 1. Setting them will indubitably break auto-deduction. 2. You can just use `as ...` in TypeScript to coerce the correct type (given it makes sense). - [**BREAKING CHANGE**] `waitFor` is officially gone. To migrate to these changes, there are only four things you may need to change: - If you set a return type using the `ReturnType` type parameter, remove it and use `as ...` and `HandleFor` (if necessary). ⛔ `evaluate<ReturnType>(a: number, b: number) => {...}, a, b)` ✅ `(await evaluate(a, b) => {...}, a, b)) as ReturnType` ⛔ `evaluateHandle<ReturnType>(a: number, b: number) => {...}, a, b)` ✅ `(await evaluateHandle(a, b) => {...}, a, b)) as HandleFor<ReturnType>` - If you set any type parameters in the *parameters* of an evaluation function, remove them. ⛔ `evaluate(a: number, b: number) => {...}, a, b)` ✅ `evaluate(a, b) => {...}, a, b)` - If you set any type parameters in the method's declaration, remove them. ⛔ `evaluate<(a: number, b: number) => void>((a, b) => {...}, a, b)` ✅ `evaluate(a, b) => {...}, a, b)`
This commit is contained in:
parent
da269c3f32
commit
26c3acbb07
@ -72,7 +72,7 @@ export * from './common/Tracing.js';
|
|||||||
export * from './common/NetworkManager.js';
|
export * from './common/NetworkManager.js';
|
||||||
export * from './common/WebWorker.js';
|
export * from './common/WebWorker.js';
|
||||||
export * from './common/USKeyboardLayout.js';
|
export * from './common/USKeyboardLayout.js';
|
||||||
export * from './common/EvalTypes.js';
|
export * from './common/types.js';
|
||||||
export * from './common/PDFOptions.js';
|
export * from './common/PDFOptions.js';
|
||||||
export * from './common/TimeoutSettings.js';
|
export * from './common/TimeoutSettings.js';
|
||||||
export * from './common/LifecycleWatcher.js';
|
export * from './common/LifecycleWatcher.js';
|
||||||
|
@ -141,7 +141,7 @@ const queryAll = async (
|
|||||||
const queryAllArray = async (
|
const queryAllArray = async (
|
||||||
element: ElementHandle,
|
element: ElementHandle,
|
||||||
selector: string
|
selector: string
|
||||||
): Promise<JSHandle> => {
|
): Promise<JSHandle<Element[]>> => {
|
||||||
const elementHandles = await queryAll(element, selector);
|
const elementHandles = await queryAll(element, selector);
|
||||||
const exeCtx = element.executionContext();
|
const exeCtx = element.executionContext();
|
||||||
const jsHandle = exeCtx.evaluateHandle((...elements) => {
|
const jsHandle = exeCtx.evaluateHandle((...elements) => {
|
||||||
@ -153,7 +153,7 @@ const queryAllArray = async (
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export const _ariaHandler: InternalQueryHandler = {
|
export const ariaHandler: InternalQueryHandler = {
|
||||||
queryOne,
|
queryOne,
|
||||||
waitFor,
|
waitFor,
|
||||||
queryAll,
|
queryAll,
|
||||||
|
@ -33,8 +33,8 @@ export {ConnectionTransport, ProtocolMapping};
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface ConnectionCallback {
|
export interface ConnectionCallback {
|
||||||
resolve: Function;
|
resolve(args: unknown): void;
|
||||||
reject: Function;
|
reject(args: unknown): void;
|
||||||
error: ProtocolError;
|
error: ProtocolError;
|
||||||
method: string;
|
method: string;
|
||||||
}
|
}
|
||||||
|
@ -18,16 +18,14 @@ import {Protocol} from 'devtools-protocol';
|
|||||||
import {assert} from './assert.js';
|
import {assert} from './assert.js';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {TimeoutError} from './Errors.js';
|
import {TimeoutError} from './Errors.js';
|
||||||
import {
|
|
||||||
EvaluateFn,
|
|
||||||
EvaluateFnReturnType,
|
|
||||||
EvaluateHandleFn,
|
|
||||||
SerializableOrJSHandle,
|
|
||||||
UnwrapPromiseLike,
|
|
||||||
WrapElementHandle,
|
|
||||||
} from './EvalTypes.js';
|
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {Frame, FrameManager} from './FrameManager.js';
|
import {Frame, FrameManager} from './FrameManager.js';
|
||||||
|
import {MouseButton} from './Input.js';
|
||||||
|
import {ElementHandle, JSHandle} from './JSHandle.js';
|
||||||
|
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
|
import {_getQueryHandlerAndSelector} from './QueryHandler.js';
|
||||||
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
|
import {EvaluateFunc, EvaluateParams, HandleFor} from './types.js';
|
||||||
import {
|
import {
|
||||||
debugError,
|
debugError,
|
||||||
isNumber,
|
isNumber,
|
||||||
@ -35,11 +33,6 @@ import {
|
|||||||
makePredicateString,
|
makePredicateString,
|
||||||
pageBindingInitString,
|
pageBindingInitString,
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
import {MouseButton} from './Input.js';
|
|
||||||
import {ElementHandle, JSHandle} from './JSHandle.js';
|
|
||||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
|
||||||
import {_getQueryHandlerAndSelector} from './QueryHandler.js';
|
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
|
||||||
|
|
||||||
// predicateQueryHandler and checkWaitForOptions are declared here so that
|
// predicateQueryHandler and checkWaitForOptions are declared here so that
|
||||||
// TypeScript knows about them when used in the predicate function below.
|
// TypeScript knows about them when used in the predicate function below.
|
||||||
@ -184,30 +177,45 @@ export class DOMWorld {
|
|||||||
return this.#contextPromise;
|
return this.#contextPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateHandle<HandlerType extends JSHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<HandlerType> {
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
const context = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
return context.evaluateHandle(pageFunction, ...args);
|
return context.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate<T extends EvaluateFn>(
|
async evaluate<
|
||||||
pageFunction: T,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>> {
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const context = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
return context.evaluate<UnwrapPromiseLike<EvaluateFnReturnType<T>>>(
|
return context.evaluate(pageFunction, ...args);
|
||||||
pageFunction,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async $<T extends Element = Element>(
|
async $<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
selector: string
|
selector: Selector
|
||||||
): Promise<ElementHandle<T> | null> {
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
const document = await this._document();
|
const document = await this._document();
|
||||||
const value = await document.$<T>(selector);
|
const value = await document.$(selector);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]>[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
|
const document = await this._document();
|
||||||
|
const value = await document.$$(selector);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,40 +243,74 @@ export class DOMWorld {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async $eval<ReturnType>(
|
async $eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
element: Element,
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
) => ReturnType | Promise<ReturnType>,
|
async $eval<
|
||||||
...args: SerializableOrJSHandle[]
|
Params extends unknown[],
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: string,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const document = await this._document();
|
const document = await this._document();
|
||||||
return document.$eval<ReturnType>(selector, pageFunction, ...args);
|
return document.$eval(selector, pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async $$eval<ReturnType>(
|
async $$eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector][], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector][], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $$eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
elements: Element[],
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
) => ReturnType | Promise<ReturnType>,
|
async $$eval<
|
||||||
...args: SerializableOrJSHandle[]
|
Params extends unknown[],
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: string,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const document = await this._document();
|
const document = await this._document();
|
||||||
const value = await document.$$eval<ReturnType>(
|
const value = await document.$$eval(selector, pageFunction, ...args);
|
||||||
selector,
|
|
||||||
pageFunction,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async $$<T extends Element = Element>(
|
|
||||||
selector: string
|
|
||||||
): Promise<Array<ElementHandle<T>>> {
|
|
||||||
const document = await this._document();
|
|
||||||
const value = await document.$$<T>(selector);
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +340,7 @@ export class DOMWorld {
|
|||||||
} = options;
|
} = options;
|
||||||
// We rely upon the fact that document.open() will reset frame lifecycle with "init"
|
// We rely upon the fact that document.open() will reset frame lifecycle with "init"
|
||||||
// lifecycle event. @see https://crrev.com/608658
|
// lifecycle event. @see https://crrev.com/608658
|
||||||
await this.evaluate<(x: string) => void>(html => {
|
await this.evaluate(html => {
|
||||||
document.open();
|
document.open();
|
||||||
document.write(html);
|
document.write(html);
|
||||||
document.close();
|
document.close();
|
||||||
@ -536,7 +578,6 @@ export class DOMWorld {
|
|||||||
|
|
||||||
async function addStyleContent(content: string): Promise<HTMLElement> {
|
async function addStyleContent(content: string): Promise<HTMLElement> {
|
||||||
const style = document.createElement('style');
|
const style = document.createElement('style');
|
||||||
style.type = 'text/css';
|
|
||||||
style.appendChild(document.createTextNode(content));
|
style.appendChild(document.createTextNode(content));
|
||||||
const promise = new Promise((res, rej) => {
|
const promise = new Promise((res, rej) => {
|
||||||
style.onload = res;
|
style.onload = res;
|
||||||
@ -598,6 +639,14 @@ export class DOMWorld {
|
|||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async waitForSelector<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector,
|
||||||
|
options: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
|
async waitForSelector(
|
||||||
|
selector: string,
|
||||||
|
options: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle | null>;
|
||||||
async waitForSelector(
|
async waitForSelector(
|
||||||
selector: string,
|
selector: string,
|
||||||
options: WaitForSelectorOptions
|
options: WaitForSelectorOptions
|
||||||
@ -825,7 +874,7 @@ export class DOMWorld {
|
|||||||
waitForFunction(
|
waitForFunction(
|
||||||
pageFunction: Function | string,
|
pageFunction: Function | string,
|
||||||
options: {polling?: string | number; timeout?: number} = {},
|
options: {polling?: string | number; timeout?: number} = {},
|
||||||
...args: SerializableOrJSHandle[]
|
...args: unknown[]
|
||||||
): Promise<JSHandle> {
|
): Promise<JSHandle> {
|
||||||
const {polling = 'raf', timeout = this.#timeoutSettings.timeout()} =
|
const {polling = 'raf', timeout = this.#timeoutSettings.timeout()} =
|
||||||
options;
|
options;
|
||||||
@ -860,7 +909,7 @@ export interface WaitTaskOptions {
|
|||||||
polling: string | number;
|
polling: string | number;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
binding?: PageBinding;
|
binding?: PageBinding;
|
||||||
args: SerializableOrJSHandle[];
|
args: unknown[];
|
||||||
root?: ElementHandle;
|
root?: ElementHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,11 +920,11 @@ const noop = (): void => {};
|
|||||||
*/
|
*/
|
||||||
export class WaitTask {
|
export class WaitTask {
|
||||||
#domWorld: DOMWorld;
|
#domWorld: DOMWorld;
|
||||||
#polling: string | number;
|
#polling: 'raf' | 'mutation' | number;
|
||||||
#timeout: number;
|
#timeout: number;
|
||||||
#predicateBody: string;
|
#predicateBody: string;
|
||||||
#predicateAcceptsContextElement: boolean;
|
#predicateAcceptsContextElement: boolean;
|
||||||
#args: SerializableOrJSHandle[];
|
#args: unknown[];
|
||||||
#binding?: PageBinding;
|
#binding?: PageBinding;
|
||||||
#runCount = 0;
|
#runCount = 0;
|
||||||
#resolve: (x: JSHandle) => void = noop;
|
#resolve: (x: JSHandle) => void = noop;
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2020 Google Inc. All rights reserved.
|
|
||||||
*
|
|
||||||
* 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} from './JSHandle.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type EvaluateFn<T = any, U = any, V = any> =
|
|
||||||
| string
|
|
||||||
| ((arg1: T, ...args: U[]) => V);
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type EvaluateFnReturnType<T extends EvaluateFn> = T extends (
|
|
||||||
...args: any[]
|
|
||||||
) => infer R
|
|
||||||
? R
|
|
||||||
: any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type EvaluateHandleFn = string | ((...args: any[]) => any);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type Serializable =
|
|
||||||
| number
|
|
||||||
| string
|
|
||||||
| boolean
|
|
||||||
| null
|
|
||||||
| bigint
|
|
||||||
| JSONArray
|
|
||||||
| JSONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type JSONArray = readonly Serializable[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface JSONObject {
|
|
||||||
[key: string]: Serializable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type SerializableOrJSHandle = Serializable | JSHandle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps a DOM element into an ElementHandle instance
|
|
||||||
* @public
|
|
||||||
**/
|
|
||||||
export type WrapElementHandle<X> = X extends Element ? ElementHandle<X> : X;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unwraps a DOM element out of an ElementHandle instance
|
|
||||||
* @public
|
|
||||||
**/
|
|
||||||
export type UnwrapElementHandle<X> = X extends ElementHandle<infer E> ? E : X;
|
|
@ -18,10 +18,10 @@ import {Protocol} from 'devtools-protocol';
|
|||||||
import {assert} from './assert.js';
|
import {assert} from './assert.js';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {DOMWorld} from './DOMWorld.js';
|
import {DOMWorld} from './DOMWorld.js';
|
||||||
import {EvaluateHandleFn, SerializableOrJSHandle} from './EvalTypes.js';
|
import {EvaluateFunc, HandleFor, EvaluateParams} from './types.js';
|
||||||
import {Frame} from './FrameManager.js';
|
import {Frame} from './FrameManager.js';
|
||||||
import {getExceptionMessage, isString, valueFromRemoteObject} from './util.js';
|
|
||||||
import {ElementHandle, JSHandle, _createJSHandle} from './JSHandle.js';
|
import {ElementHandle, JSHandle, _createJSHandle} from './JSHandle.js';
|
||||||
|
import {getExceptionMessage, isString, valueFromRemoteObject} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -134,11 +134,14 @@ export class ExecutionContext {
|
|||||||
*
|
*
|
||||||
* @returns A promise that resolves to the return value of the given function.
|
* @returns A promise that resolves to the return value of the given function.
|
||||||
*/
|
*/
|
||||||
async evaluate<ReturnType>(
|
async evaluate<
|
||||||
pageFunction: Function | string,
|
Params extends unknown[],
|
||||||
...args: unknown[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<ReturnType> {
|
>(
|
||||||
return await this.#evaluate<ReturnType>(true, pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return await this.#evaluate(true, pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -183,18 +186,40 @@ export class ExecutionContext {
|
|||||||
* @returns A promise that resolves to the return value of the given function
|
* @returns A promise that resolves to the return value of the given function
|
||||||
* as an in-page object (a {@link JSHandle}).
|
* as an in-page object (a {@link JSHandle}).
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<HandleType extends JSHandle | ElementHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<HandleType> {
|
>(
|
||||||
return this.#evaluate<HandleType>(false, pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
|
return this.#evaluate(false, pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #evaluate<ReturnType>(
|
async #evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
|
>(
|
||||||
|
returnByValue: true,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async #evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
|
>(
|
||||||
|
returnByValue: false,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
|
||||||
|
async #evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
|
>(
|
||||||
returnByValue: boolean,
|
returnByValue: boolean,
|
||||||
pageFunction: Function | string,
|
pageFunction: Func | string,
|
||||||
...args: unknown[]
|
...args: EvaluateParams<Params>
|
||||||
): Promise<ReturnType> {
|
): Promise<HandleFor<Awaited<ReturnType<Func>>> | Awaited<ReturnType<Func>>> {
|
||||||
const suffix = `//# sourceURL=${EVALUATION_SCRIPT_URL}`;
|
const suffix = `//# sourceURL=${EVALUATION_SCRIPT_URL}`;
|
||||||
|
|
||||||
if (isString(pageFunction)) {
|
if (isString(pageFunction)) {
|
||||||
@ -365,7 +390,9 @@ export class ExecutionContext {
|
|||||||
*
|
*
|
||||||
* @returns A handle to an array of objects with the given prototype.
|
* @returns A handle to an array of objects with the given prototype.
|
||||||
*/
|
*/
|
||||||
async queryObjects(prototypeHandle: JSHandle): Promise<JSHandle> {
|
async queryObjects<Prototype>(
|
||||||
|
prototypeHandle: JSHandle<Prototype>
|
||||||
|
): Promise<HandleFor<Prototype[]>> {
|
||||||
assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
|
assert(!prototypeHandle._disposed, 'Prototype JSHandle is disposed!');
|
||||||
assert(
|
assert(
|
||||||
prototypeHandle._remoteObject.objectId,
|
prototypeHandle._remoteObject.objectId,
|
||||||
@ -374,7 +401,7 @@ export class ExecutionContext {
|
|||||||
const response = await this._client.send('Runtime.queryObjects', {
|
const response = await this._client.send('Runtime.queryObjects', {
|
||||||
prototypeObjectId: prototypeHandle._remoteObject.objectId,
|
prototypeObjectId: prototypeHandle._remoteObject.objectId,
|
||||||
});
|
});
|
||||||
return _createJSHandle(this, response.objects);
|
return _createJSHandle(this, response.objects) as HandleFor<Prototype[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +37,7 @@ import {assert} from './assert.js';
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class FileChooser {
|
export class FileChooser {
|
||||||
#element: ElementHandle;
|
#element: ElementHandle<HTMLInputElement>;
|
||||||
#multiple: boolean;
|
#multiple: boolean;
|
||||||
#handled = false;
|
#handled = false;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ export class FileChooser {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
element: ElementHandle,
|
element: ElementHandle<HTMLInputElement>,
|
||||||
event: Protocol.Page.FileChooserOpenedEvent
|
event: Protocol.Page.FileChooserOpenedEvent
|
||||||
) {
|
) {
|
||||||
this.#element = element;
|
this.#element = element;
|
||||||
|
@ -18,27 +18,19 @@ import {Protocol} from 'devtools-protocol';
|
|||||||
import {assert} from './assert.js';
|
import {assert} from './assert.js';
|
||||||
import {CDPSession, Connection} from './Connection.js';
|
import {CDPSession, Connection} from './Connection.js';
|
||||||
import {DOMWorld, WaitForSelectorOptions} from './DOMWorld.js';
|
import {DOMWorld, WaitForSelectorOptions} from './DOMWorld.js';
|
||||||
import {
|
|
||||||
EvaluateFn,
|
|
||||||
EvaluateFnReturnType,
|
|
||||||
EvaluateHandleFn,
|
|
||||||
SerializableOrJSHandle,
|
|
||||||
UnwrapPromiseLike,
|
|
||||||
WrapElementHandle,
|
|
||||||
} from './EvalTypes.js';
|
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {EVALUATION_SCRIPT_URL, ExecutionContext} from './ExecutionContext.js';
|
import {EVALUATION_SCRIPT_URL, ExecutionContext} from './ExecutionContext.js';
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
import {MouseButton} from './Input.js';
|
import {MouseButton} from './Input.js';
|
||||||
import {ElementHandle, JSHandle} from './JSHandle.js';
|
import {ElementHandle} from './JSHandle.js';
|
||||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
import {NetworkManager} from './NetworkManager.js';
|
import {NetworkManager} from './NetworkManager.js';
|
||||||
import {Page} from './Page.js';
|
import {Page} from './Page.js';
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
import {debugError, isErrorLike, isNumber, isString} from './util.js';
|
import {EvaluateFunc, EvaluateParams, HandleFor} from './types.js';
|
||||||
|
import {debugError, isErrorLike} from './util.js';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
|
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
|
||||||
const xPathPattern = /^\(\/\/[^\)]+\)|^\/\//;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We use symbols to prevent external parties listening to these events.
|
* We use symbols to prevent external parties listening to these events.
|
||||||
@ -892,11 +884,14 @@ export class Frame {
|
|||||||
* @param pageFunction - a function that is run within the frame
|
* @param pageFunction - a function that is run within the frame
|
||||||
* @param args - arguments to be passed to the pageFunction
|
* @param args - arguments to be passed to the pageFunction
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<HandlerType extends JSHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<HandlerType> {
|
>(
|
||||||
return this._mainWorld.evaluateHandle<HandlerType>(pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
|
return this._mainWorld.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -908,11 +903,14 @@ export class Frame {
|
|||||||
* @param pageFunction - a function that is run within the frame
|
* @param pageFunction - a function that is run within the frame
|
||||||
* @param args - arguments to be passed to the pageFunction
|
* @param args - arguments to be passed to the pageFunction
|
||||||
*/
|
*/
|
||||||
async evaluate<T extends EvaluateFn>(
|
async evaluate<
|
||||||
pageFunction: T,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>> {
|
>(
|
||||||
return this._mainWorld.evaluate<T>(pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return this._mainWorld.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -922,10 +920,26 @@ export class Frame {
|
|||||||
* @returns A promise which resolves to an `ElementHandle` pointing at the
|
* @returns A promise which resolves to an `ElementHandle` pointing at the
|
||||||
* element, or `null` if it was not found.
|
* element, or `null` if it was not found.
|
||||||
*/
|
*/
|
||||||
async $<T extends Element = Element>(
|
async $<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
selector: string
|
selector: Selector
|
||||||
): Promise<ElementHandle<T> | null> {
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
return this._mainWorld.$<T>(selector);
|
async $(selector: string): Promise<ElementHandle | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
|
return this._mainWorld.$(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This runs `document.querySelectorAll` in the frame and returns the result.
|
||||||
|
*
|
||||||
|
* @param selector - a selector to search for
|
||||||
|
* @returns An array of element handles pointing to the found frame elements.
|
||||||
|
*/
|
||||||
|
async $$<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]>[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
|
return this._mainWorld.$$(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -956,15 +970,38 @@ export class Frame {
|
|||||||
* @param pageFunction - the function to be evaluated in the frame's context
|
* @param pageFunction - the function to be evaluated in the frame's context
|
||||||
* @param args - additional arguments to pass to `pageFunction`
|
* @param args - additional arguments to pass to `pageFunction`
|
||||||
*/
|
*/
|
||||||
async $eval<ReturnType>(
|
async $eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
element: Element,
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
) => ReturnType | Promise<ReturnType>,
|
async $eval<
|
||||||
...args: SerializableOrJSHandle[]
|
Params extends unknown[],
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
return this._mainWorld.$eval<ReturnType>(selector, pageFunction, ...args);
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: string,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return this._mainWorld.$eval(selector, pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -986,27 +1023,38 @@ export class Frame {
|
|||||||
* @param pageFunction - the function to be evaluated in the frame's context
|
* @param pageFunction - the function to be evaluated in the frame's context
|
||||||
* @param args - additional arguments to pass to `pageFunction`
|
* @param args - additional arguments to pass to `pageFunction`
|
||||||
*/
|
*/
|
||||||
async $$eval<ReturnType>(
|
async $$eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector][], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector][], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $$eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
elements: Element[],
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
) => ReturnType | Promise<ReturnType>,
|
async $$eval<
|
||||||
...args: SerializableOrJSHandle[]
|
Params extends unknown[],
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
return this._mainWorld.$$eval<ReturnType>(selector, pageFunction, ...args);
|
[Element[], ...Params]
|
||||||
}
|
>
|
||||||
|
>(
|
||||||
/**
|
selector: string,
|
||||||
* This runs `document.querySelectorAll` in the frame and returns the result.
|
pageFunction: Func | string,
|
||||||
*
|
...args: EvaluateParams<Params>
|
||||||
* @param selector - a selector to search for
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
* @returns An array of element handles pointing to the found frame elements.
|
return this._mainWorld.$$eval(selector, pageFunction, ...args);
|
||||||
*/
|
|
||||||
async $$<T extends Element = Element>(
|
|
||||||
selector: string
|
|
||||||
): Promise<Array<ElementHandle<T>>> {
|
|
||||||
return this._mainWorld.$$<T>(selector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1238,66 +1286,6 @@ export class Frame {
|
|||||||
return this._mainWorld.type(selector, text, options);
|
return this._mainWorld.type(selector, text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @remarks
|
|
||||||
*
|
|
||||||
* This method behaves differently depending on the first parameter. If it's a
|
|
||||||
* `string`, it will be treated as a `selector` or `xpath` (if the string
|
|
||||||
* starts with `//`). This method then is a shortcut for
|
|
||||||
* {@link Frame.waitForSelector} or {@link Frame.waitForXPath}.
|
|
||||||
*
|
|
||||||
* If the first argument is a function this method is a shortcut for
|
|
||||||
* {@link Frame.waitForFunction}.
|
|
||||||
*
|
|
||||||
* If the first argument is a `number`, it's treated as a timeout in
|
|
||||||
* milliseconds and the method returns a promise which resolves after the
|
|
||||||
* timeout.
|
|
||||||
*
|
|
||||||
* @param selectorOrFunctionOrTimeout - a selector, predicate or timeout to
|
|
||||||
* wait for.
|
|
||||||
* @param options - optional waiting parameters.
|
|
||||||
* @param args - arguments to pass to `pageFunction`.
|
|
||||||
*
|
|
||||||
* @deprecated Don't use this method directly. Instead use the more explicit
|
|
||||||
* methods available: {@link Frame.waitForSelector},
|
|
||||||
* {@link Frame.waitForXPath}, {@link Frame.waitForFunction} or
|
|
||||||
* {@link Frame.waitForTimeout}.
|
|
||||||
*/
|
|
||||||
waitFor(
|
|
||||||
selectorOrFunctionOrTimeout: string | number | Function,
|
|
||||||
options: Record<string, unknown> = {},
|
|
||||||
...args: SerializableOrJSHandle[]
|
|
||||||
): Promise<JSHandle | null> {
|
|
||||||
console.warn(
|
|
||||||
'waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isString(selectorOrFunctionOrTimeout)) {
|
|
||||||
const string = selectorOrFunctionOrTimeout;
|
|
||||||
if (xPathPattern.test(string)) {
|
|
||||||
return this.waitForXPath(string, options);
|
|
||||||
}
|
|
||||||
return this.waitForSelector(string, options);
|
|
||||||
}
|
|
||||||
if (isNumber(selectorOrFunctionOrTimeout)) {
|
|
||||||
return new Promise(fulfill => {
|
|
||||||
return setTimeout(fulfill, selectorOrFunctionOrTimeout);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (typeof selectorOrFunctionOrTimeout === 'function') {
|
|
||||||
return this.waitForFunction(
|
|
||||||
selectorOrFunctionOrTimeout,
|
|
||||||
options,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Promise.reject(
|
|
||||||
new Error(
|
|
||||||
'Unsupported target type: ' + typeof selectorOrFunctionOrTimeout
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes your script to wait for the given number of milliseconds.
|
* Causes your script to wait for the given number of milliseconds.
|
||||||
*
|
*
|
||||||
@ -1357,6 +1345,14 @@ export class Frame {
|
|||||||
* @returns a promise which resolves when an element matching the selector
|
* @returns a promise which resolves when an element matching the selector
|
||||||
* string is added to the DOM.
|
* string is added to the DOM.
|
||||||
*/
|
*/
|
||||||
|
async waitForSelector<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector,
|
||||||
|
options?: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
|
async waitForSelector(
|
||||||
|
selector: string,
|
||||||
|
options?: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle | null>;
|
||||||
async waitForSelector(
|
async waitForSelector(
|
||||||
selector: string,
|
selector: string,
|
||||||
options: WaitForSelectorOptions = {}
|
options: WaitForSelectorOptions = {}
|
||||||
@ -1438,12 +1434,20 @@ export class Frame {
|
|||||||
* @param args - arguments to pass to the `pageFunction`.
|
* @param args - arguments to pass to the `pageFunction`.
|
||||||
* @returns the promise which resolve when the `pageFunction` returns a truthy value.
|
* @returns the promise which resolve when the `pageFunction` returns a truthy value.
|
||||||
*/
|
*/
|
||||||
waitForFunction(
|
waitForFunction<
|
||||||
pageFunction: Function | string,
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
options: FrameWaitForFunctionOptions = {},
|
options: FrameWaitForFunctionOptions = {},
|
||||||
...args: SerializableOrJSHandle[]
|
...args: EvaluateParams<Params>
|
||||||
): Promise<JSHandle> {
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
return this._mainWorld.waitForFunction(pageFunction, options, ...args);
|
// TODO: Fix when NodeHandle has been added.
|
||||||
|
return this._mainWorld.waitForFunction(
|
||||||
|
pageFunction,
|
||||||
|
options,
|
||||||
|
...args
|
||||||
|
) as Promise<HandleFor<Awaited<ReturnType<Func>>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,14 +17,7 @@
|
|||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {assert} from './assert.js';
|
import {assert} from './assert.js';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {
|
import {EvaluateFunc, EvaluateParams, HandleFor, HandleOr} from './types.js';
|
||||||
EvaluateFn,
|
|
||||||
EvaluateFnReturnType,
|
|
||||||
EvaluateHandleFn,
|
|
||||||
SerializableOrJSHandle,
|
|
||||||
UnwrapPromiseLike,
|
|
||||||
WrapElementHandle,
|
|
||||||
} from './EvalTypes.js';
|
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {Frame, FrameManager} from './FrameManager.js';
|
import {Frame, FrameManager} from './FrameManager.js';
|
||||||
import {MouseButton} from './Input.js';
|
import {MouseButton} from './Input.js';
|
||||||
@ -37,6 +30,7 @@ import {
|
|||||||
releaseObject,
|
releaseObject,
|
||||||
valueFromRemoteObject,
|
valueFromRemoteObject,
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
|
import {WaitForSelectorOptions} from './DOMWorld.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -70,7 +64,7 @@ export interface BoundingBox extends Point {
|
|||||||
export function _createJSHandle(
|
export function _createJSHandle(
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
remoteObject: Protocol.Runtime.RemoteObject
|
remoteObject: Protocol.Runtime.RemoteObject
|
||||||
): JSHandle {
|
): JSHandle | ElementHandle {
|
||||||
const frame = context.frame();
|
const frame = context.frame();
|
||||||
if (remoteObject.subtype === 'node' && frame) {
|
if (remoteObject.subtype === 'node' && frame) {
|
||||||
const frameManager = frame._frameManager;
|
const frameManager = frame._frameManager;
|
||||||
@ -114,7 +108,7 @@ const applyOffsetsToQuad = (
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class JSHandle<HandleObjectType = unknown> {
|
export class JSHandle<T = unknown> {
|
||||||
#client: CDPSession;
|
#client: CDPSession;
|
||||||
#disposed = false;
|
#disposed = false;
|
||||||
#context: ExecutionContext;
|
#context: ExecutionContext;
|
||||||
@ -179,13 +173,14 @@ export class JSHandle<HandleObjectType = unknown> {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async evaluate<T extends EvaluateFn<HandleObjectType>>(
|
async evaluate<
|
||||||
pageFunction: T | string,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<[T, ...Params]> = EvaluateFunc<[T, ...Params]>
|
||||||
): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>> {
|
>(
|
||||||
return await this.executionContext().evaluate<
|
pageFunction: Func | string,
|
||||||
UnwrapPromiseLike<EvaluateFnReturnType<T>>
|
...args: EvaluateParams<Params>
|
||||||
>(pageFunction, this, ...args);
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,10 +198,13 @@ export class JSHandle<HandleObjectType = unknown> {
|
|||||||
*
|
*
|
||||||
* See {@link Page.evaluateHandle} for more details.
|
* See {@link Page.evaluateHandle} for more details.
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<HandleType extends JSHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<[T, ...Params]> = EvaluateFunc<[T, ...Params]>
|
||||||
): Promise<HandleType> {
|
>(
|
||||||
|
pageFunction: Func,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
return await this.executionContext().evaluateHandle(
|
return await this.executionContext().evaluateHandle(
|
||||||
pageFunction,
|
pageFunction,
|
||||||
this,
|
this,
|
||||||
@ -214,22 +212,19 @@ export class JSHandle<HandleObjectType = unknown> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fetches a single property from the referenced object.
|
/**
|
||||||
|
* Fetches a single property from the referenced object.
|
||||||
*/
|
*/
|
||||||
async getProperty(propertyName: string): Promise<JSHandle> {
|
async getProperty<K extends keyof T>(
|
||||||
const objectHandle = await this.evaluateHandle(
|
propertyName: HandleOr<K>
|
||||||
(object: Element, propertyName: keyof Element) => {
|
): Promise<HandleFor<T[K]>>;
|
||||||
const result: Record<string, unknown> = {__proto__: null};
|
async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
|
||||||
result[propertyName] = object[propertyName];
|
async getProperty<K extends keyof T>(
|
||||||
return result;
|
propertyName: HandleOr<K>
|
||||||
},
|
): Promise<HandleFor<T[K]>> {
|
||||||
propertyName
|
return await this.evaluateHandle((object, propertyName) => {
|
||||||
);
|
return object[propertyName];
|
||||||
const properties = await objectHandle.getProperties();
|
}, propertyName);
|
||||||
const result = properties.get(propertyName);
|
|
||||||
assert(result instanceof JSHandle);
|
|
||||||
await objectHandle.dispose();
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -412,13 +407,17 @@ export class ElementHandle<
|
|||||||
* (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
* (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
||||||
* by using the {@link Page.setDefaultTimeout} method.
|
* by using the {@link Page.setDefaultTimeout} method.
|
||||||
*/
|
*/
|
||||||
|
async waitForSelector<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector,
|
||||||
|
options?: Exclude<WaitForSelectorOptions, 'root'>
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
async waitForSelector(
|
async waitForSelector(
|
||||||
selector: string,
|
selector: string,
|
||||||
options: {
|
options?: Exclude<WaitForSelectorOptions, 'root'>
|
||||||
visible?: boolean;
|
): Promise<ElementHandle | null>;
|
||||||
hidden?: boolean;
|
async waitForSelector(
|
||||||
timeout?: number;
|
selector: string,
|
||||||
} = {}
|
options: Exclude<WaitForSelectorOptions, 'root'> = {}
|
||||||
): Promise<ElementHandle | null> {
|
): Promise<ElementHandle | null> {
|
||||||
const frame = this._context.frame();
|
const frame = this._context.frame();
|
||||||
assert(frame);
|
assert(frame);
|
||||||
@ -539,10 +538,7 @@ export class ElementHandle<
|
|||||||
|
|
||||||
async #scrollIntoViewIfNeeded(): Promise<void> {
|
async #scrollIntoViewIfNeeded(): Promise<void> {
|
||||||
const error = await this.evaluate(
|
const error = await this.evaluate(
|
||||||
async (
|
async (element, pageJavascriptEnabled): Promise<string | false> => {
|
||||||
element: Element,
|
|
||||||
pageJavascriptEnabled: boolean
|
|
||||||
): Promise<string | false> => {
|
|
||||||
if (!element.isConnected) {
|
if (!element.isConnected) {
|
||||||
return 'Node is detached from document';
|
return 'Node is detached from document';
|
||||||
}
|
}
|
||||||
@ -828,7 +824,7 @@ export class ElementHandle<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.evaluate((element: Element, vals: string[]): string[] => {
|
return this.evaluate((element, vals): string[] => {
|
||||||
const values = new Set(vals);
|
const values = new Set(vals);
|
||||||
if (!(element instanceof HTMLSelectElement)) {
|
if (!(element instanceof HTMLSelectElement)) {
|
||||||
throw new Error('Element is not a <select> element.');
|
throw new Error('Element is not a <select> element.');
|
||||||
@ -870,15 +866,13 @@ export class ElementHandle<
|
|||||||
* Note for locals script connecting to remote chrome environments,
|
* Note for locals script connecting to remote chrome environments,
|
||||||
* paths must be absolute.
|
* paths must be absolute.
|
||||||
*/
|
*/
|
||||||
async uploadFile(...filePaths: string[]): Promise<void> {
|
async uploadFile(
|
||||||
const isMultiple = await this.evaluate<(element: Element) => boolean>(
|
this: ElementHandle<HTMLInputElement>,
|
||||||
element => {
|
...filePaths: string[]
|
||||||
if (!(element instanceof HTMLInputElement)) {
|
): Promise<void> {
|
||||||
throw new Error('uploadFile can only be called on an input element.');
|
const isMultiple = await this.evaluate(element => {
|
||||||
}
|
return element.multiple;
|
||||||
return element.multiple;
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
assert(
|
assert(
|
||||||
filePaths.length <= 1 || isMultiple,
|
filePaths.length <= 1 || isMultiple,
|
||||||
'Multiple file uploads only work with <input type=file multiple>'
|
'Multiple file uploads only work with <input type=file multiple>'
|
||||||
@ -912,7 +906,7 @@ export class ElementHandle<
|
|||||||
so the solution is to eval the element value to a new FileList directly.
|
so the solution is to eval the element value to a new FileList directly.
|
||||||
*/
|
*/
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
await (this as ElementHandle<HTMLInputElement>).evaluate(element => {
|
await this.evaluate(element => {
|
||||||
element.files = new DataTransfer().files;
|
element.files = new DataTransfer().files;
|
||||||
|
|
||||||
// Dispatch events for this case because it should behave akin to a user action.
|
// Dispatch events for this case because it should behave akin to a user action.
|
||||||
@ -943,7 +937,10 @@ export class ElementHandle<
|
|||||||
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
|
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
|
||||||
*/
|
*/
|
||||||
async focus(): Promise<void> {
|
async focus(): Promise<void> {
|
||||||
await (this as ElementHandle<HTMLElement>).evaluate(element => {
|
await this.evaluate(element => {
|
||||||
|
if (!(element instanceof HTMLElement)) {
|
||||||
|
throw new Error('Cannot focus non-HTMLElement');
|
||||||
|
}
|
||||||
return element.focus();
|
return element.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1126,9 +1123,11 @@ export class ElementHandle<
|
|||||||
* @returns `null` if no element matches the selector.
|
* @returns `null` if no element matches the selector.
|
||||||
* @throws `Error` if the selector has no associated query handler.
|
* @throws `Error` if the selector has no associated query handler.
|
||||||
*/
|
*/
|
||||||
async $<T extends Element = Element>(
|
async $<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
selector: string
|
selector: Selector
|
||||||
): Promise<ElementHandle<T> | null> {
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, queryHandler} =
|
||||||
_getQueryHandlerAndSelector(selector);
|
_getQueryHandlerAndSelector(selector);
|
||||||
assert(
|
assert(
|
||||||
@ -1149,9 +1148,11 @@ export class ElementHandle<
|
|||||||
* @returns `[]` if no element matches the selector.
|
* @returns `[]` if no element matches the selector.
|
||||||
* @throws `Error` if the selector has no associated query handler.
|
* @throws `Error` if the selector has no associated query handler.
|
||||||
*/
|
*/
|
||||||
async $$<T extends Element = Element>(
|
async $$<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
selector: string
|
selector: Selector
|
||||||
): Promise<Array<ElementHandle<T>>> {
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]>[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, queryHandler} =
|
||||||
_getQueryHandlerAndSelector(selector);
|
_getQueryHandlerAndSelector(selector);
|
||||||
assert(
|
assert(
|
||||||
@ -1176,37 +1177,46 @@ export class ElementHandle<
|
|||||||
* expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
|
* expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
async $eval<ReturnType>(
|
async $eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
element: Element,
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
) => ReturnType | Promise<ReturnType>,
|
async $eval<
|
||||||
...args: SerializableOrJSHandle[]
|
Params extends unknown[],
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: string,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const elementHandle = await this.$(selector);
|
const elementHandle = await this.$(selector);
|
||||||
if (!elementHandle) {
|
if (!elementHandle) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error: failed to find element matching selector "${selector}"`
|
`Error: failed to find element matching selector "${selector}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const result = await elementHandle.evaluate<
|
const result = await elementHandle.evaluate(pageFunction, ...args);
|
||||||
(
|
|
||||||
element: Element,
|
|
||||||
...args: SerializableOrJSHandle[]
|
|
||||||
) => ReturnType | Promise<ReturnType>
|
|
||||||
>(pageFunction, ...args);
|
|
||||||
await elementHandle.dispose();
|
await elementHandle.dispose();
|
||||||
|
return result;
|
||||||
/**
|
|
||||||
* This `as` is a little unfortunate but helps TS understand the behavior of
|
|
||||||
* `elementHandle.evaluate`. If evaluate returns an element it will return an
|
|
||||||
* ElementHandle instance, rather than the plain object. All the
|
|
||||||
* WrapElementHandle type does is wrap ReturnType into
|
|
||||||
* ElementHandle<ReturnType> if it is an ElementHandle, or leave it alone as
|
|
||||||
* ReturnType if it isn't.
|
|
||||||
*/
|
|
||||||
return result as WrapElementHandle<ReturnType>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1232,28 +1242,44 @@ export class ElementHandle<
|
|||||||
* .toEqual(['Hello!', 'Hi!']);
|
* .toEqual(['Hello!', 'Hi!']);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
async $$eval<ReturnType>(
|
async $$eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector][], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector][], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $$eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: EvaluateFn<
|
pageFunction: Func | string,
|
||||||
Element[],
|
...args: EvaluateParams<Params>
|
||||||
unknown,
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
ReturnType | Promise<ReturnType>
|
async $$eval<
|
||||||
>,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: string,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, queryHandler} =
|
||||||
_getQueryHandlerAndSelector(selector);
|
_getQueryHandlerAndSelector(selector);
|
||||||
assert(queryHandler.queryAllArray);
|
assert(queryHandler.queryAllArray);
|
||||||
const arrayHandle = await queryHandler.queryAllArray(this, updatedSelector);
|
const arrayHandle = await queryHandler.queryAllArray(this, updatedSelector);
|
||||||
const result = await arrayHandle.evaluate<EvaluateFn<Element[]>>(
|
const result = await arrayHandle.evaluate(pageFunction, ...args);
|
||||||
pageFunction,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
await arrayHandle.dispose();
|
await arrayHandle.dispose();
|
||||||
/* This `as` exists for the same reason as the `as` in $eval above.
|
return result;
|
||||||
* See the comment there for a full explanation.
|
|
||||||
*/
|
|
||||||
return result as WrapElementHandle<ReturnType>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1262,24 +1288,21 @@ export class ElementHandle<
|
|||||||
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
|
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
|
||||||
*/
|
*/
|
||||||
async $x(expression: string): Promise<ElementHandle[]> {
|
async $x(expression: string): Promise<ElementHandle[]> {
|
||||||
const arrayHandle = await this.evaluateHandle(
|
const arrayHandle = await this.evaluateHandle((element, expression) => {
|
||||||
(element: Document, expression: string) => {
|
const document = element.ownerDocument || element;
|
||||||
const document = element.ownerDocument || element;
|
const iterator = document.evaluate(
|
||||||
const iterator = document.evaluate(
|
expression,
|
||||||
expression,
|
element,
|
||||||
element,
|
null,
|
||||||
null,
|
XPathResult.ORDERED_NODE_ITERATOR_TYPE
|
||||||
XPathResult.ORDERED_NODE_ITERATOR_TYPE
|
);
|
||||||
);
|
const array = [];
|
||||||
const array = [];
|
let item;
|
||||||
let item;
|
while ((item = iterator.iterateNext())) {
|
||||||
while ((item = iterator.iterateNext())) {
|
array.push(item);
|
||||||
array.push(item);
|
}
|
||||||
}
|
return array;
|
||||||
return array;
|
}, expression);
|
||||||
},
|
|
||||||
expression
|
|
||||||
);
|
|
||||||
const properties = await arrayHandle.getProperties();
|
const properties = await arrayHandle.getProperties();
|
||||||
await arrayHandle.dispose();
|
await arrayHandle.dispose();
|
||||||
const result = [];
|
const result = [];
|
||||||
@ -1298,8 +1321,8 @@ export class ElementHandle<
|
|||||||
async isIntersectingViewport(options?: {
|
async isIntersectingViewport(options?: {
|
||||||
threshold?: number;
|
threshold?: number;
|
||||||
}): Promise<boolean> {
|
}): Promise<boolean> {
|
||||||
const {threshold = 0} = options || {};
|
const {threshold = 0} = options ?? {};
|
||||||
return await this.evaluate(async (element: Element, threshold: number) => {
|
return await this.evaluate(async (element, threshold) => {
|
||||||
const visibleRatio = await new Promise<number>(resolve => {
|
const visibleRatio = await new Promise<number>(resolve => {
|
||||||
const observer = new IntersectionObserver(entries => {
|
const observer = new IntersectionObserver(entries => {
|
||||||
resolve(entries[0]!.intersectionRatio);
|
resolve(entries[0]!.intersectionRatio);
|
||||||
|
@ -23,15 +23,8 @@ import {CDPSession, CDPSessionEmittedEvents, Connection} from './Connection.js';
|
|||||||
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
||||||
import {Coverage} from './Coverage.js';
|
import {Coverage} from './Coverage.js';
|
||||||
import {Dialog} from './Dialog.js';
|
import {Dialog} from './Dialog.js';
|
||||||
|
import {WaitForSelectorOptions} from './DOMWorld.js';
|
||||||
import {EmulationManager} from './EmulationManager.js';
|
import {EmulationManager} from './EmulationManager.js';
|
||||||
import {
|
|
||||||
EvaluateFn,
|
|
||||||
EvaluateFnReturnType,
|
|
||||||
EvaluateHandleFn,
|
|
||||||
SerializableOrJSHandle,
|
|
||||||
UnwrapPromiseLike,
|
|
||||||
WrapElementHandle,
|
|
||||||
} from './EvalTypes.js';
|
|
||||||
import {EventEmitter, Handler} from './EventEmitter.js';
|
import {EventEmitter, Handler} from './EventEmitter.js';
|
||||||
import {FileChooser} from './FileChooser.js';
|
import {FileChooser} from './FileChooser.js';
|
||||||
import {
|
import {
|
||||||
@ -39,6 +32,23 @@ import {
|
|||||||
FrameManager,
|
FrameManager,
|
||||||
FrameManagerEmittedEvents,
|
FrameManagerEmittedEvents,
|
||||||
} from './FrameManager.js';
|
} from './FrameManager.js';
|
||||||
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
|
import {Keyboard, Mouse, MouseButton, Touchscreen} from './Input.js';
|
||||||
|
import {ElementHandle, JSHandle, _createJSHandle} from './JSHandle.js';
|
||||||
|
import {PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
|
import {
|
||||||
|
Credentials,
|
||||||
|
NetworkConditions,
|
||||||
|
NetworkManagerEmittedEvents,
|
||||||
|
} from './NetworkManager.js';
|
||||||
|
import {LowerCasePaperFormat, PDFOptions, _paperFormats} from './PDFOptions.js';
|
||||||
|
import {Viewport} from './PuppeteerViewport.js';
|
||||||
|
import {Target} from './Target.js';
|
||||||
|
import {TaskQueue} from './TaskQueue.js';
|
||||||
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
|
import {Tracing} from './Tracing.js';
|
||||||
|
import {EvaluateFunc, EvaluateParams, HandleFor} from './types.js';
|
||||||
import {
|
import {
|
||||||
debugError,
|
debugError,
|
||||||
evaluationString,
|
evaluationString,
|
||||||
@ -57,22 +67,6 @@ import {
|
|||||||
waitForEvent,
|
waitForEvent,
|
||||||
waitWithTimeout,
|
waitWithTimeout,
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
|
||||||
import {Keyboard, Mouse, MouseButton, Touchscreen} from './Input.js';
|
|
||||||
import {ElementHandle, JSHandle, _createJSHandle} from './JSHandle.js';
|
|
||||||
import {PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
|
||||||
import {
|
|
||||||
Credentials,
|
|
||||||
NetworkConditions,
|
|
||||||
NetworkManagerEmittedEvents,
|
|
||||||
} from './NetworkManager.js';
|
|
||||||
import {LowerCasePaperFormat, PDFOptions, _paperFormats} from './PDFOptions.js';
|
|
||||||
import {Viewport} from './PuppeteerViewport.js';
|
|
||||||
import {Target} from './Target.js';
|
|
||||||
import {TaskQueue} from './TaskQueue.js';
|
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
|
||||||
import {Tracing} from './Tracing.js';
|
|
||||||
import {WebWorker} from './WebWorker.js';
|
import {WebWorker} from './WebWorker.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -466,9 +460,7 @@ export class Page extends EventEmitter {
|
|||||||
#viewport: Viewport | null;
|
#viewport: Viewport | null;
|
||||||
#screenshotTaskQueue: TaskQueue;
|
#screenshotTaskQueue: TaskQueue;
|
||||||
#workers = new Map<string, WebWorker>();
|
#workers = new Map<string, WebWorker>();
|
||||||
// TODO: improve this typedef - it's a function that takes a file chooser or
|
#fileChooserInterceptors = new Set<(chooser: FileChooser) => void>();
|
||||||
// something?
|
|
||||||
#fileChooserInterceptors = new Set<Function>();
|
|
||||||
|
|
||||||
#disconnectPromise?: Promise<Error>;
|
#disconnectPromise?: Promise<Error>;
|
||||||
#userDragInterceptionEnabled = false;
|
#userDragInterceptionEnabled = false;
|
||||||
@ -638,9 +630,13 @@ export class Page extends EventEmitter {
|
|||||||
const element = await context._adoptBackendNodeId(event.backendNodeId);
|
const element = await context._adoptBackendNodeId(event.backendNodeId);
|
||||||
const interceptors = Array.from(this.#fileChooserInterceptors);
|
const interceptors = Array.from(this.#fileChooserInterceptors);
|
||||||
this.#fileChooserInterceptors.clear();
|
this.#fileChooserInterceptors.clear();
|
||||||
const fileChooser = new FileChooser(element, event);
|
const fileChooser = new FileChooser(
|
||||||
|
// This is guaranteed by the event.
|
||||||
|
element as ElementHandle<HTMLInputElement>,
|
||||||
|
event
|
||||||
|
);
|
||||||
for (const interceptor of interceptors) {
|
for (const interceptor of interceptors) {
|
||||||
interceptor.call(null, fileChooser);
|
interceptor.call(undefined, fileChooser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,7 +732,7 @@ export class Page extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {timeout = this.#timeoutSettings.timeout()} = options;
|
const {timeout = this.#timeoutSettings.timeout()} = options;
|
||||||
let callback!: (value: FileChooser | PromiseLike<FileChooser>) => void;
|
let callback!: (value: FileChooser) => void;
|
||||||
const promise = new Promise<FileChooser>(x => {
|
const promise = new Promise<FileChooser>(x => {
|
||||||
return (callback = x);
|
return (callback = x);
|
||||||
});
|
});
|
||||||
@ -1008,10 +1004,27 @@ export class Page extends EventEmitter {
|
|||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
|
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
|
||||||
* to query page for.
|
* to query page for.
|
||||||
*/
|
*/
|
||||||
async $<T extends Element = Element>(
|
async $<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
selector: string
|
selector: Selector
|
||||||
): Promise<ElementHandle<T> | null> {
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
return this.mainFrame().$<T>(selector);
|
async $(selector: string): Promise<ElementHandle | null>;
|
||||||
|
async $(selector: string): Promise<ElementHandle | null> {
|
||||||
|
return this.mainFrame().$(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method runs `document.querySelectorAll` within the page. If no elements
|
||||||
|
* match the selector, the return value resolves to `[]`.
|
||||||
|
* @remarks
|
||||||
|
* Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }.
|
||||||
|
* @param selector - A `selector` to query page for
|
||||||
|
*/
|
||||||
|
async $$<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]>[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]>;
|
||||||
|
async $$(selector: string): Promise<ElementHandle[]> {
|
||||||
|
return this.mainFrame().$$(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1063,12 +1076,15 @@ export class Page extends EventEmitter {
|
|||||||
* @param pageFunction - a function that is run within the page
|
* @param pageFunction - a function that is run within the page
|
||||||
* @param args - arguments to be passed to the pageFunction
|
* @param args - arguments to be passed to the pageFunction
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<HandlerType extends JSHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<HandlerType> {
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
const context = await this.mainFrame().executionContext();
|
const context = await this.mainFrame().executionContext();
|
||||||
return context.evaluateHandle<HandlerType>(pageFunction, ...args);
|
return context.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1098,7 +1114,9 @@ export class Page extends EventEmitter {
|
|||||||
* @returns Promise which resolves to a handle to an array of objects with
|
* @returns Promise which resolves to a handle to an array of objects with
|
||||||
* this prototype.
|
* this prototype.
|
||||||
*/
|
*/
|
||||||
async queryObjects(prototypeHandle: JSHandle): Promise<JSHandle> {
|
async queryObjects<Prototype>(
|
||||||
|
prototypeHandle: JSHandle<Prototype>
|
||||||
|
): Promise<JSHandle<Prototype[]>> {
|
||||||
const context = await this.mainFrame().executionContext();
|
const context = await this.mainFrame().executionContext();
|
||||||
return context.queryObjects(prototypeHandle);
|
return context.queryObjects(prototypeHandle);
|
||||||
}
|
}
|
||||||
@ -1161,25 +1179,38 @@ export class Page extends EventEmitter {
|
|||||||
* is wrapped in an {@link ElementHandle}, else the raw value itself is
|
* is wrapped in an {@link ElementHandle}, else the raw value itself is
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
async $eval<ReturnType>(
|
async $eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
|
[Element, ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
element: Element,
|
...args: EvaluateParams<Params>
|
||||||
/* Unfortunately this has to be unknown[] because it's hard to get
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
* TypeScript to understand that the arguments will be left alone unless
|
async $eval<
|
||||||
* they are an ElementHandle, in which case they will be unwrapped.
|
Params extends unknown[],
|
||||||
* The nice thing about unknown vs any is that unknown will force the user
|
Func extends EvaluateFunc<[Element, ...Params]> = EvaluateFunc<
|
||||||
* to type the item before using it to avoid errors.
|
[Element, ...Params]
|
||||||
*
|
>
|
||||||
* TODO(@jackfranklin): We could fix this by using overloads like
|
>(
|
||||||
* DefinitelyTyped does:
|
selector: string,
|
||||||
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/puppeteer/index.d.ts#L114
|
pageFunction: Func | string,
|
||||||
*/
|
...args: EvaluateParams<Params>
|
||||||
...args: unknown[]
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
) => ReturnType | Promise<ReturnType>,
|
return this.mainFrame().$eval(selector, pageFunction, ...args);
|
||||||
...args: SerializableOrJSHandle[]
|
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
|
||||||
return this.mainFrame().$eval<ReturnType>(selector, pageFunction, ...args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1244,32 +1275,38 @@ export class Page extends EventEmitter {
|
|||||||
* is wrapped in an {@link ElementHandle}, else the raw value itself is
|
* is wrapped in an {@link ElementHandle}, else the raw value itself is
|
||||||
* returned.
|
* returned.
|
||||||
*/
|
*/
|
||||||
async $$eval<ReturnType>(
|
async $$eval<
|
||||||
|
Selector extends keyof HTMLElementTagNameMap,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<
|
||||||
|
[HTMLElementTagNameMap[Selector][], ...Params]
|
||||||
|
> = EvaluateFunc<[HTMLElementTagNameMap[Selector][], ...Params]>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $$eval<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
|
[Element[], ...Params]
|
||||||
|
>
|
||||||
|
>(
|
||||||
selector: string,
|
selector: string,
|
||||||
pageFunction: (
|
pageFunction: Func | string,
|
||||||
elements: Element[],
|
...args: EvaluateParams<Params>
|
||||||
/* These have to be typed as unknown[] for the same reason as the $eval
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
* definition above, please see that comment for more details and the TODO
|
async $$eval<
|
||||||
* that will improve things.
|
Params extends unknown[],
|
||||||
*/
|
Func extends EvaluateFunc<[Element[], ...Params]> = EvaluateFunc<
|
||||||
...args: unknown[]
|
[Element[], ...Params]
|
||||||
) => ReturnType | Promise<ReturnType>,
|
>
|
||||||
...args: SerializableOrJSHandle[]
|
>(
|
||||||
): Promise<WrapElementHandle<ReturnType>> {
|
selector: string,
|
||||||
return this.mainFrame().$$eval<ReturnType>(selector, pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
}
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
/**
|
return this.mainFrame().$$eval(selector, pageFunction, ...args);
|
||||||
* The method runs `document.querySelectorAll` within the page. If no elements
|
|
||||||
* match the selector, the return value resolves to `[]`.
|
|
||||||
* @remarks
|
|
||||||
* Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }.
|
|
||||||
* @param selector - A `selector` to query page for
|
|
||||||
*/
|
|
||||||
async $$<T extends Element = Element>(
|
|
||||||
selector: string
|
|
||||||
): Promise<Array<ElementHandle<T>>> {
|
|
||||||
return this.mainFrame().$$<T>(selector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1390,7 +1427,7 @@ export class Page extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* NOTE: Functions installed via `page.exposeFunction` survive navigations.
|
* NOTE: Functions installed via `page.exposeFunction` survive navigations.
|
||||||
* @param name - Name of the function on the window object
|
* @param name - Name of the function on the window object
|
||||||
* @param puppeteerFunction - Callback function which will be called in
|
* @param pptrFunction - Callback function which will be called in
|
||||||
* Puppeteer's context.
|
* Puppeteer's context.
|
||||||
* @example
|
* @example
|
||||||
* An example of adding an `md5` function into the page:
|
* An example of adding an `md5` function into the page:
|
||||||
@ -1442,7 +1479,7 @@ export class Page extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
async exposeFunction(
|
async exposeFunction(
|
||||||
name: string,
|
name: string,
|
||||||
puppeteerFunction: Function | {default: Function}
|
pptrFunction: Function | {default: Function}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.#pageBindings.has(name)) {
|
if (this.#pageBindings.has(name)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -1451,14 +1488,13 @@ export class Page extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let exposedFunction: Function;
|
let exposedFunction: Function;
|
||||||
if (typeof puppeteerFunction === 'function') {
|
switch (typeof pptrFunction) {
|
||||||
exposedFunction = puppeteerFunction;
|
case 'function':
|
||||||
} else if (typeof puppeteerFunction.default === 'function') {
|
exposedFunction = pptrFunction;
|
||||||
exposedFunction = puppeteerFunction.default;
|
break;
|
||||||
} else {
|
default:
|
||||||
throw new Error(
|
exposedFunction = pptrFunction.default;
|
||||||
`Failed to add page binding with name ${name}: ${puppeteerFunction} is not a function or a module with a default export.`
|
break;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#pageBindings.set(name, exposedFunction);
|
this.#pageBindings.set(name, exposedFunction);
|
||||||
@ -2640,11 +2676,14 @@ export class Page extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @returns the return value of `pageFunction`.
|
* @returns the return value of `pageFunction`.
|
||||||
*/
|
*/
|
||||||
async evaluate<T extends EvaluateFn>(
|
async evaluate<
|
||||||
pageFunction: T,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>> {
|
>(
|
||||||
return this.#frameManager.mainFrame().evaluate<T>(pageFunction, ...args);
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return this.#frameManager.mainFrame().evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2678,10 +2717,10 @@ export class Page extends EventEmitter {
|
|||||||
* await page.evaluateOnNewDocument(preloadFile);
|
* await page.evaluateOnNewDocument(preloadFile);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
async evaluateOnNewDocument(
|
async evaluateOnNewDocument<
|
||||||
pageFunction: Function | string,
|
Params extends unknown[],
|
||||||
...args: unknown[]
|
Func extends (...args: Params) => unknown = (...args: Params) => unknown
|
||||||
): Promise<void> {
|
>(pageFunction: Func | string, ...args: Params): Promise<void> {
|
||||||
const source = evaluationString(pageFunction, ...args);
|
const source = evaluationString(pageFunction, ...args);
|
||||||
await this.#client.send('Page.addScriptToEvaluateOnNewDocument', {
|
await this.#client.send('Page.addScriptToEvaluateOnNewDocument', {
|
||||||
source,
|
source,
|
||||||
@ -3203,48 +3242,6 @@ export class Page extends EventEmitter {
|
|||||||
return this.mainFrame().type(selector, text, options);
|
return this.mainFrame().type(selector, text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @remarks
|
|
||||||
*
|
|
||||||
* This method behaves differently depending on the first parameter. If it's a
|
|
||||||
* `string`, it will be treated as a `selector` or `xpath` (if the string
|
|
||||||
* starts with `//`). This method then is a shortcut for
|
|
||||||
* {@link Page.waitForSelector} or {@link Page.waitForXPath}.
|
|
||||||
*
|
|
||||||
* If the first argument is a function this method is a shortcut for
|
|
||||||
* {@link Page.waitForFunction}.
|
|
||||||
*
|
|
||||||
* If the first argument is a `number`, it's treated as a timeout in
|
|
||||||
* milliseconds and the method returns a promise which resolves after the
|
|
||||||
* timeout.
|
|
||||||
*
|
|
||||||
* @param selectorOrFunctionOrTimeout - a selector, predicate or timeout to
|
|
||||||
* wait for.
|
|
||||||
* @param options - optional waiting parameters.
|
|
||||||
* @param args - arguments to pass to `pageFunction`.
|
|
||||||
*
|
|
||||||
* @deprecated Don't use this method directly. Instead use the more explicit
|
|
||||||
* methods available: {@link Page.waitForSelector},
|
|
||||||
* {@link Page.waitForXPath}, {@link Page.waitForFunction} or
|
|
||||||
* {@link Page.waitForTimeout}.
|
|
||||||
*/
|
|
||||||
waitFor(
|
|
||||||
selectorOrFunctionOrTimeout: string | number | Function,
|
|
||||||
options: {
|
|
||||||
visible?: boolean;
|
|
||||||
hidden?: boolean;
|
|
||||||
timeout?: number;
|
|
||||||
polling?: string | number;
|
|
||||||
} = {},
|
|
||||||
...args: SerializableOrJSHandle[]
|
|
||||||
): Promise<JSHandle | null> {
|
|
||||||
return this.mainFrame().waitFor(
|
|
||||||
selectorOrFunctionOrTimeout,
|
|
||||||
options,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Causes your script to wait for the given number of milliseconds.
|
* Causes your script to wait for the given number of milliseconds.
|
||||||
*
|
*
|
||||||
@ -3316,15 +3313,19 @@ export class Page extends EventEmitter {
|
|||||||
* (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
* (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
||||||
* by using the {@link Page.setDefaultTimeout} method.
|
* by using the {@link Page.setDefaultTimeout} method.
|
||||||
*/
|
*/
|
||||||
waitForSelector(
|
async waitForSelector<Selector extends keyof HTMLElementTagNameMap>(
|
||||||
|
selector: Selector,
|
||||||
|
options?: Exclude<WaitForSelectorOptions, 'root'>
|
||||||
|
): Promise<ElementHandle<HTMLElementTagNameMap[Selector]> | null>;
|
||||||
|
async waitForSelector(
|
||||||
selector: string,
|
selector: string,
|
||||||
options: {
|
options?: Exclude<WaitForSelectorOptions, 'root'>
|
||||||
visible?: boolean;
|
): Promise<ElementHandle | null>;
|
||||||
hidden?: boolean;
|
async waitForSelector(
|
||||||
timeout?: number;
|
selector: string,
|
||||||
} = {}
|
options: Exclude<WaitForSelectorOptions, 'root'> = {}
|
||||||
): Promise<ElementHandle | null> {
|
): Promise<ElementHandle | null> {
|
||||||
return this.mainFrame().waitForSelector(selector, options);
|
return await this.mainFrame().waitForSelector(selector, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3452,14 +3453,17 @@ export class Page extends EventEmitter {
|
|||||||
* {@link Page.setDefaultTimeout | page.setDefaultTimeout(timeout)} method.
|
* {@link Page.setDefaultTimeout | page.setDefaultTimeout(timeout)} method.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
waitForFunction(
|
waitForFunction<
|
||||||
pageFunction: Function | string,
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
options: {
|
options: {
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
polling?: string | number;
|
polling?: string | number;
|
||||||
} = {},
|
} = {},
|
||||||
...args: SerializableOrJSHandle[]
|
...args: EvaluateParams<Params>
|
||||||
): Promise<JSHandle> {
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
|
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import {WaitForSelectorOptions, DOMWorld} from './DOMWorld.js';
|
import {WaitForSelectorOptions, DOMWorld} from './DOMWorld.js';
|
||||||
import {ElementHandle, JSHandle} from './JSHandle.js';
|
import {ElementHandle, JSHandle} from './JSHandle.js';
|
||||||
import {_ariaHandler} from './AriaQueryHandler.js';
|
import {ariaHandler} from './AriaQueryHandler.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -99,12 +99,13 @@ function makeQueryHandler(handler: CustomQueryHandler): InternalQueryHandler {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
internalHandler.queryAllArray = async (element, selector) => {
|
internalHandler.queryAllArray = async (element, selector) => {
|
||||||
const resultHandle = await element.evaluateHandle(queryAll, selector);
|
const resultHandle = (await element.evaluateHandle(
|
||||||
const arrayHandle = await resultHandle.evaluateHandle(
|
queryAll,
|
||||||
(res: Element[] | NodeListOf<Element>) => {
|
selector
|
||||||
return Array.from(res);
|
)) as JSHandle<Element[] | NodeListOf<Element>>;
|
||||||
}
|
const arrayHandle = await resultHandle.evaluateHandle(res => {
|
||||||
);
|
return Array.from(res);
|
||||||
|
});
|
||||||
return arrayHandle;
|
return arrayHandle;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -172,7 +173,7 @@ const pierceHandler = makeQueryHandler({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const builtInHandlers = new Map([
|
const builtInHandlers = new Map([
|
||||||
['aria', _ariaHandler],
|
['aria', ariaHandler],
|
||||||
['pierce', pierceHandler],
|
['pierce', pierceHandler],
|
||||||
]);
|
]);
|
||||||
const queryHandlers = new Map(builtInHandlers);
|
const queryHandlers = new Map(builtInHandlers);
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {ConsoleMessageType} from './ConsoleMessage.js';
|
import {ConsoleMessageType} from './ConsoleMessage.js';
|
||||||
import {EvaluateHandleFn, SerializableOrJSHandle} from './EvalTypes.js';
|
import {EvaluateFunc, EvaluateParams, HandleFor} from './types.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {debugError} from './util.js';
|
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {JSHandle} from './JSHandle.js';
|
||||||
|
import {debugError} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -136,11 +136,14 @@ export class WebWorker extends EventEmitter {
|
|||||||
* @param args - Arguments to pass to `pageFunction`.
|
* @param args - Arguments to pass to `pageFunction`.
|
||||||
* @returns Promise which resolves to the return value of `pageFunction`.
|
* @returns Promise which resolves to the return value of `pageFunction`.
|
||||||
*/
|
*/
|
||||||
async evaluate<ReturnType>(
|
async evaluate<
|
||||||
pageFunction: Function | string,
|
Params extends unknown[],
|
||||||
...args: any[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<ReturnType> {
|
>(
|
||||||
return (await this.#executionContextPromise).evaluate<ReturnType>(
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return (await this.#executionContextPromise).evaluate(
|
||||||
pageFunction,
|
pageFunction,
|
||||||
...args
|
...args
|
||||||
);
|
);
|
||||||
@ -158,11 +161,14 @@ export class WebWorker extends EventEmitter {
|
|||||||
* @param args - Arguments to pass to `pageFunction`.
|
* @param args - Arguments to pass to `pageFunction`.
|
||||||
* @returns Promise which resolves to the return value of `pageFunction`.
|
* @returns Promise which resolves to the return value of `pageFunction`.
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<HandlerType extends JSHandle = JSHandle>(
|
async evaluateHandle<
|
||||||
pageFunction: EvaluateHandleFn,
|
Params extends unknown[],
|
||||||
...args: SerializableOrJSHandle[]
|
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>
|
||||||
): Promise<JSHandle> {
|
>(
|
||||||
return (await this.#executionContextPromise).evaluateHandle<HandlerType>(
|
pageFunction: Func | string,
|
||||||
|
...args: EvaluateParams<Params>
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
|
return (await this.#executionContextPromise).evaluateHandle(
|
||||||
pageFunction,
|
pageFunction,
|
||||||
...args
|
...args
|
||||||
);
|
);
|
||||||
|
32
src/common/types.ts
Normal file
32
src/common/types.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2020 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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} from './JSHandle.js';
|
||||||
|
|
||||||
|
export type Awaitable<T> = T | PromiseLike<T>;
|
||||||
|
|
||||||
|
export type HandleFor<T> = T extends Element ? ElementHandle<T> : JSHandle<T>;
|
||||||
|
export type HandleOr<T> = HandleFor<T> | JSHandle<T> | T;
|
||||||
|
|
||||||
|
export type EvaluateParams<T extends unknown[]> = {
|
||||||
|
[K in keyof T]: T[K] extends HandleOr<unknown> ? T[K] : HandleOr<T[K]>;
|
||||||
|
};
|
||||||
|
export type InnerParams<T extends unknown[]> = {
|
||||||
|
[K in keyof T]: T[K] extends HandleOr<infer U> ? U : never;
|
||||||
|
};
|
||||||
|
export type EvaluateFunc<T extends unknown[]> = (
|
||||||
|
...params: InnerParams<T>
|
||||||
|
) => Awaitable<unknown>;
|
@ -276,8 +276,9 @@ describeChromeOnly('AriaQueryHandler', () => {
|
|||||||
page.waitForSelector('aria/anything'),
|
page.waitForSelector('aria/anything'),
|
||||||
page.setContent(`<h1>anything</h1>`),
|
page.setContent(`<h1>anything</h1>`),
|
||||||
]);
|
]);
|
||||||
|
assert(handle);
|
||||||
expect(
|
expect(
|
||||||
await page.evaluate((x: HTMLElement) => {
|
await page.evaluate(x => {
|
||||||
return x.textContent;
|
return x.textContent;
|
||||||
}, handle)
|
}, handle)
|
||||||
).toBe('anything');
|
).toBe('anything');
|
||||||
@ -651,7 +652,9 @@ describeChromeOnly('AriaQueryHandler', () => {
|
|||||||
});
|
});
|
||||||
it('should find by role "button"', async () => {
|
it('should find by role "button"', async () => {
|
||||||
const {page} = getTestState();
|
const {page} = getTestState();
|
||||||
const found = await page.$$<HTMLButtonElement>('aria/[role="button"]');
|
const found = (await page.$$(
|
||||||
|
'aria/[role="button"]'
|
||||||
|
)) as ElementHandle<HTMLButtonElement>[];
|
||||||
const ids = await getIds(found);
|
const ids = await getIds(found);
|
||||||
expect(ids).toEqual([
|
expect(ids).toEqual([
|
||||||
'node5',
|
'node5',
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
setupTestBrowserHooks,
|
|
||||||
itFailsFirefox,
|
itFailsFirefox,
|
||||||
|
setupTestBrowserHooks,
|
||||||
} from './mocha-utils.js';
|
} from './mocha-utils.js';
|
||||||
import utils from './utils.js';
|
import {waitEvent} from './utils.js';
|
||||||
|
|
||||||
describe('BrowserContext', function () {
|
describe('BrowserContext', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -67,8 +67,8 @@ describe('BrowserContext', function () {
|
|||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const [popupTarget] = await Promise.all([
|
const [popupTarget] = await Promise.all([
|
||||||
utils.waitEvent(browser, 'targetcreated'),
|
waitEvent(browser, 'targetcreated'),
|
||||||
page.evaluate<(url: string) => void>(url => {
|
page.evaluate(url => {
|
||||||
return window.open(url);
|
return window.open(url);
|
||||||
}, server.EMPTY_PAGE),
|
}, server.EMPTY_PAGE),
|
||||||
]);
|
]);
|
||||||
|
@ -430,12 +430,12 @@ describe('Cookie specs', () => {
|
|||||||
|
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
await page.setCookie({name: 'localhost-cookie', value: 'best'});
|
await page.setCookie({name: 'localhost-cookie', value: 'best'});
|
||||||
await page.evaluate<(src: string) => Promise<void>>(src => {
|
await page.evaluate(src => {
|
||||||
let fulfill!: () => void;
|
let fulfill!: () => void;
|
||||||
const promise = new Promise<void>(x => {
|
const promise = new Promise<void>(x => {
|
||||||
return (fulfill = x);
|
return (fulfill = x);
|
||||||
});
|
});
|
||||||
const iframe = document.createElement('iframe') as HTMLIFrameElement;
|
const iframe = document.createElement('iframe');
|
||||||
document.body.appendChild(iframe);
|
document.body.appendChild(iframe);
|
||||||
iframe.onload = fulfill;
|
iframe.onload = fulfill;
|
||||||
iframe.src = src;
|
iframe.src = src;
|
||||||
@ -499,7 +499,7 @@ describe('Cookie specs', () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await page.goto(httpsServer.PREFIX + '/grid.html');
|
await page.goto(httpsServer.PREFIX + '/grid.html');
|
||||||
await page.evaluate<(src: string) => Promise<void>>(src => {
|
await page.evaluate(src => {
|
||||||
let fulfill!: () => void;
|
let fulfill!: () => void;
|
||||||
const promise = new Promise<void>(x => {
|
const promise = new Promise<void>(x => {
|
||||||
return (fulfill = x);
|
return (fulfill = x);
|
||||||
|
@ -287,7 +287,7 @@ describe('Coverage specs', function () {
|
|||||||
const {page, server} = getTestState();
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
await page.coverage.startCSSCoverage();
|
await page.coverage.startCSSCoverage();
|
||||||
await page.evaluate<(url: string) => Promise<void>>(async url => {
|
await page.evaluate(async url => {
|
||||||
document.body.textContent = 'hello, world';
|
document.body.textContent = 'hello, world';
|
||||||
|
|
||||||
const link = document.createElement('link');
|
const link = document.createElement('link');
|
||||||
|
@ -17,15 +17,14 @@
|
|||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import {
|
import {
|
||||||
|
describeFailsFirefox,
|
||||||
getTestState,
|
getTestState,
|
||||||
|
itFailsFirefox,
|
||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
describeFailsFirefox,
|
|
||||||
itFailsFirefox,
|
|
||||||
} from './mocha-utils.js';
|
} from './mocha-utils.js';
|
||||||
|
|
||||||
import utils from './utils.js';
|
import utils from './utils.js';
|
||||||
import {ElementHandle} from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
|
||||||
|
|
||||||
describe('ElementHandle specs', function () {
|
describe('ElementHandle specs', function () {
|
||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
@ -86,7 +85,7 @@ describe('ElementHandle specs', function () {
|
|||||||
`);
|
`);
|
||||||
const element = (await page.$('#therect'))!;
|
const element = (await page.$('#therect'))!;
|
||||||
const pptrBoundingBox = await element.boundingBox();
|
const pptrBoundingBox = await element.boundingBox();
|
||||||
const webBoundingBox = await page.evaluate((e: HTMLElement) => {
|
const webBoundingBox = await page.evaluate(e => {
|
||||||
const rect = e.getBoundingClientRect();
|
const rect = e.getBoundingClientRect();
|
||||||
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
return {x: rect.x, y: rect.y, width: rect.width, height: rect.height};
|
||||||
}, element);
|
}, element);
|
||||||
@ -189,9 +188,9 @@ describe('ElementHandle specs', function () {
|
|||||||
const {page, server} = getTestState();
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
await page.goto(server.PREFIX + '/shadow.html');
|
await page.goto(server.PREFIX + '/shadow.html');
|
||||||
const buttonHandle = await page.evaluateHandle<ElementHandle>(() => {
|
const buttonHandle = await page.evaluateHandle(() => {
|
||||||
// @ts-expect-error button is expected to be in the page's scope.
|
// @ts-expect-error button is expected to be in the page's scope.
|
||||||
return button;
|
return button as HTMLButtonElement;
|
||||||
});
|
});
|
||||||
await buttonHandle.click();
|
await buttonHandle.click();
|
||||||
expect(
|
expect(
|
||||||
@ -205,8 +204,8 @@ describe('ElementHandle specs', function () {
|
|||||||
const {page, server} = getTestState();
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
const buttonTextNode = await page.evaluateHandle<ElementHandle>(() => {
|
const buttonTextNode = await page.evaluateHandle(() => {
|
||||||
return document.querySelector('button')!.firstChild;
|
return document.querySelector('button')!.firstChild as HTMLElement;
|
||||||
});
|
});
|
||||||
let error!: Error;
|
let error!: Error;
|
||||||
await buttonTextNode.click().catch(error_ => {
|
await buttonTextNode.click().catch(error_ => {
|
||||||
@ -401,7 +400,7 @@ describe('ElementHandle specs', function () {
|
|||||||
});
|
});
|
||||||
const element = (await page.$('getById/foo'))!;
|
const element = (await page.$('getById/foo'))!;
|
||||||
expect(
|
expect(
|
||||||
await page.evaluate<(element: HTMLElement) => string>(element => {
|
await page.evaluate(element => {
|
||||||
return element.id;
|
return element.id;
|
||||||
}, element)
|
}, element)
|
||||||
).toBe('foo');
|
).toBe('foo');
|
||||||
@ -454,12 +453,9 @@ describe('ElementHandle specs', function () {
|
|||||||
const elements = await page.$$('getByClass/foo');
|
const elements = await page.$$('getByClass/foo');
|
||||||
const classNames = await Promise.all(
|
const classNames = await Promise.all(
|
||||||
elements.map(async element => {
|
elements.map(async element => {
|
||||||
return await page.evaluate<(element: HTMLElement) => string>(
|
return await page.evaluate(element => {
|
||||||
element => {
|
return element.className;
|
||||||
return element.className;
|
}, element);
|
||||||
},
|
|
||||||
element
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -539,7 +535,7 @@ describe('ElementHandle specs', function () {
|
|||||||
return element.querySelector(`.${selector}`);
|
return element.querySelector(`.${selector}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const waitFor = page.waitFor('getByClass/foo');
|
const waitFor = page.waitForSelector('getByClass/foo');
|
||||||
|
|
||||||
// Set the page content after the waitFor has been started.
|
// Set the page content after the waitFor has been started.
|
||||||
await page.setContent(
|
await page.setContent(
|
||||||
|
@ -357,7 +357,7 @@ describe('Evaluation specs', function () {
|
|||||||
return error_.message;
|
return error_.message;
|
||||||
});
|
});
|
||||||
const error = await page
|
const error = await page
|
||||||
.evaluate<(errorText: string) => Error>(errorText => {
|
.evaluate(errorText => {
|
||||||
throw new Error(errorText);
|
throw new Error(errorText);
|
||||||
}, errorText)
|
}, errorText)
|
||||||
.catch(error_ => {
|
.catch(error_ => {
|
||||||
@ -477,7 +477,7 @@ describe('Evaluation specs', function () {
|
|||||||
it('should transfer 100Mb of data from page to node.js', async function () {
|
it('should transfer 100Mb of data from page to node.js', async function () {
|
||||||
const {page} = getTestState();
|
const {page} = getTestState();
|
||||||
|
|
||||||
const a = await page.evaluate<() => string>(() => {
|
const a = await page.evaluate(() => {
|
||||||
return Array(100 * 1024 * 1024 + 1).join('a');
|
return Array(100 * 1024 * 1024 + 1).join('a');
|
||||||
});
|
});
|
||||||
expect(a.length).toBe(100 * 1024 * 1024);
|
expect(a.length).toBe(100 * 1024 * 1024);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
|
import {ElementHandle} from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
||||||
@ -29,8 +30,8 @@ describeFailsFirefox('Emulate idle state', () => {
|
|||||||
async function getIdleState() {
|
async function getIdleState() {
|
||||||
const {page} = getTestState();
|
const {page} = getTestState();
|
||||||
|
|
||||||
const stateElement = (await page.$('#state'))!;
|
const stateElement = (await page.$('#state')) as ElementHandle<HTMLElement>;
|
||||||
return await page.evaluate((element: HTMLElement) => {
|
return await page.evaluate(element => {
|
||||||
return element.innerText;
|
return element.innerText;
|
||||||
}, stateElement);
|
}, stateElement);
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import {JSHandle} from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
|
itFailsFirefox,
|
||||||
setupTestBrowserHooks,
|
setupTestBrowserHooks,
|
||||||
setupTestPageAndContextHooks,
|
setupTestPageAndContextHooks,
|
||||||
itFailsFirefox,
|
|
||||||
shortWaitForArrayToHaveAtLeastNElements,
|
shortWaitForArrayToHaveAtLeastNElements,
|
||||||
} from './mocha-utils.js';
|
} from './mocha-utils.js';
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ describe('JSHandle', function () {
|
|||||||
const navigatorHandle = await page.evaluateHandle(() => {
|
const navigatorHandle = await page.evaluateHandle(() => {
|
||||||
return navigator;
|
return navigator;
|
||||||
});
|
});
|
||||||
const text = await page.evaluate((e: Navigator) => {
|
const text = await page.evaluate(e => {
|
||||||
return e.userAgent;
|
return e.userAgent;
|
||||||
}, navigatorHandle);
|
}, navigatorHandle);
|
||||||
expect(text).toContain('Mozilla');
|
expect(text).toContain('Mozilla');
|
||||||
@ -68,9 +67,10 @@ describe('JSHandle', function () {
|
|||||||
await page
|
await page
|
||||||
.evaluateHandle(
|
.evaluateHandle(
|
||||||
opts => {
|
opts => {
|
||||||
|
// @ts-expect-error we are deliberately passing a bad type here
|
||||||
|
// (nested object)
|
||||||
return opts.elem;
|
return opts.elem;
|
||||||
},
|
},
|
||||||
// @ts-expect-error we are deliberately passing a bad type here (nested object)
|
|
||||||
{test}
|
{test}
|
||||||
)
|
)
|
||||||
.catch(error_ => {
|
.catch(error_ => {
|
||||||
@ -98,8 +98,8 @@ describe('JSHandle', function () {
|
|||||||
return window;
|
return window;
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
await page.evaluate((e: {FOO: number}) => {
|
await page.evaluate(e => {
|
||||||
return e.FOO;
|
return (e as any).FOO;
|
||||||
}, aHandle)
|
}, aHandle)
|
||||||
).toBe(123);
|
).toBe(123);
|
||||||
});
|
});
|
||||||
@ -119,21 +119,6 @@ describe('JSHandle', function () {
|
|||||||
const twoHandle = await aHandle.getProperty('two');
|
const twoHandle = await aHandle.getProperty('two');
|
||||||
expect(await twoHandle.jsonValue()).toEqual(2);
|
expect(await twoHandle.jsonValue()).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a JSHandle even if the property does not exist', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
const aHandle = await page.evaluateHandle(() => {
|
|
||||||
return {
|
|
||||||
one: 1,
|
|
||||||
two: 2,
|
|
||||||
three: 3,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
const undefinedHandle = await aHandle.getProperty('doesnotexist');
|
|
||||||
expect(undefinedHandle).toBeInstanceOf(JSHandle);
|
|
||||||
expect(await undefinedHandle.jsonValue()).toBe(undefined);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('JSHandle.jsonValue', function () {
|
describe('JSHandle.jsonValue', function () {
|
||||||
|
@ -467,7 +467,7 @@ describe('Keyboard', function () {
|
|||||||
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
|
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
|
||||||
expect(
|
expect(
|
||||||
await page.$eval('textarea', textarea => {
|
await page.$eval('textarea', textarea => {
|
||||||
return (textarea as HTMLInputElement).value;
|
return textarea.value;
|
||||||
})
|
})
|
||||||
).toBe('👹 Tokyo street Japan 🇯🇵');
|
).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||||
});
|
});
|
||||||
@ -485,7 +485,7 @@ describe('Keyboard', function () {
|
|||||||
await textarea.type('👹 Tokyo street Japan 🇯🇵');
|
await textarea.type('👹 Tokyo street Japan 🇯🇵');
|
||||||
expect(
|
expect(
|
||||||
await frame.$eval('textarea', textarea => {
|
await frame.$eval('textarea', textarea => {
|
||||||
return (textarea as HTMLInputElement).value;
|
return textarea.value;
|
||||||
})
|
})
|
||||||
).toBe('👹 Tokyo street Japan 🇯🇵');
|
).toBe('👹 Tokyo street Japan 🇯🇵');
|
||||||
});
|
});
|
||||||
|
@ -61,7 +61,7 @@ describe('Mouse', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await page.mouse.click(50, 60);
|
await page.mouse.click(50, 60);
|
||||||
const event = await page.evaluate<() => MouseEvent>(() => {
|
const event = await page.evaluate(() => {
|
||||||
return (globalThis as any).clickPromise;
|
return (globalThis as any).clickPromise;
|
||||||
});
|
});
|
||||||
expect(event.type).toBe('click');
|
expect(event.type).toBe('click');
|
||||||
@ -75,15 +75,13 @@ describe('Mouse', function () {
|
|||||||
const {page, server} = getTestState();
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
await page.goto(server.PREFIX + '/input/textarea.html');
|
await page.goto(server.PREFIX + '/input/textarea.html');
|
||||||
const {x, y, width, height} = await page.evaluate<() => Dimensions>(
|
const {x, y, width, height} = await page.evaluate(dimensions);
|
||||||
dimensions
|
|
||||||
);
|
|
||||||
const mouse = page.mouse;
|
const mouse = page.mouse;
|
||||||
await mouse.move(x + width - 4, y + height - 4);
|
await mouse.move(x + width - 4, y + height - 4);
|
||||||
await mouse.down();
|
await mouse.down();
|
||||||
await mouse.move(x + width + 100, y + height + 100);
|
await mouse.move(x + width + 100, y + height + 100);
|
||||||
await mouse.up();
|
await mouse.up();
|
||||||
const newDimensions = await page.evaluate<() => Dimensions>(dimensions);
|
const newDimensions = await page.evaluate(dimensions);
|
||||||
expect(newDimensions.width).toBe(Math.round(width + 104));
|
expect(newDimensions.width).toBe(Math.round(width + 104));
|
||||||
expect(newDimensions.height).toBe(Math.round(height + 104));
|
expect(newDimensions.height).toBe(Math.round(height + 104));
|
||||||
});
|
});
|
||||||
|
@ -422,7 +422,7 @@ describe('network', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Trigger a request with a preflight.
|
// Trigger a request with a preflight.
|
||||||
await page.evaluate<(src: string) => void>(async src => {
|
await page.evaluate(async src => {
|
||||||
const response = await fetch(src, {
|
const response = await fetch(src, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'x-ping': 'pong'},
|
headers: {'x-ping': 'pong'},
|
||||||
@ -855,7 +855,7 @@ describe('network', function () {
|
|||||||
const response = await new Promise<HTTPResponse>(resolve => {
|
const response = await new Promise<HTTPResponse>(resolve => {
|
||||||
page.on('response', resolve);
|
page.on('response', resolve);
|
||||||
const url = httpsServer.CROSS_PROCESS_PREFIX + '/setcookie.html';
|
const url = httpsServer.CROSS_PROCESS_PREFIX + '/setcookie.html';
|
||||||
page.evaluate<(src: string) => void>(src => {
|
page.evaluate(src => {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', src);
|
xhr.open('GET', src);
|
||||||
xhr.send();
|
xhr.send();
|
||||||
|
@ -20,7 +20,6 @@ import path from 'path';
|
|||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import {CDPSession} from '../../lib/cjs/puppeteer/common/Connection.js';
|
import {CDPSession} from '../../lib/cjs/puppeteer/common/Connection.js';
|
||||||
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
import {ConsoleMessage} from '../../lib/cjs/puppeteer/common/ConsoleMessage.js';
|
||||||
import {JSHandle} from '../../lib/cjs/puppeteer/common/JSHandle.js';
|
|
||||||
import {Metrics, Page} from '../../lib/cjs/puppeteer/common/Page.js';
|
import {Metrics, Page} from '../../lib/cjs/puppeteer/common/Page.js';
|
||||||
import {
|
import {
|
||||||
describeFailsFirefox,
|
describeFailsFirefox,
|
||||||
@ -341,8 +340,8 @@ describe('Page', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('BrowserContext.overridePermissions', function () {
|
describe('BrowserContext.overridePermissions', function () {
|
||||||
function getPermission(page: Page, name: string) {
|
function getPermission(page: Page, name: PermissionName) {
|
||||||
return page.evaluate((name: PermissionName) => {
|
return page.evaluate(name => {
|
||||||
return navigator.permissions.query({name}).then(result => {
|
return navigator.permissions.query({name}).then(result => {
|
||||||
return result.state;
|
return result.state;
|
||||||
});
|
});
|
||||||
@ -559,7 +558,7 @@ describe('Page', function () {
|
|||||||
return Set.prototype;
|
return Set.prototype;
|
||||||
});
|
});
|
||||||
const objectsHandle = await page.queryObjects(prototypeHandle);
|
const objectsHandle = await page.queryObjects(prototypeHandle);
|
||||||
const count = await page.evaluate((objects: JSHandle[]) => {
|
const count = await page.evaluate(objects => {
|
||||||
return objects.length;
|
return objects.length;
|
||||||
}, objectsHandle);
|
}, objectsHandle);
|
||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
@ -580,7 +579,7 @@ describe('Page', function () {
|
|||||||
return Set.prototype;
|
return Set.prototype;
|
||||||
});
|
});
|
||||||
const objectsHandle = await page.queryObjects(prototypeHandle);
|
const objectsHandle = await page.queryObjects(prototypeHandle);
|
||||||
const count = await page.evaluate((objects: JSHandle[]) => {
|
const count = await page.evaluate(objects => {
|
||||||
return objects.length;
|
return objects.length;
|
||||||
}, objectsHandle);
|
}, objectsHandle);
|
||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
@ -1246,11 +1245,9 @@ describe('Page', function () {
|
|||||||
return {x: a.x + b.x};
|
return {x: a.x + b.x};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const result = await page.evaluate<() => Promise<{x: number}>>(
|
const result = await page.evaluate(async () => {
|
||||||
async () => {
|
return (globalThis as any).complexObject({x: 5}, {x: 2});
|
||||||
return (globalThis as any).complexObject({x: 5}, {x: 2});
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(result.x).toBe(7);
|
expect(result.x).toBe(7);
|
||||||
});
|
});
|
||||||
it('should fallback to default export when passed a module object', async () => {
|
it('should fallback to default export when passed a module object', async () => {
|
||||||
|
@ -433,7 +433,7 @@ describe('querySelector', function () {
|
|||||||
const html = (await page.$('html'))!;
|
const html = (await page.$('html'))!;
|
||||||
const second = await html.$x(`./body/div[contains(@class, 'second')]`);
|
const second = await html.$x(`./body/div[contains(@class, 'second')]`);
|
||||||
const inner = await second[0]!.$x(`./div[contains(@class, 'inner')]`);
|
const inner = await second[0]!.$x(`./div[contains(@class, 'inner')]`);
|
||||||
const content = await page.evaluate((e: HTMLElement) => {
|
const content = await page.evaluate(e => {
|
||||||
return e.textContent;
|
return e.textContent;
|
||||||
}, inner[0]!);
|
}, inner[0]!);
|
||||||
expect(content).toBe('A');
|
expect(content).toBe('A');
|
||||||
@ -480,7 +480,7 @@ describe('querySelector', function () {
|
|||||||
const elements = await html.$$('allArray/div');
|
const elements = await html.$$('allArray/div');
|
||||||
expect(elements.length).toBe(2);
|
expect(elements.length).toBe(2);
|
||||||
const promises = elements.map(element => {
|
const promises = elements.map(element => {
|
||||||
return page.evaluate((e: HTMLElement) => {
|
return page.evaluate(e => {
|
||||||
return e.textContent;
|
return e.textContent;
|
||||||
}, element);
|
}, element);
|
||||||
});
|
});
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import expect from 'expect';
|
import expect from 'expect';
|
||||||
import sinon from 'sinon';
|
|
||||||
import {isErrorLike} from '../../lib/cjs/puppeteer/common/util.js';
|
import {isErrorLike} from '../../lib/cjs/puppeteer/common/util.js';
|
||||||
import {
|
import {
|
||||||
getTestState,
|
getTestState,
|
||||||
@ -29,122 +28,6 @@ describe('waittask specs', function () {
|
|||||||
setupTestBrowserHooks();
|
setupTestBrowserHooks();
|
||||||
setupTestPageAndContextHooks();
|
setupTestPageAndContextHooks();
|
||||||
|
|
||||||
describe('Page.waitFor', function () {
|
|
||||||
/* This method is deprecated but we don't want the warnings showing up in
|
|
||||||
* tests. Until we remove this method we still want to ensure we don't break
|
|
||||||
* it.
|
|
||||||
*/
|
|
||||||
beforeEach(() => {
|
|
||||||
return sinon.stub(console, 'warn').callsFake(() => {});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should wait for selector', async () => {
|
|
||||||
const {page, server} = getTestState();
|
|
||||||
|
|
||||||
let found = false;
|
|
||||||
const waitFor = page.waitForSelector('div').then(() => {
|
|
||||||
return (found = true);
|
|
||||||
});
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
expect(found).toBe(false);
|
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
|
||||||
await waitFor;
|
|
||||||
expect(found).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should wait for an xpath', async () => {
|
|
||||||
const {page, server} = getTestState();
|
|
||||||
|
|
||||||
let found = false;
|
|
||||||
const waitFor = page.waitFor('//div').then(() => {
|
|
||||||
return (found = true);
|
|
||||||
});
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
expect(found).toBe(false);
|
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
|
||||||
await waitFor;
|
|
||||||
expect(found).toBe(true);
|
|
||||||
});
|
|
||||||
it('should allow you to select an element with parenthesis-starting xpath', async () => {
|
|
||||||
const {page, server} = getTestState();
|
|
||||||
let found = false;
|
|
||||||
const waitFor = page.waitFor('(//img)[200]').then(() => {
|
|
||||||
found = true;
|
|
||||||
});
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
expect(found).toBe(false);
|
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
|
||||||
await waitFor;
|
|
||||||
expect(found).toBe(true);
|
|
||||||
});
|
|
||||||
it('should not allow you to select an element with single slash xpath', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
await page.setContent(`<div>some text</div>`);
|
|
||||||
let error!: Error;
|
|
||||||
await page.waitFor('/html/body/div').catch(error_ => {
|
|
||||||
return (error = error_);
|
|
||||||
});
|
|
||||||
expect(error).toBeTruthy();
|
|
||||||
});
|
|
||||||
it('should timeout', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
const timeout = 42;
|
|
||||||
await page.waitFor(timeout);
|
|
||||||
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
|
|
||||||
});
|
|
||||||
it('should work with multiline body', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
const result = await page.waitForFunction(`
|
|
||||||
(() => true)()
|
|
||||||
`);
|
|
||||||
expect(await result.jsonValue()).toBe(true);
|
|
||||||
});
|
|
||||||
it('should wait for predicate', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
page.waitFor(() => {
|
|
||||||
return window.innerWidth < 100;
|
|
||||||
}),
|
|
||||||
page.setViewport({width: 10, height: 10}),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
it('should wait for predicate with arguments', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
await page.waitFor(
|
|
||||||
(arg1: number, arg2: number) => {
|
|
||||||
return arg1 !== arg2;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log a deprecation warning', async () => {
|
|
||||||
const {page} = getTestState();
|
|
||||||
|
|
||||||
await page.waitFor(() => {
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const consoleWarnStub = console.warn as sinon.SinonSpy;
|
|
||||||
|
|
||||||
expect(consoleWarnStub.calledOnce).toBe(true);
|
|
||||||
expect(
|
|
||||||
consoleWarnStub.firstCall.calledWith(
|
|
||||||
'waitFor is deprecated and will be removed in a future release. See https://github.com/puppeteer/puppeteer/issues/6214 for details and how to migrate your code.'
|
|
||||||
)
|
|
||||||
).toBe(true);
|
|
||||||
expect((console.warn as sinon.SinonSpy).calledOnce).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Frame.waitForFunction', function () {
|
describe('Frame.waitForFunction', function () {
|
||||||
it('should accept a string', async () => {
|
it('should accept a string', async () => {
|
||||||
const {page} = getTestState();
|
const {page} = getTestState();
|
||||||
|
Loading…
Reference in New Issue
Block a user