Custom code
The Custom code step (script.run, category Data) runs a short JavaScript or
TypeScript snippet in a sandbox and keeps what it returns. Use it for logic that is
awkward as steps or expressions: formatting values, calculating totals, generating
unique data, checking the shape of an API response.
Writing or changing custom code needs the Write custom code steps permission (Test Developer, Test Manager, Admin). Testers can run tests that contain code, and edit their other steps, but cannot change the code.
Fields
Section titled “Fields”| Field | Meaning |
|---|---|
| Language | JavaScript or TypeScript (types are checked for syntax and then stripped) |
| Time limit (ms) | CPU time for the snippet. Default 1000, maximum 10000. Separate from the step’s timeout. |
| Code * | The body of a function. Use return for the result, or throw to fail the step. Up to 20,000 characters. |
| Save result as | Variable that receives the returned value. Leave it empty to make the step a check: it passes unless the code throws. |
| Secrets it may read | Names of secrets the code may use, e.g. API_TOKEN, SIGNING_KEY. None by default. Up to 20. |
| Test run | Try the code in the editor against sample context.variables (JSON). Secrets are replaced by placeholders. Choose ▶ Run code. |
Code that does not compile is reported when you save, and the test cannot be saved.
What the code can see
Section titled “What the code can see”The function receives one argument, context, which is read-only:
| Property | Contents |
|---|---|
context.variables |
Every variable in scope, with the same precedence as {{name}}: runtime, test, environment |
context.data |
The current row of a data-driven run, or null |
context.locals |
Loop variables (For each / Repeat) and component parameters |
context.secrets |
Only the secrets listed in Secrets it may read, as context.secrets.NAME |
console.log(...) output is kept with the step result.
Examples
Section titled “Examples”Transform a value (Save result as paddedInvoice):
const value = context.variables.invoiceNumber; // "INV-12345"return value.replace("INV-", "").padStart(10, "0"); // "0000012345"A check (Save result as left empty):
const padded = context.variables.paddedInvoice;if (!/^\d{10}$/.test(padded)) { throw new Error(`Expected 10 digits, got "${padded}"`);}console.log("Invoice number looks right:", padded);TypeScript, returning an object (Save result as totals):
interface Line { sku: string; qty: number; unitPrice: number }
const lines: Line[] = context.variables.orderLines;const cents = (n: number): number => Math.round(n * 100) / 100;const subtotal = cents(lines.reduce((sum, l) => sum + l.qty * l.unitPrice, 0));return { subtotal, lineCount: lines.length };Later steps use {{totals.subtotal}}, or totals.subtotal > 0 in an expression.
The sandbox
Section titled “The sandbox”The code runs in an isolated JavaScript engine (QuickJS), not in the browser and not on the server’s Node.js:
| Limit | Value |
|---|---|
| Network, files, processes, timers | Not available (fetch, require, process, setTimeout are undefined) |
| The page under test | Not reachable. Read values from the page first with an Extract step. |
| CPU time | Time limit, default 1 s, maximum 10 s |
| Memory | 32 MB |
| Returned value | Must be JSON-compatible, at most 256 KB |
context |
Frozen; changing it throws a TypeError |
Errors you may see:
| Error | Meaning |
|---|---|
ScriptTimeoutError: The code ran longer than its time limit. |
An endless loop, or too much work for the limit |
ScriptMemoryError: The code used more memory than it is allowed. |
Over 32 MB |
ScriptResultError: The code returned nothing (undefined). |
Save result as is set but the code did not return. Return a value, or clear Save result as. |
Line N: … |
A syntax or runtime error, with the line in your code |
Custom code runs on the web engine only.
Examples to explore
Section titled “Examples to explore”examples/custom-code/ has seven tests (CC 01–07): formatting, TypeScript totals,
generating unique data, response-shape checks, a tour of the sandbox limits, per-row
logic in a data-driven test, and building an API order. Import them with
scripts/seed-custom-code-examples.mts (see
Data, backup and seeding).