feat(types): improve typing of .evaluate() (#6096)

* feat(types): improve typing of `.evaluate()`

This is the start of the work to take the types from the
`@types/puppeteer` repository and port them into our repo so we can ship
our built-in types out the box.

This change types the `evaluate` function properly. It takes a generic
type which is the type of the function you're passing, and the arguments
and the return that you get back from the `evaluate` call are typed
correctly.
This commit is contained in:
Jack Franklin 2020-06-25 13:38:01 +01:00 committed by GitHub
parent a4d12a2b21
commit 46fc6ca41a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 264 additions and 96 deletions

View File

@ -96,6 +96,8 @@ module.exports = {
"rules": { "rules": {
"no-unused-vars": 0, "no-unused-vars": 0,
"@typescript-eslint/no-unused-vars": 2, "@typescript-eslint/no-unused-vars": 2,
"func-call-spacing": 0,
"@typescript-eslint/func-call-spacing": 2,
"semi": 0, "semi": 0,
"@typescript-eslint/semi": 2, "@typescript-eslint/semi": 2,
"@typescript-eslint/no-empty-function": 0, "@typescript-eslint/no-empty-function": 0,

View File

@ -292,9 +292,25 @@ Puppeteer creates its own browser user profile which it **cleans up on every run
- debug your test inside chromium like a boss! - debug your test inside chromium like a boss!
<!-- [END debugging] --> <!-- [END debugging] -->
<!-- [START typescript] -->
## Usage with TypeScript
We have recently completed a migration to move the Puppeteer source code from JavaScript to TypeScript and we're currently working on shipping type definitions for TypeScript with Puppeteer's npm package.
Until this work is complete we recommend installing the Puppeteer type definitions from the [DefinitelyTyped](https://definitelytyped.org/) repository:
```bash
npm install --save-dev @types/puppeteer
```
The types that you'll see appearing in the Puppeteer source code are based off the great work of those who have contributed to the `@types/puppeteer` package. We really appreciate the hard work those people put in to providing high quality TypeScript definitions for Puppeteer's users.
<!-- [END typescript] -->
## Contributing to Puppeteer ## Contributing to Puppeteer
Check out [contributing guide](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) to get an overview of Puppeteer development. Check out [contributing guide](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) to get an overview of Puppeteer development.

View File

@ -11,7 +11,7 @@ If `pageFunction` returns a Promise, then `frame.$$eval` would wait for the prom
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -19,8 +19,8 @@ $$eval<ReturnType extends any>(selector: string, pageFunction: Function | string
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -11,7 +11,7 @@ If `pageFunction` returns a Promise, then `frame.$eval` would wait for the promi
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -19,8 +19,8 @@ $eval<ReturnType extends any>(selector: string, pageFunction: Function | string,
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [EvaluateFn](./puppeteer.evaluatefn.md)
## EvaluateFn type
<b>Signature:</b>
```typescript
export declare type EvaluateFn<T = any> = string | ((arg1: T, ...args: any[]) => any);
```

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)
## EvaluateFnReturnType type
<b>Signature:</b>
```typescript
export declare type EvaluateFnReturnType<T extends EvaluateFn> = T extends (...args: any[]) => infer R ? R : unknown;
```

View File

@ -7,7 +7,7 @@
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -15,8 +15,8 @@ $$eval<ReturnType extends any>(selector: string, pageFunction: Function | string
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -7,7 +7,7 @@
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -15,8 +15,8 @@ $eval<ReturnType extends any>(selector: string, pageFunction: Function | string,
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -9,19 +9,19 @@ This method passes this handle as the first argument to `pageFunction`<!-- -->.
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
evaluate<ReturnType extends any>(pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; evaluate<T extends EvaluateFn>(pageFunction: T | string, ...args: SerializableOrJSHandle[]): Promise<EvaluateFnReturnType<T>>;
``` ```
## Parameters ## Parameters
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| pageFunction | Function \| string | | | pageFunction | T \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>
Promise&lt;ReturnType&gt; Promise&lt;[EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md)<!-- -->&lt;T&gt;&gt;
## Example ## Example

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [JSONArray](./puppeteer.jsonarray.md)
## JSONArray type
<b>Signature:</b>
```typescript
export declare type JSONArray = Serializable[];
```

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [JSONObject](./puppeteer.jsonobject.md)
## JSONObject interface
<b>Signature:</b>
```typescript
export interface JSONObject
```

View File

@ -52,6 +52,7 @@
| [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | | | [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | |
| [ClickOptions](./puppeteer.clickoptions.md) | | | [ClickOptions](./puppeteer.clickoptions.md) | |
| [ConsoleMessageLocation](./puppeteer.consolemessagelocation.md) | | | [ConsoleMessageLocation](./puppeteer.consolemessagelocation.md) | |
| [JSONObject](./puppeteer.jsonobject.md) | |
| [KeyDefinition](./puppeteer.keydefinition.md) | Copyright 2017 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. | | [KeyDefinition](./puppeteer.keydefinition.md) | Copyright 2017 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. |
| [Metrics](./puppeteer.metrics.md) | | | [Metrics](./puppeteer.metrics.md) | |
| [PressOptions](./puppeteer.pressoptions.md) | | | [PressOptions](./puppeteer.pressoptions.md) | |
@ -73,7 +74,12 @@
| Type Alias | Description | | Type Alias | Description |
| --- | --- | | --- | --- |
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. | | [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
| [EvaluateFn](./puppeteer.evaluatefn.md) | |
| [EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md) | |
| [JSONArray](./puppeteer.jsonarray.md) | |
| [KeyInput](./puppeteer.keyinput.md) | | | [KeyInput](./puppeteer.keyinput.md) | |
| [MouseButtonInput](./puppeteer.mousebuttoninput.md) | | | [MouseButtonInput](./puppeteer.mousebuttoninput.md) | |
| [PuppeteerErrors](./puppeteer.puppeteererrors.md) | | | [PuppeteerErrors](./puppeteer.puppeteererrors.md) | |
| [Serializable](./puppeteer.serializable.md) | |
| [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md) | |

View File

@ -7,7 +7,7 @@
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $$eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -15,8 +15,8 @@ $$eval<ReturnType extends any>(selector: string, pageFunction: Function | string
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -7,7 +7,7 @@
<b>Signature:</b> <b>Signature:</b>
```typescript ```typescript
$eval<ReturnType extends any>(selector: string, pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>; $eval<ReturnType extends any>(selector: string, pageFunction: EvaluateFn | string, ...args: SerializableOrJSHandle[]): Promise<ReturnType>;
``` ```
## Parameters ## Parameters
@ -15,8 +15,8 @@ $eval<ReturnType extends any>(selector: string, pageFunction: Function | string,
| Parameter | Type | Description | | Parameter | Type | Description |
| --- | --- | --- | | --- | --- | --- |
| selector | string | | | selector | string | |
| pageFunction | Function \| string | | | pageFunction | [EvaluateFn](./puppeteer.evaluatefn.md) \| string | |
| args | unknown\[\] | | | args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)<!-- -->\[\] | |
<b>Returns:</b> <b>Returns:</b>

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [Serializable](./puppeteer.serializable.md)
## Serializable type
<b>Signature:</b>
```typescript
export declare type Serializable = number | string | boolean | null | JSONArray | JSONObject;
```

View File

@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [puppeteer](./puppeteer.md) &gt; [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)
## SerializableOrJSHandle type
<b>Signature:</b>
```typescript
export declare type SerializableOrJSHandle = Serializable | JSHandle;
```

View File

@ -48,3 +48,4 @@ export * from './common/Errors';
export * from './common/Tracing'; export * from './common/Tracing';
export * from './common/WebWorker'; export * from './common/WebWorker';
export * from './common/USKeyboardLayout'; export * from './common/USKeyboardLayout';
export * from './common/EvalTypes';

View File

@ -24,6 +24,7 @@ import { TimeoutSettings } from './TimeoutSettings';
import { MouseButtonInput } from './Input'; import { MouseButtonInput } from './Input';
import { FrameManager, Frame } from './FrameManager'; import { FrameManager, Frame } from './FrameManager';
import { getQueryHandlerAndSelector, QueryHandler } from './QueryHandler'; import { getQueryHandlerAndSelector, QueryHandler } from './QueryHandler';
import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
import { isNode } from '../environment'; import { isNode } from '../environment';
// This predicateQueryHandler is declared here so that TypeScript knows about it // This predicateQueryHandler is declared here so that TypeScript knows about it
@ -155,8 +156,8 @@ export class DOMWorld {
async $eval<ReturnType extends any>( async $eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
const document = await this._document(); const document = await this._document();
return document.$eval<ReturnType>(selector, pageFunction, ...args); return document.$eval<ReturnType>(selector, pageFunction, ...args);
@ -164,8 +165,8 @@ export class DOMWorld {
async $$eval<ReturnType extends any>( async $$eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
const document = await this._document(); const document = await this._document();
const value = await document.$$eval<ReturnType>( const value = await document.$$eval<ReturnType>(

58
src/common/EvalTypes.ts Normal file
View File

@ -0,0 +1,58 @@
/**
* 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 } from './JSHandle';
/**
* @public
*/
export type EvaluateFn<T = any> = string | ((arg1: T, ...args: any[]) => any);
/**
* @public
*/
export type EvaluateFnReturnType<T extends EvaluateFn> = T extends (
...args: any[]
) => infer R
? R
: unknown;
/**
* @public
*/
export type Serializable =
| number
| string
| boolean
| null
| JSONArray
| JSONObject;
/**
* @public
*/
export type JSONArray = Serializable[];
/**
* @public
*/
export interface JSONObject {
[key: string]: Serializable;
}
/**
* @public
*/
export type SerializableOrJSHandle = Serializable | JSHandle;

View File

@ -29,6 +29,7 @@ import { MouseButtonInput } from './Input';
import { Page } from './Page'; import { Page } from './Page';
import { HTTPResponse } from './HTTPResponse'; import { HTTPResponse } from './HTTPResponse';
import Protocol from '../protocol'; import Protocol from '../protocol';
import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__'; const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
@ -454,16 +455,16 @@ export class Frame {
async $eval<ReturnType extends any>( async $eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
return this._mainWorld.$eval<ReturnType>(selector, pageFunction, ...args); return this._mainWorld.$eval<ReturnType>(selector, pageFunction, ...args);
} }
async $$eval<ReturnType extends any>( async $$eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
return this._mainWorld.$$eval<ReturnType>(selector, pageFunction, ...args); return this._mainWorld.$$eval<ReturnType>(selector, pageFunction, ...args);
} }

View File

@ -23,6 +23,11 @@ import { KeyInput } from './USKeyboardLayout';
import { FrameManager, Frame } from './FrameManager'; import { FrameManager, Frame } from './FrameManager';
import { getQueryHandlerAndSelector } from './QueryHandler'; import { getQueryHandlerAndSelector } from './QueryHandler';
import Protocol from '../protocol'; import Protocol from '../protocol';
import {
EvaluateFn,
SerializableOrJSHandle,
EvaluateFnReturnType,
} from './EvalTypes';
/** /**
* @public * @public
@ -113,11 +118,12 @@ export class JSHandle {
* expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10'); * expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
* ``` * ```
*/ */
async evaluate<ReturnType extends any>(
pageFunction: Function | string, async evaluate<T extends EvaluateFn>(
...args: unknown[] pageFunction: T | string,
): Promise<ReturnType> { ...args: SerializableOrJSHandle[]
return await this.executionContext().evaluate<ReturnType>( ): Promise<EvaluateFnReturnType<T>> {
return await this.executionContext().evaluate<EvaluateFnReturnType<T>>(
pageFunction, pageFunction,
this, this,
...args ...args
@ -307,46 +313,48 @@ export class ElementHandle extends JSHandle {
} }
private async _scrollIntoViewIfNeeded(): Promise<void> { private async _scrollIntoViewIfNeeded(): Promise<void> {
const error = await this.evaluate<Promise<string | false>>( const error = await this.evaluate<
async (element: HTMLElement, pageJavascriptEnabled: boolean) => { (
if (!element.isConnected) return 'Node is detached from document'; element: HTMLElement,
if (element.nodeType !== Node.ELEMENT_NODE) pageJavascriptEnabled: boolean
return 'Node is not of type HTMLElement'; ) => Promise<string | false>
// force-scroll if page's javascript is disabled. >(async (element, pageJavascriptEnabled) => {
if (!pageJavascriptEnabled) { if (!element.isConnected) return 'Node is detached from document';
element.scrollIntoView({ if (element.nodeType !== Node.ELEMENT_NODE)
block: 'center', return 'Node is not of type HTMLElement';
inline: 'center', // force-scroll if page's javascript is disabled.
// Chrome still supports behavior: instant but it's not in the spec if (!pageJavascriptEnabled) {
// so TS shouts We don't want to make this breaking change in element.scrollIntoView({
// Puppeteer yet so we'll ignore the line. block: 'center',
// @ts-ignore inline: 'center',
behavior: 'instant', // Chrome still supports behavior: instant but it's not in the spec
}); // so TS shouts We don't want to make this breaking change in
return false; // Puppeteer yet so we'll ignore the line.
} // @ts-ignore
const visibleRatio = await new Promise((resolve) => { behavior: 'instant',
const observer = new IntersectionObserver((entries) => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
}); });
if (visibleRatio !== 1.0) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// Chrome still supports behavior: instant but it's not in the spec
// so TS shouts We don't want to make this breaking change in
// Puppeteer yet so we'll ignore the line.
// @ts-ignore
behavior: 'instant',
});
}
return false; return false;
}, }
this._page.isJavaScriptEnabled() const visibleRatio = await new Promise((resolve) => {
); const observer = new IntersectionObserver((entries) => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
});
if (visibleRatio !== 1.0) {
element.scrollIntoView({
block: 'center',
inline: 'center',
// Chrome still supports behavior: instant but it's not in the spec
// so TS shouts We don't want to make this breaking change in
// Puppeteer yet so we'll ignore the line.
// @ts-ignore
behavior: 'instant',
});
}
return false;
}, this._page.isJavaScriptEnabled());
if (error) throw new Error(error); if (error) throw new Error(error);
} }
@ -491,9 +499,9 @@ export class ElementHandle extends JSHandle {
* relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory} * relative to the {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}
*/ */
async uploadFile(...filePaths: string[]): Promise<void> { async uploadFile(...filePaths: string[]): Promise<void> {
const isMultiple = await this.evaluate<boolean>( const isMultiple = await this.evaluate<
(element: HTMLInputElement) => element.multiple (element: HTMLInputElement) => boolean
); >((element) => 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>'
@ -772,15 +780,15 @@ export class ElementHandle extends JSHandle {
*/ */
async $eval<ReturnType extends any>( async $eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
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<ReturnType>( const result = await elementHandle.evaluate<(...args: any[]) => ReturnType>(
pageFunction, pageFunction,
...args ...args
); );
@ -813,8 +821,8 @@ export class ElementHandle extends JSHandle {
*/ */
async $$eval<ReturnType extends any>( async $$eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
const defaultHandler = (element: Element, selector: string) => const defaultHandler = (element: Element, selector: string) =>
Array.from(element.querySelectorAll(selector)); Array.from(element.querySelectorAll(selector));
@ -827,7 +835,7 @@ export class ElementHandle extends JSHandle {
queryHandler, queryHandler,
updatedSelector updatedSelector
); );
const result = await arrayHandle.evaluate<ReturnType>( const result = await arrayHandle.evaluate<(...args: any[]) => ReturnType>(
pageFunction, pageFunction,
...args ...args
); );
@ -868,16 +876,18 @@ export class ElementHandle extends JSHandle {
* Resolves to true if the element is visible in the current viewport. * Resolves to true if the element is visible in the current viewport.
*/ */
async isIntersectingViewport(): Promise<boolean> { async isIntersectingViewport(): Promise<boolean> {
return await this.evaluate<Promise<boolean>>(async (element) => { return await this.evaluate<(element: Element) => Promise<boolean>>(
const visibleRatio = await new Promise((resolve) => { async (element) => {
const observer = new IntersectionObserver((entries) => { const visibleRatio = await new Promise((resolve) => {
resolve(entries[0].intersectionRatio); const observer = new IntersectionObserver((entries) => {
observer.disconnect(); resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
}); });
observer.observe(element); return visibleRatio > 0;
}); }
return visibleRatio > 0; );
});
} }
} }

View File

@ -42,6 +42,7 @@ import { FileChooser } from './FileChooser';
import { ConsoleMessage, ConsoleMessageType } from './ConsoleMessage'; import { ConsoleMessage, ConsoleMessageType } from './ConsoleMessage';
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher'; import { PuppeteerLifeCycleEvent } from './LifecycleWatcher';
import Protocol from '../protocol'; import Protocol from '../protocol';
import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
const writeFileAsync = promisify(fs.writeFile); const writeFileAsync = promisify(fs.writeFile);
@ -514,16 +515,16 @@ export class Page extends EventEmitter {
async $eval<ReturnType extends any>( async $eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
return this.mainFrame().$eval<ReturnType>(selector, pageFunction, ...args); return this.mainFrame().$eval<ReturnType>(selector, pageFunction, ...args);
} }
async $$eval<ReturnType extends any>( async $$eval<ReturnType extends any>(
selector: string, selector: string,
pageFunction: Function | string, pageFunction: EvaluateFn | string,
...args: unknown[] ...args: SerializableOrJSHandle[]
): Promise<ReturnType> { ): Promise<ReturnType> {
return this.mainFrame().$$eval<ReturnType>(selector, pageFunction, ...args); return this.mainFrame().$$eval<ReturnType>(selector, pageFunction, ...args);
} }