feat(ElementHandle): add ElementHandle.$ and ElementHandle.$$ (#1151)

This patch adds `ElementHandle.$` and `ElementHandle.$$` methods to query nested
elements.

Fixes #508
This commit is contained in:
Adam Stankiewicz 2017-10-27 11:08:58 +02:00 committed by Andrey Lushnikov
parent 9e39f5f8a6
commit 5ffbd0d221
3 changed files with 88 additions and 1 deletions

View File

@ -145,6 +145,8 @@
* [jsHandle.getProperty(propertyName)](#jshandlegetpropertypropertyname)
* [jsHandle.jsonValue()](#jshandlejsonvalue)
- [class: ElementHandle](#class-elementhandle)
* [elementHandle.$(selector)](#elementhandleselector)
* [elementHandle.$$(selector)](#elementhandleselector)
* [elementHandle.asElement()](#elementhandleaselement)
* [elementHandle.boundingBox()](#elementhandleboundingbox)
* [elementHandle.click([options])](#elementhandleclickoptions)
@ -1720,8 +1722,20 @@ ElementHandle prevents DOM element from garbage collection unless the handle is
ElementHandle instances can be used as arguments in [`page.$eval()`](#pageevalselector-pagefunction-args) and [`page.evaluate()`](#pageevaluatepagefunction-args) methods.
#### elementHandle.$(selector)
- `selector` <[string]> A [selector] to query element for
- returns: <[Promise]<[ElementHandle]>>
The method runs `element.querySelector` within the page. If no element matches the selector, the return value resolve to `null`.
#### elementHandle.$$(selector)
- `selector` <[string]> A [selector] to query element for
- returns: <[Promise]<[Array]<[ElementHandle]>>>
The method runs `element.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
#### elementHandle.asElement()
- returns: <[ElementHandle]>
- returns: <[elementhandle]>
#### elementHandle.boundingBox()
- returns: <[Object]>

View File

@ -147,6 +147,42 @@ class ElementHandle extends JSHandle {
clip: boundingBox
}, options));
}
/**
* @param {string} selector
* @return {!Promise<?ElementHandle>}
*/
async $(selector) {
const handle = await this.executionContext().evaluateHandle(
(element, selector) => element.querySelector(selector),
this, selector
);
const element = handle.asElement();
if (element)
return element;
await handle.dispose();
return null;
}
/**
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
const arrayHandle = await this.executionContext().evaluateHandle(
(element, selector) => element.querySelectorAll(selector),
this, selector
);
const properties = await arrayHandle.getProperties();
await arrayHandle.dispose();
const result = [];
for (const property of properties.values()) {
const elementHandle = property.asElement();
if (elementHandle)
result.push(elementHandle);
}
return result;
}
}
module.exports = ElementHandle;

View File

@ -1718,6 +1718,43 @@ describe('Page', function() {
}));
});
describe('ElementHandle.$', function() {
it('should query existing element', SX(async function() {
await page.goto(PREFIX + '/playground.html');
await page.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>');
const html = await page.$('html');
const second = await html.$('.second');
const inner = await second.$('.inner');
const content = await page.evaluate(e => e.textContent, inner);
expect(content).toBe('A');
}));
it('should return null for non-existing element', SX(async function() {
await page.setContent('<html><body><div class="second"><div class="inner">B</div></div></body></html>');
const html = await page.$('html');
const second = await html.$('.third');
expect(second).toBe(null);
}));
});
describe('ElementHandle.$$', function() {
it('should query existing elements', SX(async function() {
await page.setContent('<html><body><div>A</div><br/><div>B</div></body></html>');
const html = await page.$('html');
const elements = await html.$$('div');
expect(elements.length).toBe(2);
const promises = elements.map(element => page.evaluate(e => e.textContent, element));
expect(await Promise.all(promises)).toEqual(['A', 'B']);
}));
it('should return empty array for non-existing elements', SX(async function() {
await page.setContent('<html><body><span>A</span><br/><span>B</span></body></html>');
const html = await page.$('html');
const elements = await html.$$('div');
expect(elements.length).toBe(0);
}));
});
describe('input', function() {
it('should click the button', SX(async function() {
await page.goto(PREFIX + '/input/button.html');