Skip to main content
screen builds lazy queries. A Locator is a query expression, not a node: nothing resolves until an action, read, or assertion runs. Locator extends Screen, so every query method is also available on a locator and is scoped to its subtree.
An action, read, or assertion requires exactly one match; two matches fail immediately with LOCATOR_AMBIGUOUS. Reads do not retry for a value: they resolve once and read once, so use expect when you need to wait for a value to change.

Queries

Every query returns a Locator synchronously and performs no work.

Echoed text

getByText answers with the innermost matching node. A node whose match is also matched by one of its descendants is not returned, so a container that echoes its child’s text counts once. Browsers behave this way natively; on a device the engine applies the same rule, because iOS reports a React Native Text as a host view and a StaticText child carrying the same label, and a plain view inherits the labels of its children. Two matches that do not contain each other, such as the same text on two pages, remain LOCATOR_AMBIGUOUS.

Hidden duplicates

Every query accepts visible: true, which drops nodes the platform reports as hidden before the exactly-one rule runs. Frameworks routinely keep a hidden copy of content in the document: a prerendered segment that lingers after web.reload(), a closed drawer, an inactive tab panel. Without visible, both copies match and the query fails with LOCATOR_AMBIGUOUS even though only one is on screen.
The predicate is the one toBeVisible(), isVisible(), and waitFor() read, so a node that resolves with visible: true is one they accept. It composes with scopes, filter, first, last, and nth: getByText('Save', { visible: true }).first() is the first visible match, not the first match. Omitted or false keeps every node, so existing LOCATOR_AMBIGUOUS failures still fire. Prefer it over a platform selector such as web.locator('p:visible'), which ties the test to CSS and to one engine.

Refinement

Synchronous, returns a new locator, performs no work. Throws INVALID_LOCATOR when filter gets neither hasText nor has, when has is not a runner locator, or when nth gets a negative or non-integer index.
In the @e2edev/playwright engine a getByDisplayValue locator supports filter, first, last, nth, actions, and assertions like any other query. Two compositions stay unsupported and fail with UNSUPPORTED_CAPABILITY: using it as the scope of a child query (getByDisplayValue(v).getByRole(...)) and passing it as a has filter. Playwright has no selector for a control’s current value, so the engine reads the candidates and filters afterwards; those two shapes would need the value check inside Playwright’s own locator chain.

screen.swipe

Viewport-level gesture. direction is required, momentum optional. Uses config.actionTimeout. Throws ACTION_FAILED when the gesture is rejected.

screen.scrollUntilVisible

Throws INVALID_LOCATOR for a non-runner locator, LOCATOR_NOT_FOUND when the deadline expires first.

Locator actions

Each action resolves exactly one node, waits for actionability inside its deadline, and performs one operation. Default timeout config.actionTimeout, 30000 ms, overridable with { timeout }.
  • click is an alias of tap.
  • longPress duration is milliseconds, default 500, integer 100 through 10000. Out of range throws INVALID_ARGUMENT.
  • fill replaces the current content. A Secret is resolved on the host immediately before the call and never logged, reported, or sent to a model.
  • setInputFiles needs one or more non-empty paths, resolved from the project root. An empty list or blank entry throws INVALID_ARGUMENT.
  • dragTo resolves the target first. A non-runner target throws INVALID_LOCATOR.
  • An action that may already have committed surfaces as ACTION_FAILED rather than being repeated.

Locator reads

waitFor defaults state to 'visible' and timeout to config.actionTimeout, and throws LOCATOR_NOT_FOUND on timeout. textContent, inputValue, and getAttribute throw POLICY_DENIED on a node the platform reports as a secure field. value is never exposed on secure fields. isEnabled, isChecked, and boundingBox are allowed there because they expose no value.

Types

Role is closed; an unsupported role is a type error. String matching is exact by default. exact: false is case-insensitive substring matching. A RegExp ignores exact. A role query never matches a node hidden from the accessibility tree, on every engine. visible applies the same rule to the other query kinds, so getByText or getByLabel can drop a hidden twin too. See Hidden duplicates. SelectOption: a bare string is the option label. The object form names one of label, value (the option’s value attribute, what the form submits), or index, never two.

Writing tests

expect

app

Playwright

agent