fix(selectors): properly determine visibility of display:contents (#11212)

This commit is contained in:
Dmitry Gozman 2022-01-05 16:54:15 -08:00 committed by GitHub
parent 3ecac56cc0
commit be896848bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 2 deletions

View File

@ -660,10 +660,28 @@ export function isVisible(element: Element): boolean {
const style = element.ownerDocument.defaultView.getComputedStyle(element);
if (!style || style.visibility === 'hidden')
return false;
if (style.display === 'contents') {
// display:contents is not rendered itself, but its child nodes are.
for (let child = element.firstChild; child; child = child.nextSibling) {
if (child.nodeType === 1 /* Node.ELEMENT_NODE */ && isVisible(child as Element))
return true;
if (child.nodeType === 3 /* Node.TEXT_NODE */ && isVisibleTextNode(child as Text))
return true;
}
return false;
}
const rect = element.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
function isVisibleTextNode(node: Text) {
// https://stackoverflow.com/questions/1461059/is-there-an-equivalent-to-getboundingclientrect-for-text-nodes
const range = document.createRange();
range.selectNode(node);
const rect = range.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
function sortInDOMOrder(elements: Element[]): Element[] {
type SortEntry = { children: Element[], taken: boolean };

View File

@ -306,8 +306,7 @@ it('data-testid on the handle should be relative', async ({ page }) => {
expect(await page.$eval(`div >> data-testid=find-me`, e => e.id)).toBe('target2');
});
it('should consider display:contents elements visible', async ({ page }) => {
it.fixme(true, 'Nested <p> returns empty client rect');
it('should properly determine visibility of display:contents elements', async ({ page }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/11202' });
await page.setContent(`
@ -315,4 +314,28 @@ it('should consider display:contents elements visible', async ({ page }) => {
<p style="display:contents">DISPLAY CONTENTS</p>
</div>`);
await page.waitForSelector('"DISPLAY CONTENTS"');
await page.setContent(`
<div>
<article style="display:contents"><div>DISPLAY CONTENTS</div></article>
</div>`);
await page.waitForSelector('article');
await page.setContent(`
<div>
<article style="display:contents"><div style="display:contents">DISPLAY CONTENTS</div></article>
</div>`);
await page.waitForSelector('article');
await page.setContent(`
<div>
<article style="display:contents"><div></div>DISPLAY CONTENTS<span></span></article>
</div>`);
await page.waitForSelector('article');
await page.setContent(`
<div>
<article style="display:contents"><div></div></article>
</div>`);
await page.waitForSelector('article', { state: 'hidden' });
});