Repeatable flows
For AI agents: this page is Markdown at https://squiggle.sh/docs/flows/repeatable.md. Every docs page is listed at https://squiggle.sh/llms.txt, and all of them are one file at https://squiggle.sh/llms-full.txt.
A flow should pass on the tenth run as it did on the first. Declare where it starts, stub the network when the data changes, reuse flows instead of copying them, and mark the steps that may not apply.
Start from a known screen
A flow starts wherever the last run left the app. If that was a different screen, or a form still holding the last run’s text, the flow fails for a reason that has nothing to do with your app. Declare the starting point with prerequisite:
flow: edit profileprerequisite: route: Home reset: autosteps: …Before step 1, Squiggle takes the app to that screen. It reports this on its own line, so getting there never changes the flow’s result. reset says how:
| reset | What it does |
|---|---|
auto | The default. Scrolls to the top, then goes back, then relaunches the app, stopping as soon as the screen is reached. |
scroll | Only scrolls to the top. |
back | Only goes back. |
relaunch | Always relaunches the app, even when it is already on the screen. Use it for forms: typing adds to what a field already holds, and only a fresh launch clears it. |
squiggle flow does this whenever the flow declares a prerequisite; --no-auto-correct skips it. In Studio, turn on Return to start screen in the Replay options. When you record in Studio, the screen you start on is saved as the prerequisite.
For a screen deep in the app, with data behind it, a save slot is faster than tapping your way there. Get the app there once and run squiggle save cart-full; before a run, squiggle load cart-full puts the screen and its stores back. See Save and load app state, and App state for finding which saved values make a flow fail.
Stub the network
When a screen shows data from a server, the data changes and the flow breaks. Answer the request yourself:
- network.stub: urlMatch: /api/orders status: 200 body: '{"orders":[]}'- tap: { testID: refresh }- waitFor: { networkIdle: true }- assert: { visible: { text: "No orders yet" } }- network.stub: clearStubs need the app launched with network capture: run with --capture-net, or turn on Capture network in Studio’s Replay options. The same launch makes waitFor: { networkIdle: true } work. urlMatch is part of the request’s URL, matched anywhere in it and ignoring case; the first stub that matches answers.
To stub every request a flow makes, build the stubs from a run instead of writing them: --stubs in Run settings. To see what your app does when requests fail, see Find what breaks on a bad network.
Reuse flows
Call another flow with runFlow, and pass env to change its test data:
flow: checkoutenv: USER: dana@example.comsecrets: PASS: correct-horsesteps: - runFlow: file: login.yaml env: { USER: admin@example.com }Give each flow defaults that let it run on its own. Then squiggle flow login still works by itself, and the caller decides which account to use. Put passwords and tokens under secrets instead of env: they work the same way, but their values are never written to recordings or run logs.
Steps that may not apply
- tap: { text: Accept, when: { visible: { text: Cookies } } }- tap: { testID: dismiss-promo, optional: true, label: close the promo }- repeat: times: 3 commands: - tap: { testID: load-more }- retry: maxRetries: 2 commands: - tap: { testID: submit } - assert: { route: Done }- branch: if: { visible: { text: Signed in } } then: - tap: { testID: continue } else: - runFlow: { file: login.yaml }| Key | What it does |
|---|---|
when | Runs the step only if the condition holds. Takes the same checks as assert. |
optional | The step may fail without failing the run. |
label | Names the step in the report. |
repeat | Runs its commands a number of times. |
retry | Runs its commands again if one fails, up to maxRetries times. |
branch | Runs then or else, depending on a condition. |
For every key, see the Flow syntax reference.