[Bug]: getByLabel (and accessible name) ignore <label> for form-associated custom elements

Chiusa
#43,898 2 commenti 1 reazione 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Una pull request collegata è già stata integrata.

  • #43900 di @microsoft-playwright-automation — integrata

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
25/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Ferma
Stack tecnologico
html, playwright, typescript

Direzione di ricerca

Start in packages/injected/src/selectorUtils.ts and read getElementLabels, then trace how those labels feed getByLabel and accessible name computation. A linked pull request (#43900) is already open; completion would require both locators in the reproduction to resolve to the custom element and the related tests to pass.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

needs-triage
Version

1.64.1

Steps to reproduce
import { test, expect } from '@playwright/test';

test('label of a form-associated custom element', async ({ page }) => {
  await page.setContent(`
    <label for="city">City</label><my-field id="city">Pick one</my-field>
    <label>Street <my-field>Pick one</my-field></label>`);
  await page.addScriptTag({ content: `
    customElements.define('my-field', class extends HTMLElement {
      static formAssociated = true;
      constructor() { super(); this.attachInternals(); }
      connectedCallback() { this.tabIndex = 0; }
    });` });

  // The browser associates both labels with the custom elements:
  expect(await page.evaluate(() => [...document.querySelectorAll('label')].map((l) => l.control?.localName)))
    .toEqual(['my-field', 'my-field']);

  await expect(page.getByLabel('City')).toHaveCount(1);   // ❌ 0
  await expect(page.getByLabel('Street')).toHaveCount(1); // ❌ 0
});
Expected behavior

getByLabel('City') and getByLabel('Street') each resolve to the <my-field> element.

Form-associated custom elements are labelable elements. The browser associates the <label> with them, both for for= and for nesting: label.control returns the element, and Chrome's accessibility tree names it from the label (with role = 'combobox' set through ElementInternals, Chrome exposes combobox "City").

Actual behavior

Both locators resolve to 0 elements.

getElementLabels (packages/injected/src/selectorUtils.ts) reads element.labels only for a fixed list of native elements:

const isNonHiddenInput = element.nodeName === 'INPUT' && element.type !== 'hidden';
if (['BUTTON', 'METER', 'OUTPUT', 'PROGRESS', 'SELECT', 'TEXTAREA'].includes(element.nodeName) || isNonHiddenInput) {
  const labels = element.labels;
  ...

A form-associated custom element has no public .labels (only ElementInternals.labels, which is private to the element), so the element side can't be read. The label side can: HTMLLabelElement.control returns the custom element for both for= and nesting, so no new browser API is needed.

Additional context
  • Related, but different: #34264 (roles and states set through ElementInternals). That one needs access to the element's internals. This one doesn't: the association is readable today through label.control.
  • Possible fix: when the element is a form-associated custom element (customElements.get(el.localName)?.formAssociated), collect the <label> elements in its tree whose control === el, and use their text, excluding the text of the control nested inside a label (as is already done for native controls). The same labels should feed the accessible name computation (step 2D) used by getByRole({ name }).
  • It matters more and more: design systems build form controls as form-associated custom elements precisely so that <label for> works as with native controls.
Environment
Playwright 1.64.1, Chrome. The lookup happens in Playwright's injected script, so it shouldn't depend on the browser.
Lingua principale
TypeScript
Stelle
99.1k
Fork
6.8k
Merge medio
16h 20m
PR unite (30g)
324

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di microsoft/playwright

Tutte le issue di microsoft/playwright

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.