Repository navigation
Description
Version
1.61.1 (also reproduced against main @ 4cd8608744bf4d18c9f23578c3cf4a1cd1862b2d, packages/injected/src/roleUtils.ts)
Steps to reproduce
Save as repro.js next to a node_modules with playwright 1.61.1 (chromium installed), run node repro.js:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
// list="nope" points to nothing: there is no <datalist>, so no suggestion source.
await page.setContent('<!DOCTYPE html><input type=search list=nope>');
console.log('combobox count =', await page.getByRole('combobox').count()); // 1 <-- BUG
console.log('searchbox count =', await page.getByRole('searchbox').count()); // 0 <-- BUG
// Same shape with list pointing to a <div> (not a <datalist>): same wrong result.
await page.setContent('<!DOCTYPE html><input type=search list=dv><div id=dv></div>');
console.log('combobox count =', await page.getByRole('combobox').count()); // 1 <-- BUG
console.log('searchbox count =', await page.getByRole('searchbox').count()); // 0 <-- BUG
// Control: a real <datalist>. Playwright and Chrome agree on combobox here.
await page.setContent('<!DOCTYPE html><input type=search list=dl><datalist id=dl></datalist>');
console.log('combobox count =', await page.getByRole('combobox').count()); // 1 ok
await browser.close();
})();Cross-check against the browser's own accessibility tree: open the first snippet in Chrome DevTools,
Elements, Accessibility pane. Chrome computes role searchbox for <input type=search list=nope>.
Per https://html.spec.whatwg.org/multipage/input.html#concept-input-list a list attribute that does
not point at a <datalist> denotes no suggestion source, and HTML-AAM then maps input[type=search]
without a list to searchbox.
Expected behavior
getByRole('searchbox') matches the element (and getByRole('combobox') does not) whenever the list
attribute does not resolve to a <datalist>, matching Chrome's computed role and HTML-AAM. Verified via
CDP Accessibility.getPartialAXTree on the same DOM node: Chrome 149.0.7827.55 reports role=searchbox
for list=nope, list pointing at a <div>, and an empty list attribute (3/3 broken-list cases),
and combobox only for a real <datalist>.
Actual behavior
Playwright computes combobox for all three broken-list cases: getByRole('combobox') matches (count 1)
and getByRole('searchbox') misses (count 0). expect(locator).toMatchAriaSnapshot and role assertions
disagree with what the browser's AX inspector and screen readers report.
Additional context
Root cause: packages/injected/src/roleUtils.ts:126 (INPUT implicit-role mapping):
if (type === 'search')
return e.hasAttribute('list') ? 'combobox' : 'searchbox';The search branch checks hasAttribute('list') and nothing else. The next branch (for
email/tel/text/url) validates that the IDREF resolves to an actual <datalist>:
if (['email', 'tel', 'text', 'url', ''].includes(type)) {
// https://html.spec.whatwg.org/multipage/input.html#concept-input-list
const list = getIdRefs(e, e.getAttribute('list'))[0];
return (list && elementSafeTagName(list) === 'DATALIST') ? 'combobox' : 'textbox';
}Suggested fix: give search the same treatment.
if (type === 'search') {
const list = getIdRefs(e, e.getAttribute('list'))[0];
return (list && elementSafeTagName(list) === 'DATALIST') ? 'combobox' : 'searchbox';
}Differential evidence (same DOM node, roleUtils.ts bundle @ 4cd8608 vs Chromium AX tree via CDP,
chromium 149.0.7827.55 / playwright 1.61.1): 3/3 broken-list cases diverge (PW combobox,
Chrome searchbox: nonexistent IDREF, IDREF to a <div>, empty list attribute); 5/5 controls agree
(real <datalist>, no list, and the analogous text/email inputs, which Playwright resolves
through the datalist check above). The search branch carries no SPEC DIFFERENCE comment, so the
divergence from browser behavior looks unintentional.
Environment
(paste your `npx envinfo --preset playwright` output here before submitting)
Evidence gathered with:
playwright 1.61.1 (npm), bundled Chromium 149.0.7827.55, headless
macOS (darwin arm64), Node 24.10.0
source cross-checked at microsoft/playwright @ 4cd8608744bf4d18c9f23578c3cf4a1cd1862b2d
Found via property-based & differential bug-hunting, part of an effort to scale PBT (DepTyCheck-based) testing across the OSS ecosystem.
If this is intended / by-design: I'm really sorry, please just close it — no need to flag or ban me. I'm trying to scale property-based testing across the whole ecosystem and my publishing agents may have gotten this one wrong. I read every issue and follow up on each.
Activity
fix(role): map input[type=search] to searchbox unless list is a datal…