Capabilities Guide

    How to use RobotActions' ra:* desired capabilities — at session create, at runtime, and via the REST/WebSocket APIs. For the full per-capability table see the Capabilities Reference.

    1. What are capabilities?

    Capabilities are a JSON object you send to the grid at the POST /session call. They tell the grid what platform you want (Android, iOS, browser X), which device/version, and what extra behaviour you want from the session — recording, profiling, test naming, etc.

    The W3C WebDriver spec defines a core set of capabilities (platformName, browserName, browserVersion, …). Drivers add vendor-specific caps under their own namespace — Appium uses appium:*, Selenium uses se:*, and RobotActions uses ra:*.

    json
    // Capabilities are a JSON payload you send when creating a WebDriver session.
    // The grid extracts ra:* values, strips them before forwarding to Appium /
    // Selenium, and uses them to opt into recording, profiling, suite grouping,
    // and so on.
    {
      "platformName": "Android",
      "appium:app": "path/to/app.apk",
      "appium:deviceName": "Google Pixel 8",
    
      // RobotActions namespace
      "ra:testName": "Login — happy path",
      "ra:videoRecording": true
    }

    2. The ra:* namespace

    Every RobotActions capability is prefixed with ra:. Three reasons:

    • W3C compliance — vendor-prefixed extension caps are the supported way to add proprietary behaviour without breaking spec validators.
    • Conflict-free — won't collide with future Appium or Selenium caps, or with caps from other cloud vendors.
    • Predictable lifecycle — the proxy strips ra:* before forwarding to the Selenium Hub / Appium server, so the underlying drivers never see them. No risk of an unrecognised-cap rejection.
    Where to put them in the JSON body: W3C clients place caps in capabilities.alwaysMatch. Legacy JSON Wire clients use desiredCapabilities. Both work; the grid reads from either bucket and from every firstMatch[*] entry as well.

    3. Setting caps at session create

    Pass ra:* caps the same way you pass any other vendor cap — through your client's options/capabilities builder. Three examples in three popular clients:

    Selenium (Python)

    python
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    
    opts = Options()
    opts.set_capability("ra:testName", "Checkout — happy path")
    opts.set_capability("ra:testsuite", "regression-2026-05-15")
    opts.set_capability("ra:videoRecording", True)
    
    driver = webdriver.Remote(
        command_executor="https://<subdomain>.robotactions.com/t/<token>",
        options=opts,
    )

    Appium (Java)

    java
    UiAutomator2Options opts = new UiAutomator2Options()
        .setUdid("9B051FFBA007KA")
        .setAppPackage("com.myapp")
        .setAppActivity("com.myapp.MainActivity")
        .amend("ra:testName", "Login flow")
        .amend("ra:testsuite", "smoke-2026-05-16")
        .amend("ra:videoRecording", true)
        .amend("ra:appProfiling", true);   // CPU/memory/network samples — Android only
    
    AndroidDriver driver = new AndroidDriver(
        new URL("https://<subdomain>.robotactions.com/t/<token>"),
        opts);

    WebdriverIO (JavaScript)

    javascript
    const browser = await remote({
      protocol: "https",
      hostname: "<subdomain>.robotactions.com",
      path: "/t/<token>",
      port: 443,
      capabilities: {
        platformName: "Android",
        "appium:automationName": "uiautomator2",
        "appium:udid": "9B051FFBA007KA",
        "ra:testName": "Checkout regression",
        "ra:testsuite": "nightly-2026-05-16",
        "ra:videoRecording": true,
      },
    });

    4. Setting them at runtime

    Some caps you don't know at session-create time — the test name might be derived from the test framework's runtime, the result is by definition known only after the test runs, the suite name might depend on a CI build ID injected mid-run.

    For these, RobotActions intercepts magic strings passed to executeScript — before the string reaches the underlying browser or device — and acts on them server-side. Works in every WebDriver-protocol client.

    python
    // Inside the test body — set pass/fail + naming from the test itself.
    // Works in any WebDriver-protocol client (Selenium, Appium, WebdriverIO).
    // The grid intercepts the magic string BEFORE it reaches the underlying
    // browser/device, so the test never sees an error if the verb is unknown.
    
    driver.execute_script("ra:job-name=Checkout — happy path")
    driver.execute_script("ra:testsuite=regression-2026-05-15")
    
    try:
        run_checkout_flow()
        driver.execute_script("ra:job-result=passed")
    except AssertionError as e:
        driver.execute_script(f"ra:job-result=failed:{e}")

    Verbs available: ra:job-result=passed / ra:job-result=failed:<reason>, ra:job-name=<name>, ra:testsuite=<suite>, ra:fail-reason=<msg> (message only, doesn't change result), ra:profile-start / ra:profile-stop (Android profiling, runtime control).

    5. REST & WebSocket result APIs

    The executeScript magic works for WebDriver-protocol clients. For Playwright — which doesn't expose executeScript in the same shape — or for CI scripts that need to write the result after the test process has exited, the grid exposes a REST endpoint:

    bash
    # Set result + test name from outside the test (CI script, post-step hook, etc).
    # Works for any client — including Playwright where executeScript magic isn't available.
    
    curl -X POST "https://<subdomain>.robotactions.com:3001/api/sessions/$SESSION_ID/result" \
      -H "Authorization: Bearer $JWT" \
      -H "Content-Type: application/json" \
      -d '{
        "result": "failed",
        "message": "Login API returned 503",
        "test_name": "Auth — invalid creds",
        "test_suite": "smoke-2026-05-16"
      }'

    The same write is also available over WebSocket if your client already has a connection to the grid's WS endpoint — send { "type": "setSessionResult", "sessionId": "...", "result": "passed" | "failed", "message": "...", "testName": "..." } and the grid replies with { "type": "sessionResultUpdated", "success": true }.

    All four paths (executeScript magic / REST / WS / Playwright recording binding) write to the sessions.result + sessions.result_message + sessions.test_name columns. Use whichever fits your client; the dashboard, Reports tab, and suite rollups treat them identically.

    6. Common patterns

    Headless CI runs

    When the test runs unattended (nightly regression, PR check), nobody's watching the dashboard live-view and Inspector isn't connected. Turn off the live streams to save CPU + bandwidth, but keep MP4 recording so failures can be reviewed post-mortem.

    json
    // CI runs where the screen-mirror isn't needed — save CPU + bandwidth.
    {
      "platformName": "Android",
      "appium:app": "build/app-debug.apk",
      "appium:deviceName": "Pixel 8",
    
      "ra:liveView": false,       // disable the dashboard live-view button
      "ra:liveVideo": false,      // strip mjpeg* — no Inspector screen-mirror
      "ra:videoRecording": true,  // keep MP4 recording for post-run debugging
      "ra:autoFailDetect": true,  // default — let the grid mark failed if the last cmd 4xx'd
    
      "ra:testName": "${env.TEST_NAME}",
      "ra:testsuite": "ci-${env.BUILD_ID}"
    }

    Test grouping for Reports

    Pair ra:testsuite with ra:testName so the Reports tab can show per-suite rollup cards (total runs, passed, failed, unmarked).

    json
    // Surface a coherent build/suite view in the Reports tab.
    // ra:testsuite groups runs; ra:testName labels each leaf test.
    {
      "platformName": "Android",
      "appium:app": "build/app-debug.apk",
      "ra:testsuite": "regression-2026-05-15",   // shared across every test in the suite
      "ra:testName": "Settings — toggle dark mode"
    }

    Android app profiling

    Add "ra:appProfiling": true at session create. The Appium plugin samples mobile:getPerformanceData every ~3s and writes one JSONL line per tick (CPU user/kernel, totalPss memory, network rx/tx bytes, battery power). Available on the session's Performance tab as four stacked SVG line charts with hover correlation. Requires a resolvable appium:appPackage (or appium:appActivity prefix).

    Runtime control: driver.execute_script("ra:profile-start") / "ra:profile-stop". Idempotent.

    7. Reference table

    For the full per-capability table with type, default, scope, and detail card for each ra:* cap, see the dedicated reference page.

    Open Capabilities Reference

    👋 Hi! Need help? Chat with us!

    Chat with us

    Online

    Before we start

    Share your details so we can follow up with you.