feat(ElementHandle): add EH.boundingBox and EH.screenshot
This patch: - adds `ElementHandle.boundingBox()` method to get bounding box of element relative to the page - adds `ElementHandle.screenshot()` method to capture a screenshot of an element
This commit is contained in:
parent
515f2cd03d
commit
7e28dbafb5
18
docs/api.md
18
docs/api.md
@ -137,6 +137,7 @@
|
|||||||
+ [jsHandle.toString()](#jshandletostring)
|
+ [jsHandle.toString()](#jshandletostring)
|
||||||
* [class: ElementHandle](#class-elementhandle)
|
* [class: ElementHandle](#class-elementhandle)
|
||||||
+ [elementHandle.asElement()](#elementhandleaselement)
|
+ [elementHandle.asElement()](#elementhandleaselement)
|
||||||
|
+ [elementHandle.boundingBox()](#elementhandleboundingbox)
|
||||||
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
+ [elementHandle.click([options])](#elementhandleclickoptions)
|
||||||
+ [elementHandle.dispose()](#elementhandledispose)
|
+ [elementHandle.dispose()](#elementhandledispose)
|
||||||
+ [elementHandle.executionContext()](#elementhandleexecutioncontext)
|
+ [elementHandle.executionContext()](#elementhandleexecutioncontext)
|
||||||
@ -146,6 +147,7 @@
|
|||||||
+ [elementHandle.hover()](#elementhandlehover)
|
+ [elementHandle.hover()](#elementhandlehover)
|
||||||
+ [elementHandle.jsonValue()](#elementhandlejsonvalue)
|
+ [elementHandle.jsonValue()](#elementhandlejsonvalue)
|
||||||
+ [elementHandle.press(key[, options])](#elementhandlepresskey-options)
|
+ [elementHandle.press(key[, options])](#elementhandlepresskey-options)
|
||||||
|
+ [elementHandle.screenshot([options])](#elementhandlescreenshotoptions)
|
||||||
+ [elementHandle.tap()](#elementhandletap)
|
+ [elementHandle.tap()](#elementhandletap)
|
||||||
+ [elementHandle.toString()](#elementhandletostring)
|
+ [elementHandle.toString()](#elementhandletostring)
|
||||||
+ [elementHandle.type(text[, options])](#elementhandletypetext-options)
|
+ [elementHandle.type(text[, options])](#elementhandletypetext-options)
|
||||||
@ -1535,6 +1537,15 @@ ElementHandle instances can be used as arguments in [`page.$eval()`](#pageevalse
|
|||||||
#### elementHandle.asElement()
|
#### elementHandle.asElement()
|
||||||
- returns: <[ElementHandle]>
|
- returns: <[ElementHandle]>
|
||||||
|
|
||||||
|
#### elementHandle.boundingBox()
|
||||||
|
- returns: <[Object]>
|
||||||
|
- x <[number]> the x coordinate of the element in pixels.
|
||||||
|
- y <[number]> the y coordinate of the element in pixels.
|
||||||
|
- width <[number]> the width of the element in pixels.
|
||||||
|
- height <[number]> the height of the element in pixels.
|
||||||
|
|
||||||
|
This method returns the bounding box of the element (relative to the main frame), or `null` if element is detached from dom.
|
||||||
|
|
||||||
#### 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`.
|
||||||
@ -1603,6 +1614,13 @@ Returns a JSON representation of the object. The JSON is generated by running [`
|
|||||||
|
|
||||||
Focuses the element, and then uses [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
|
Focuses the element, and then uses [`keyboard.down`](#keyboarddownkey-options) and [`keyboard.up`](#keyboardupkey).
|
||||||
|
|
||||||
|
#### elementHandle.screenshot([options])
|
||||||
|
- `options` <[Object]> Same options as in [page.screenshot](#pagescreenshotoptions).
|
||||||
|
- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with captured screenshot.
|
||||||
|
|
||||||
|
This method scrolls element into view if needed, and then uses [page.screenshot](#pagescreenshotoptions) to take a screenshot of the element.
|
||||||
|
If the element is detached from DOM, the method throws an error.
|
||||||
|
|
||||||
#### elementHandle.tap()
|
#### elementHandle.tap()
|
||||||
- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.
|
- returns: <[Promise]> Promise which resolves when the element is successfully tapped. Promise gets rejected if the element is detached from DOM.
|
||||||
|
|
||||||
|
@ -40,25 +40,28 @@ class ElementHandle extends JSHandle {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async _scrollIntoViewIfNeeded() {
|
||||||
* @return {!Promise<{x: number, y: number}>}
|
|
||||||
*/
|
|
||||||
async _visibleCenter() {
|
|
||||||
const error = await this.executionContext().evaluate(element => {
|
const error = await this.executionContext().evaluate(element => {
|
||||||
if (!element.ownerDocument.contains(element))
|
if (!element.ownerDocument.contains(element))
|
||||||
return 'Node is detached from document';
|
return 'Node is detached from document';
|
||||||
if (element.nodeType !== Node.ELEMENT_NODE)
|
if (element.nodeType !== Node.ELEMENT_NODE)
|
||||||
return 'Node is not of type HTMLElement';
|
return 'Node is not of type HTMLElement';
|
||||||
element.scrollIntoViewIfNeeded();
|
element.scrollIntoViewIfNeeded();
|
||||||
|
return false;
|
||||||
}, this);
|
}, this);
|
||||||
if (error)
|
if (error)
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
const {model} = await this._client.send('DOM.getBoxModel', {
|
}
|
||||||
objectId: this._remoteObject.objectId
|
|
||||||
});
|
/**
|
||||||
|
* @return {!Promise<{x: number, y: number}>}
|
||||||
|
*/
|
||||||
|
async _visibleCenter() {
|
||||||
|
await this._scrollIntoViewIfNeeded();
|
||||||
|
const box = await this.boundingBox();
|
||||||
return {
|
return {
|
||||||
x: (model.content[0] + model.content[4]) / 2,
|
x: box.x + box.width / 2,
|
||||||
y: (model.content[1] + model.content[5]) / 2
|
y: box.y + box.height / 2
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +114,37 @@ class ElementHandle extends JSHandle {
|
|||||||
await this.focus();
|
await this.focus();
|
||||||
await this._page.keyboard.press(key, options);
|
await this._page.keyboard.press(key, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<Object>}
|
||||||
|
*/
|
||||||
|
async boundingBox() {
|
||||||
|
const boxModel = await this._client.send('DOM.getBoxModel', {
|
||||||
|
objectId: this._remoteObject.objectId
|
||||||
|
});
|
||||||
|
if (!boxModel || !boxModel.model)
|
||||||
|
return null;
|
||||||
|
return {
|
||||||
|
x: boxModel.model.margin[0],
|
||||||
|
y: boxModel.model.margin[1],
|
||||||
|
width: boxModel.model.width,
|
||||||
|
height: boxModel.model.height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {!Object=} options
|
||||||
|
* @returns {!Promise<Object>}
|
||||||
|
*/
|
||||||
|
async screenshot(options = {}) {
|
||||||
|
await this._scrollIntoViewIfNeeded();
|
||||||
|
const boundingBox = await this.boundingBox();
|
||||||
|
|
||||||
|
return await this._page.screenshot(Object.assign({}, {
|
||||||
|
clip: boundingBox
|
||||||
|
}, options));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ElementHandle;
|
module.exports = ElementHandle;
|
||||||
|
@ -29,8 +29,8 @@ class ExecutionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()|string} pageFunction
|
* @param {function(*)|string} pageFunction
|
||||||
* @param {!Array<*>} args
|
* @param {...*} args
|
||||||
* @return {!Promise<(!Object|undefined)>}
|
* @return {!Promise<(!Object|undefined)>}
|
||||||
*/
|
*/
|
||||||
async evaluate(pageFunction, ...args) {
|
async evaluate(pageFunction, ...args) {
|
||||||
@ -41,8 +41,8 @@ class ExecutionContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()|string} pageFunction
|
* @param {function(*)|string} pageFunction
|
||||||
* @param {!Array<*>} args
|
* @param {...*} args
|
||||||
* @return {!Promise<!JSHandle>}
|
* @return {!Promise<!JSHandle>}
|
||||||
*/
|
*/
|
||||||
async evaluateHandle(pageFunction, ...args) {
|
async evaluateHandle(pageFunction, ...args) {
|
||||||
|
BIN
test/golden/screenshot-element-bounding-box.png
Normal file
BIN
test/golden/screenshot-element-bounding-box.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 461 B |
29
test/test.js
29
test/test.js
@ -1373,6 +1373,24 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ElementHandle.boundingBox', function() {
|
||||||
|
it('should work', SX(async function() {
|
||||||
|
await page.setViewport({width: 500, height: 500});
|
||||||
|
await page.goto(PREFIX + '/grid.html');
|
||||||
|
const elementHandle = await page.$('.box:nth-of-type(13)');
|
||||||
|
const box = await elementHandle.boundingBox();
|
||||||
|
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 });
|
||||||
|
}));
|
||||||
|
it('should handle nested frames', SX(async function() {
|
||||||
|
await page.setViewport({width: 500, height: 500});
|
||||||
|
await page.goto(PREFIX + '/frames/nested-frames.html');
|
||||||
|
const nestedFrame = page.frames()[1].childFrames()[1];
|
||||||
|
const elementHandle = await nestedFrame.$('div');
|
||||||
|
const box = await elementHandle.boundingBox();
|
||||||
|
expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe('ElementHandle.click', function() {
|
describe('ElementHandle.click', function() {
|
||||||
it('should work', SX(async function() {
|
it('should work', SX(async function() {
|
||||||
await page.goto(PREFIX + '/input/button.html');
|
await page.goto(PREFIX + '/input/button.html');
|
||||||
@ -1406,6 +1424,17 @@ describe('Page', function() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ElementHandle.screenshot', function() {
|
||||||
|
it('should work', SX(async function() {
|
||||||
|
await page.setViewport({width: 500, height: 500});
|
||||||
|
await page.goto(PREFIX + '/grid.html');
|
||||||
|
await page.evaluate(() => window.scrollBy(50, 100));
|
||||||
|
const elementHandle = await page.$('.box:nth-of-type(3)');
|
||||||
|
const screenshot = await elementHandle.screenshot();
|
||||||
|
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
describe('input', function() {
|
describe('input', function() {
|
||||||
it('should click the button', SX(async function() {
|
it('should click the button', SX(async function() {
|
||||||
await page.goto(PREFIX + '/input/button.html');
|
await page.goto(PREFIX + '/input/button.html');
|
||||||
|
Loading…
Reference in New Issue
Block a user