mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
fix(Page.click): throw a meaningful message for invisible elements (#1309)
This patch starts throwing a meaningful error message when trying to click the hidden node. References #1294
This commit is contained in:
parent
3cb0f1af34
commit
b58d319926
@ -1786,7 +1786,7 @@ The method runs `element.querySelectorAll` within the page. If no elements match
|
|||||||
- width <[number]> the width of the element in pixels.
|
- width <[number]> the width of the element in pixels.
|
||||||
- height <[number]> the height 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.
|
This method returns the bounding box of the element (relative to the main frame), or `null` if the element is not visible.
|
||||||
|
|
||||||
#### elementHandle.click([options])
|
#### elementHandle.click([options])
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const {JSHandle} = require('./ExecutionContext');
|
const {JSHandle} = require('./ExecutionContext');
|
||||||
const {helper} = require('./helper');
|
const {helper, debugError} = require('./helper');
|
||||||
|
|
||||||
class ElementHandle extends JSHandle {
|
class ElementHandle extends JSHandle {
|
||||||
/**
|
/**
|
||||||
@ -59,6 +59,8 @@ class ElementHandle extends JSHandle {
|
|||||||
async _visibleCenter() {
|
async _visibleCenter() {
|
||||||
await this._scrollIntoViewIfNeeded();
|
await this._scrollIntoViewIfNeeded();
|
||||||
const box = await this.boundingBox();
|
const box = await this.boundingBox();
|
||||||
|
if (!box)
|
||||||
|
throw new Error('Node is not visible');
|
||||||
return {
|
return {
|
||||||
x: box.x + box.width / 2,
|
x: box.x + box.width / 2,
|
||||||
y: box.y + box.height / 2
|
y: box.y + box.height / 2
|
||||||
@ -116,16 +118,17 @@ class ElementHandle extends JSHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {!Promise<{x: number, y: number, width: number, height: number}>}
|
* @return {!Promise<?{x: number, y: number, width: number, height: number}>}
|
||||||
*/
|
*/
|
||||||
async boundingBox() {
|
async boundingBox() {
|
||||||
const {model} = await this._client.send('DOM.getBoxModel', {
|
const result = await this._client.send('DOM.getBoxModel', {
|
||||||
objectId: this._remoteObject.objectId
|
objectId: this._remoteObject.objectId
|
||||||
});
|
}).catch(error => void debugError(error));
|
||||||
if (!model)
|
|
||||||
throw new Error('Node is detached from document');
|
|
||||||
|
|
||||||
const quad = model.border;
|
if (!result)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const quad = result.model.border;
|
||||||
const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
||||||
const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
||||||
const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
|
const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
|
||||||
@ -142,6 +145,8 @@ class ElementHandle extends JSHandle {
|
|||||||
async screenshot(options = {}) {
|
async screenshot(options = {}) {
|
||||||
await this._scrollIntoViewIfNeeded();
|
await this._scrollIntoViewIfNeeded();
|
||||||
const boundingBox = await this.boundingBox();
|
const boundingBox = await this.boundingBox();
|
||||||
|
if (!boundingBox)
|
||||||
|
throw new Error('Node is not visible');
|
||||||
|
|
||||||
return await this._page.screenshot(Object.assign({}, {
|
return await this._page.screenshot(Object.assign({}, {
|
||||||
clip: boundingBox
|
clip: boundingBox
|
||||||
|
33
test/test.js
33
test/test.js
@ -1688,6 +1688,11 @@ describe('Page', function() {
|
|||||||
const box = await elementHandle.boundingBox();
|
const box = await elementHandle.boundingBox();
|
||||||
expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
|
expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
|
||||||
}));
|
}));
|
||||||
|
it('should return null for invisible elements', SX(async function() {
|
||||||
|
await page.setContent('<div style="display:none">hi</div>');
|
||||||
|
const element = await page.$('div');
|
||||||
|
expect(await element.boundingBox()).toBe(null);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ElementHandle.click', function() {
|
describe('ElementHandle.click', function() {
|
||||||
@ -1718,6 +1723,26 @@ describe('Page', function() {
|
|||||||
await button.click().catch(err => error = err);
|
await button.click().catch(err => error = err);
|
||||||
expect(error.message).toBe('Node is detached from document');
|
expect(error.message).toBe('Node is detached from document');
|
||||||
}));
|
}));
|
||||||
|
it('should throw for hidden nodes', SX(async function() {
|
||||||
|
await page.goto(PREFIX + '/input/button.html');
|
||||||
|
const button = await page.$('button');
|
||||||
|
await page.evaluate(button => button.style.display = 'none', button);
|
||||||
|
const error = await button.click().catch(err => err);
|
||||||
|
expect(error.message).toBe('Node is not visible');
|
||||||
|
}));
|
||||||
|
it('should throw for recursively hidden nodes', SX(async function() {
|
||||||
|
await page.goto(PREFIX + '/input/button.html');
|
||||||
|
const button = await page.$('button');
|
||||||
|
await page.evaluate(button => button.parentElement.style.display = 'none', button);
|
||||||
|
const error = await button.click().catch(err => err);
|
||||||
|
expect(error.message).toBe('Node is not visible');
|
||||||
|
}));
|
||||||
|
it('should throw for <br> elements', SX(async function() {
|
||||||
|
await page.setContent('hello<br>goodbye');
|
||||||
|
const br = await page.$('br');
|
||||||
|
const error = await br.click().catch(err => err);
|
||||||
|
expect(error.message).toBe('Node is not visible');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ElementHandle.hover', function() {
|
describe('ElementHandle.hover', function() {
|
||||||
@ -2583,6 +2608,14 @@ describe('Page', function() {
|
|||||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Safari');
|
expect(await page.evaluate(() => navigator.userAgent)).toContain('Safari');
|
||||||
}));
|
}));
|
||||||
|
it('should support clicking', SX(async function() {
|
||||||
|
await page.emulate(iPhone);
|
||||||
|
await page.goto(PREFIX + '/input/button.html');
|
||||||
|
const button = await page.$('button');
|
||||||
|
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||||
|
await button.click();
|
||||||
|
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.emulateMedia', function() {
|
describe('Page.emulateMedia', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user