How to Handle Dynamic Data in Maestro Flows: Complete Guide to Variable Interpolation

Maestro handles dynamic data by treating every string as a JavaScript template that uses ${...} syntax for interpolation, evaluated at runtime by the Env.evaluateScripts engine.

The mobile-dev-inc/Maestro framework provides a powerful variable interpolation system that allows you to inject, compute, and reuse dynamic values throughout your test flows using JavaScript-backed template syntax. This enables data-driven testing where credentials, timestamps, calculated values, and environment-specific configuration can be injected at execution time.

Understanding Maestro's Variable Interpolation Engine

At the core of Maestro's dynamic data handling is the JS-engine-backed evaluateScripts system implemented in maestro-orchestra-models/src/main/java/maestro/orchestra/util/Env.kt. This engine walks through every string in your flow, identifies unescaped ${...} patterns, evaluates the inner content using the embedded JavaScript engine (JsEngine), and substitutes the result.

The interpolation process respects escaped sequences (\${...}), leaving them untouched after removing the backslash. This architecture means any valid JavaScript expression can be embedded inside the braces—arithmetic operations, string concatenation, ternary logic, or calls to exposed library functions like Date.now().

Setting Variables in Maestro Flows

Maestro provides three primary mechanisms for defining variables that become available for interpolation.

CLI Environment Variables

Pass key-value pairs directly from the command line using the --env flag. According to Env.kt (lines 28-31), these values are automatically merged into the execution context via the MaestroCommand.withEnv extension.

maestro test flow.yaml --env USERNAME=jdoe,PASSWORD=secret123

All subsequent ${USERNAME} and ${PASSWORD} placeholders in flow.yaml will resolve to the supplied values.

Flow-Level Environment Variables

Define static variables at the top of your flow file using the env: map. This block is automatically converted into a DefineVariablesCommand during parsing.


# flow.yaml

env:
  API_TOKEN: abcdef123456
  LANG: en

- launchApp: com.example.myapp
- inputText:
    selector: { text: "Username" }
    text: "${USERNAME}"
- inputText:
    selector: { text: "Password" }
    text: "${PASSWORD}"
- assertVisible:
    selector: { text: "Welcome ${USERNAME}" }

Runtime Variables with DefineVariablesCommand

Create variables dynamically during execution using the defineVariables command (implemented in maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt lines 52-70). This is ideal for generating timestamps, UUIDs, or computed values mid-flow.

- defineVariables:
    SESSION_ID: "${java.util.UUID.randomUUID().toString()}"
    TODAY: "${new Date().toISOString().split('T')[0]}"
- tapOn:
    selector: { text: "Start session ${SESSION_ID}" }
- assertVisible:
    selector: { text: "Today is ${TODAY}" }

Using JavaScript Expressions for Dynamic Values

Because the interpolation engine is JavaScript-backed, you can perform complex computations directly inside ${...} placeholders.

- inputText:
    selector: { text: "Discount" }
    text: "${(price * quantity).toFixed(2)}"

Assuming price and quantity were defined earlier, the expression evaluates to a formatted string (e.g., "19.99") before being sent to the device.

Reserved Variables and Escaping Syntax

Accessing System-Injected Variables

Maestro automatically injects reserved environment variables through Env.withDefaultEnvVars (defined in Env.kt lines 33-48). These include:

  • MAESTRO_FILENAME – The current flow file name
  • MAESTRO_DEVICE_UDID – The target device identifier
  • MAESTRO_SHARD_ID – Shard identifier for parallel execution
  • MAESTRO_SHARD_INDEX – Current shard index
- assertVisible:
    selector: { text: "Running on shard ${MAESTRO_SHARD_INDEX}" }

Important: Reserved variables like MAESTRO_SHARD_ID and MAESTRO_SHARD_INDEX cannot be overridden by user-defined values or CLI arguments.

Escaping Literal Syntax

To prevent evaluation and include literal ${...} text in your selectors, prepend a backslash. The Env.evaluateScripts function removes the escape character while preserving the literal braces.

- tapOn:
    selector: { text: "\${STATIC_PLACEHOLDER}" }

This outputs the literal string ${STATIC_PLACEHOLDER} without JavaScript evaluation.

Execution Flow of Variable Resolution

According to the orchestration logic in Orchestra.kt, Maestro performs the following steps for every command:

  1. Variable collection – CLI --env, flow-level env: blocks, and any inline DefineVariablesCommand instances are merged using withEnv.
  2. Default injection – Env.withDefaultEnvVars adds MAESTRO_FILENAME, MAESTRO_DEVICE_UDID, shard identifiers, and other system values.
  3. Script evaluation – Each command's evaluateScripts(jsEngine) method walks its fields (e.g., text, selector, label) and runs Env.evaluateScripts to resolve ${...} patterns.
  4. Command execution – The fully interpolated command (with concrete values) is passed to the iOS or Android driver for interaction.

Flow-wide configuration commands defined in MaestroConfig.kt (lines 18-24) for onFlowStart and onFlowComplete also support ${...} interpolation and follow the same evaluation pipeline.

Summary

  • Variable interpolation in Maestro uses JavaScript template syntax ${...} evaluated by Env.evaluateScripts.
  • Three definition methods: CLI --env flags, flow-level env: maps (converted to DefineVariablesCommand), and runtime defineVariables steps.
  • JavaScript expressions are fully supported inside interpolation blocks, enabling dynamic calculations.
  • Reserved variables like MAESTRO_SHARD_INDEX are injected automatically and cannot be overridden.
  • Escape literal ${...} by prefixing with a backslash: \${...}.

Frequently Asked Questions

Can I override the MAESTRO_SHARD_INDEX variable?

No. According to Env.kt lines 33-48, variables like MAESTRO_SHARD_ID and MAESTRO_SHARD_INDEX are reserved internal variables injected automatically by the orchestration engine. These cannot be overridden by CLI arguments, flow-level environment variables, or defineVariables commands.

How do I include a literal dollar-brace string without triggering JavaScript evaluation?

Use a leading backslash to escape the interpolation syntax. The Env.evaluateScripts function in Env.kt removes the backslash while leaving the literal ${...} text intact. For example: text: "\${UNINTERPOLATED}" renders as ${UNINTERPOLATED}.

Can I use any JavaScript library or npm package inside the interpolation?

You can use any JavaScript expressions or standard library functions exposed by the embedded JsEngine, such as Date, Math, or java.util.UUID. However, you cannot import external npm packages or Node.js-specific modules. The evaluation context is sandboxed and limited to what the Maestro JS engine exposes.

What is the difference between flow-level env: and the defineVariables command?

The env: block at the top of a flow file defines static variables before execution begins and is processed via MaestroCommand.withEnv. The defineVariables command (implemented by DefineVariablesCommand in Commands.kt) executes at runtime during the flow, allowing you to create variables based on previous test state or dynamic JavaScript evaluation (like generating UUIDs or timestamps).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →