feat(selectors): replace pierceFrames() with frameLocator() - #42395
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Hi, I'm the Playwright bot and I took a first look at the CI failures here. 🟡 One failure, can't pin it on this PR either way
DetailsThis PR is a selector-engine refactor ( Uncertain
Not triaged
Triaged by the Playwright bot - agent run |
This comment has been minimized.
This comment has been minimized.
9fd1ca2 to
20ca261
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
page.anyFrame() and frame.anyFrame() start the match in any frame of the subtree, and the rest of the selector resolves inside that single frame, just like a regular locator. This drops the mid-chain frame piercing that pierceFrames() did, together with the restrictions it required: composite locators, *-capture and mid-chain nth are all allowed now. Also removes the pierceFrames context and test option. Codegen prefers an anyFrame() prefix whenever it saves at least two frameLocator() calls and still pinpoints the target frame.
A nested "any-frame" token used to reach the injected control engine and
match nothing, so page.locator('button').or(page.anyFrame().locator('a'))
silently dropped the second operand.
The token applies to the whole locator, so an operand never carries one:
page.anyFrame().locator('.classname').or(page.getByRole('button'))
matches ".classname or button" inside every frame. Skip a leading
"any-frame" when matching the nested frame prefix, so that an operand may
still repeat the frame chain, and reject any leftover token.
parseSelector() strips the frame prefix a nested selector shares with the
outer one, but left "capture" pointing at the old part index, so
frameLocator('#f').locator('body')
.locator(frameLocator('#f').locator('*css=section >> span'))
resolved to the span instead of the section. Shift the capture along with
the removed parts, and reject a capture that falls inside the prefix.
Calling page.frameLocator() or frame.frameLocator() without a selector now starts the match in any frame of the subtree, replacing anyFrame(). The selector format and the behavior are unchanged - only the API shape. Locator descriptions and codegen render the token as frameLocator() in all languages, which also makes it round-trip through the locator parser, unlike the bare any_frame/AnyFrame property forms.
e69eda3 to
796831d
Compare
Test results for "MCP"1 failed 8258 passed, 1361 skipped Merge workflow run. |
Test results for "tests 1"6 flaky51218 passed, 1237 skipped Merge workflow run. |
|
Hi, I'm the Playwright bot and I took a look at the CI failures here. 🟢 The one failure is a known flake — this PR is clear
DetailsThis PR replaces Pre-existing flake / infra
Not triaged
Triaged by the Playwright bot - agent run |
6748802
into
microsoft:main
Summary
page.frameLocator()/frame.frameLocator()without a selector replacepierceFrames(): they pick the frame the match starts in, and the rest of the selector resolves inside that single frame like a regular locator.*-capture and mid-chainnthnow work — and replaces the recursive piercing resolver with a per-frame run of the existing chained resolution.pierceFramescontext option and the corresponding test option, along with theno-pierce-framesopt-out token.frameLocator()prefix whenever it saves at least twoframeLocator(selector)calls and still pinpoints the target frame, falling back to the full chain otherwise.