feat(elementhandle): add elementHandle.$$eval method (#2589)
Fixes #2401
This commit is contained in:
parent
32f4c173c8
commit
1e07925e26
23
docs/api.md
23
docs/api.md
@ -205,6 +205,7 @@
|
||||
- [class: ElementHandle](#class-elementhandle)
|
||||
* [elementHandle.$(selector)](#elementhandleselector)
|
||||
* [elementHandle.$$(selector)](#elementhandleselector)
|
||||
* [elementHandle.$$eval(selector, pageFunction, ...args)](#elementhandleevalselector-pagefunction-args)
|
||||
* [elementHandle.$eval(selector, pageFunction, ...args)](#elementhandleevalselector-pagefunction-args)
|
||||
* [elementHandle.$x(expression)](#elementhandlexexpression)
|
||||
* [elementHandle.asElement()](#elementhandleaselement)
|
||||
@ -2397,6 +2398,28 @@ 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.querySelectorAll` 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:
|
||||
```html
|
||||
<div class="feed">
|
||||
<div class="tweet">Hello!</div>
|
||||
<div class="tweet">Hi!</div>
|
||||
</div>
|
||||
```
|
||||
```js
|
||||
const feedHandle = await page.$('.feed');
|
||||
expect(await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText)).toEqual(['Hello!', 'Hi!']);
|
||||
```
|
||||
|
||||
#### elementHandle.$eval(selector, pageFunction, ...args)
|
||||
- `selector` <[string]> A [selector] to query page for
|
||||
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||
|
@ -295,6 +295,25 @@ 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 arrayHandle = await this.executionContext().evaluateHandle(
|
||||
(element, selector) => Array.from(element.querySelectorAll(selector)),
|
||||
this, selector
|
||||
);
|
||||
if (!(await arrayHandle.jsonValue()).length)
|
||||
throw new Error(`Error: failed to find elements matching selector "${selector}"`);
|
||||
|
||||
const result = await this.executionContext().evaluate(pageFunction, arrayHandle, ...args);
|
||||
await arrayHandle.dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @return {!Promise<!Array<!ElementHandle>>}
|
||||
|
@ -378,11 +378,9 @@ class Frame {
|
||||
* @return {!Promise<(!Object|undefined)>}
|
||||
*/
|
||||
async $$eval(selector, pageFunction, ...args) {
|
||||
const context = await this._contextPromise;
|
||||
const arrayHandle = await context.evaluateHandle(selector => Array.from(document.querySelectorAll(selector)), selector);
|
||||
const result = await this.evaluate(pageFunction, arrayHandle, ...args);
|
||||
await arrayHandle.dispose();
|
||||
return result;
|
||||
const document = await this._document();
|
||||
const value = await document.$$eval(selector, pageFunction, ...args);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -318,6 +318,30 @@ module.exports.addTests = function({testRunner, expect}) {
|
||||
expect(errorMessage).toBe(`Error: failed to find element matching selector ".a"`);
|
||||
});
|
||||
});
|
||||
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="like">10</div></div></body></html>');
|
||||
const tweet = await page.$('.tweet');
|
||||
const content = await tweet.$$eval('.like', nodes => nodes.map(n => n.innerText));
|
||||
expect(content).toEqual(['100', '10']);
|
||||
});
|
||||
|
||||
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">a1-child-div</div><div class="a">a2-child-div</div></div>';
|
||||
await page.setContent(htmlContent);
|
||||
const elementHandle = await page.$('#myId');
|
||||
const content = await elementHandle.$$eval('.a', nodes => nodes.map(n => n.innerText));
|
||||
expect(content).toEqual(['a1-child-div', 'a2-child-div']);
|
||||
});
|
||||
|
||||
it('should throw in case of missing selector', async({page, server}) => {
|
||||
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"></div>';
|
||||
await page.setContent(htmlContent);
|
||||
const elementHandle = await page.$('#myId');
|
||||
const errorMessage = await elementHandle.$$eval('.a', nodes => nodes.map(n => n.innerText)).catch(error => error.message);
|
||||
expect(errorMessage).toBe(`Error: failed to find elements matching selector ".a"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.$$', function() {
|
||||
it('should query existing elements', async({page, server}) => {
|
||||
|
Loading…
Reference in New Issue
Block a user