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

# Steps and expected outcomes

> The actions a step can take and the closed grammar of conditions it can assert.

You write English; Testorim writes steps. This page documents what those steps
are, because reading them is the fastest way to understand why a run did what
it did, and because a saved test shows them to you.

## Actions

| Action        | Target                                          | Value                                |
| ------------- | ----------------------------------------------- | ------------------------------------ |
| `navigate`    | A URL, absolute or relative to the base address | None                                 |
| `click`       | The element to click                            | None                                 |
| `type`        | The field to type into                          | The text                             |
| `select`      | The dropdown                                    | The option                           |
| `press`       | The element to focus first (optional)           | The key, e.g. `Enter`                |
| `hover`       | The element                                     | None                                 |
| `scroll`      | The anchor (optional)                           | None                                 |
| `upload`      | The file input                                  | The fixture to upload                |
| `wait`        | None                                            | Milliseconds                         |
| `assert`      | What to check                                   | The condition                        |
| `assertDb`    | None                                            | A read-only query payload            |
| `prompt_user` | The question shown to you                       | The field name to bind the answer to |

## Assertion conditions

`assert` takes a condition from a **closed grammar**. Anything outside it fails
the step with the list of what is supported. It never degrades into "is it
visible?", which is how an enabled button used to satisfy a check for
*disabled*.

<Tabs>
  <Tab title="State">
    | Condition                                           | Passes when                            |
    | --------------------------------------------------- | -------------------------------------- |
    | `visible` · `exists` · `present`                    | The element is on the page and painted |
    | `hidden` · `not visible` · `absent` · `not present` | It is not visible, or not there at all |
    | `enabled` / `disabled`                              | The control's enabled state            |
    | `checked` / `unchecked`                             | The checkbox, radio or switch state    |
    | `editable` / `readonly`                             | The field accepts input, or does not   |
    | `selected` / `not selected`                         | The option, tab or chip is chosen      |
  </Tab>

  <Tab title="Text">
    | Condition                      | Passes when                                       |
    | ------------------------------ | ------------------------------------------------- |
    | `contains:<text>`              | The element's text contains it (case-insensitive) |
    | `not contains:<text>`          | It does not                                       |
    | `text:<text>` · `exact:<text>` | The whole normalised text equals it               |
    | `value:<text>`                 | A form control's value equals it exactly          |
  </Tab>

  <Tab title="Page">
    | Condition | Passes when                                                                                                                                              |
    | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `loaded`  | The document finished loading, its own HTTP status is under 400, it has real visible content, and its title and first heading carry no error-page signal |

    `loaded` is a check on the **document**, whatever the target says, which
    is what "see if the pricing page loads" actually means.
  </Tab>
</Tabs>

<Note>
  Keywords are case-insensitive and tolerate natural wrappers, so *"is
  disabled"* and *"loads successfully"* both parse.
</Note>

### Scope is decided by the target

If the target names a **control**, meaning a control noun (`button`, `link`,
`field`, `toggle`, `dropdown`, `checkbox`…), an identifier (`submitBtn`,
`cart_link`) or a selector (`#total`, `.toast`), the condition is checked against that
control and nothing else. A heading elsewhere on the page cannot satisfy it.

If the target is `page`, `body`, empty, or prose that names no control at all
(*"the welcome message"*), the check widens to the page, keeping the condition
exactly as written.

A control-scoped text check that fails while the text *is* somewhere else on
the page says so, and is attributed to the test rather than the app.

### Asserting something is gone

`hidden` and `absent` are the one case where failing to find the element is the
*point*. Testorim runs a dedicated absence probe that searches wider than
normal resolution (text split across inline children, accessible names,
identity attributes, `aria-labelledby`, every frame, open shadow roots) and
then requires three pieces of evidence before certifying absence: that this
target was seen or resolved earlier, on this same page, and that some other
element from that page is still there (so a blank, crashed or signed-out render
cannot pass). Absence is read twice, half a second apart.

If any of that is missing, the step fails and names what was missing. It does
not quietly pass because nothing matched.

## Step options

| Option            | What it does                                                                                                                                               |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Expectation**   | `success` (default) or `failure`. A `failure` step passes only when the app refuses the action.                                                            |
| **Timeout**       | Per-step, in milliseconds. Mostly used to let an `assert` poll long enough for a slow operation to finish.                                                 |
| **Retry on fail** | Re-run the step once after a short wait. For genuine flake such as animations or lazy loads, never as a band-aid for a real bug. `press` is never retried. |

## Extra checks bolted onto a step

<AccordionGroup>
  <Accordion title="API assertion" icon="webhook">
    After the step's UI action, check a request it triggered. Filter by URL
    substring or glob, optionally by method, and assert an exact status or a
    bucket (`2xx`, `4xx`…).

    *"Click Sign in and verify POST /api/login returned 200."*
  </Accordion>

  <Accordion title="Download expectation" icon="download">
    For a step that triggers a download, assert the filename contains
    something, the content type matches, or the file is at least N bytes. The
    file is captured either way.

    *"Click Export CSV and check the file is named invoice-something and is
    over 1 KB."*
  </Accordion>

  <Accordion title="Database assertion" icon="database">
    `assertDb` runs a read-only `SELECT` against a Postgres connection you
    configured in project settings, inside a read-only transaction. Anything
    that is not a `SELECT` is rejected. Assert an exact row count, a minimum
    row count, or that one cell equals a value.

    The query text is never echoed back into a report.
  </Accordion>
</AccordionGroup>

## Coverage gaps

If you ask for a check and the plan does not contain it, a judge catches it.
The planner gets one chance to write the missing check; anything still
uncovered becomes a visible skipped step saying so, and the run does not pass.

A gap is never *inferred* to be stricter than you asked. If you said "until a
colour is chosen", a plan that checks both sides of that transition covers it.
the judge does not invent a "before any colour is chosen" requirement you never
stated.


## Related topics

- [Core capabilities](/introduction/core-capabilities.md)
- [Writing tests in plain English](/test-creation/writing-tests.md)
- [Run status](/test-execution/run-status.md)
- [Support](/resources/support.md)
- [Create your first test](/getting-started/first-test.md)
