From 870b14de945f63a02a98536c78c41b5211411b3c Mon Sep 17 00:00:00 2001 From: Yuriy Mikityuk Date: Sat, 26 Sep 2026 20:02:58 +0500 Subject: [PATCH] fix(webkit): hide content of a details nested in a closed details WebKit doesn't use checkVisibility(), so visibility falls back to a manual check for details. It only looked at the closest details or summary, so an open details inside a closed one was reported as visible together with everything in it. Check all ancestors instead. --- packages/injected/src/domUtils.ts | 9 ++++++--- tests/page/locator-is-visible.spec.ts | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/injected/src/domUtils.ts b/packages/injected/src/domUtils.ts index c88184506a424..4fa51ef387efb 100644 --- a/packages/injected/src/domUtils.ts +++ b/packages/injected/src/domUtils.ts @@ -108,9 +108,12 @@ function computeElementStyleVisibilityVisible(element: Element, style?: CSSStyle return false; } else { // Manual workaround for WebKit that does not have checkVisibility. - const detailsOrSummary = element.closest('details,summary'); - if (detailsOrSummary !== element && detailsOrSummary?.nodeName === 'DETAILS' && !(detailsOrSummary as HTMLDetailsElement).open) - return false; + // Check all ancestors: an open details inside a closed one is still hidden. + for (let child: Element | undefined = element; child; child = parentElementOrShadowHost(child)) { + const parent = parentElementOrShadowHost(child); + if (parent?.nodeName === 'DETAILS' && !(parent as HTMLDetailsElement).open && child.nodeName !== 'SUMMARY') + return false; + } } if (style.visibility !== 'visible') return false; diff --git a/tests/page/locator-is-visible.spec.ts b/tests/page/locator-is-visible.spec.ts index 4349f3b3d8625..1773e63437835 100644 --- a/tests/page/locator-is-visible.spec.ts +++ b/tests/page/locator-is-visible.spec.ts @@ -60,6 +60,21 @@ it('isVisible and isHidden should work with details', async ({ page }) => { await expect(page.locator('ul')).toBeHidden(); }); +it('isVisible and isHidden should work with nested details', async ({ page }) => { + await page.setContent(`
+ outer +
+ inner + +
+
`); + + await expect(page.locator('summary', { hasText: 'outer' })).toBeVisible(); + await expect(page.locator('summary', { hasText: 'inner' })).toBeHidden(); + await expect(page.locator('button')).toBeHidden(); + await expect(page.getByRole('button')).toHaveCount(0); +}); + it('isVisible inside a button', async ({ page }) => { await page.setContent(``); const span = page.locator('span');