Web Commands

Web-Only Commands

These commands are exclusive to the web platform. They are not available on Android or iOS.


# 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: evalScript and runScript (without "Browser") run in Maestro's internal JavaScript engine — they do NOT have access to window, document, or the browser DOM. Use evalBrowserScript or runBrowserScript for 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