feat(ElementHandle): introduce elementHandle.$eval (#2407)

This patch introduces `elementHandle.$eval` method.

References #2401.
This commit is contained in:
Denny Ku(kuni) 2018-05-09 10:17:59 +09:00 committed by Andrey Lushnikov
parent 1d225cfa17
commit 88b996877f
4 changed files with 52 additions and 6 deletions

View File

@ -189,6 +189,7 @@
- [class: ElementHandle](#class-elementhandle)
* [elementHandle.$(selector)](#elementhandleselector)
* [elementHandle.$$(selector)](#elementhandleselector)
* [elementHandle.$eval(selector, pageFunction, ...args)](#elementhandleevalselector-pagefunction-args)
* [elementHandle.$x(expression)](#elementhandlexexpression)
* [elementHandle.asElement()](#elementhandleaselement)
* [elementHandle.boundingBox()](#elementhandleboundingbox)
@ -2230,6 +2231,23 @@ The method runs `element.querySelector` within the page. If no element matches t
The method runs `element.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
#### elementHandle.$eval(selector, pageFunction, ...args)
- `selector` <[string]> A [selector] to query page for
- `pageFunction` <[function]> Function to be evaluated in browser context
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
This method runs `document.querySelector` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
Examples:
```js
const tweetHandle = await page.$('.tweet');
expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
```
#### elementHandle.$x(expression)
- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves to ElementHandle pointing to the frame element.

View File

@ -280,6 +280,21 @@ class ElementHandle extends JSHandle {
return result;
}
/**
* @param {string} selector
* @param {Function|String} pageFunction
* @param {!Array<*>} args
* @return {!Promise<(!Object|undefined)>}
*/
async $eval(selector, pageFunction, ...args) {
const elementHandle = await this.$(selector);
if (!elementHandle)
throw new Error(`Error: failed to find element matching selector "${selector}"`);
const result = await this.executionContext().evaluate(pageFunction, elementHandle, ...args);
await elementHandle.dispose();
return result;
}
/**
* @param {string} expression
* @return {!Promise<!Array<!ElementHandle>>}

View File

@ -366,12 +366,9 @@ class Frame {
* @return {!Promise<(!Object|undefined)>}
*/
async $eval(selector, pageFunction, ...args) {
const elementHandle = await this.$(selector);
if (!elementHandle)
throw new Error(`Error: failed to find element matching selector "${selector}"`);
const result = await this.evaluate(pageFunction, elementHandle, ...args);
await elementHandle.dispose();
return result;
const document = await this._document();
const value = await document.$eval(selector, pageFunction, ...args);
return value;
}
/**

View File

@ -294,6 +294,22 @@ module.exports.addTests = function({testRunner, expect}) {
expect(second).toBe(null);
});
});
describe('ElementHandle.$eval', function() {
it('should work', async({page, server}) => {
await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="retweets">10</div></div></body></html>');
const tweet = await page.$('.tweet');
const content = await tweet.$eval('.like', node => node.innerText);
expect(content).toBe('100');
});
it('should retrieve content from subtree', async({page, server}) => {
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a-child-div</div></div>';
await page.setContent(htmlContent);
const elementHandle = await page.$('#myId');
const content = await elementHandle.$eval('.a', node => node.innerText);
expect(content).toBe('a-child-div');
});
});
describe('ElementHandle.$$', function() {
it('should query existing elements', async({page, server}) => {