Skip to main content
Skip to Content

Run tests in a browser or on a device agent-qa does not start, such as a grid, a cloud service or another machine.

By default, agent-qa launches the browser on this machine and finds devices here. A remote is somewhere else tests run: a Playwright server, a Chrome DevTools Protocol (CDP) endpoint, an Appium server, or a service a plugin connects to. You list remotes once under registry.remotes, and your files decide which tests run where.

RemoteWhat it isRuns
protocol: playwrightA Playwright server, at a ws:// or wss:// URL.Web tests, on any engine the server has.
protocol: cdpA Chromium DevTools endpoint, at a ws://, wss://, http:// or https:// URL.Web tests, on Chromium only.
protocol: appiumAn Appium server agent-qa does not start, at an http:// or https:// URL.Mobile tests.
plugin: <entry>A service a plugin creates sessions on, such as Kernel or BrowserStack.What the plugin serves.

Add a web remote

registry:
  remotes:
    - name: grid
      protocol: playwright
      url: wss://grid.example.com/playwright
      query:
        token: "{{secret:GRID_TOKEN}}"

use:
  browser:
    name: chromium
    headless: true
    remote: grid

Put GRID_TOKEN in your secrets file, .env.secrets.local by default. With use.browser.remote in the config, every web test runs on grid.

KeyDefaultMeaning
nameA lowercase slug, unique in the list. local is reserved for this machine.
protocolplaywright, cdp or appium.
urlThe endpoint. {{secret:NAME}} and {{playwright:version}} may appear in its path and query, never as user:password@.
queryParameters added to the URL when agent-qa connects. A value is text, or a map sent as JSON. Not for appium.
headersHeaders sent when agent-qa connects.
basicAuthuser and password, the password a {{secret:NAME}} reference. Use it or an Authorization header, not both.
connectTimeout5mHow long the connection may take. It bounds the connect only, never the test.
contextnewcdp only. new opens agent-qa's own browser context; reuse uses the browser's first context.
capabilitiesappium only. Appium capabilities for every device on this remote.

{{playwright:version}} becomes the Playwright version agent-qa runs, for services that ask the client for it, such as url: wss://browsers.example.com/connect?playwright={{playwright:version}}.

Pick where tests run

use.browser.remote picks a remote for web tests, and local means this machine. Set it in agent-qa.config.yaml for the project, or in the use block of a suite or a test:

# In a suite: run its tests on the grid's Firefox
use:
  browser:
    name: firefox
    remote: grid
  • A suite's pick wins, then the test's, then the config's. With none, the test runs here.
  • A suite runs in one place. When it names none, its tests must agree, or it is refused before it starts.
  • No run flag or environment variable moves a test. The files decide, so a run in CI and a run on your laptop place tests the same way.
  • A mobile test runs where its device is: a device with a remote runs there. See Mobile testing.

The dashboard's Config page lists the remotes read-only and sets the project's default, and each test's and suite's settings show where it runs. Coding agents can change use.browser.remote through agent_qa_change_config, but nothing except your own edit changes registry.remotes.

Credentials

  • Write every credential as a {{secret:NAME}} reference, quoted in YAML, to a name in the file workspace.secretsFile names. validate refuses a literal value under a header or query name that holds a credential word: authorization, cookie, token, secret, key, password, jwt, bearer, credential, signature, passwd or pwd. That covers nested maps, JSON values and the URL's own query. true, false and null are allowed there.
  • Credentials travel encrypted. A remote that carries a credential must use wss:// or https://, unless it points at this machine.
  • A remote's secrets are used only for its connection. Steps, expected values, target URLs and hooks cannot use them, and validate says so when one does.
  • The address stays in the file. The dashboard, MCP and config show show a remote by its name, protocol and origin only. Its path, query, headers and credentials are masked in every log and record.
  • Some settings refuse a remote test. A DEBUG value that turns on Playwright's own logging would print the remote's address and requests, so remote tests refuse to start while it is set. SELENIUM_REMOTE_URL would send a local launch to another machine, so local tests refuse it. Unset either one.

CDP endpoints

A CDP remote drives Chromium only. With context: new, agent-qa opens its own context and closes only what it opened. With context: reuse, it uses the browser's first context, which holds a signed-in profile but is shared with anything else using that browser.

registry:
  remotes:
    - name: signed-in-chrome
      protocol: cdp
      url: http://127.0.0.1:9222
      context: reuse

What a remote browser cannot give

A remote browser starts with the service's settings, not agent-qa's. The test's evidence records what the remote could not give, so a missing value never reads as an empty result:

  • launch flags, and use.browser.headless: the service decides whether its browser shows a window;
  • clipboard read-back;
  • downloaded files, on a CDP remote;
  • the clipboard probe, on a CDP remote with context: reuse;
  • the live screencast for Firefox and WebKit, which show a snapshot each second instead.

A target on this machine's loopback, such as http://127.0.0.1:3000, is reachable from a remote browser only through a tunnel you run. doctor warns when a remote's tests point at one. BrowserStack Local is one such tunnel.

Add an Appium remote

registry:
  remotes:
    - name: farm
      protocol: appium
      url: https://farm.example.com/wd/hub
      basicAuth:
        user: "{{secret:FARM_USER}}"
        password: "{{secret:FARM_KEY}}"
  devices:
    galaxy-farm:
      platform: android
      remote: farm
      model: Samsung Galaxy S24
      os: "14"
  targets:
    shop-android:
      platform: android
      appPackage: com.example.shop
      device: galaxy-farm
      app:
        ref: https://builds.example.com/shop-latest.apk
  • The server picks the device. agent-qa sends model as appium:deviceName, os as appium:platformVersion and id as appium:udid, and the server matches them its own way. For a server that selects devices by another key, leave model out and set that key in capabilities.
  • The server installs the app. The target's app.ref, an https URL or the server's own reference, is sent as appium:app; an appium:app in capabilities wins. agent-qa uploads nothing to an Appium remote, so a device there whose target has only app.path is refused. A plugin remote that uploads, such as BrowserStack's, takes app.path.
  • Capabilities merge in one order: agent-qa's own, then the remote's, then the device's. Maps merge key by key, and null removes a key.
  • No Appium starts here. agent-qa starts no local Appium for a device on a remote, and doctor skips the local mobile tools when every device that needs them is remote.
  • An Appium remote takes no query parameters. Send credentials as headers or basicAuth.

Android emulators and devices, and iOS simulators, are covered by agent-qa's own tests. Real iPhones behind a remote server work through capabilities, but agent-qa's tests do not cover them.

Plugin remotes

A plugin remote names a plugins entry instead of a URL, and holds the settings for one place on that service:

plugins:
  - name: kernel
    package: "@vostride/agent-qa-plugin-kernel"
    options:
      apiKey: "{{secret:KERNEL_API_KEY}}"

registry:
  remotes:
    - name: kernel
      plugin: kernel
      settings:
        headless: true

It has no url, query, headers or basicAuth, because the plugin supplies the connection, and it adds uploadTimeout, ten minutes by default, for app uploads. See Plugins.

During a run

  • Each test says where it ran. The run prints each remote test's place, and the run record and the dashboard's run page name it: this machine, a remote by name and origin, or a remote and its plugin entry.
  • Sessions stay alive. While a test holds a remote session, agent-qa sends it a heartbeat every 30 seconds, never in the middle of an action.
  • A remote that cannot connect is refused once. The test fails with one sentence naming the remote, and agent-qa does not retry it. Only the tests placed on that remote fail; the rest run, and the run exits 2. The analysis classes it as an environment problem.
  • A dropped session fails its test with Remote <name> ended the session while this test ran. or The connection to remote <name> was lost while this test ran.
  • Cleanup is bounded and closes only what agent-qa opened.
  • Stopping releases sessions. On Ctrl+C, agent-qa releases every open remote session within a few seconds and exits 130.
  • The live editor uses the same place. A dashboard live-editor session connects where its files say, and a remote session ends after 30 minutes without activity.

Check your remotes

CommandChecks
agent-qa validateEvery remote, credential rule and pick, including picks a suite or test cannot honour.
agent-qa doctorOne row per remote. For a URL remote, it asks whether the server answers, with a plain request that sends no credentials, and names the Appium version when it is an Appium server. It also warns about undeclared secrets. It opens no session.
agent-qa doctor --remote <name>Only that remote's rows.
agent-qa devicesEach device, and the remote it runs on.

doctor skips the local browser check when every web test runs on a remote.