Locator¶
The locator finds a page element again after the page has changed. It lives in packages/locator and uses only the DOM, no extension API.
What is stored¶
A Locator holds frame (a list of iframe selectors), shadowPath (a list of shadow host selectors), an ordered list of strategies, and a fingerprint. See the content schema reference for every field.
- Strategies, tried in stored order. Kinds:
testid,id,aria,label,text,css,xpath. - Fingerprint: tag, role, short element text (up to 80 characters), up to 8 attributes, up to three ancestor headings, the sibling index and a rectangle with the viewport size. Form field values and select options are left out.
Resolution order¶
flowchart TB
a["walkToRoot<br/>frames, then shadow"] -->|"fail"| f["Failure reason"]
a --> b["Try each strategy<br/>in stored order"]
b -->|"one visible match,<br/>score ok"| ok["Hit"]
b -->|"none accepted"| c["Fuzzy search<br/>nearest scope"]
c -->|"no result"| d["Fuzzy search<br/>whole root"]
c --> ok2["Fuzzy hit"]
d --> ok2
d -->|"none"| f
style ok fill:#e6f4ea,stroke:#1e7e34
style ok2 fill:#e6f4ea,stroke:#1e7e34
style f fill:#fde8e8,stroke:#b42318
| Node | Meaning |
|---|---|
walkToRoot |
Follows frame into iframes, then shadowPath through open shadow roots. Can fail with a reason below |
| Try each strategy | A strategy counts only when it finds exactly one visible element. The candidate is then scored against the stored fingerprint |
| Fuzzy search | Scores every visible element with the same tag or role. Run first inside the closest unique ancestor taken from the css strategy, then over the whole root |
| Failure reason | See the table below |
Scoring and thresholds¶
Fingerprint weights: tag and role 0.2, text 0.3, ancestors 0.2, attributes 0.15, position 0.15.
| Constant | Value | Use |
|---|---|---|
HIT_THRESHOLD |
0.6 | Minimum score for a non-positional strategy to be accepted |
FUZZY_THRESHOLD |
0.75 | Minimum score for a fuzzy match. Also the bar for xpath hits, because a positional path matches whatever now sits there |
FUZZY_MARGIN |
0.15 | The best fuzzy candidate must beat the runner-up by this much, otherwise the result is ambiguous |
An xpath hit is also rejected when every stored ancestor heading has changed.
Failure reasons¶
| Reason | Cause |
|---|---|
not-found |
Nothing matched |
low-confidence |
Something matched, below the bar |
ambiguous |
Two fuzzy candidates scored within the margin |
cross-origin-frame |
A frame in the path has no readable document. The player uses the frame agent instead. Not retried |
frame-not-found |
The selector in frame matched no iframe |
shadow-host-not-found |
A host selector in shadowPath matched nothing |
shadow-root-closed |
The shadow root is closed and cannot be entered |
resolveLocator can wait. With waitMs set it re-checks after DOM mutations (debounced 50 ms) and on a poll (250 ms by default) until the deadline.
Gotchas¶
- Repeated elements with no distinguishing label (a row of identical icon buttons) score close to each other and resolve as
ambiguous. Anchor on something with a distinguishing label or ancestor heading. - Visibility is part of matching. A target hidden by a scroll container or sticky header is treated as not renderable.
- Weights and thresholds are constants in the code, not settings.