# Repeatable flows

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`:

```yaml
flow: edit profile
prerequisite:
  route: Home
  reset: auto
steps: …
```

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](https://squiggle.sh/docs/reference/cli#save-and-load-app-state), and [App state](https://squiggle.sh/docs/flows/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:

```yaml
- network.stub:
    urlMatch: /api/orders
    status: 200
    body: '{"orders":[]}'
- tap: { testID: refresh }
- waitFor: { networkIdle: true }
- assert: { visible: { text: "No orders yet" } }
- network.stub: clear
```

Stubs 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](https://squiggle.sh/docs/run/settings). To see what your app does when requests fail, see [Find what breaks on a bad network](https://squiggle.sh/docs/flows/network).

## Reuse flows

Call another flow with `runFlow`, and pass `env` to change its test data:

```yaml
flow: checkout
env:
  USER: dana@example.com
secrets:
  PASS: correct-horse
steps:
  - 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

```yaml
- 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. |

> **when, optional and label go inside the step**
>
> A step has exactly one key, its kind. Write `- tap: { testID: dismiss-promo, optional: true }`, not `optional: true` on a line of its own.

For every key, see the [Flow syntax](https://squiggle.sh/docs/reference/flow) reference.
