> ## 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.

# Test format

> How Momentic tests and modules are written on disk. One simplified YAML format for web and mobile, with single-key command aliases, natural-language targets, modules, and conditionals.

Momentic tests and modules are YAML files. The on-disk format favors readable,
human-editable shapes: single-key command aliases, natural-language targets, and
no volatile IDs.

Web (`momentic`) and mobile (`momentic-mobile`) are separate CLIs that share
this format. The shapes on this page apply to both; only the command set and
targeting differ. For the full list of commands, see the
[Web steps reference](/reference/commands/index) and the
[Mobile steps reference](/reference/mobile-commands/index).

## File shapes

A test is YAML with a `fileType`, a readable kebab-case `id`, and a list of
`steps`. A module uses a `module` `fileType` and adds a human-readable `name`.
Both use the same `id` key for their stable identifier.

<Tabs>
  <Tab title="Web">
    A web test sets the starting `url`.

    ```yaml checkout.test.yaml theme={null}
    fileType: momentic/test/v2
    id: checkout-happy-path
    url: https://shop.example.com
    steps:
      - click: Checkout
      - type:
          text: jeff@example.com
          into: Email input
      - assert: An order confirmation is visible
    ```

    ```yaml log-in.module.yaml theme={null}
    fileType: momentic/module/v2
    id: log-in
    name: Log in
    parameters:
      - name: USERNAME
        default:
          string: test@example.com
      - name: PASSWORD
    steps:
      - type:
          text: "{{ env.USERNAME }}"
          into: Email input
      - type:
          text: "{{ env.PASSWORD }}"
          into: Password input
      - click: Log in
    ```
  </Tab>

  <Tab title="Mobile">
    A mobile test adds a `platform` key (`ios` or `android`, lowercase on disk).

    ```yaml checkout.test.yaml theme={null}
    fileType: momentic/mobile-test/v2
    id: checkout-happy-path
    platform: ios
    steps:
      - openApp: com.example.shop
      - tap: the Checkout button
      - type:
          text: jeff@example.com
          into: the Email field
      - assert: An order confirmation is visible
    ```

    ```yaml log-in.module.yaml theme={null}
    fileType: momentic/mobile-module/v2
    id: log-in
    name: Log in
    platform: android
    parameters:
      - name: USERNAME
        default:
          string: test@example.com
      - name: PASSWORD
    steps:
      - type:
          text: "{{ env.USERNAME }}"
          into: the Email field
      - type:
          text: "{{ env.PASSWORD }}"
          into: the Password field
      - tap: the Log in button
    ```

    Mobile tests also accept top-level settings (`disabled`, `labels`, `retries`,
    `defaultChannel`, `defaultTag`, `defaultEnv`, `emulator`, `ai`, and the local
    artifact paths). See the [Mobile steps reference](/reference/mobile-commands/index)
    for the full list.
  </Tab>
</Tabs>

Each module parameter is `{ name, default?, enum? }`. `default` is a value
object (`{ string }` or `{ javascript }`) used when an invocation does not
supply an input for that parameter.

## Before and after sections

Alongside `steps`, a test can declare optional `before` and `after` sections.
They are sibling top-level keys with the same step shape as `steps`. In the
editor these are the **Setup** (before) and **Teardown** (after) sections.

```yaml checkout.test.yaml theme={null}
fileType: momentic/test/v2
id: checkout-flow
url: https://shop.example.com
before:
  - module: ../modules/log-in.module.yaml # authenticate before the main steps
steps:
  - act: Add a "Gravity Blanket" to the cart and check out
  - assert: The order confirmation page is shown
after:
  - act: Empty the cart # cleanup that runs even if the main steps fail
```

* `before` runs first, then `steps`, then `after`.
* If a `before` step fails, the main steps are skipped and the test is marked as
  a setup failure.
* `after` runs after the main steps regardless of whether they passed, which
  makes it the right place for teardown (resetting state, deleting test data).

Use `before` for prerequisites the main steps assume, most commonly
[authentication](/guides/auth/overview).

<Warning>
  `before` and `after` run **only when the whole test runs**. Running a single
  step, or running "from" / "until" a step in the main body (common while
  editing), skips both sections. If a mid-body run starts on a logged-out page,
  it is because the `before` section that logs in did not run. Run the whole
  test to exercise it.
</Warning>

## Step shapes

Every step is one of:

* A bare command alias for commands that take no options (web: `- refresh`,
  `- goBack`; mobile: `- killApp`, `- openNotifications`).
* A simplified single-key scalar where the string is the target description
  (`- click: Submit`, `- tap: the Submit button`).
* A detailed single-key object when the command needs more than a description.
* An AI action: `- act: <goal>`, or a detailed form with `goal` and `version`.
* A module invocation: `- module: <relative path or id>`, or a detailed form
  with `path` and `inputs`.
* A [conditional](#conditionals) with one `if` key.
* A [while loop](#while-loops) with one `while` key.

```yaml theme={null}
- click:
    on: Submit
    timeout: 5000
- type:
    text: jeff@example.com
    into: Email input
    pressEnter: true
```

Detailed options always live under the command key; sibling top-level keys are
rejected. Shared metadata such as `retries`, `skipped`, `comment`, and `saveAs`
can be set on any step. `saveAs` stores the step's return value on `env.<NAME>`
for later steps to read.

## Targets

Target-bearing commands take a natural-language description of the element. The
key name varies by command (`on`, `into`, `from`, `that`, and so on), which the
command reference lists per command.

<Tabs>
  <Tab title="Web">
    Web commands also accept a CSS selector (`css`) or absolute coordinates
    (`coords`, or split `x` / `y` when a coordinate needs templating).

    ```yaml theme={null}
    - click:
        on: Submit
    - click:
        css: "button[type='submit']"
    - click:
        coords: 120, 240
    - click:
        x: 120
        y: "{{ env.SUBMIT_Y }}"
    ```
  </Tab>

  <Tab title="Mobile">
    Mobile commands locate elements with a single string. There are no CSS
    selectors and no absolute pixel targets. A target is either a
    natural-language description or a **percent pair** (`"50%, 50%"` is the
    center of the screen).

    ```yaml theme={null}
    - tap: the Add to cart button
    - tap: "50%, 50%"
    - tap:
        on: the kebab menu in the top-right
        longPress: true
    ```

    `swipe` and `scrollTo` take an `in` key naming the scroll container: a
    hardcoded alias (`screen`, `app`, `webview`), a natural-language description
    of a scrollable region, or raw `{ x, y, delta }` coordinates.

    ```yaml theme={null}
    - scrollTo:
        on: the "Delete account" row
        in: the settings list
    ```
  </Tab>
</Tabs>

## Commands

Each command is a single-key alias. The available commands differ by platform:

* **Web**: clicks, navigation, tabs, storage, and network mocking. See the
  [Web steps reference](/reference/commands/index).
* **Mobile**: taps, swipes, device buttons, and app lifecycle. See the
  [Mobile steps reference](/reference/mobile-commands/index).

A few shorthands are worth knowing: `doubleClick: Submit` is shorthand for
`click: { on: Submit, times: 2 }`, and `rightClick: Submit` is shorthand for
`click: { on: Submit, rightClick: true }`.

## AI actions and assertions

`act` performs a goal in natural language. `assert` checks a condition and fails
the test by default. `extract` pulls a value and fails the step when the payload
does not match its `schema`, storing the result on `env.<saveAs>`.

```yaml theme={null}
- act:
    goal: Complete the new-user signup flow using a fresh email.
    postcondition: The welcome screen is visible.
- assert: The dashboard chart is visible and not cut off
- extract:
    goal: The discounted subtotal in the order summary
    saveAs: SUBTOTAL
    schema:
      type: object
      properties:
        amount:
          type: number
      required: [amount]
```

See [Agentic testing](/core-concepts/agentic-testing) and
[Writing assertions](/core-concepts/writing-assertions) for usage patterns.

## Element checks

`checkElement<...>` aliases are deterministic assertions against a single
element. They take a target and, where relevant, a `name` and `value`. Aliases
compose a subject and a condition:

* Subjects: `Element` (existence-style: `Exists`, `Visible`, `Enabled`,
  `Editable`, `Focused`), `ElementContent`, `ElementAttribute`, `ElementName`,
  `ElementStyle` (content-style: `Contains`, `Equals`, `StartsWith`).
* Each positive condition has a paired negation, e.g. `DoesNotExist`,
  `NotVisible`, `DoesNotContain`, `DoesNotEqual`, `DoesNotStartWith`.

<Tabs>
  <Tab title="Web">
    The target is `element` (or `css` / `coords` / `x`+`y`).

    ```yaml theme={null}
    - checkElementVisible: The sign-in button
    - checkElementContentEquals:
        element: The price
        value: "$9.99"
    ```
  </Tab>

  <Tab title="Mobile">
    The target is `on` (natural-language description or percent pair). Mobile
    also has whole-screen text checks: `checkScreenContains` and
    `checkScreenDoesNotContain`.

    ```yaml theme={null}
    - checkElementVisible: the Sign in button
    - checkElementContentEquals:
        on: the price label
        value: "$9.99"
    - checkScreenContains: Order #12345
    ```
  </Tab>
</Tabs>

## Conditionals

A conditional is a single `if` key holding exactly one condition plus a `then`
list. The condition is an inline `assert`, an element or screen check, or a
`javascript` expression. There is no `else`; use a separate step instead.

```yaml theme={null}
- if:
    checkElementVisible: the cookie banner
    then:
      - click: Accept all
```

## While loops

A while loop is a single `while` key holding a `do` list plus a loop control: an
optional condition and an optional `maxIterations` cap. At least one of the two
must be present.

The condition takes the same forms as a [conditional](#conditionals) — an inline
`assert`, an element or screen check, or a `javascript` expression — and is
re-evaluated before every iteration. The `do` steps run each pass and share the
test's `env`, so a step that writes a variable with `saveAs` is visible to the
condition on the next iteration.

```yaml theme={null}
- while:
    checkElementVisible: the Load more button
    maxIterations: 20
    do:
      - click: Load more
```

Provide only `maxIterations` to repeat a fixed number of times:

```yaml theme={null}
- while:
    maxIterations: 5
    do:
      - click: Next
```

## Modules

Module invocations reference a module file with a path relative to the file that
contains the invocation:

```yaml theme={null}
steps:
  - module:
      path: ../modules/log-in.module.yaml
      inputs:
        USERNAME: env.QA_EMAIL # shorthand string = JS expression
        FIRST_NAME:
          string: Alice # literal
        PASSWORD:
          javascript: "return env.QA_PASSWORD"
```

Each value in `inputs` is one of:

* A shorthand string, evaluated as a **JavaScript expression**. Use this to
  reference env vars (`env.PARAM`) or call helpers. It is **not** mustache
  templating.
* `{ string: ... }`: a literal string.
* `{ javascript: ... }`: the script's return value is bound to the parameter.

When a module takes no inputs, the scalar form is enough:

```yaml theme={null}
steps:
  - module: ../modules/log-in.module.yaml
```

See [Modules](/core-concepts/modules) for parameters, defaults, and caching.

## File references

Relative file references resolve from the YAML file containing the step, not
from the project root and not from the test file when the step is inside a
module.

```yaml theme={null}
steps:
  - javascript: ./scripts/setup.js
  - authLoad: ./auth-state.json
after:
  - authSave: ./auth-state.json
```

## Variables and templating

Any string field can include `{{ expression }}` templating, evaluated at runtime
against the test's `env` map. Use `saveAs` on any step to write its return value
to `env.<NAME>` for later steps. See [Variables](/core-concepts/variables) for
setting and reading values across steps.

## Durations

Every duration written in the simplified format is milliseconds. Format
converters translate to and from the internal second-based fields automatically:

```yaml theme={null}
- assert:
    that: The chart is visible
    timeout: 10000 # 10 seconds
```

## IDs

The format omits per-step and per-command IDs from disk. The runtime mints fresh
internal IDs on load. Step-cache continuity across saves is preserved by the
snapshot identity cache, so cached steps stay cached after a reorder or file
move when the content is stable.

See [Step cache](/reliability/step-cache) and
[Auto-heal](/reliability/auto-heal) for cache behavior on reruns.

## Related

* [Web steps reference](/reference/commands/index): the full web command set.
* [Mobile steps reference](/reference/mobile-commands/index): the full mobile
  command set.
* [Modules](/core-concepts/modules), [Variables](/core-concepts/variables), and
  [Writing assertions](/core-concepts/writing-assertions).
* [Migrate to the simplified format](/get-started/migrate-to-simplified-format).
