mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: rename page.xpath into page.$x, return an array of elements (#1713)
Fixes #1705.
This commit is contained in:
parent
d062381978
commit
f183664d0f
45
docs/api.md
45
docs/api.md
@ -44,6 +44,7 @@
|
||||
* [page.$$(selector)](#pageselector)
|
||||
* [page.$$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
|
||||
* [page.$eval(selector, pageFunction[, ...args])](#pageevalselector-pagefunction-args)
|
||||
* [page.$x(expression)](#pagexexpression)
|
||||
* [page.addScriptTag(options)](#pageaddscripttagoptions)
|
||||
* [page.addStyleTag(options)](#pageaddstyletagoptions)
|
||||
* [page.authenticate(credentials)](#pageauthenticatecredentials)
|
||||
@ -94,7 +95,6 @@
|
||||
* [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
||||
* [page.waitForNavigation(options)](#pagewaitfornavigationoptions)
|
||||
* [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
|
||||
* [page.xpath(expression)](#pagexpathexpression)
|
||||
- [class: Keyboard](#class-keyboard)
|
||||
* [keyboard.down(key[, options])](#keyboarddownkey-options)
|
||||
* [keyboard.press(key[, options])](#keyboardpresskey-options)
|
||||
@ -126,6 +126,7 @@
|
||||
* [frame.$$(selector)](#frameselector)
|
||||
* [frame.$$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args)
|
||||
* [frame.$eval(selector, pageFunction[, ...args])](#frameevalselector-pagefunction-args)
|
||||
* [frame.$x(expression)](#framexexpression)
|
||||
* [frame.addScriptTag(options)](#frameaddscripttagoptions)
|
||||
* [frame.addStyleTag(options)](#frameaddstyletagoptions)
|
||||
* [frame.childFrames()](#framechildframes)
|
||||
@ -142,7 +143,6 @@
|
||||
* [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
|
||||
* [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
|
||||
* [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
||||
* [frame.xpath(expression)](#framexpathexpression)
|
||||
- [class: ExecutionContext](#class-executioncontext)
|
||||
* [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args)
|
||||
* [executionContext.evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args)
|
||||
@ -157,6 +157,7 @@
|
||||
- [class: ElementHandle](#class-elementhandle)
|
||||
* [elementHandle.$(selector)](#elementhandleselector)
|
||||
* [elementHandle.$$(selector)](#elementhandleselector)
|
||||
* [elementHandle.$x(expression)](#elementhandlexexpression)
|
||||
* [elementHandle.asElement()](#elementhandleaselement)
|
||||
* [elementHandle.boundingBox()](#elementhandleboundingbox)
|
||||
* [elementHandle.click([options])](#elementhandleclickoptions)
|
||||
@ -173,7 +174,6 @@
|
||||
* [elementHandle.toString()](#elementhandletostring)
|
||||
* [elementHandle.type(text[, options])](#elementhandletypetext-options)
|
||||
* [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
|
||||
* [elementHandle.xpath(expression)](#elementhandlexpathexpression)
|
||||
- [class: Request](#class-request)
|
||||
* [request.abort([errorCode])](#requestaborterrorcode)
|
||||
* [request.continue([overrides])](#requestcontinueoverrides)
|
||||
@ -531,6 +531,14 @@ const html = await page.$eval('.main-container', e => e.outerHTML);
|
||||
|
||||
Shortcut for [page.mainFrame().$eval(selector, pageFunction)](#frameevalselector-pagefunction-args).
|
||||
|
||||
#### page.$x(expression)
|
||||
- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
|
||||
- returns: <[Promise]<[Array]<[ElementHandle]>>>
|
||||
|
||||
The method evluates the XPath expression.
|
||||
|
||||
Shortcut for [page.mainFrame().$x(expression)](#frameexpression)
|
||||
|
||||
#### page.addScriptTag(options)
|
||||
- `options` <[Object]>
|
||||
- `url` <[string]> Url of a script to be added.
|
||||
@ -1226,13 +1234,6 @@ puppeteer.launch().then(async browser => {
|
||||
```
|
||||
Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options).
|
||||
|
||||
#### page.xpath(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 page element.
|
||||
|
||||
The method evluates the XPath expression. If there's no such element within the page, the method will resolve to `null`.
|
||||
|
||||
Shortcut for [page.mainFrame().xpath(expression)](#framexpathexpression)
|
||||
|
||||
### class: Keyboard
|
||||
|
||||
@ -1518,6 +1519,12 @@ const preloadHref = await frame.$eval('link[rel=preload]', el => el.href);
|
||||
const html = await frame.$eval('.main-container', e => e.outerHTML);
|
||||
```
|
||||
|
||||
#### frame.$x(expression)
|
||||
- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
|
||||
- returns: <[Promise]<[Array]<[ElementHandle]>>>
|
||||
|
||||
The method evluates the XPath expression.
|
||||
|
||||
#### frame.addScriptTag(options)
|
||||
- `options` <[Object]>
|
||||
- `url` <[string]> Url of a script to be added.
|
||||
@ -1682,12 +1689,6 @@ puppeteer.launch().then(async browser => {
|
||||
});
|
||||
```
|
||||
|
||||
#### frame.xpath(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.
|
||||
|
||||
The method evluates the XPath expression. If there's no such element within the frame, the method will resolve to `null`.
|
||||
|
||||
### class: ExecutionContext
|
||||
|
||||
The class represents a context for JavaScript execution. Examples of JavaScript contexts are:
|
||||
@ -1860,6 +1861,12 @@ 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.$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.
|
||||
|
||||
The method evluates the XPath expression relative to the elementHandle. If there's no such element, the method will resolve to `null`.
|
||||
|
||||
#### elementHandle.asElement()
|
||||
- returns: <[elementhandle]>
|
||||
|
||||
@ -1988,12 +1995,6 @@ await elementHandle.press('Enter');
|
||||
|
||||
This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
|
||||
|
||||
#### elementHandle.xpath(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.
|
||||
|
||||
The method evluates the XPath expression relative to the elementHandle. If there's no such element, the method will resolve to `null`.
|
||||
|
||||
### class: Request
|
||||
|
||||
Whenever the page sends a request, the following events are emitted by puppeteer's page:
|
||||
|
@ -195,21 +195,30 @@ class ElementHandle extends JSHandle {
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @return {!Promise<?ElementHandle>}
|
||||
* @return {!Promise<!Array<!ElementHandle>>}
|
||||
*/
|
||||
async xpath(expression) {
|
||||
const handle = await this.executionContext().evaluateHandle(
|
||||
async $x(expression) {
|
||||
const arrayHandle = await this.executionContext().evaluateHandle(
|
||||
(element, expression) => {
|
||||
const document = element.ownerDocument || element;
|
||||
return document.evaluate(expression, element, null, XPathResult.FIRST_ORDERED_NODE_TYPE).singleNodeValue;
|
||||
const iterator = document.evaluate(expression, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
|
||||
const array = [];
|
||||
let item;
|
||||
while ((item = iterator.iterateNext()))
|
||||
array.push(item);
|
||||
return array;
|
||||
},
|
||||
this, expression
|
||||
);
|
||||
const element = handle.asElement();
|
||||
if (element)
|
||||
return element;
|
||||
await handle.dispose();
|
||||
return null;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,11 +307,11 @@ class Frame {
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @return {!Promise<?ElementHandle>}
|
||||
* @return {!Promise<!Array<!ElementHandle>>}
|
||||
*/
|
||||
async xpath(expression) {
|
||||
async $x(expression) {
|
||||
const document = await this._document();
|
||||
const value = await document.xpath(expression);
|
||||
const value = await document.$x(expression);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -239,10 +239,10 @@ class Page extends EventEmitter {
|
||||
|
||||
/**
|
||||
* @param {string} expression
|
||||
* @return {!Promise<?Puppeteer.ElementHandle>}
|
||||
* @return {!Promise<!Array<!Puppeteer.ElementHandle>>}
|
||||
*/
|
||||
async xpath(expression) {
|
||||
return this.mainFrame().xpath(expression);
|
||||
async $x(expression) {
|
||||
return this.mainFrame().$x(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
|
30
test/test.js
30
test/test.js
@ -1727,15 +1727,21 @@ describe('Page', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Path.xpath', function() {
|
||||
describe('Path.$x', function() {
|
||||
it('should query existing element', async({page, server}) => {
|
||||
await page.setContent('<section>test</section>');
|
||||
const element = await page.xpath('/html/body/section');
|
||||
expect(element).toBeTruthy();
|
||||
const elements = await page.$x('/html/body/section');
|
||||
expect(elements[0]).toBeTruthy();
|
||||
expect(elements.length).toBe(1);
|
||||
});
|
||||
it('should return null for non-existing element', async({page, server}) => {
|
||||
const element = await page.xpath('/html/body/non-existing-element');
|
||||
expect(element).toBe(null);
|
||||
it('should return empty array for non-existing element', async({page, server}) => {
|
||||
const element = await page.$x('/html/body/non-existing-element');
|
||||
expect(element).toEqual([]);
|
||||
});
|
||||
it('should return multiple elements', async({page, sever}) => {
|
||||
await page.setContent('<div></div><div></div>');
|
||||
const elements = await page.$x('/html/body/div');
|
||||
expect(elements.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1930,22 +1936,22 @@ describe('Page', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('ElementHandle.xpath', function() {
|
||||
describe('ElementHandle.$x', function() {
|
||||
it('should query existing element', async({page, server}) => {
|
||||
await page.goto(server.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.xpath(`./body/div[contains(@class, 'second')]`);
|
||||
const inner = await second.xpath(`./div[contains(@class, 'inner')]`);
|
||||
const content = await page.evaluate(e => e.textContent, inner);
|
||||
const second = await html.$x(`./body/div[contains(@class, 'second')]`);
|
||||
const inner = await second[0].$x(`./div[contains(@class, 'inner')]`);
|
||||
const content = await page.evaluate(e => e.textContent, inner[0]);
|
||||
expect(content).toBe('A');
|
||||
});
|
||||
|
||||
it('should return null for non-existing element', async({page, server}) => {
|
||||
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.xpath(`/div[contains(@class, 'third')]`);
|
||||
expect(second).toBe(null);
|
||||
const second = await html.$x(`/div[contains(@class, 'third')]`);
|
||||
expect(second).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user