diff --git a/docs/api.md b/docs/api.md
index 7d28d790..17934bad 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1456,7 +1456,7 @@ Shortcut for [page.mainFrame().evaluate(pageFunction, ...args)](#frameevaluatepa
#### page.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
The only difference between `page.evaluate` and `page.evaluateHandle` is that `page.evaluateHandle` returns in-page object (JSHandle).
@@ -1475,6 +1475,14 @@ console.log(await resultHandle.jsonValue());
await resultHandle.dispose();
```
+This function will return a [JSHandle] by default, however if your `pageFunction` returns an HTML element you will get back an `ElementHandle`:
+
+```js
+const button = await page.evaluateHandle(() => document.querySelector('button'))
+// button is an ElementHandle, so you can call methods such as click:
+await button.click();
+```
+
Shortcut for [page.mainFrame().executionContext().evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args).
#### page.evaluateOnNewDocument(pageFunction[, ...args])
@@ -2298,12 +2306,14 @@ Shortcut for [(await worker.executionContext()).evaluate(pageFunction, ...args)]
#### webWorker.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
The only difference between `worker.evaluate` and `worker.evaluateHandle` is that `worker.evaluateHandle` returns in-page object (JSHandle).
If the function passed to the `worker.evaluateHandle` returns a [Promise], then `worker.evaluateHandle` would wait for the promise to resolve and return its value.
+If the function returns an element, the returned handle is an [ElementHandle].
+
Shortcut for [(await worker.executionContext()).evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args).
#### webWorker.executionContext()
@@ -2855,12 +2865,14 @@ await bodyHandle.dispose();
#### frame.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
The only difference between `frame.evaluate` and `frame.evaluateHandle` is that `frame.evaluateHandle` returns in-page object (JSHandle).
If the function, passed to the `frame.evaluateHandle`, returns a [Promise], then `frame.evaluateHandle` would wait for the promise to resolve and return its value.
+If the function returns an element, the returned handle is an [ElementHandle].
+
```js
const aWindowHandle = await frame.evaluateHandle(() => Promise.resolve(window));
aWindowHandle; // Handle for the window object.
@@ -3184,10 +3196,12 @@ console.log(result); // prints '3'.
#### executionContext.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated in the `executionContext`
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
The only difference between `executionContext.evaluate` and `executionContext.evaluateHandle` is that `executionContext.evaluateHandle` returns in-page object (JSHandle).
+If the function returns an element, the returned handle is an [ElementHandle].
+
If the function passed to the `executionContext.evaluateHandle` returns a [Promise], then `executionContext.evaluateHandle` would wait for the promise to resolve and return its value.
```js
@@ -3277,12 +3291,14 @@ expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
#### jsHandle.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
This method passes this handle as the first argument to `pageFunction`.
The only difference between `jsHandle.evaluate` and `jsHandle.evaluateHandle` is that `executionContext.evaluateHandle` returns in-page object (JSHandle).
+If the function returns an element, the returned handle is an [ElementHandle].
+
If the function passed to the `jsHandle.evaluateHandle` returns a [Promise], then `jsHandle.evaluateHandle` would wait for the promise to resolve and return its value.
See [Page.evaluateHandle](#pageevaluatehandlepagefunction-args) for more details.
@@ -3466,12 +3482,14 @@ expect(await tweetHandle.evaluate(node => node.innerText)).toBe('10');
#### elementHandle.evaluateHandle(pageFunction[, ...args])
- `pageFunction` <[function]|[string]> Function to be evaluated
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
-- returns: <[Promise]<[JSHandle]>> Promise which resolves to the return value of `pageFunction` as in-page object (JSHandle)
+- returns: <[Promise]<[JSHandle]|[ElementHandle]>> Promise which resolves to the return value of `pageFunction` as an in-page object.
This method passes this handle as the first argument to `pageFunction`.
The only difference between `evaluateHandle.evaluate` and `evaluateHandle.evaluateHandle` is that `executionContext.evaluateHandle` returns in-page object (JSHandle).
+If the function returns an element, the returned handle is an [ElementHandle].
+
If the function passed to the `evaluateHandle.evaluateHandle` returns a [Promise], then `evaluateHandle.evaluateHandle` would wait for the promise to resolve and return its value.
See [Page.evaluateHandle](#pageevaluatehandlepagefunction-args) for more details.
diff --git a/new-docs/puppeteer.evaluatehandlefn.md b/new-docs/puppeteer.evaluatehandlefn.md
new file mode 100644
index 00000000..65596a1f
--- /dev/null
+++ b/new-docs/puppeteer.evaluatehandlefn.md
@@ -0,0 +1,12 @@
+
+
+[Home](./index.md) > [puppeteer](./puppeteer.md) > [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md)
+
+## EvaluateHandleFn type
+
+
+Signature:
+
+```typescript
+export declare type EvaluateHandleFn = string | ((...args: unknown[]) => unknown);
+```
diff --git a/new-docs/puppeteer.executioncontext.evaluatehandle.md b/new-docs/puppeteer.executioncontext.evaluatehandle.md
index 77309491..3f89892f 100644
--- a/new-docs/puppeteer.executioncontext.evaluatehandle.md
+++ b/new-docs/puppeteer.executioncontext.evaluatehandle.md
@@ -7,19 +7,19 @@
Signature:
```typescript
-evaluateHandle(pageFunction: Function | string, ...args: unknown[]): Promise;
+evaluateHandle(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| pageFunction | Function \| string | a function to be evaluated in the executionContext
|
-| args | unknown\[\] | argument to pass to the page function |
+| pageFunction | [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | a function to be evaluated in the executionContext
|
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | argument to pass to the page function |
Returns:
-Promise<[JSHandle](./puppeteer.jshandle.md)>
+Promise<HandleType>
A promise that resolves to the return value of the given function as an in-page object (a [JSHandle](./puppeteer.jshandle.md)).
diff --git a/new-docs/puppeteer.frame.evaluatehandle.md b/new-docs/puppeteer.frame.evaluatehandle.md
index 57b20326..83e1ee45 100644
--- a/new-docs/puppeteer.frame.evaluatehandle.md
+++ b/new-docs/puppeteer.frame.evaluatehandle.md
@@ -7,17 +7,17 @@
Signature:
```typescript
-evaluateHandle(pageFunction: Function | string, ...args: unknown[]): Promise;
+evaluateHandle(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| pageFunction | Function \| string | |
-| args | unknown\[\] | |
+| pageFunction | [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
-Promise<[JSHandle](./puppeteer.jshandle.md)>
+Promise<HandlerType>
diff --git a/new-docs/puppeteer.frame.waitfor.md b/new-docs/puppeteer.frame.waitfor.md
index 52a4dc47..9f540b13 100644
--- a/new-docs/puppeteer.frame.waitfor.md
+++ b/new-docs/puppeteer.frame.waitfor.md
@@ -7,7 +7,7 @@
Signature:
```typescript
-waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {}, ...args: unknown[]): Promise;
+waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {}, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
@@ -16,7 +16,7 @@ waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {}, .
| --- | --- | --- |
| selectorOrFunctionOrTimeout | string \| number \| Function | |
| options | {} | |
-| args | unknown\[\] | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
diff --git a/new-docs/puppeteer.frame.waitforfunction.md b/new-docs/puppeteer.frame.waitforfunction.md
index 93692db5..5c98f20c 100644
--- a/new-docs/puppeteer.frame.waitforfunction.md
+++ b/new-docs/puppeteer.frame.waitforfunction.md
@@ -10,7 +10,7 @@
waitForFunction(pageFunction: Function | string, options?: {
polling?: string | number;
timeout?: number;
- }, ...args: unknown[]): Promise;
+ }, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
@@ -19,7 +19,7 @@ waitForFunction(pageFunction: Function | string, options?: {
| --- | --- | --- |
| pageFunction | Function \| string | |
| options | { polling?: string \| number; timeout?: number; } | |
-| args | unknown\[\] | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
diff --git a/new-docs/puppeteer.jshandle.evaluatehandle.md b/new-docs/puppeteer.jshandle.evaluatehandle.md
index 46078670..a217d359 100644
--- a/new-docs/puppeteer.jshandle.evaluatehandle.md
+++ b/new-docs/puppeteer.jshandle.evaluatehandle.md
@@ -9,19 +9,19 @@ This method passes this handle as the first argument to `pageFunction`.
Signature:
```typescript
-evaluateHandle(pageFunction: Function | string, ...args: unknown[]): Promise;
+evaluateHandle(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| pageFunction | Function \| string | |
-| args | unknown\[\] | |
+| pageFunction | [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
-Promise<[JSHandle](./puppeteer.jshandle.md)>
+Promise<HandleType>
## Remarks
diff --git a/new-docs/puppeteer.md b/new-docs/puppeteer.md
index 13db4adb..9e0f572d 100644
--- a/new-docs/puppeteer.md
+++ b/new-docs/puppeteer.md
@@ -82,6 +82,7 @@
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
| [EvaluateFn](./puppeteer.evaluatefn.md) | |
| [EvaluateFnReturnType](./puppeteer.evaluatefnreturntype.md) | |
+| [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | |
| [JSONArray](./puppeteer.jsonarray.md) | |
| [KeyInput](./puppeteer.keyinput.md) | |
| [MouseButtonInput](./puppeteer.mousebuttoninput.md) | |
diff --git a/new-docs/puppeteer.page.evaluatehandle.md b/new-docs/puppeteer.page.evaluatehandle.md
index 6d1142de..5c56ce24 100644
--- a/new-docs/puppeteer.page.evaluatehandle.md
+++ b/new-docs/puppeteer.page.evaluatehandle.md
@@ -7,17 +7,62 @@
Signature:
```typescript
-evaluateHandle(pageFunction: Function | string, ...args: unknown[]): Promise;
+evaluateHandle(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| pageFunction | Function \| string | |
-| args | unknown\[\] | |
+| pageFunction | [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | a function that is run within the page |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | arguments to be passed to the pageFunction |
Returns:
-Promise<[JSHandle](./puppeteer.jshandle.md)>
+Promise<HandlerType>
+
+## Remarks
+
+The only difference between [page.evaluate](./puppeteer.page.evaluate.md) and `page.evaluateHandle` is that `evaluateHandle` will return the value wrapped in an in-page object.
+
+If the function passed to `page.evaluteHandle` returns a Promise, the function will wait for the promise to resolve and return its value.
+
+You can pass a string instead of a function (although functions are recommended as they are easier to debug and use with TypeScript):
+
+## Example 1
+
+
+```
+const aHandle = await page.evaluateHandle('document')
+
+```
+
+## Example 2
+
+[JSHandle](./puppeteer.jshandle.md) instances can be passed as arguments to the `pageFunction`:
+
+```
+const aHandle = await page.evaluateHandle(() => document.body);
+const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle);
+console.log(await resultHandle.jsonValue());
+await resultHandle.dispose();
+
+```
+Most of the time this function returns a [JSHandle](./puppeteer.jshandle.md), but if `pageFunction` returns a reference to an element, you instead get an [ElementHandle](./puppeteer.elementhandle.md) back:
+
+## Example 3
+
+
+```
+const button = await page.evaluateHandle(() => document.querySelector('button'));
+// can call `click` because `button` is an `ElementHandle`
+await button.click();
+
+```
+The TypeScript definitions assume that `evaluateHandle` returns a `JSHandle`, but if you know it's going to return an `ElementHandle`, pass it as the generic argument:
+
+```
+const button = await page.evaluateHandle(...);
+
+```
diff --git a/new-docs/puppeteer.page.waitfor.md b/new-docs/puppeteer.page.waitfor.md
index 8a2eb518..e808df9a 100644
--- a/new-docs/puppeteer.page.waitfor.md
+++ b/new-docs/puppeteer.page.waitfor.md
@@ -12,7 +12,7 @@ waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {
hidden?: boolean;
timeout?: number;
polling?: string | number;
- }, ...args: unknown[]): Promise;
+ }, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
@@ -21,7 +21,7 @@ waitFor(selectorOrFunctionOrTimeout: string | number | Function, options?: {
| --- | --- | --- |
| selectorOrFunctionOrTimeout | string \| number \| Function | |
| options | { visible?: boolean; hidden?: boolean; timeout?: number; polling?: string \| number; } | |
-| args | unknown\[\] | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
diff --git a/new-docs/puppeteer.page.waitforfunction.md b/new-docs/puppeteer.page.waitforfunction.md
index 697ff57d..e18be076 100644
--- a/new-docs/puppeteer.page.waitforfunction.md
+++ b/new-docs/puppeteer.page.waitforfunction.md
@@ -10,7 +10,7 @@
waitForFunction(pageFunction: Function | string, options?: {
timeout?: number;
polling?: string | number;
- }, ...args: unknown[]): Promise;
+ }, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
@@ -19,7 +19,7 @@ waitForFunction(pageFunction: Function | string, options?: {
| --- | --- | --- |
| pageFunction | Function \| string | |
| options | { timeout?: number; polling?: string \| number; } | |
-| args | unknown\[\] | |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | |
Returns:
diff --git a/new-docs/puppeteer.webworker.evaluatehandle.md b/new-docs/puppeteer.webworker.evaluatehandle.md
index f187a930..aeb5f785 100644
--- a/new-docs/puppeteer.webworker.evaluatehandle.md
+++ b/new-docs/puppeteer.webworker.evaluatehandle.md
@@ -9,15 +9,15 @@ The only difference between `worker.evaluate` and `worker.evaluateHandle` is tha
Signature:
```typescript
-evaluateHandle(pageFunction: Function | string, ...args: any[]): Promise;
+evaluateHandle(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
-| pageFunction | Function \| string | Function to be evaluated in the page context. |
-| args | any\[\] | Arguments to pass to pageFunction
. |
+| pageFunction | [EvaluateHandleFn](./puppeteer.evaluatehandlefn.md) | Function to be evaluated in the page context. |
+| args | [SerializableOrJSHandle](./puppeteer.serializableorjshandle.md)\[\] | Arguments to pass to pageFunction
. |
Returns:
diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts
index 3ea66d50..ed7ee4b4 100644
--- a/src/common/DOMWorld.ts
+++ b/src/common/DOMWorld.ts
@@ -24,7 +24,11 @@ import { TimeoutSettings } from './TimeoutSettings';
import { MouseButtonInput } from './Input';
import { FrameManager, Frame } from './FrameManager';
import { getQueryHandlerAndSelector, QueryHandler } from './QueryHandler';
-import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
+import {
+ EvaluateFn,
+ SerializableOrJSHandle,
+ EvaluateHandleFn,
+} from './EvalTypes';
import { isNode } from '../environment';
// This predicateQueryHandler is declared here so that TypeScript knows about it
@@ -103,15 +107,10 @@ export class DOMWorld {
return this._contextPromise;
}
- /**
- * @param {Function|string} pageFunction
- * @param {!Array<*>} args
- * @returns {!Promise}
- */
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: unknown[]
- ): Promise {
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
+ ): Promise {
const context = await this.executionContext();
return context.evaluateHandle(pageFunction, ...args);
}
@@ -470,7 +469,7 @@ export class DOMWorld {
waitForFunction(
pageFunction: Function | string,
options: { polling?: string | number; timeout?: number } = {},
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
): Promise {
const {
polling = 'raf',
@@ -581,7 +580,7 @@ class WaitTask {
_polling: string | number;
_timeout: number;
_predicateBody: string;
- _args: unknown[];
+ _args: SerializableOrJSHandle[];
_runCount = 0;
promise: Promise;
_resolve: (x: JSHandle) => void;
@@ -596,7 +595,7 @@ class WaitTask {
title: string,
polling: string | number,
timeout: number,
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
) {
if (helper.isString(polling))
assert(
diff --git a/src/common/EvalTypes.ts b/src/common/EvalTypes.ts
index c898d22b..f8588245 100644
--- a/src/common/EvalTypes.ts
+++ b/src/common/EvalTypes.ts
@@ -29,6 +29,11 @@ export type EvaluateFnReturnType = T extends (
? R
: unknown;
+/**
+ * @public
+ */
+export type EvaluateHandleFn = string | ((...args: unknown[]) => unknown);
+
/**
* @public
*/
diff --git a/src/common/ExecutionContext.ts b/src/common/ExecutionContext.ts
index 5681e9d0..df7d4024 100644
--- a/src/common/ExecutionContext.ts
+++ b/src/common/ExecutionContext.ts
@@ -21,6 +21,7 @@ import { CDPSession } from './Connection';
import { DOMWorld } from './DOMWorld';
import { Frame } from './FrameManager';
import Protocol from '../protocol';
+import { EvaluateHandleFn, SerializableOrJSHandle } from './EvalTypes';
export const EVALUATION_SCRIPT_URL = '__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\040\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
@@ -175,15 +176,15 @@ export class ExecutionContext {
* @returns A promise that resolves to the return value of the given function
* as an in-page object (a {@link JSHandle}).
*/
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: unknown[]
- ): Promise {
- return this._evaluateInternal(false, pageFunction, ...args);
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
+ ): Promise {
+ return this._evaluateInternal(false, pageFunction, ...args);
}
private async _evaluateInternal(
- returnByValue,
+ returnByValue: boolean,
pageFunction: Function | string,
...args: unknown[]
): Promise {
diff --git a/src/common/FrameManager.ts b/src/common/FrameManager.ts
index 859b649e..4ac57044 100644
--- a/src/common/FrameManager.ts
+++ b/src/common/FrameManager.ts
@@ -29,7 +29,11 @@ import { MouseButtonInput } from './Input';
import { Page } from './Page';
import { HTTPResponse } from './HTTPResponse';
import Protocol from '../protocol';
-import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
+import {
+ EvaluateFn,
+ SerializableOrJSHandle,
+ EvaluateHandleFn,
+} from './EvalTypes';
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
@@ -431,11 +435,11 @@ export class Frame {
return this._mainWorld.executionContext();
}
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: unknown[]
- ): Promise {
- return this._mainWorld.evaluateHandle(pageFunction, ...args);
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
+ ): Promise {
+ return this._mainWorld.evaluateHandle(pageFunction, ...args);
}
async evaluate(
@@ -562,7 +566,7 @@ export class Frame {
waitFor(
selectorOrFunctionOrTimeout: string | number | Function,
options: {} = {},
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
): Promise {
const xPathPattern = '//';
@@ -619,7 +623,7 @@ export class Frame {
waitForFunction(
pageFunction: Function | string,
options: { polling?: string | number; timeout?: number } = {},
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
): Promise {
return this._mainWorld.waitForFunction(pageFunction, options, ...args);
}
diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts
index 2202d76a..51741ef4 100644
--- a/src/common/JSHandle.ts
+++ b/src/common/JSHandle.ts
@@ -27,6 +27,7 @@ import {
EvaluateFn,
SerializableOrJSHandle,
EvaluateFnReturnType,
+ EvaluateHandleFn,
} from './EvalTypes';
export interface BoxModel {
@@ -174,10 +175,10 @@ export class JSHandle {
*
* See {@link Page.evaluateHandle} for more details.
*/
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: unknown[]
- ): Promise {
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
+ ): Promise {
return await this.executionContext().evaluateHandle(
pageFunction,
this,
@@ -891,19 +892,22 @@ export class ElementHandle extends JSHandle {
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
*/
async $x(expression: string): Promise {
- const arrayHandle = await this.evaluateHandle((element, expression) => {
- const document = element.ownerDocument || element;
- const iterator = document.evaluate(
- expression,
- element,
- null,
- XPathResult.ORDERED_NODE_ITERATOR_TYPE
- );
- const array = [];
- let item;
- while ((item = iterator.iterateNext())) array.push(item);
- return array;
- }, expression);
+ const arrayHandle = await this.evaluateHandle(
+ (element: Document, expression: string) => {
+ const document = element.ownerDocument || element;
+ const iterator = document.evaluate(
+ expression,
+ element,
+ null,
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE
+ );
+ const array = [];
+ let item;
+ while ((item = iterator.iterateNext())) array.push(item);
+ return array;
+ },
+ expression
+ );
const properties = await arrayHandle.getProperties();
await arrayHandle.dispose();
const result = [];
diff --git a/src/common/Page.ts b/src/common/Page.ts
index ad7a077d..de9f9017 100644
--- a/src/common/Page.ts
+++ b/src/common/Page.ts
@@ -42,7 +42,11 @@ import { FileChooser } from './FileChooser';
import { ConsoleMessage, ConsoleMessageType } from './ConsoleMessage';
import { PuppeteerLifeCycleEvent } from './LifecycleWatcher';
import Protocol from '../protocol';
-import { EvaluateFn, SerializableOrJSHandle } from './EvalTypes';
+import {
+ EvaluateFn,
+ SerializableOrJSHandle,
+ EvaluateHandleFn,
+} from './EvalTypes';
const writeFileAsync = promisify(fs.writeFile);
@@ -639,12 +643,61 @@ export class Page extends EventEmitter {
return this.mainFrame().$(selector);
}
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: unknown[]
- ): Promise {
+ /**
+ * @remarks
+ *
+ * The only difference between {@link Page.evaluate | page.evaluate} and
+ * `page.evaluateHandle` is that `evaluateHandle` will return the value
+ * wrapped in an in-page object.
+ *
+ * If the function passed to `page.evaluteHandle` returns a Promise, the
+ * function will wait for the promise to resolve and return its value.
+ *
+ * You can pass a string instead of a function (although functions are
+ * recommended as they are easier to debug and use with TypeScript):
+ *
+ * @example
+ * ```
+ * const aHandle = await page.evaluateHandle('document')
+ * ```
+ *
+ * @example
+ * {@link JSHandle} instances can be passed as arguments to the `pageFunction`:
+ * ```
+ * const aHandle = await page.evaluateHandle(() => document.body);
+ * const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle);
+ * console.log(await resultHandle.jsonValue());
+ * await resultHandle.dispose();
+ * ```
+ *
+ * Most of the time this function returns a {@link JSHandle},
+ * but if `pageFunction` returns a reference to an element,
+ * you instead get an {@link ElementHandle} back:
+ *
+ * @example
+ * ```
+ * const button = await page.evaluateHandle(() => document.querySelector('button'));
+ * // can call `click` because `button` is an `ElementHandle`
+ * await button.click();
+ * ```
+ *
+ * The TypeScript definitions assume that `evaluateHandle` returns
+ * a `JSHandle`, but if you know it's going to return an
+ * `ElementHandle`, pass it as the generic argument:
+ *
+ * ```
+ * const button = await page.evaluateHandle(...);
+ * ```
+ *
+ * @param pageFunction - a function that is run within the page
+ * @param args - arguments to be passed to the pageFunction
+ */
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
+ ): Promise {
const context = await this.mainFrame().executionContext();
- return context.evaluateHandle(pageFunction, ...args);
+ return context.evaluateHandle(pageFunction, ...args);
}
async queryObjects(prototypeHandle: JSHandle): Promise {
@@ -1471,7 +1524,7 @@ export class Page extends EventEmitter {
timeout?: number;
polling?: string | number;
} = {},
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
): Promise {
return this.mainFrame().waitFor(
selectorOrFunctionOrTimeout,
@@ -1508,7 +1561,7 @@ export class Page extends EventEmitter {
timeout?: number;
polling?: string | number;
} = {},
- ...args: unknown[]
+ ...args: SerializableOrJSHandle[]
): Promise {
return this.mainFrame().waitForFunction(pageFunction, options, ...args);
}
diff --git a/src/common/WebWorker.ts b/src/common/WebWorker.ts
index 4b9e4ab3..dacdd4ab 100644
--- a/src/common/WebWorker.ts
+++ b/src/common/WebWorker.ts
@@ -19,6 +19,7 @@ import { ExecutionContext } from './ExecutionContext';
import { JSHandle } from './JSHandle';
import { CDPSession } from './Connection';
import Protocol from '../protocol';
+import { EvaluateHandleFn, SerializableOrJSHandle } from './EvalTypes';
type ConsoleAPICalledCallback = (
eventType: string,
@@ -152,11 +153,11 @@ export class WebWorker extends EventEmitter {
* @param args - Arguments to pass to `pageFunction`.
* @returns Promise which resolves to the return value of `pageFunction`.
*/
- async evaluateHandle(
- pageFunction: Function | string,
- ...args: any[]
+ async evaluateHandle(
+ pageFunction: EvaluateHandleFn,
+ ...args: SerializableOrJSHandle[]
): Promise {
- return (await this._executionContextPromise).evaluateHandle(
+ return (await this._executionContextPromise).evaluateHandle(
pageFunction,
...args
);
diff --git a/test/elementhandle.spec.ts b/test/elementhandle.spec.ts
index 38af4b66..fe6252b4 100644
--- a/test/elementhandle.spec.ts
+++ b/test/elementhandle.spec.ts
@@ -182,17 +182,11 @@ describe('ElementHandle specs', function () {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/shadow.html');
- const buttonHandle = await page.evaluateHandle(
+ const buttonHandle = await page.evaluateHandle(
// @ts-expect-error button is expected to be in the page's scope.
() => button
);
- // TODO (@jackfranklin): TS types are off here. evaluateHandle returns a
- // JSHandle but that doesn't have a click() method. In this case it seems
- // to return an ElementHandle. I'm not sure if the tests are wrong here
- // and should use evaluate or if the type of evaluateHandle
- // should change to enable the user to tell us they are expecting an
- // ElementHandle rather than the default JSHandle.
- await (buttonHandle as ElementHandle).click();
+ await buttonHandle.click();
expect(
await page.evaluate(
// @ts-expect-error clicked is expected to be in the page's scope.
diff --git a/test/jshandle.spec.ts b/test/jshandle.spec.ts
index 0d66f3e4..276d88b9 100644
--- a/test/jshandle.spec.ts
+++ b/test/jshandle.spec.ts
@@ -53,6 +53,7 @@ describe('JSHandle', function () {
const aHandle = await page.evaluateHandle(() => document.body);
let error = null;
await page
+ // @ts-expect-error we are deliberately passing a bad type here (nested object)
.evaluateHandle((opts) => opts.elem.querySelector('p'), {
elem: aHandle,
})