Skip to navigation

find

The find action locates an element in the current interface based on its display text or a CSS/XPath selector. After finding the element, you can optionally interact with it, such as clicking it or typing into it.

If multiple elements match the specified criteria (elementText and/or selector), the step operates on the first matched element. Ensure your criteria uniquely identify the target element.

Specify the target element directly with a string for basic text or selector lookup. Use an object for more detailed options and interactions:

  • String Shorthand: Provide the display text or CSS/XPath selector directly as the value for the find key. If elements are found by both text and selector, the element found by matching text is used.
    • Example: "find": "Login" or "find": "#username".
  • Object Format: Use an object with the following properties:
    • elementText: (Optional) The display text of the element to find. If combined with selector, the element must match both. See How text matching works below.
    • selector: (Optional) The CSS selector or XPath selector of the element to find. If combined with elementText, the element must match both. At least one of elementText or selector is required.
    • timeout: (Optional) Maximum duration in milliseconds to wait for the element to exist (default: 5000).
    • moveTo: (Optional) Move the cursor to the element. An element that isn’t visible is scrolled into view. Defaults to false. A bare find leaves the viewport and cursor where they’re already sitting. Set this explicitly when a step should scroll, or when a recording needs the cursor to travel to the element.
    • click: (Optional) Click the element after finding it. Can be true (for a default left click), "left", "right", "middle", or an object like { "button": "right" } or { "duration": 800 } (a long-press / press-and-hold, in milliseconds).
    • type: (Optional) Type keys after finding the element. Requires the element to be made active first (for example, by using click: true). Accepts a string or an object like { "keys": "my text", "inputDelay": 100 }. See type for details.
    • surface: (Optional) The browser session, window, or tab the element lives in, such as { "browser": "chrome", "tab": "cart" } or a named browser { "browser": "firefox", "name": "admin" }. It can also be the native app surface the element lives in, such as { "app": "notepad" } or a specific app window { "app": "notepad", "window": { "title": "/Open/" } }. Omit it to act on the active surface, the most recently opened, focused, or explicitly targeted one, whether a browser tab or an app window. Specifying a surface switches the active surface for the steps that follow. On an app surface, elementText, elementId, and elementAria map to native controls. selector there accepts a native locator, either XPath or a ~-prefixed accessibility ID, instead of a CSS selector. See Test across multiple tabs, windows, and browsers, Target an app surface, and App windows below.

How text matching works

elementText (and the string shorthand’s text form) matches an element’s full visible text with whitespace normalized. Doc Detective trims leading and trailing whitespace and collapses internal runs of spaces, tabs, and newlines to a single space. Two consequences follow from this:

  • Framework-rendered text just works. Frameworks like React and Svelte often split an element’s text across several DOM text nodes, sometimes with an empty leading node. The match uses the element’s whole text. A heading like Garden companion API therefore matches even when it isn’t a single, tidy text node. The result stays the same on Chrome and Firefox no matter how each browser’s driver reports whitespace.
  • It’s a whole-element match, not a substring. elementText matches an element whose entire normalized text equals your string. A broad selector like body combined with elementText therefore won’t match, since the body’s text is the whole page. Target a tighter element, or match a substring with a regular expression by wrapping the value in slashes: "elementText": "/Garden companion API/".

App windows

On Windows and macOS app surfaces, window selects which of the app’s windows the step acts on. That includes dialogs and secondary document windows:

  • "window": { "title": "Create New Data Source" } selects by title; wrap the value in slashes for a regex ("/Data Source/").
  • "window": -1 selects the newest window (typically a dialog the app just opened).
  • A plain string or { "name": … } matches the window title exactly.
  • On macOS, a non-negative "window": 2 selects by position; on Windows, positional indexes aren’t supported (select by title or -1 instead).

Window selection is sticky. Later steps on the same app surface act in the selected window until another selector changes it. click, type, screenshot, record, swipe, and closeSurface all accept the same selector. Android and iOS app surfaces are single-window, so a window selector there fails with guidance to omit it.

On Windows, resolving a selector probes the app’s candidate windows to read their titles, which can briefly bring each probed window to the foreground. This matters for recordings: if a record is running, a probe can flash another window into a frame or two of the capture. Select the window before starting the recording (selection is sticky) to avoid it.

Mobile app surfaces auto-scroll. On an Android or iOS app surface, an element that isn’t on screen makes find scroll toward content further down the list before giving up. It takes up to 5 scroll steps, within timeout. That matches how browser find scrolls elements into view. click and element-targeted type locate elements the same way, so they inherit this. For a different gesture, sideways or a precise drag, use swipe.

Setting Variables: to capture a found element’s attributes into variables for later steps, use the step-level variables object. You can assign values based on the element using expressions like $$element.text.

Outputs: the action also exposes outputs.found, a boolean indicating whether an element matching your criteria was located. To capture it for later steps, use the step-level variables object with the expression $$found.

For comprehensive options, see the find reference.

Examples

Here are a few ways you might use the find action:

Find an element by its text (string shorthand)

{
"tests": [
{
"steps": [
{
"description": "Find the login button by its text.",
"find": "Login"
}
]
}
]
}

Find an element by its selector (string shorthand)

{
"tests": [
{
"steps": [
{
"description": "Find the username field by its ID (CSS selector).",
"find": "#username"
}
]
}
]
}

Find an element by selector and click it (object format)

{
"tests": [
{
"steps": [
{
"description": "Find the submit button by selector and click it.",
"find": {
"selector": "button[type='submit']",
"click": true
}
}
]
}
]
}

Find an element by selector, click it, and type into it (object format)

{
"tests": [
{
"steps": [
{
"description": "Find the search input by selector, click, and type.",
"find": {
"selector": "#input",
"click": true,
"type": "find action"
}
}
]
}
]
}

Find an element combining selector and text with a timeout

{
"tests": [
{
"steps": [
{
"description": "Find a specific link within a nav bar, waiting up to 10 seconds.",
"find": {
"selector": "nav > ul > li > a",
"elementText": "Downloads",
"timeout": 10000
}
}
]
}
]
}

Find an element and right-click it

{
"tests": [
{
"steps": [
{
"description": "Find an image by selector and right-click it.",
"find": {
"selector": "img.product-image",
"click": "right"
}
}
]
}
]
}

Find an element and middle-click it

{
"tests": [
{
"steps": [
{
"description": "Find a button by text and middle-click it.",
"find": {
"elementText": "Open New Tab",
"click": "middle"
}
}
]
}
]
}