Write scripts
This page shows how to structure a Reflex script so it is easy to read, retry and debug.
A script is a plain Lua file. The libraries (Rest, Json, Xlsx, Sel, Dates and more) are global, so there is nothing to import. The same script runs as a server job, from the CLI, in FitNesse and under TestNG.
local response = Rest.get("https://api.example.com/status")Assert.equals(200, response.status)print(Json.decode(response.body).version)For ready-made recipes see the examples. Testing a web UI? Read browser testing.
Project layout
Section titled “Project layout”A run gets the whole project. Every file is unpacked into a clean workspace on the agent, and the workspace is removed after the run.
- Address files with paths relative to the project root, such as
files/report.xlsx. - Put shared code in other
.luafiles and load it withrequire(). - Files your script writes come back as run results. That is how generated reports leave the agent.
Split anything longer than a few lines into steps. Define a global Steps table. Each step shows up separately in the run, with its own status and output.
local function fetch(url) local response = Rest.get(url) Assert.equals(200, response.status) Data.set("orders", Json.decode(response.body))end
Steps = { { name = "fetch", description = "Download the open orders", condition = "always", func = fetch, params = { "https://api.example.com/orders?status=open" }, retry = 2, }, { name = "report", description = "Write the report", condition = "previous", func = function() print(Data.json()) end, params = {}, retry = 0, },}All six fields are required. A step that misses one is skipped without a warning.
| Field | Meaning |
|---|---|
name | Shown in the run. Spaces become _. setup is reserved. |
description | What the step does. |
condition | always, previous (only if the previous step succeeded) or never. |
func | The step body. It receives the values in params. |
params | Arguments for func. Use {} for none. |
retry | How often to retry the step when it fails. |
End with an always step for cleanup, such as Sel.close().
To retry a single call inside a step, use Retry.run(attempts, delayMs, fn). To check results, use Assert.
Secrets
Section titled “Secrets”Never put passwords or tokens in a script. Add them as project secrets and read them with secret("name"), which takes exactly one argument:
local sftp = Sftp.newSession("report-user", secret("sftp_password"), "sftp.example.com")Secret values are masked in run output. Add and manage secrets in Projects, secrets and roles.
Pass data between runs
Section titled “Pass data between runs”- Out:
Data.set("key", value)stores a value with the run. - In: a job can import the data of another job’s last run. Your script reads it from
Values:Values.key, orValues.job["other-job"].key. - Job inputs: values set on the job also arrive in
Values.
Set up the import on the job: see Schedule jobs and suites.
Python
Section titled “Python”Agents also run Python, with the same libraries as Python objects. Results behave like dictionaries:
reflex exec --lang python "print(Rest.get('https://httpbin.org/get')['status'])"Python runs in a sandbox with access to the run workspace only. Lua is the most used path, so check the library reference for the functions you need.
When a script fails
Section titled “When a script fails”Open the failed run and read the console of the failing step. The debugging guide covers replays, live debugging and the healer.