Scenarios
--expect-text answers one question: did this line ever appear? A scenario
answers the rest. It is a YAML file listing steps the runner executes in
order, on the simulated clock of the primary board.
The field names are Wokwi’s, so an existing Wokwi scenario runs unchanged.
name: uno-ready boots and blinksversion: 1steps: - wait-serial: READY - delay: 600ms - expect-pin: part-id: uno pin: 13 expected: 1velxio-cli run --scenario scenario.yaml .The run passes when the last step passes, and fails at the first step that does not. Each step is reported as it happens:
ok wait-serial "READY" at 0.004 sok delay 600 ms at 0.604 sok expect-pin uno:13 expected 1 at 0.604 sPASS in 0.60 s simulated (2.9 s wall) · billed 1 s · exit 0| step | fields | what it does |
|---|---|---|
delay |
500ms, 2s, 100us; a bare number is milliseconds |
waits until the simulated clock reaches t0 + n |
wait-serial |
a string, at most 512 bytes | substring match, byte-exact, over the serial received since the previous wait-serial. If the budget runs out first the run ends timeout |
write-serial |
a UTF-8 string, or a list of bytes 0..255 |
writes to the primary board’s UART |
expect-pin |
part-id, pin, expected (0/1, high/low, true/false; value also accepted) |
reads the pin once, immediately. A mismatch fails the run and reports the level it actually read |
set-control |
part-id, control, value (number, string or boolean) |
pressed on a button presses or releases it; other controls are the part’s sensor controls and attributes. An unknown control fails the run and lists the ones that part has |
take-screenshot |
part-id, save-to and/or compare-with, tolerance |
captures a PNG of that part at that point in the run |
Any step may carry a name: alongside its key, purely for the log.
Limits: 200 steps and 20 screenshots per run.
Everything is simulated time
Section titled “Everything is simulated time”delay: 600ms is 600 milliseconds of the guest’s clock, not of the wall
clock. The same scenario takes the same simulated time on a loaded runner
and on an idle one, which is what makes the result reproducible - and what
you are billed for.
Driving inputs
Section titled “Driving inputs”steps: - wait-serial: READY - set-control: part-id: btn1 control: pressed value: 1 - delay: 50ms - set-control: part-id: btn1 control: pressed value: 0 - wait-serial: "pressed"part-id is the id from your diagram.json (or the .vlx), never an
internal one. A step naming a part that does not exist is refused before
the run starts, with scenario_part_missing and exit 2 - nothing billed.
Bytes go the other way with write-serial, and come back byte-exact,
including values above 0x7f:
steps: - wait-serial: ECHO READY - write-serial: "hi\n" - wait-serial: "hi"Screenshots
Section titled “Screenshots”- take-screenshot: part-id: oled1 save-to: shots/oled.pngThe PNG of that part is captured at that point of the run and written to
save-to, resolved against the project directory. A run whose only
expectation is a screenshot passes once the last screenshot is taken.
Flags that become steps
Section titled “Flags that become steps”You can express the simple cases without a file, and they combine with one:
| flag | equivalent |
|---|---|
--expect-text X |
a final wait-serial X |
--screenshot-part P --screenshot-time T |
delay T then take-screenshot P |
--fail-text Y |
not a step: Y is watched on every serial chunk of every board, for the whole run, and ends it failed the moment it appears |
Typing at the board
Section titled “Typing at the board”--interactive forwards your stdin to the primary board’s serial port,
coalesced every 20 ms. It works together with a scenario: both write to the
same UART, in arrival order. Closing stdin ends the input, not the run -
the budget or the scenario does that.
Not supported yet
Section titled “Not supported yet”- Touch steps (
touch-press,touch-move,touch-release). The CLI refuses them at lint time rather than skipping them. - Screenshot comparison, as above.
- Custom chips in a CI run: a
[[chip]]in the config is refused withfeature_unsupported.
See Exit codes for what each failure returns to your job, and velxio.toml for how a scenario is attached to a project by default.