mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
$ and $$ for querySelector and querySelectorAll
This commit is contained in:
parent
a0eeb415f2
commit
4416ad1d07
17
docs/api.md
17
docs/api.md
@ -11,6 +11,8 @@
|
||||
* [browser.newPage()](#browsernewpage)
|
||||
* [browser.version()](#browserversion)
|
||||
- [class: Page](#class-page)
|
||||
* [page.$(querySelector, fun, args)](#pagequeryselector-fun-args)
|
||||
* [page.$$(querySelector, fun, args)](#pagequeryselector-fun-args-1)
|
||||
* [page.addScriptTag(url)](#pageaddscripttagurl)
|
||||
* [page.click()](#pageclick)
|
||||
* [page.close()](#pageclose)
|
||||
@ -120,6 +122,20 @@ browser.newPage().then(page => {
|
||||
```
|
||||
Pages could be closed by `page.close()` method.
|
||||
|
||||
#### page.$(querySelector, fun, args)
|
||||
|
||||
- `querySelector` <[string]> Query selector to be run on the page
|
||||
- `fun` <[function<[Element]>]> Function to be evaluated with first element matching `querySelector`
|
||||
- `args` <[Array]<[string]>> Arguments to pass to `fun`
|
||||
- returns: <[Promise<[Object]]> Promise which resolves to function return value.
|
||||
|
||||
#### page.$$(querySelector, fun, args)
|
||||
|
||||
- `querySelector` <[string]> Query selector to be run on the page
|
||||
- `fun` <[function<[Element]>]> Function to be evaluted for every element matching `querySelector`.
|
||||
- `args` <[Array]<[string]>> Arguments to pass to `fun`
|
||||
- returns: <[Promise<[Array<[Object]>]>]> Promise which resolves to array of function return values.
|
||||
|
||||
#### page.addScriptTag(url)
|
||||
|
||||
- `url` <[string]> Url of a script to be added
|
||||
@ -306,3 +322,4 @@ Pages could be closed by `page.close()` method.
|
||||
[Page]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page "Page"
|
||||
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
|
||||
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "String"
|
||||
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
|
||||
|
@ -174,31 +174,11 @@ class FrameManager extends EventEmitter {
|
||||
let message = await helper.getExceptionMessage(this._client, exceptionDetails);
|
||||
throw new Error('Evaluation failed: ' + message);
|
||||
}
|
||||
if (remoteObject.unserializableValue) {
|
||||
switch (remoteObject.unserializableValue) {
|
||||
case '-0':
|
||||
return -0;
|
||||
case 'NaN':
|
||||
return NaN;
|
||||
case 'Infinity':
|
||||
return Infinity;
|
||||
case '-Infinity':
|
||||
return -Infinity;
|
||||
default:
|
||||
throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue);
|
||||
}
|
||||
}
|
||||
if (!remoteObject.objectId)
|
||||
return remoteObject.value;
|
||||
let response = await this._client.send('Runtime.callFunctionOn', {
|
||||
objectId: remoteObject.objectId,
|
||||
functionDeclaration: 'function() { return this; }',
|
||||
returnByValue: true,
|
||||
});
|
||||
return response.result.value;
|
||||
return helper.serializeRemoteObjet(this._client, remoteObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** @enum {string} */
|
||||
FrameManager.Events = {
|
||||
FrameAttached: 'frameattached',
|
||||
|
58
lib/Page.js
58
lib/Page.js
@ -499,6 +499,17 @@ class Page extends EventEmitter {
|
||||
})).nodeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} selector
|
||||
* @param {!Promise<!Array<number>>}
|
||||
*/
|
||||
async _querySelectorAll(selector) {
|
||||
return (await this._client.send('DOM.querySelectorAll', {
|
||||
nodeId: await this._rootNodeId(),
|
||||
selector
|
||||
})).nodeIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} selector
|
||||
* @param {!Promise}
|
||||
@ -561,6 +572,53 @@ class Page extends EventEmitter {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} nodeId
|
||||
* @param {function(!Element):T} fun
|
||||
* @param {!Array<*>} args
|
||||
* @return {!Promise<T>}
|
||||
*/
|
||||
async _callFunctionOnNode(nodeId, fun, ...args) {
|
||||
let { objectId } = (await this._client.send('DOM.resolveNode', { nodeId })).object;
|
||||
let argsString = ['this'].concat(args.map(x => JSON.stringify(x))).join(',');
|
||||
let {result, exceptionDetails} = await this._client.send('Runtime.callFunctionOn', {
|
||||
objectId,
|
||||
functionDeclaration: `function(){return (${fun})(${argsString})}`,
|
||||
returnByValue: false,
|
||||
});
|
||||
this._client.send('Runtime.releaseObject', {objectId});
|
||||
if (exceptionDetails) {
|
||||
let message = await helper.getExceptionMessage(this._client, exceptionDetails);
|
||||
throw new Error('Evaluation failed: ' + message);
|
||||
}
|
||||
let value = helper.serializeRemoteObjet(this._client, result);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} querySelector
|
||||
* @param {function(!Element):T} fun
|
||||
* @param {!Array<*>} args
|
||||
* @return {!Promise<?T>}
|
||||
*/
|
||||
async $(querySelector, fun, ...args) {
|
||||
let nodeId = await this._querySelector(querySelector);
|
||||
if (!nodeId)
|
||||
return null;
|
||||
return this._callFunctionOnNode(await this._querySelector(querySelector), fun, ...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} querySelector
|
||||
* @param {function(!Element):T} fun
|
||||
* @param {!Array<*>} args
|
||||
* @return {!Promise<!Array<T>>}
|
||||
*/
|
||||
async $$(querySelector, fun, ...args) {
|
||||
let nodeIds = await this._querySelectorAll(querySelector);
|
||||
return Promise.all(nodeIds.map((nodeId, index) => this._callFunctionOnNode(nodeId, fun, index, ...args)));
|
||||
}
|
||||
}
|
||||
|
||||
/** @enum {string} */
|
||||
|
@ -52,6 +52,36 @@ class Helper {
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Connection} client
|
||||
* @param {!Object} remoteObject
|
||||
*/
|
||||
static async serializeRemoteObjet(client, remoteObject) {
|
||||
if (remoteObject.unserializableValue) {
|
||||
switch (remoteObject.unserializableValue) {
|
||||
case '-0':
|
||||
return -0;
|
||||
case 'NaN':
|
||||
return NaN;
|
||||
case 'Infinity':
|
||||
return Infinity;
|
||||
case '-Infinity':
|
||||
return -Infinity;
|
||||
default:
|
||||
throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue);
|
||||
}
|
||||
}
|
||||
if (!remoteObject.objectId)
|
||||
return remoteObject.value;
|
||||
let response = client.send('Runtime.callFunctionOn', {
|
||||
objectId: remoteObject.objectId,
|
||||
functionDeclaration: 'function() { return this; }',
|
||||
returnByValue: true,
|
||||
});
|
||||
client.send('Runtime.releaseObject', {objectId: remoteObject.objectId});
|
||||
return (await response).result.value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Helper;
|
||||
|
15
test/assets/playground.html
Normal file
15
test/assets/playground.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Playground</title>
|
||||
</head>
|
||||
<body>
|
||||
<button>A button</button>
|
||||
<textarea>A text area</textarea>
|
||||
<div id="first">First div</div>
|
||||
<div id="second">
|
||||
Second div
|
||||
<span class="inner">Inner span</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
27
test/test.js
27
test/test.js
@ -633,6 +633,33 @@ describe('Puppeteer', function() {
|
||||
]);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Query selector', function() {
|
||||
it('Page.$', SX(async function() {
|
||||
await page.navigate(STATIC_PREFIX + '/playground.html');
|
||||
expect(await page.$('#first', element => element.textContent)).toBe('First div');
|
||||
expect(await page.$('#second span', element => element.textContent)).toBe('Inner span');
|
||||
expect(await page.$('#first', (element, arg1) => arg1, 'value1')).toBe('value1');
|
||||
expect(await page.$('#first', (element, arg1, arg2) => arg2, 'value1', 'value2')).toBe('value2');
|
||||
expect(await page.$('doesnot-exist', element => 5)).toBe(null);
|
||||
expect(await page.$('button', function(element, arg1) {
|
||||
element.textContent = arg1;
|
||||
return true;
|
||||
}, 'new button text')).toBe(true);
|
||||
expect(await page.$('button', function(element) {
|
||||
return element.textContent;
|
||||
})).toBe('new button text');
|
||||
}));
|
||||
it('Page.$$', SX(async function() {
|
||||
await page.navigate(STATIC_PREFIX + '/playground.html');
|
||||
expect((await page.$$('div', element => element.textContent)).length).toBe(2);
|
||||
expect((await page.$$('div', (element, index) => index))[0]).toBe(0);
|
||||
expect((await page.$$('div', (element, index) => index))[1]).toBe(1);
|
||||
expect((await page.$$('doesnotexist', function(){})).length).toBe(0);
|
||||
expect((await page.$$('div', element => element.textContent))[0]).toBe('First div');
|
||||
expect((await page.$$('span', (element, index, arg1) => arg1, 'value1'))[0]).toBe('value1');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// Since Jasmine doesn't like async functions, they should be wrapped
|
||||
|
Loading…
Reference in New Issue
Block a user