fix(scrollIntoView): ensure similar behavior across browsers, handle errors (#2599)

This commit is contained in:
Dmitry Gozman 2020-06-17 10:48:07 -07:00 committed by GitHub
parent 7ba72ce3d1
commit f9633ea9b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 2 deletions

View file

@ -6,7 +6,7 @@
}, },
{ {
"name": "firefox", "name": "firefox",
"revision": "1108" "revision": "1111"
}, },
{ {
"name": "webkit", "name": "webkit",

View file

@ -213,7 +213,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
} }
async scrollIntoViewIfNeeded() { async scrollIntoViewIfNeeded() {
throwIfNotConnected(await this._scrollRectIntoViewIfNeeded()); const result = await this._scrollRectIntoViewIfNeeded();
if (result === 'notvisible')
throw new Error('Element is not visible');
throwIfNotConnected(result);
} }
private async _clickablePoint(): Promise<types.Point | 'notvisible' | 'notinviewport'> { private async _clickablePoint(): Promise<types.Point | 'notvisible' | 'notinviewport'> {

View file

@ -418,6 +418,8 @@ export class FFPage implements PageDelegate {
}).then(() => 'done' as const).catch(e => { }).then(() => 'done' as const).catch(e => {
if (e instanceof Error && e.message.includes('Node is detached from document')) if (e instanceof Error && e.message.includes('Node is detached from document'))
return 'notconnected'; return 'notconnected';
if (e instanceof Error && e.message.includes('Node does not have a layout object'))
return 'notvisible';
throw e; throw e;
}); });
} }

View file

@ -323,6 +323,41 @@ describe('ElementHandle.scrollIntoViewIfNeeded', function() {
await page.evaluate(() => window.scrollTo(0, 0)); await page.evaluate(() => window.scrollTo(0, 0));
} }
}); });
it('should throw for detached element', async({page, server}) => {
await page.setContent('<div>Hello</div>');
const div = await page.$('div');
await div.evaluate(div => div.remove());
const error = await div.scrollIntoViewIfNeeded().catch(e => e);
expect(error.message).toContain('Element is not attached to the DOM');
});
it('should throw for display:none element', async({page, server}) => {
await page.setContent('<div style="display:none">Hello</div>');
const div = await page.$('div');
const error = await div.scrollIntoViewIfNeeded().catch(e => e);
expect(error.message).toContain('Element is not visible');
});
it('should throw for nested display:none element', async({page, server}) => {
await page.setContent('<span style="display:none"><div>Hello</div></span>');
const div = await page.$('div');
const error = await div.scrollIntoViewIfNeeded().catch(e => e);
expect(error.message).toContain('Element is not visible');
});
it('should throw for display:contents element', async({page, server}) => {
await page.setContent('<div style="display:contents">Hello</div>');
const div = await page.$('div');
const error = await div.scrollIntoViewIfNeeded().catch(e => e);
expect(error.message).toContain('Element is not visible');
});
it('should scroll a zero-sized element', async({page, server}) => {
await page.setContent('<br>');
const br = await page.$('br');
await br.scrollIntoViewIfNeeded();
});
it('should scroll a visibility:hidden element', async({page, server}) => {
await page.setContent('<div style="visibility:hidden">Hello</div>');
const div = await page.$('div');
await div.scrollIntoViewIfNeeded();
});
}); });
describe('ElementHandle.fill', function() { describe('ElementHandle.fill', function() {