Waiting & Finding Actions
Actions for waiting for elements to appear, pages to load, or network activity to finish.
waitForElement
Wait for an element to appear in the DOM. Keeps checking until found or the timeout runs out.
Value: string (selector)
<!-- appears after the page fetches data -->
<div class="results">...</div>
[
{ "waitForElement": ".results" }
]
Wait until visible on screen:
[
{
"waitForElement": ".modal",
"options": { "visible": true, "timeout": 5000 }
}
]
Save for later use:
[
{ "waitForElement": ".result-card", "options": { "save": "card" } },
{ "click": "$card" }
]
Options
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 10000 | How long to wait (ms) |
visible | boolean | "all" | false | false = exists anywhere in DOM, true = must be visible on screen, "all" = accept any visibility state (hidden or visible) |
save | string | — | Save the found element for later |
iframe | string | string[] | — | Look inside an iframe |
find
Shorthand for waitForElement. Works exactly the same way.
Value: string (selector)
[
{ "find": ".search-results" }
]
With timeout and save:
[
{
"find": "#dynamic-content",
"options": { "timeout": 15000, "save": "content" }
}
]
findByText
Find the most specific element that contains certain text. Case-insensitive.
Value: string (text to search for)
<div class="actions">
<button>Cancel</button>
<button>Submit Order</button>
</div>
[
{ "findByText": "Submit Order" }
]
Partial text match:
[
{ "findByText": "Submit", "options": { "selector": "button", "exact": false } }
]
Save for later use:
[
{ "findByText": "Sign In", "options": { "selector": "button", "save": "signInBtn" } },
{ "click": "$signInBtn" }
]
Options
| Option | Type | Default | Description |
|---|---|---|---|
selector | string | "*" | Only search within elements matching this selector |
exact | boolean | false | true = full text match, false = contains |
save | string | — | Save the element for later |
waitFor | boolean | true | Wait for the element to appear |
timeout | number | 10000 | How long to wait (ms) |
iframe | string | string[] | — | Look inside an iframe |
findByAttribute
Find an element by one of its HTML attributes, like data-testid or aria-label.
Value: [attribute, value]
<button data-testid="submit-button">Submit</button>
[
{ "findByAttribute": ["data-testid", "submit-button"] }
]
Partial value match:
[
{ "findByAttribute": ["data-testid", "submit"], "options": { "exact": false } }
]
Save for later use:
[
{
"findByAttribute": ["aria-label", "Close dialog"],
"options": { "save": "closeBtn" }
},
{ "click": "$closeBtn" }
]
Options
| Option | Type | Default | Description |
|---|---|---|---|
selector | string | "*" | Only search within elements matching this selector |
exact | boolean | false | true = full value match, false = contains |
save | string | — | Save the element for later |
waitFor | boolean | true | Wait for the element to appear |
timeout | number | 10000 | How long to wait (ms) |
iframe | string | string[] | — | Look inside an iframe |
waitForEvent
Wait for a browser-level page event.
Value: "load" | "networkidle"
Wait for the page and all its resources (images, scripts, stylesheets) to fully load:
[
{ "waitForEvent": "load" }
]
Wait until there are no active network requests for 500ms:
[
{ "waitForEvent": "networkidle" }
]
"networkidle" watches active network connections in the tab. Once all requests have settled and no new ones start for 500ms, it resolves. This is useful when the page visually appears loaded but is still fetching data in the background — a common pattern in:
- Single-page apps (React, Vue, Angular) — route changes don't reload the page, so
"load"never fires again after the first visit - Dashboards and data grids — the shell loads first, then separate API calls populate tables and charts
- Infinite scroll — the initial batch of items loads via XHR after the page is ready
- Search-as-you-type — waiting for results to finish loading before reading them
Use "load" right after navigating to a new page to wait for all static assets (images, scripts, stylesheets) to finish. Use "networkidle" when content is fetched dynamically after the initial page load.
Options
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | 10000 | How long to wait (ms) |
Custom timeout:
[
{ "waitForEvent": "networkidle", "options": { "timeout": 15000 } }
]
waitForResponse
Intercept a network request and capture its response. Non-blocking — starts listening immediately and returns the captured response at the end of the task. Matches the URL by substring or regex.
Value: string (URL substring or regex pattern)
[
{ "waitForResponse": "/api/products" }
]
Filter by HTTP method:
[
{ "waitForResponse": "/api/search", "options": { "method": "GET" } }
]
Returns: The response body, parsed as JSON if possible.
Set up the listener first, then trigger the action that causes the request:
[
{ "open": "https://example.com/search" },
{ "waitForResponse": "/api/search" },
{ "fill": ["input#search", "laptop"] },
{ "click": "#search-btn" }
]
The response data is returned in the task result. If you also need to interact with the rendered results, add waitForElement after the click.
Options
| Option | Type | Default | Description |
|---|---|---|---|
method | string | — | Only match a specific HTTP method: "GET", "POST", etc. |
timeout | number | 10000 | How long to wait (ms) |
waitForResponse cannot be used inside loop or if branches.
Example: Wait for Page Data to Load
[
{ "openNewTab": "https://example.com/dashboard" },
{ "waitForEvent": "networkidle" },
{ "waitForElement": ".data-table", "options": { "visible": true } },
{ "screenshot": "viewport" }
]
Example: Capture an API Response
[
{ "openNewTab": "https://example.com/search" },
{ "waitForResponse": "/api/search", "options": { "method": "GET" } },
{ "fill": ["input#search", "laptop"] },
{ "click": "#search-btn" }
]