page.evaluate takes a string in addition to function (#135)
This patch improves on page.evaluate to accept a string. The string can have a trailing '//# sourceURL=' comment which would name the evaluation to make stacks beautiful. In order to make sourceURL comments possible, this patch: - removes wrapping of the client function into `Promise.resolve()` - stops passing `awaitPromise` parameter to `Runtime.evaluate` - starts to await promise via the `Runtime.awaitPromise` if the return type of the evaluation is promise closes #118
This commit is contained in:
parent
8870aaee17
commit
bbde8fd1c2
40
docs/api.md
40
docs/api.md
@ -354,14 +354,34 @@ Adds a `<script></script>` tag to the page with the desired url. Alternatively,
|
||||
- returns: <[Promise]> Returns promise which resolves when page gets closed.
|
||||
|
||||
#### page.evaluate(pageFunction, ...args)
|
||||
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||
- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
|
||||
- `...args` <...[string]> Arguments to pass to `pageFunction`
|
||||
- returns: <[Promise]<[Object]>> Promise which resolves to function return value
|
||||
|
||||
If the function, passed to the `page.evaluate`, returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return it's value.
|
||||
|
||||
```js
|
||||
const {Browser} = require('puppeteer');
|
||||
const browser = new Browser();
|
||||
browser.newPage().then(async page =>
|
||||
const result = await page.evaluate(() => {
|
||||
return Promise.resolve().then(() => 8 * 7);
|
||||
});
|
||||
console.log(result); // prints "56"
|
||||
browser.close();
|
||||
});
|
||||
```
|
||||
|
||||
A string can also be passed in instead of a function.
|
||||
|
||||
```js
|
||||
console.log(await page.evaluate('1 + 2')); // prints "3"
|
||||
```
|
||||
|
||||
This is a shortcut for [page.mainFrame().evaluate()](#frameevaluatefun-args) method.
|
||||
|
||||
#### page.evaluateOnNewDocument(pageFunction, ...args)
|
||||
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||
- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
|
||||
- `...args` <...[string]> Arguments to pass to `pageFunction`
|
||||
- returns: <[Promise]<[Object]>> Promise which resolves to function
|
||||
|
||||
@ -825,23 +845,11 @@ Adds a `<script></script>` tag to the frame with the desired url. Alternatively,
|
||||
- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully clicked. Promise gets rejected if there's no element matching `selector`.
|
||||
|
||||
#### frame.evaluate(pageFunction, ...args)
|
||||
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||
- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
|
||||
- `...args` <...[string]> Arguments to pass to `pageFunction`
|
||||
- returns: <[Promise]<[Object]>> Promise which resolves to function return value
|
||||
|
||||
If the function, passed to the `page.evaluate`, returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return it's value.
|
||||
|
||||
```js
|
||||
const {Browser} = require('puppeteer');
|
||||
const browser = new Browser();
|
||||
browser.newPage().then(async page =>
|
||||
const result = await page.evaluate(() => {
|
||||
return Promise.resolve().then(() => 8 * 7);
|
||||
});
|
||||
console.log(result); // prints "56"
|
||||
browser.close();
|
||||
});
|
||||
```
|
||||
If the function, passed to the `frame.evaluate`, returns a [Promise], then `frame.evaluate` would wait for the promise to resolve and return it's value.
|
||||
|
||||
#### frame.focus(selector)
|
||||
- `selector` <[string]> A query selector of element to focus. If there are multiple elements satisfying the selector, the first will be focused.
|
||||
|
@ -187,24 +187,14 @@ class Frame {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function()} pageFunction
|
||||
* @param {function()|string} pageFunction
|
||||
* @param {!Array<*>} args
|
||||
* @return {!Promise<(!Object|undefined)>}
|
||||
*/
|
||||
async evaluate(pageFunction, ...args) {
|
||||
return this._evaluateExpression(helper.evaluationString(pageFunction, ...args), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @param {boolean} awaitPromise
|
||||
* @return {!Promise<(!Object|undefined)>}
|
||||
*/
|
||||
async _evaluateExpression(expression, awaitPromise) {
|
||||
let expression = helper.evaluationString(pageFunction, ...args);
|
||||
const contextId = this._defaultContextId;
|
||||
if (awaitPromise)
|
||||
expression = `Promise.resolve(${expression})`;
|
||||
let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise});
|
||||
let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false});
|
||||
if (exceptionDetails)
|
||||
throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
|
||||
return await helper.serializeRemoteObject(this._client, remoteObject);
|
||||
@ -257,7 +247,7 @@ class Frame {
|
||||
});
|
||||
});
|
||||
contents += `//# sourceURL=` + filePath.replace(/\n/g,'');
|
||||
return this._evaluateExpression(contents, false);
|
||||
return this.evaluate(contents);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,7 +309,7 @@ class Frame {
|
||||
return null;
|
||||
return (${pageFunction})(${argsString});
|
||||
})()`;
|
||||
return this._evaluateExpression(expression, true);
|
||||
return this.evaluate(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,7 +325,7 @@ class Frame {
|
||||
let nodes = document.querySelectorAll(${JSON.stringify(selector)});
|
||||
return Array.prototype.map.call(nodes, (node, index) => (${pageFunction})(${argsString}));
|
||||
})()`;
|
||||
return this._evaluateExpression(expression, true);
|
||||
return this.evaluate(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -457,7 +447,7 @@ class WaitTask {
|
||||
let success = false;
|
||||
let error = null;
|
||||
try {
|
||||
success = await this._frame._evaluateExpression(this._pageScript, true);
|
||||
success = await this._frame.evaluate(this._pageScript);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ class Page extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function()} pageFunction
|
||||
* @param {function()|string} pageFunction
|
||||
* @param {!Array<*>} args
|
||||
* @return {!Promise}
|
||||
*/
|
||||
|
@ -16,11 +16,15 @@
|
||||
|
||||
class Helper {
|
||||
/**
|
||||
* @param {function()} fun
|
||||
* @param {function()|string} fun
|
||||
* @param {!Array<*>} args
|
||||
* @return {string}
|
||||
*/
|
||||
static evaluationString(fun, ...args) {
|
||||
if (typeof fun === 'string') {
|
||||
console.assert(args.length === 0, 'Cannot evaluate a string with arguments');
|
||||
return fun;
|
||||
}
|
||||
return `(${fun})(${args.map(x => JSON.stringify(x)).join(',')})`;
|
||||
}
|
||||
|
||||
@ -48,6 +52,16 @@ class Helper {
|
||||
* @return {!Promise<!Object>}
|
||||
*/
|
||||
static async serializeRemoteObject(client, remoteObject) {
|
||||
if (remoteObject.subtype === 'promise') {
|
||||
let response = (await client.send('Runtime.awaitPromise', {
|
||||
promiseObjectId: remoteObject.objectId,
|
||||
returnByValue: false
|
||||
}));
|
||||
Helper.releaseObject(client, remoteObject);
|
||||
if (response.exceptionDetails)
|
||||
throw new Error('Evaluation failed: ' + Helper.getExceptionMessage(response.exceptionDetails));
|
||||
remoteObject = response.result;
|
||||
}
|
||||
if (remoteObject.unserializableValue) {
|
||||
switch (remoteObject.unserializableValue) {
|
||||
case '-0':
|
||||
|
@ -533,10 +533,12 @@ class WebPage {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {function()} fun
|
||||
* @param {function()|string} fun
|
||||
* @param {!Array<!Object>} args
|
||||
*/
|
||||
evaluate(fun, ...args) {
|
||||
if (typeof fun === 'string')
|
||||
fun = `(${fun})()`;
|
||||
if (this._deferEvaluate)
|
||||
return await(this._page.evaluateOnNewDocument(fun, ...args));
|
||||
return await(this._currentFrame.evaluate(fun, ...args));
|
||||
|
12
test/test.js
12
test/test.js
@ -144,6 +144,18 @@ describe('Puppeteer', function() {
|
||||
let result = await page.evaluate(() => window);
|
||||
expect(result).toBe('Window');
|
||||
}));
|
||||
it('should accept a string', SX(async function() {
|
||||
let result = await page.evaluate('1 + 2');
|
||||
expect(result).toBe(3);
|
||||
}));
|
||||
it('should accept a string with semi colons', SX(async function() {
|
||||
let result = await page.evaluate('1 + 5;');
|
||||
expect(result).toBe(6);
|
||||
}));
|
||||
it('should accept a string with comments', SX(async function() {
|
||||
let result = await page.evaluate('2 + 5;\n// do some math!');
|
||||
expect(result).toBe(7);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Page.injectFile', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user