Scripts & pm APIs

Pre-req and Post-res scripts

  • The Postman format lets you write custom JavaScript in pre-req and post-res tabs that execute before and after a step respectively. When you export the collection, these scripts come bundled.

  • ReṼoman executes this JavaScript on the JVM. This ensures the same API metadata used for manual testing can be used as-is for automation — no resistance to modify, no overhead of separate manual/automation versions.

    • Pre-req JS runs first, before unmarshalling the request.

    • Post-res JS runs right after receiving an HTTP response.

npm modules with require(…​)

ReṼoman supports using npm modules inside your pre-req and post-res JS. Install modules in any folder with npm install <module>, supply the parent folder of node_modules to the Kick config via nodeModulesPath(…​) (absolute, or relative to the project directory), and require(…​) them:

Install moment with npm
npm install moment
Use inside pre-req and post-res scripts
var moment = require("moment")
var _ = require('lodash')

pm.environment.set("$currentDate", moment().format(("YYYY-MM-DD")))
var futureDateTime = moment().add(365, 'days')
pm.environment.set('$randomFutureDate', futureDateTime.format('YYYY-MM-DD'))

pm.environment.set("$quantity", _.random(1, 10))
Loading node_modules from a jar is not supported. For example, /Users/username/some-path/revoman-root/build/libs/revoman-root-0.40.0.jar!/js is an invalid path. (See this known issue.)
  • node_modules adds a lot of files to check in. You may replace them with a single distribution file.

node modules
  • If node_modules is ignored in your git repo, you can force-add it with git add --all -f <path>/node_modules.

Don’t add too much code in pre-req/post-res scripts — it’s not intuitive to troubleshoot via debugging. Use them for simple operations understandable without debugging, and use Pre-Step/Post-Step Hooks for non-trivial operations (which are intuitive to debug).

Supported pm script APIs

ReṼoman runs your collection’s pre-request and post-response JavaScript on the JVM through a real Postman-compatible sandbox. The following script-only pm APIs are supported and surface their effects on the Rundown:

API Behavior in ReṼoman

pm.environment (+ pm.environment.name)

Reads/writes flow into the mutable environment. Surfaced on rundown.mutableEnv.

pm.collectionVariables (.get / .set / .toObject)

Collection-scoped key/value store; values set in one step’s script are visible to later steps' scripts. Surfaced on rundown.collectionVariables.

pm.globals (.get / .set / .unset)

Global-scoped key/value store; like pm.collectionVariables, persists across steps. Surfaced on rundown.globals.

pm.variables (.get / .has)

Aggregate read view across all scopes, honoring Postman precedence (environmentcollectionVariablesglobals).

pm.request.body, pm.response.code / .json / .text / .to.have.status

Request/response fed into the script context.

pm.test, pm.expect

Assertion results recorded on StepReport.pmTestAssertions. A failing assertion fails its step (and the Rundown) — see A failing pm.test fails the step.

pm.execution.setNextRequest / skipRequest

Control-flow directives; now honored — see Control-flow directives.

All three persistent scopes — pm.environment, pm.collectionVariables, pm.globals — are also resolved in {{key}} variable substitution, by the same precedence. Only the environment scope feeds the mutable environment and the warm-run ledger; collectionVariables and globals are sibling stores.

Two read-only fields on each StepReport expose script outcomes:

  • pmTestAssertions: List<PmTestAssertion> — every pm.test(…​) result across the step’s pre-request and post-response scripts. PmTestAssertion carries name, passed, skipped, error, and exeType (the producing phase: PRE_REQ_JS or POST_RES_JS).

  • pmTestFailure: List<PmTestFailure> — the failed assertions of this step, grouped by phase (0–2 entries, pre-request first). Always populated when any assertion failed, independent of `failure’s precedence — see A failing pm.test fails the step.

  • nextRequest: String? — the value passed to pm.execution.setNextRequest(…​), if any (honored by the sequencer).

Control-flow directives

ReṼoman now honors pm.execution.setNextRequest(name) (jump to a named step), setNextRequest(null) (stop the run), and pm.execution.skipRequest() (skip HTTP dispatch in pre-request scripts). These let your scripts implement conditional logic, loops, and early exits.

  • Jump: pm.execution.setNextRequest("Step Name") causes the next executed step to be the named step (case-sensitive match on the step’s name field). A jump disables the ledger warm-path from the divergence point onward.

  • Stop: pm.execution.setNextRequest(null) stops the run immediately after the current step. Rundown.stopReason will be STOPPED_BY_DIRECTIVE.

  • Skip: pm.execution.skipRequest() (pre-request only) skips the HTTP dispatch for the current step; the step succeeds with no request/response. Post-response scripts are also skipped; the run continues with the next step.

Loops are bounded by the maxStepExecutionFactor Kick knob (default 10): if a run executes more than factor × collectionStepCount steps, it halts with Rundown.stopReason = LOOP_BUDGET_EXCEEDED. The terminal reason for any run appears on Rundown.stopReason (COMPLETED / STOPPED_BY_DIRECTIVE / HALTED_ON_FAILURE / LOOP_BUDGET_EXCEEDED).

Current limitations
  • pm.sendRequest is not supported.

  • Collection-root variable[] is not parsed; pm.collectionVariables and pm.globals are script-seeded only (a producer step’s script `.set`s a value, a downstream step reads it).

End-to-end: script-only pm APIs through a real run → PokemonSandboxApiTest.java
// --- pm.collectionVariables set in step 1, read in steps 2-3 (cross-step) ---
final StepReport byName = rundown.reportForStepName("pokemon-by-name");
assertThat(byName).isNotNull();
assertThat(byName.pmTestAssertions).isNotEmpty();
assertThat(byName.pmTestAssertions.stream().allMatch(a -> a.passed)).isTrue();

// --- pm.execution.setNextRequest: CAPTURED (not executed — Phase 2 reorders) ---
// ReVoman still runs steps linearly; we assert only that the directive was surfaced.
assertThat(byName.nextRequest).isEqualTo("pokemon-species");
// Proof it was NOT executed: pokemon-species still ran in linear order after pokemon-by-name.
assertThat(rundown.reportForStepName("pokemon-species")).isNotNull();
// Guard the crown-jewel cross-step collectionVariable proof against silently vanishing:
// a failing pm.test is DATA (passed=false), and allMatch on an empty list passes vacuously, so
// assert this step actually produced assertions.
assertThat(rundown.reportForStepName("pokemon-species").pmTestAssertions).isNotEmpty();

These script outcomes land on the Rundown; environment reads and writes flow through the mutable environment; and for logic too involved for a pre-req/post-res script, reach for Hooks instead.

A failing pm.test fails the step

A failing pm.test(…​) assertion fails its step — and therefore the Rundown — matching Postman/newman semantics. By default the run continues to the next step (the step is simply marked failed); set haltOnAnyFailure(true) to fail-fast.

A pm.test failure carries the ExeType of the phase that produced it — PRE_REQ_JS or POST_RES_JS — so you can exempt it from halting via haltOnFailureOfTypeExcept(POST_RES_JS) { stepReport, rundown → …​ } (or PRE_REQ_JS), exactly as for a script error.

When both an HTTP/transport failure and a pm.test failure occur on the same step, both are surfaced: StepReport.failure carries the higher-precedence HTTP/transport cause (so a redundant pm.test('status 200') does not mask the real non-2xx response), while StepReport.pmTestFailure independently lists the assertion failures. skipped assertions (pm.test.skip) never contribute to failure.

Per-assertion results — including exeType — appear in the JSON Rundown under each step’s pmTestAssertions.