diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts index d1e6538458..898573c7ea 100644 --- a/packages/playwright-core/src/server/injected/injectedScript.ts +++ b/packages/playwright-core/src/server/injected/injectedScript.ts @@ -437,10 +437,12 @@ export class InjectedScript { return { left: parseInt(style.borderLeftWidth || '', 10), top: parseInt(style.borderTopWidth || '', 10) }; } - retarget(node: Node, behavior: 'follow-label' | 'no-follow-label'): Element | null { + retarget(node: Node, behavior: 'none' | 'follow-label' | 'no-follow-label'): Element | null { let element = node.nodeType === Node.ELEMENT_NODE ? node as Element : node.parentElement; if (!element) return null; + if (behavior === 'none') + return element; if (!element.matches('input, textarea, select')) element = element.closest('button, [role=button], [role=checkbox], [role=radio]') || element; if (behavior === 'follow-label') { @@ -517,7 +519,7 @@ export class InjectedScript { } elementState(node: Node, state: ElementStateWithoutStable): boolean | 'error:notconnected' { - const element = this.retarget(node, ['stable', 'visible', 'hidden'].includes(state) ? 'no-follow-label' : 'follow-label'); + const element = this.retarget(node, ['stable', 'visible', 'hidden'].includes(state) ? 'none' : 'follow-label'); if (!element || !element.isConnected) { if (state === 'hidden') return true; diff --git a/tests/page/locator-convenience.spec.ts b/tests/page/locator-convenience.spec.ts index 540d9b163c..96d2250bd0 100644 --- a/tests/page/locator-convenience.spec.ts +++ b/tests/page/locator-convenience.spec.ts @@ -228,3 +228,29 @@ it('should return page', async ({ page, server }) => { const inFrame = page.frames()[1].locator('div'); expect(inFrame.page()).toBe(page); }); + +it('isVisible inside a button', async ({ page }) => { + await page.setContent(``); + const span = page.locator('span'); + expect(await span.isVisible()).toBe(false); + expect(await span.isHidden()).toBe(true); + expect(await page.isVisible('span')).toBe(false); + expect(await page.isHidden('span')).toBe(true); + await expect(span).not.toBeVisible(); + await expect(span).toBeHidden(); + await span.waitFor({ state: 'hidden' }); + await page.locator('button').waitFor({ state: 'visible' }); +}); + +it('isVisible inside a role=button', async ({ page }) => { + await page.setContent(`
a button
`); + const span = page.locator('span'); + expect(await span.isVisible()).toBe(false); + expect(await span.isHidden()).toBe(true); + expect(await page.isVisible('span')).toBe(false); + expect(await page.isHidden('span')).toBe(true); + await expect(span).not.toBeVisible(); + await expect(span).toBeHidden(); + await span.waitFor({ state: 'hidden' }); + await page.locator('[role=button]').waitFor({ state: 'visible' }); +});