6. Target Resolution Model
Permalink to 6. Target Resolution ModelThis section is normative.
6.1 Algorithm
Permalink to 6.1 AlgorithmTo resolve a target descriptor { role, name, scope? } to a Playwright locator
against a page P, optionally given a current iteration element E (set when
the descriptor appears inside a scope.for_each body,
§5.8):
- Let
Lbe undefined. - If
roleis"self", setLtoE. IfEis undefined the processor MUST raise an error. - Else if
roleis"id", setLtoP.locator('#' + cssIdent(name)), wherecssIdentis the CSS identifier escape defined by CSSOM (CSS.escape): an id such asmain:contentor1st-sectionis a valid HTML id and MUST reach the selector as one. - Else if
rolematches the patterndata-*, setLtoP.locator('[' + role + '="' + cssString(name) + '"]'), wherecssStringescapes backslash and double quote with a backslash and a newline as\a, so the value cannot end the string or inject selector syntax. - Else look up
rolein the role token table (§5.4).- If found and the entry's method is
getByRole, setLtoP.getByRole(entry.role, { name }). - If found and the method is
getByLabel, setLtoP.getByLabel(name). - If found and the method is
getByText, setLtoP.getByText(name).
- If found and the entry's method is
- Else (custom role), set
LtoP.getByRole(role, { name }). - If
scopeis present, recursively resolvescopeto a locatorS, and returnS.locator(L). Otherwise returnL.
Name matching. The authored name is matched in full by default: whitespace is normalised, and the name must equal the element's accessible name rather than appear within it. A processor MUST request exact matching explicitly rather than inheriting the automation engine's default.
contains opts into substring matching for one target:
locate: button contains "Sign"
verify: text contains "results found"
The default is the name because the name is the contract. Under a substring
default button "Sign" would match both "Sign In" and "Sign Up" — and on a page
carrying only one of them it would pass, so the document would read as an
assertion about a specific control and be satisfied by any control whose name
began with a fragment. verify: text "Error" would be satisfied by "No errors
found".
Ambiguity. When a target resolves to more than one element, the step MUST
fail with a message listing the candidates' accessible names, and the result
MUST carry failure_reason: "ambiguous_target" (§10.1). Two
controls sharing an accessible name are indistinguishable to anyone listing them
by name, so this is a finding about the page rather than the automation engine's
strict-mode error, and MUST NOT be reported as one.
6.2 Equivalence between modes
Permalink to 6.2 Equivalence between modesThe same algorithm MUST be used by:
- the live runner (
runUseCase) when computing locators for direct execution; and - the code generator when emitting the locator expression embedded in the
produced
.spec.tssource.
A conforming implementation SHOULD share a single resolution module across both
paths to avoid drift. (The reference implementation does so via
src/shared/locator.ts.)
6.3 select fallback
Permalink to 6.3 select fallbackThe select token resolves first to a combobox locator. At runtime, if the
combobox locator's count() is greater than zero, that locator is used.
Otherwise, the processor MUST fall back to getByLabel(name). A native
<select> has the implicit role combobox (or listbox when it is multiple
or sized), so the first branch finds most native selects and the fallback
catches the rest by label. The fallback does not make a custom ARIA combobox
driveable: selectOption() requires a native <select> whichever branch
located it (§5.6.4).
In codegen, the emitted code MUST embody the same fallback logic.
A scope on the target (§5.5) binds to both branches:
the combobox probe and the getByLabel fallback MUST each be resolved inside
the scope, in both execution modes. Resolving either against the page discards a
clause the document states — the step reads as scoped and asserts page-wide, and
on a page carrying two controls of that name the result is the engine's
strict-mode error rather than a finding.
6.4 Heading level
Permalink to 6.4 Heading levelWhen the role token is heading and a level N modifier is present, the
locator MUST be constructed with the level option:
getByRole('heading', { name, level: N }). This applies to both locate and
verify: heading.