Web-Only Commands
These commands are exclusive to the web platform. They are not available on Android or iOS.
Cookie Management
# Set cookies
- setCookies:
cookies:
- name: session
value: abc123
domain: .example.com
path: /
httpOnly: true
secure: true
sameSite: Lax
- name: theme
value: dark
domain: .example.com
# Get all cookies as JSON (store in variable)
- getCookies: myCookies
# Or with explicit output field
- getCookies:
output: myCookies
Cookie fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Cookie name |
value |
string | Yes | Cookie value |
domain |
string | Yes | Cookie domain (e.g., .example.com) |
path |
string | No | Cookie path (default: /) |
httpOnly |
bool | No | HTTP-only flag |
secure |
bool | No | Secure flag |
sameSite |
string | No | Strict, Lax, or None |
expires |
float | No | Expiry as Unix timestamp |
Auth State Persistence
Save and restore the full browser authentication state (cookies + localStorage + sessionStorage). This enables the "login once, reuse everywhere" pattern.
# After logging in, save the auth state
- saveAuthState: auth-state.json
# In another flow, restore it to skip login
- loadAuthState: auth-state.json
- launchApp
# User is now logged in
The auth state file is a JSON file containing:
{
"cookies": [...],
"localStorage": {"key": "value", ...},
"sessionStorage": {"key": "value", ...}
}
This is portable — you can save it once in CI and reuse across multiple flows or runs.
# Full form
- saveAuthState:
path: /tmp/auth-state.json
- loadAuthState:
path: /tmp/auth-state.json
Browser Script Execution
Run JavaScript directly in the browser page context with full access to window, document, DOM APIs, fetch, etc.
# Inline script (scalar shorthand)
- evalBrowserScript: "return document.title"
# With output variable
- evalBrowserScript:
script: |
const el = document.querySelector('#price');
return el ? el.textContent : 'not found';
output: priceText
# Use the result in subsequent steps
- assertTrue: "${priceText !== 'not found'}"
Scripts run as async functions — you can use await:
- evalBrowserScript:
script: |
const response = await fetch('/api/status');
const data = await response.json();
return data.version;
output: apiVersion
Run Browser Script File
Load and execute a JavaScript file in the browser. Environment variables are injected as window.__env.
# Simple — just the file path
- runBrowserScript: "scripts/setup.js"
# With env vars and output
- runBrowserScript:
file: "scripts/setup-test-data.js"
env:
API_KEY: "${API_KEY}"
ENV: "test"
output: setupResult
scripts/setup-test-data.js:
const apiKey = window.__env.API_KEY;
const res = await fetch('/api/setup', {
method: 'POST',
headers: { 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({ reset: true })
});
const data = await res.json();
return data.status; // stored in setupResult variable
Note:
evalScriptandrunScript(without "Browser") run in Maestro's internal JavaScript engine — they do NOT have access towindow,document, or the browser DOM. UseevalBrowserScriptorrunBrowserScriptfor browser-side scripts.
Console Capture & JS Error Detection
The browser driver automatically captures all console.log, console.warn, console.error, console.info, and uncaught JavaScript exceptions in the background from the moment the driver starts.
# Assert no JS errors occurred during the test
- assertNoJSErrors
# Get all captured console logs as JSON
- getConsoleLogs: logs
# Clear the log buffer (reset for next phase)
- clearConsoleLogs
assertNoJSErrors fails the step if any console.error() calls or uncaught exceptions were captured since the last clearConsoleLogs (or since the start of the flow).
Console log entries are JSON objects with level and message:
[
{"level": "log", "message": "App loaded"},
{"level": "warn", "message": "Deprecated API call"},
{"level": "error", "message": "TypeError: Cannot read property 'x' of null"},
{"level": "exception", "message": "Uncaught ReferenceError: foo is not defined"}
]
Levels: log, info, warn, error, exception (uncaught exceptions).
Typical usage — catch silent JS errors:
- launchApp
- tapOn: "Submit"
- assertNoJSErrors # Fails if the submit triggered any JS errors
# Phase-based checking
- clearConsoleLogs # Reset for checkout phase
- tapOn: "Checkout"
- tapOn: "Pay"
- assertNoJSErrors # Only checks errors since clearConsoleLogs
File Upload
Set files on <input type="file"> elements.
# Single file
- uploadFile:
css: "input[type=file]"
path: "test-data/photo.jpg"
# Multiple files
- uploadFile:
testId: file-input
paths:
- "test-data/doc1.pdf"
- "test-data/doc2.pdf"
# By any selector
- uploadFile:
name: avatar
path: "test-data/avatar.png"
File paths are relative to the flow file directory.
File Download
Wait for a browser download to complete.
# Wait for download (saves to temp dir)
- waitForDownload
# Save to specific directory
- waitForDownload:
saveTo: "downloads/"
# Assert filename
- waitForDownload:
saveTo: "downloads/"
assertFilename: "report.pdf"
timeout: 10000
The download must be triggered by a user action (clicking a download link) before waitForDownload. The command sets up Chrome's download behavior, then waits for the DownloadWillBegin + DownloadProgress CDP events.
# Typical pattern: click download link, then wait
- tapOn: "Download Report"
- waitForDownload:
saveTo: "/tmp/downloads"
assertFilename: "report.pdf"
Browser Permissions
Grant or reset browser permissions (notifications, camera, microphone, etc.).
# Grant permissions
- grantPermissions:
permissions:
- notifications
- camera
- microphone
- geolocation
- clipboard-read
- clipboard-write
# Grant for specific origin
- grantPermissions:
permissions:
- notifications
origin: "https://myapp.example.com"
# Reset all permissions
- resetPermissions
Permission values are CDP BrowserPermissionType strings.
Note: setLocation automatically grants geolocation permission — no need to call grantPermissions separately for location.
Multi-Tab Management
Open, switch between, and close browser tabs. Essential for testing OAuth flows, payment redirects, and target="_blank" links.
# Open a new tab
- openTab: "https://accounts.google.com/oauth"
# Open with a label for easy switching
- openTab:
url: "https://accounts.google.com/oauth"
tabLabel: "oauth"
# Switch to tab by label
- switchTab: oauth
# Switch by index (0-based, 0 = first tab)
- switchTab:
index: 0
# Switch by URL pattern (glob matching with *)
- switchTab:
url: "*/oauth/callback*"
# Close current tab (switches to remaining tab)
- closeTab
Tab labels are driver-level identifiers — they're set when opening a tab and used for switching. They're not related to the page title.
URL pattern matching supports * wildcards: */api/* matches any URL containing /api/.
Typical OAuth flow:
- launchApp
- tapOn: "Sign in with Google"
# A new tab opens for OAuth
- openTab:
url: "https://accounts.google.com"
tabLabel: "google"
# Interact with the OAuth page
- inputText:
text: "[email protected]"
placeholder: "Email"
- tapOn: "Next"
# After OAuth redirects back, switch to main app
- switchTab:
index: 0
# Verify login succeeded
- assertVisible: "Welcome"
Network Mocking
Mock API responses to test against controlled data without hitting real backends.
# Mock a GET endpoint
- mockNetwork:
url: "*/api/users"
method: "GET"
response:
status: 200
headers:
Content-Type: "application/json"
body: '{"users": [{"id": 1, "name": "Test User"}]}'
# Mock a POST endpoint
- mockNetwork:
url: "*/api/orders"
method: "POST"
response:
status: 201
body: '{"orderId": "ORD-42"}'
# Mock error response
- mockNetwork:
url: "*/api/payment"
response:
status: 500
body: '{"error": "Service unavailable"}'
Mocks use CDP's Fetch domain to intercept requests before they reach the network. URL patterns support * wildcards.
Priority: When a request matches both a block and a mock, blocks take priority — the request is blocked.
Mock response fields:
| Field | Type | Default | Description |
|---|---|---|---|
status |
int | 200 | HTTP status code |
headers |
map | Response headers | |
body |
string | Response body |
Network Blocking
Block requests by URL pattern. Useful for blocking analytics, ads, images, or third-party scripts to speed up tests.
- blockNetwork:
patterns:
- "*.analytics.com/*"
- "*/tracking/*"
- "*.png"
- "*.jpg"
- "https://ads.example.com/*"
Blocked requests fail with BlockedByClient — the browser sees a network error, same as if the server was unreachable.
Network Throttling
Simulate slow networks or offline mode.
# Simulate slow 3G
- setNetworkConditions:
latency: 200
downloadSpeed: 500
uploadSpeed: 100
# Go offline
- setNetworkConditions:
offline: true
# Back to normal (or use clearNetworkMocks)
- setNetworkConditions:
offline: false
latency: 0
downloadSpeed: -1
uploadSpeed: -1
| Field | Type | Description |
|---|---|---|
offline |
bool | Simulate offline (no network) |
latency |
float | Added latency in milliseconds |
downloadSpeed |
float | Max download speed in KB/s (-1 = no throttle) |
uploadSpeed |
float | Max upload speed in KB/s (-1 = no throttle) |
Wait for Network Request
Wait for a specific network request to be made. Useful for verifying that a form submission or API call happened.
# Wait for any request matching URL pattern
- waitForRequest: "*/api/submit"
# Wait for specific method, capture request body
- waitForRequest:
url: "*/api/orders"
method: "POST"
timeout: 10000
output: requestBody
The step waits until a request matching the URL pattern (and optional method) is observed, then returns. If output is set, the request's POST data is stored in the variable. Default timeout is 30 seconds.
Clear Network Mocks
Remove all mocks, blocks, and network conditions. Restores normal network behavior.
- clearNetworkMocks
This disables the CDP Fetch domain, clears all mock rules and block patterns, and resets network throttling to defaults.
Complete Example
A full login flow with auth state reuse, network mocking, and error detection:
flows/login.yaml — Login and save auth state:
url: https://myapp.example.com
name: Login and Save Auth
tags:
- auth
---
- launchApp:
appId: https://myapp.example.com
clearState: true
- tapOn:
css: "button[type=button]"
text: "Sign In"
- inputText:
text: "[email protected]"
css: "input[type=email]"
- inputText:
text: "Test1234!"
css: "input[type=password]"
- tapOn:
css: "button[type=submit]"
- assertVisible: "Dashboard"
- assertNoJSErrors
# Save auth state for other flows
- saveAuthState: /tmp/auth-state.json
flows/checkout.yaml — Reuse auth, mock payment API:
url: https://myapp.example.com
name: Checkout Flow
tags:
- checkout
---
# Skip login — load saved auth state
- loadAuthState: /tmp/auth-state.json
- launchApp
# Block analytics to speed up tests
- blockNetwork:
patterns:
- "*.analytics.com/*"
# Mock the payment API
- mockNetwork:
url: "*/api/payment/charge"
method: "POST"
response:
status: 200
headers:
Content-Type: "application/json"
body: '{"success": true, "transactionId": "TXN-12345"}'
# Run the checkout flow
- tapOn: "Products"
- tapOn: "Add to Cart"
- tapOn: "Checkout"
# Verify form state
- assertVisible:
css: "input[type=email]"
enabled: false
- tapOn:
css: "button[type=submit]"
label: "Submit payment"
# Wait for the payment API call
- waitForRequest:
url: "*/api/payment/charge"
method: "POST"
output: paymentRequest
# Verify the mocked payment response was used
- assertVisible: "TXN-12345"
# No JS errors during the entire flow
- assertNoJSErrors
# Clean up
- clearNetworkMocks