fix(web): locators resolve inside closed shadow roots the reader observes
Merged
OK
1mo ago+1826+1826+0123456789012345678901234567890123456789-1078-1078-0123456789012345678901234567890123456789
The observation has pierced closed shadow roots for a while, so a failure printed nodes no screen query could reach.
- An
e2e-roots=selector engine hands Playwright's own role, text, label, and test-id engines every closed root the page attached under the query root, so each element matches exactly once and the matching rules inside a root are the ones outside it. A page without closed roots pays nothing: the init script counts attached roots and the engine returns the query root alone at zero. - The six query kinds declare their candidates in one table in
locators.ts; the exact-label path is no longer a second projection. read-node.tsis 153 lines (from 1188): the closed-root scripts live inclosed-shadow.ts, the in-page reader inin-page/read-semantics.ts.- Limits, documented:
web.locator(css),frameLocator, andfilter({ hasText })keep Playwright's reach and stop at a closed root;hascrosses it. Matches outside closed roots come first, then each root's; a test pins that order against the reader's. - Tests:
closed-shadow-locators.test.ts(every query kind inside a root, nesting both ways, the visible flag, index, scope,has), the flipped lifecycle assertion, the engine source unit tests. Theshadow-dom-formtwin lands here, and the agentic scenario loses a gap that was already stale. Changeset@e2edev/webpatch. - Two reads the reader stopped at the document, from review: an
aria-labelledbyid now resolves in the control's own tree root, so an exactgetByLabelfinds a labelled-by control inside an open or closed root and the observed tree names it; focus follows each root's active element through open roots and the recorded closed ones, sotoBeFocusedholds for a control inside a shadow root. Covered on/closed-formand in the reader names test with a same-id decoy in the light DOM.