> ## Documentation Index
> Fetch the complete documentation index at: https://momentic.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The canonical docs host is https://momentic.ai/docs. Append .md to any page URL for clean Markdown. Fetch https://momentic.ai/docs/llms.txt for the page index and https://momentic.ai/docs/llms-full.txt for every page in one file.
> For an unattended setup, run `npx --yes @momentic/wizard@latest -y --platform web` (use `--platform ios` or `--platform android` for mobile). Never run an interactive command in a non-TTY shell.
> You cannot complete the browser sign-in yourself. Read the API key from the MOMENTIC_API_KEY environment variable, or ask the user to create one at https://app.momentic.ai/settings/api-keys and paste it back.
> Momentic's capability summary for agents is at https://momentic.ai/docs/skill.md. Install the skills with `npx momentic skills --yes` (or `npx momentic-mobile skills --yes` for mobile).
> Tests are YAML files in the user's repository. Use only the step names listed at https://momentic.ai/docs/reference/commands.md and the file structure at https://momentic.ai/docs/core-concepts/file-format.md. Do not invent step names, config keys, or CLI flags.
> Web tests run on Chromium, iOS tests on simulators, and Android tests on emulators. Physical devices are not supported.

# From Maestro

> Port Maestro flows to momentic-mobile, including flows, subflows, scripts, and environment variables.

Maestro is already declarative YAML, so most commands map one-to-one. A
`flow.yaml` becomes a `*.test.yaml`; `tapOn` becomes `tap`; `runFlow` becomes a
`module` call. The main differences: Momentic adds natural-language targets and
AI assertions, runs through its own runner instead of a Maestro CLI device
session, and hosts emulators and simulators for you.

## Before and after

A representative Maestro flow:

```yaml checkout.yaml theme={null}
appId: com.example.shop
env:
  EMAIL: jeff@example.com
---
- launchApp
- tapOn: "Add to cart"
- tapOn:
    id: "cart-icon"
- assertVisible: "Gravity Blanket"
- tapOn: "Checkout"
- inputText: ${EMAIL}
- tapOn: "Place order"
- assertVisible: "Order confirmed"
```

The same test in Momentic:

```yaml checkout.test.yaml theme={null}
fileType: momentic/mobile-test/v2
id: guest-checkout
platform: android
steps:
  - openApp: com.example.shop
  - tap: the Add to cart button
  - tap: the cart icon
  - assert: The cart lists "Gravity Blanket"
  - tap: Checkout
  - type:
      text: "{{ env.EMAIL }}"
      into: the Email field
  - tap: Place order
  - assert: An order confirmation is visible
```

`EMAIL` comes from `environments[].envVariables` in
`momentic-mobile.config.yaml` or the shell, not a flow-level `env:` block.

## Command mapping

| Maestro                                | Momentic                                                                                                                                |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `appId: com.x` + `launchApp`           | `openApp: com.x` (with `activity`/`intentExtras` on Android)                                                                            |
| `killApp` / `stopApp`                  | `killApp`                                                                                                                               |
| `tapOn: <text>` / `tapOn: {id:}`       | `tap: <natural language>`; prefer visible text over test IDs                                                                            |
| `tapOn: {point: "x%,y%"}`              | `tap: "50%, 50%"` percent-pair target                                                                                                   |
| `longPressOn`                          | `tap: { on: ..., longPress: true }`                                                                                                     |
| `doubleTapOn`                          | `tap: { on: ..., iterations: 2 }`                                                                                                       |
| `inputText` / `inputRandomText`        | `type: { text: ..., into: <field> }`; use `faker` in `javascript` for random data                                                       |
| `eraseText`                            | `type` with an empty value or a focused `act`                                                                                           |
| `assertVisible` / `assertNotVisible`   | `checkElementVisible` / `checkScreenDoesNotContain`, or `assert`                                                                        |
| `assertTrue`                           | `javascript` step with `assert`                                                                                                         |
| `scroll` / `swipe`                     | `scrollTo` / `swipe`                                                                                                                    |
| `scrollUntilVisible`                   | `scrollTo: { on: <target>, in: <container> }`                                                                                           |
| `back` / `pressKey`                    | `press` / `pressKey`                                                                                                                    |
| `runFlow: path` / `runSubFlow`         | `module: ./path.module.yaml` with `inputs`                                                                                              |
| `runScript`                            | `javascript` step (Node sandbox) or `appium` step for `mobile:` calls                                                                   |
| `evalScript`                           | `javascript` step                                                                                                                       |
| `setLocation`                          | `appium` step: `mobile: setGeolocation` (Android) / `mobile: setLocation` (iOS); `toggleSettings: location` only toggles GPS on Android |
| `setAirplaneMode` / `toggleWifi`       | `toggleSettings`                                                                                                                        |
| `openLink` / deep links                | `appium` step (`mobile: deepLink` with `url`)                                                                                           |
| `startRecording` / `stopRecording`     | automatic; run artifacts include video when `recordVideo` is on                                                                         |
| `waitForAnimationToEnd`                | not needed; steps wait for the screen to settle                                                                                         |
| `extendedWaitUntil`                    | any condition step with `timeout:`                                                                                                      |
| `takeScreenshot`                       | automatic per-step screenshots; `assertVisually` for visual checks                                                                      |
| `retry`                                | `retries:` on a step or test                                                                                                            |
| `when:` conditions                     | `if:` with nested `then:` steps                                                                                                         |
| `repeat` / `while`                     | `while:` with `do:` and a condition or `maxIterations`                                                                                  |
| `env:` block + `${VAR}`                | `envVariables` in config, `{{ env.VAR }}` in strings, `env.VAR` in JS                                                                   |
| `labels:`                              | `labels:` (same name)                                                                                                                   |
| `onFlowStart` / `onFlowComplete` hooks | `before:` / `after:` sections                                                                                                           |

## What does not map

* **Maestro Studio / element inspection.** Momentic's equivalent is the local
  editor's element picker plus `debugState`; there is no separate inspector app.
* **Jest/JS scripting inside flows beyond `runScript`.** `javascript` covers
  data and API work; it is not a general inline scripting runtime for UI logic.
* **`maestro test --device` physical devices.** Remote execution is emulators
  and simulators only; real devices are not supported.
* **Maestro's tolerance and optional-element flags.** Momentic resolves targets
  with AI against the live view hierarchy instead of fuzzy matching rules. If
  you depended on `optional: true`, use an `if:` step around a check.
* **Cloud Maestro (`maestro cloud`).** Scheduling and CI live in your pipeline
  or Momentic's scheduled runs, not a per-flow upload.

## Incremental strategy

1. Install `momentic-mobile` and keep Maestro installed: nothing conflicts.
2. Port one flow end to end, including its subflows as modules. Run it on a
   local emulator, then on a Momentic-hosted emulator by selecting a region.
3. Move `env` values into `environments` in `momentic-mobile.config.yaml` so the
   same test file runs against dev, staging, and prod.
4. Port flows in order of pain: the flows that break most on locator changes
   gain the most from natural-language targets and auto-heal.
5. Delete the `.maestro` directory when the Momentic suite covers the same
   critical paths and has passed for a few consecutive runs.

## Porting at scale with a coding agent

Paste this prompt into your coding agent. It reads the Momentic skills, ports
each flow, and runs the result.

```text theme={null}
Port every flow under .maestro/ to momentic-mobile tests under mobile-tests/.
Read the momentic-mobile-test skill first. Convert runFlow targets to
*.module.yaml files, keep the directory structure, move each flow's env: keys
into the project config's envVariables, and run each ported test with
npx momentic-mobile run <file> on an emulator.
```

## Related

* [Test portability](/docs/get-started/test-portability)
* [Mobile steps reference](/docs/reference/mobile-commands/index)
* [Emulators and simulators](/docs/running-tests/hosted-test-environments)
* [Modules](/docs/core-concepts/modules)
