diff --git a/packages/playwright-core/src/server/injected/roleUtils.ts b/packages/playwright-core/src/server/injected/roleUtils.ts index 50564fa31f..25035b6998 100644 --- a/packages/playwright-core/src/server/injected/roleUtils.ts +++ b/packages/playwright-core/src/server/injected/roleUtils.ts @@ -383,7 +383,11 @@ export function getAriaLabelledByElements(element: Element): Element[] | null { const ref = element.getAttribute('aria-labelledby'); if (ref === null) return null; - return getIdRefs(element, ref); + const refs = getIdRefs(element, ref); + // step 2b: + // "if the current node has an aria-labelledby attribute that contains at least one valid IDREF" + // Therefore, if none of the refs match an element, we consider aria-labelledby to be missing. + return refs.length ? refs : null; } function allowsNameFromContent(role: string, targetDescendant: boolean) { diff --git a/tests/library/role-utils.spec.ts b/tests/library/role-utils.spec.ts index a02680ce86..2b5792d0f1 100644 --- a/tests/library/role-utils.spec.ts +++ b/tests/library/role-utils.spec.ts @@ -495,6 +495,16 @@ test('should not include hidden pseudo into accessible name', async ({ page }) = expect.soft(await getNameAndRole(page, 'a')).toEqual({ role: 'link', name: 'hello hello' }); }); +test('should ignore invalid aria-labelledby', async ({ page }) => { + await page.setContent(` + + `); + expect.soft(await getNameAndRole(page, 'input')).toEqual({ role: 'textbox', name: 'Text here' }); +}); + function toArray(x: any): any[] { return Array.isArray(x) ? x : [x]; }