Implement page.$$ method (#463)

This patch implements page.$$ which runs document.querySelectorAll
in page and returns results as an array of ElementHandle instances.

Fixes #384.
This commit is contained in:
Andrey Lushnikov 2017-08-22 22:56:55 -07:00 committed by GitHub
parent 7e1f2f0609
commit 151d512ae2
4 changed files with 63 additions and 3 deletions

View File

@ -27,6 +27,7 @@
+ [event: 'requestfinished'](#event-requestfinished) + [event: 'requestfinished'](#event-requestfinished)
+ [event: 'response'](#event-response) + [event: 'response'](#event-response)
+ [page.$(selector)](#pageselector) + [page.$(selector)](#pageselector)
+ [page.$$(selector)](#pageselector)
+ [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)
@ -85,6 +86,7 @@
+ [dialog.type](#dialogtype) + [dialog.type](#dialogtype)
* [class: Frame](#class-frame) * [class: Frame](#class-frame)
+ [frame.$(selector)](#frameselector) + [frame.$(selector)](#frameselector)
+ [frame.$$(selector)](#frameselector)
+ [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)
@ -297,6 +299,14 @@ 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)
- `selector` <[string]> Selector to query page for
- returns: <[Promise]<[Array]<[ElementHandle]>>>
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).
#### 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.
@ -976,10 +986,15 @@ puppeteer.launch().then(async browser => {
#### frame.$(selector) #### frame.$(selector)
- `selector` <[string]> Selector to query page for - `selector` <[string]> Selector to query page for
- returns: <[Promise]<[ElementHandle]>> Promise which resolves to ElementHandle pointing to the page element. - returns: <[Promise]<[ElementHandle]>> Promise which resolves to ElementHandle pointing to the frame element.
The method queries page for the selector. If there's no such element within the page, the method will resolve to `null`. The method queries frame for the selector. If there's no such element within the frame, the method will resolve to `null`.
#### frame.$$(selector)
- `selector` <[string]> Selector to query page for
- returns: <[Promise]<[Array]<[ElementHandle]>>> Promise which resolves to ElementHandles pointing to the frame elements.
The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolve to `[]`.
#### frame.addScriptTag(url) #### frame.addScriptTag(url)
- `url` <[string]> Url of a script to be added - `url` <[string]> Url of a script to be added

View File

@ -191,10 +191,33 @@ class Frame {
const remoteObject = await this._rawEvaluate(selector => document.querySelector(selector), selector); const remoteObject = await this._rawEvaluate(selector => document.querySelector(selector), selector);
if (remoteObject.subtype === 'node') if (remoteObject.subtype === 'node')
return new ElementHandle(this._client, remoteObject, this._mouse); return new ElementHandle(this._client, remoteObject, this._mouse);
helper.releaseObject(this._client, remoteObject); await helper.releaseObject(this._client, remoteObject);
return null; return null;
} }
/**
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
const remoteObject = await this._rawEvaluate(selector => Array.from(document.querySelectorAll(selector)), selector);
const response = await this._client.send('Runtime.getProperties', {
objectId: remoteObject.objectId,
ownProperties: true
});
const properties = response.result;
const result = [];
const releasePromises = [helper.releaseObject(this._client, remoteObject)];
for (const property of properties) {
if (property.enumerable && property.value.subtype === 'node')
result.push(new ElementHandle(this._client, property.value, this._mouse));
else
releasePromises.push(helper.releaseObject(this._client, property.value));
}
await Promise.all(releasePromises);
return result;
}
/** /**
* @param {function()|string} pageFunction * @param {function()|string} pageFunction
* @param {!Array<*>} args * @param {!Array<*>} args

View File

@ -146,6 +146,14 @@ class Page extends EventEmitter {
return this.mainFrame().$(selector); return this.mainFrame().$(selector);
} }
/**
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
return this.mainFrame().$$(selector);
}
/** /**
* @param {string} url * @param {string} url
* @return {!Promise} * @return {!Promise}

View File

@ -1074,6 +1074,20 @@ describe('Page', function() {
expect(element).toBe(null); expect(element).toBe(null);
})); }));
}); });
describe('Page.$$', function() {
it('should query existing elements', SX(async function() {
await page.setContent('<div>A</div><br/><div>B</div>');
const elements = await page.$$('div');
expect(elements.length).toBe(2);
const promises = elements.map(element => element.evaluate(e => e.textContent));
expect(await Promise.all(promises)).toEqual(['A', 'B']);
}));
it('should return ampty array if nothing is found', SX(async function() {
await page.goto(EMPTY_PAGE);
const elements = await page.$$('div');
expect(elements.length).toBe(0);
}));
});
describe('ElementHandle.evaluate', function() { describe('ElementHandle.evaluate', function() {
it('should work', SX(async function() { it('should work', SX(async function() {