---
title: Statement Types
description: The statement types in a YAML E2E test — ACTION, VERIFY, DRAFT, URL, STEP, and iframe handling.
---

# Statement Types

<div class="view-markdown-wrapper">
<ViewMarkdown />
</div>

| Type               | Syntax                                              | Description                                        |
| ------------------ | --------------------------------------------------- | -------------------------------------------------- |
| ACTION (`action:`) | `- intent: Enter email` + `action: input_text`      | Fast replay with AI self-healing fallback          |
| ACTION (`js:`)     | `- intent: Click login` + `js: "await ..."`         | Fast replay (Playwright code) with AI self-healing |
| VERIFY             | `- VERIFY: page shows welcome message`              | AI assertion, optional `js:` cache                 |
| DRAFT              | `- intent: Click the login button`                  | AI resolves at runtime (~5-10s)                    |
| URL                | `- URL: /path`                                      | Navigation shorthand                               |
| Code               | `- description: ...` + `js: await request.get(...)` | Inline Playwright code (no self-healing)           |
| STEP               | `- STEP: Login` + `statements: [...]`               | Group related actions                              |
| IF/ELSE            | `- IF: cookie banner is visible` + `THEN: [...]`    | Conditional execution                              |
| WHILE              | `- WHILE: more items to load` + `DO: [...]`         | Repeat until condition                             |
| Function           | `- call: "file#export"` + `args: [...]`             | Call custom TypeScript function                    |
| Template           | `- template: ./path.yaml`                           | Inline reusable statement flow                     |

IF/ELSE and WHILE are covered in [Conditionals & Loops](/local/yaml-tests/conditionals-loops); functions and templates have their own pages.

## VERIFY

Asserts a condition using AI. Use the `VERIFY:` shorthand (unquoted key):

```yaml
statements:
  - VERIFY: The success message is displayed
  - VERIFY: The order total is $49.99
    js: "await expect(page.getByTestId('order-total')).toHaveText('$49.99')"
```

The `js:` cache speeds up simple checks. If the `js:` assertion fails, it automatically falls back to AI verification using the natural language statement.

## ACTION

Fast deterministic replay (<1s) with AI self-healing fallback. Use the **structured `action:` form** for all supported actions:

```yaml
statements:
  - intent: Type email address
    action: input_text
    text: "{{USER_EMAIL}}"
    locator: "getByLabel('Email')"
```

`intent` describes **what** the step should accomplish in natural language; the `action:`/`locator:` field is a cache for fast replay. When the cache fails (e.g., a locator becomes stale), Shiplight's agentic layer falls back to the intent to self-heal.

For complex interactions that don't map to a supported action (e.g., drag-and-drop), use the [`description: + js:` code step](/local/code-step) — but note raw JS does **not** self-heal:

```yaml
statements:
  - description: Drag the card to the Done column
    js: |
      const card = page.getByText('My Task');
      const target = page.getByTestId('column-done');
      await card.dragTo(target);
```

## STEP (grouping)

Groups related statements under a label.

```yaml
statements:
  - STEP: Fill in the registration form
    statements:
      - intent: Type "John" in the first name field
      - intent: Type "Doe" in the last name field
      - intent: Type "john@example.com" in the email field
```

## Frames

For elements inside iframes, use `frame_path` with `action:` form:

```yaml
- intent: Click Hello inside iframe
  action: click
  frame_path:
    - "iframe#main"
  locator: "getByText('Hello')"
```
