Implement page.$eval (#638)
This patch: - implements page.$eval and frame.$eval - drops elementHandle.attribute() method in favor of the page.$eval References #625
This commit is contained in:
parent
bf66696770
commit
62ececb1c7
58
docs/api.md
58
docs/api.md
@ -31,6 +31,7 @@
|
|||||||
+ [event: 'response'](#event-response)
|
+ [event: 'response'](#event-response)
|
||||||
+ [page.$(selector)](#pageselector)
|
+ [page.$(selector)](#pageselector)
|
||||||
+ [page.$$(selector)](#pageselector)
|
+ [page.$$(selector)](#pageselector)
|
||||||
|
+ [page.$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
|
||||||
+ [page.addScriptTag(url)](#pageaddscripttagurl)
|
+ [page.addScriptTag(url)](#pageaddscripttagurl)
|
||||||
+ [page.click(selector[, options])](#pageclickselector-options)
|
+ [page.click(selector[, options])](#pageclickselector-options)
|
||||||
+ [page.close()](#pageclose)
|
+ [page.close()](#pageclose)
|
||||||
@ -94,6 +95,7 @@
|
|||||||
* [class: Frame](#class-frame)
|
* [class: Frame](#class-frame)
|
||||||
+ [frame.$(selector)](#frameselector)
|
+ [frame.$(selector)](#frameselector)
|
||||||
+ [frame.$$(selector)](#frameselector)
|
+ [frame.$$(selector)](#frameselector)
|
||||||
|
+ [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args)
|
||||||
+ [frame.addScriptTag(url)](#frameaddscripttagurl)
|
+ [frame.addScriptTag(url)](#frameaddscripttagurl)
|
||||||
+ [frame.childFrames()](#framechildframes)
|
+ [frame.childFrames()](#framechildframes)
|
||||||
+ [frame.evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args)
|
+ [frame.evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args)
|
||||||
@ -107,7 +109,6 @@
|
|||||||
+ [frame.waitForFunction(pageFunction[, options, ...args])](#framewaitforfunctionpagefunction-options-args)
|
+ [frame.waitForFunction(pageFunction[, options, ...args])](#framewaitforfunctionpagefunction-options-args)
|
||||||
+ [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
+ [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
||||||
* [class: ElementHandle](#class-elementhandle)
|
* [class: ElementHandle](#class-elementhandle)
|
||||||
+ [elementHandle.attribute(key)](#elementhandleattributekey)
|
|
||||||
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
||||||
+ [elementHandle.dispose()](#elementhandledispose)
|
+ [elementHandle.dispose()](#elementhandledispose)
|
||||||
+ [elementHandle.evaluate(pageFunction, ...args)](#elementhandleevaluatepagefunction-args)
|
+ [elementHandle.evaluate(pageFunction, ...args)](#elementhandleevaluatepagefunction-args)
|
||||||
@ -308,7 +309,7 @@ Emitted when a request finishes successfully.
|
|||||||
Emitted when a [response] is received.
|
Emitted when a [response] is received.
|
||||||
|
|
||||||
#### page.$(selector)
|
#### page.$(selector)
|
||||||
- `selector` <[string]> Selector to query page for
|
- `selector` <[string]> A [selector] to query page for
|
||||||
- returns: <[Promise]<[ElementHandle]>>
|
- returns: <[Promise]<[ElementHandle]>>
|
||||||
|
|
||||||
The method runs `document.querySelector` within the page. If no element matches the selector, the return value resolve to `null`.
|
The method runs `document.querySelector` within the page. If no element matches the selector, the return value resolve to `null`.
|
||||||
@ -316,13 +317,32 @@ The method runs `document.querySelector` within the page. If no element matches
|
|||||||
Shortcut for [page.mainFrame().$(selector)](#frameselector).
|
Shortcut for [page.mainFrame().$(selector)](#frameselector).
|
||||||
|
|
||||||
#### page.$$(selector)
|
#### page.$$(selector)
|
||||||
- `selector` <[string]> Selector to query page for
|
- `selector` <[string]> A [selector] to query page for
|
||||||
- returns: <[Promise]<[Array]<[ElementHandle]>>>
|
- returns: <[Promise]<[Array]<[ElementHandle]>>>
|
||||||
|
|
||||||
The method runs `document.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
|
The method runs `document.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
|
||||||
|
|
||||||
Shortcut for [page.mainFrame().$$(selector)](#frameselector-1).
|
Shortcut for [page.mainFrame().$$(selector)](#frameselector-1).
|
||||||
|
|
||||||
|
#### page.$eval(selector, pageFunction[, ...args])
|
||||||
|
- `selector` <[string]> A [selector] to query page for
|
||||||
|
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||||
|
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
|
||||||
|
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
|
||||||
|
|
||||||
|
This method runs `document.querySelector` within the page 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 `page.$eval` would wait for the promise to resolve and return it's value.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```js
|
||||||
|
const searchValue = await page.$eval('#search', el => el.value);
|
||||||
|
const preloadHref = await page.$eval('link[rel=preload]', el => el.href);
|
||||||
|
const html = await page.$eval('.main-container', e => e.outerHTML);
|
||||||
|
```
|
||||||
|
|
||||||
|
Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-args).
|
||||||
|
|
||||||
#### page.addScriptTag(url)
|
#### page.addScriptTag(url)
|
||||||
- `url` <[string]> Url of the `<script>` tag
|
- `url` <[string]> Url of the `<script>` tag
|
||||||
- returns: <[Promise]> which resolves when the script's onload fires.
|
- returns: <[Promise]> which resolves when the script's onload fires.
|
||||||
@ -1056,6 +1076,23 @@ The method queries frame for the selector. If there's no such element within the
|
|||||||
|
|
||||||
The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolve to `[]`.
|
The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolve to `[]`.
|
||||||
|
|
||||||
|
#### frame.$eval(selector, pageFunction[, ...args])
|
||||||
|
- `selector` <[string]> A [selector] to query frame for
|
||||||
|
- `pageFunction` <[function]> Function to be evaluated in browser context
|
||||||
|
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
|
||||||
|
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
|
||||||
|
|
||||||
|
This method runs `document.querySelector` within the frame 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 it's value.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```js
|
||||||
|
const searchValue = await frame.$eval('#search', el => el.value);
|
||||||
|
const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
|
||||||
|
const html = await frame.$eval('.main-container', e => e.outerHTML);
|
||||||
|
```
|
||||||
|
|
||||||
#### frame.addScriptTag(url)
|
#### frame.addScriptTag(url)
|
||||||
- `url` <[string]> Url of a script to be added
|
- `url` <[string]> Url of a script to be added
|
||||||
- returns: <[Promise]> Promise which resolves as the script gets added and loads.
|
- returns: <[Promise]> Promise which resolves as the script gets added and loads.
|
||||||
@ -1200,21 +1237,6 @@ puppeteer.launch().then(async browser => {
|
|||||||
|
|
||||||
ElementHandle prevents DOM element from garbage collection unless the handle is [disposed](#elementhandledispose). ElementHandles are auto-disposed when their origin frame gets navigated.
|
ElementHandle prevents DOM element from garbage collection unless the handle is [disposed](#elementhandledispose). ElementHandles are auto-disposed when their origin frame gets navigated.
|
||||||
|
|
||||||
#### elementHandle.attribute(key)
|
|
||||||
- `key` <string> the name the attribute of this Element.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
```js
|
|
||||||
const puppeteer = require('puppeteer');
|
|
||||||
|
|
||||||
puppeteer.launch().then(async browser => {
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.goto('https://google.com');
|
|
||||||
const inputElement = await page.$('input');
|
|
||||||
const inputType = await inputElement.attribute('type');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
#### elementHandle.click([options])
|
#### elementHandle.click([options])
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `button` <[string]> `left`, `right`, or `middle`, defaults to `left`.
|
- `button` <[string]> `left`, `right`, or `middle`, defaults to `left`.
|
||||||
|
@ -103,15 +103,6 @@ class ElementHandle {
|
|||||||
const objectId = this._remoteObject.objectId;
|
const objectId = this._remoteObject.objectId;
|
||||||
return this._client.send('DOM.setFileInputFiles', { objectId, files });
|
return this._client.send('DOM.setFileInputFiles', { objectId, files });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {!<string} key
|
|
||||||
* @return {!Promise}
|
|
||||||
*/
|
|
||||||
async attribute(key) {
|
|
||||||
return await this.evaluate((element, key) => element.getAttribute(key), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ElementHandle;
|
module.exports = ElementHandle;
|
||||||
|
@ -195,6 +195,21 @@ class Frame {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 elementHandle.evaluate(pageFunction, ...args);
|
||||||
|
await elementHandle.dispose();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} selector
|
* @param {string} selector
|
||||||
* @return {!Promise<!Array<!ElementHandle>>}
|
* @return {!Promise<!Array<!ElementHandle>>}
|
||||||
|
10
lib/Page.js
10
lib/Page.js
@ -146,6 +146,16 @@ class Page extends EventEmitter {
|
|||||||
return this.mainFrame().$(selector);
|
return this.mainFrame().$(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selector
|
||||||
|
* @param {function()|string} pageFunction
|
||||||
|
* @param {!Array<*>} args
|
||||||
|
* @return {!Promise<(!Object|undefined)>}
|
||||||
|
*/
|
||||||
|
async $eval(selector, pageFunction, ...args) {
|
||||||
|
return this.mainFrame().$eval(selector, pageFunction, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} selector
|
* @param {string} selector
|
||||||
* @return {!Promise<!Array<!ElementHandle>>}
|
* @return {!Promise<!Array<!ElementHandle>>}
|
||||||
|
24
test/test.js
24
test/test.js
@ -1121,6 +1121,24 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Page.$eval', function() {
|
||||||
|
it('should work', SX(async function() {
|
||||||
|
await page.setContent('<section id="testAttribute">43543</section>');
|
||||||
|
const idAttribute = await page.$eval('section', e => e.id);
|
||||||
|
expect(idAttribute).toBe('testAttribute');
|
||||||
|
}));
|
||||||
|
it('should accept arguments', SX(async function() {
|
||||||
|
await page.setContent('<section>hello</section>');
|
||||||
|
const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' world!');
|
||||||
|
expect(text).toBe('hello world!');
|
||||||
|
}));
|
||||||
|
it('should throw error if no element is found', SX(async function() {
|
||||||
|
let error = null;
|
||||||
|
await page.$eval('section', e => e.id).catch(e => error = e);
|
||||||
|
expect(error.message).toContain('failed to find element matching selector "section"');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe('Page.$', function() {
|
describe('Page.$', function() {
|
||||||
it('should query existing element', SX(async function() {
|
it('should query existing element', SX(async function() {
|
||||||
await page.setContent('<section>test</section>');
|
await page.setContent('<section>test</section>');
|
||||||
@ -1187,12 +1205,6 @@ describe('Page', function() {
|
|||||||
}
|
}
|
||||||
expect(error.message).toContain('ElementHandle is disposed');
|
expect(error.message).toContain('ElementHandle is disposed');
|
||||||
}));
|
}));
|
||||||
it('should return attribute', SX(async function() {
|
|
||||||
await page.setContent('<section id="testAttribute">43543</section>');
|
|
||||||
const element = await page.$('section');
|
|
||||||
expect(element).toBeTruthy();
|
|
||||||
expect(await element.attribute('id')).toBe('testAttribute');
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ElementHandle.click', function() {
|
describe('ElementHandle.click', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user