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 nameMAESTRO_DEVICE_UDID– The target device identifierMAESTRO_SHARD_ID– Shard identifier for parallel executionMAESTRO_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:
- Variable collection – CLI
--env, flow-levelenv:blocks, and any inlineDefineVariablesCommandinstances are merged usingwithEnv. - Default injection –
Env.withDefaultEnvVarsaddsMAESTRO_FILENAME,MAESTRO_DEVICE_UDID, shard identifiers, and other system values. - Script evaluation – Each command's
evaluateScripts(jsEngine)method walks its fields (e.g.,text,selector,label) and runsEnv.evaluateScriptsto resolve${...}patterns. - 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 byEnv.evaluateScripts. - Three definition methods: CLI
--envflags, flow-levelenv:maps (converted toDefineVariablesCommand), and runtimedefineVariablessteps. - JavaScript expressions are fully supported inside interpolation blocks, enabling dynamic calculations.
- Reserved variables like
MAESTRO_SHARD_INDEXare 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →