# Actions

> Enter text, click controls and use the keyboard while understanding when Retest waits or refuses an action.

Source: https://rehearsal.dev/retest/docs/essentials/actions

Use actions to move through a flow by clicking, typing and pressing keys. This page explains the common web actions and what Retest waits for before sending input.

## Open, fill and click

Use `goto` to open a web page, `fill` to replace a field's value, and `click` to press a control. Await each call before starting the next one on the same app.

```ts filename="tests/task.retest.ts"
import { expect, test } from '@rehearsal-labs/retest'

test('saves a task', async ({ page }) => {
  await page.goto('/')
  await page.getByTestId('task-title').fill('Release checklist')
  await page.getByTestId('save-task').click()
  await expect(page.getByTestId('saved-task')).toHaveText('Release checklist')
  await expect(page.getByTestId('saved-task')).toBeVisible()
})
```

`goto('/')` resolves against your app's `baseUrl` and waits for the page's load event.

`fill` focuses the field, selects its existing value and replaces it. It supports textareas and inputs of type `text`, `search`, `email`, `url`, `tel`, `password` and `number`; other controls fail with `unsupported`. Chrome and WebKit insert the text in one edit without a keydown for each character, while Firefox types key by key. If your app needs each key event, use `press` for each character.

`click` presses the element's centre with the mouse, or taps it on a touch target. It confirms that input reached the button. The two checks confirm the saved text and its visibility, because `toHaveText` alone can read a hidden element. Always check the outcome your flow needs after sending input.

## What an action waits for

A click needs one element that is visible, stable, enabled and not covered. Retest brings the element into view and checks it again before sending input. If it is still hidden, moving, disabled or covered when time runs out, the action reports `not_actionable` with the reason.

If no element matches, the action waits and then reports `not_found`; several matches fail immediately as `ambiguous`. Refine the [locator](https://rehearsal.dev/retest/docs/essentials/locators.md), close a covering overlay or wait for the app's state to change before running the flow again.

The checks depend on the action. `hover` needs a visible, stable and uncovered element but can hover a disabled control. `press` needs a visible and enabled element that keeps keyboard focus. Click readiness checks native disabled state, so `aria-disabled` alone does not prevent a click.

## Press keys

Use a locator's `press` when a key should go to a specific control. Use `page.keyboard.press` for an app shortcut or when the flow has already placed focus where you need it.

```ts
await page.getByTestId('task-title').press('ControlOrMeta+A')
await page.getByTestId('task-title').press('Enter')
await page.keyboard.press('Escape')
```

`ControlOrMeta` uses Meta on macOS and Control elsewhere. If your app uses Enter to submit a form, follow it with a check of the result. The page keyboard sends its key to whatever has focus without checking a locator; prefer the locator form when you need that guarantee.

`press` accepts named keys such as `Tab` and `ArrowDown`, one character, or modifiers joined with `+`, such as `Shift+Tab`. Write `Control` and `Meta`, not `Ctrl` and `Cmd`. The [Test API](https://rehearsal.dev/retest/docs/reference/test-api.md) lists the supported keys and modifiers.

On Chrome on macOS, Retest supports editing shortcuts for select all, undo, redo, caret movement and deletion. Copy, paste and cut shortcuts reach page listeners but do not edit the field.

## Choose options and tick controls

For a task form with a native priority select and a notification checkbox, use `select` and `check` to request the state you want. Replace these labels with the controls in your own form.

```ts
await page.getByLabel('Priority').select('High')
await page.getByLabel('Priority').select({ value: 'high' })
await page.getByLabel('Notify me').check()
await expect(page.getByLabel('Notify me')).toBeChecked()
```

The two select calls show alternative ways to identify an option, by its visible label or its value attribute. `select` uses keyboard input and works only on an HTML `<select>`; use clicks for a custom menu. For `<select multiple>`, pass a list of labels or values.

Firefox refuses a request for several options before looking for the select, even when those options are already selected. It also refuses any keyboard change to a multiple select. Selecting one option that is already selected alone succeeds without input.

WebKit supports only some adjacent selections; others fail with `unsupported`. Use a Chromium target for other multiple selections; [Browsers](https://rehearsal.dev/retest/docs/platforms/browsers.md) covers engine support.

`check` clicks only when the control is not already checked, then reads its state without clicking again. `uncheck` requests the opposite state for checkboxes and switches. To untick a radio button, choose another one, because `uncheck` on a radio is unsupported.

## Scroll when the flow needs it

You do not need to scroll before clicking a control that Retest can bring into view. Scroll explicitly when the app reacts to scrolling, for example by loading more tasks.

```ts
await page.scroll({ y: 600 })
await page.locator('.task-list').scroll({ y: 300 })
```

Distances are in CSS pixels, with positive values moving right or down. `page.scroll` sends the wheel event at the viewport's centre; a locator sends it at its element's centre after checking readiness. The call finishes when the wheel arrives, so use a check if you need to wait for new content.

Firefox moves at most one page per wheel turn, even with a larger delta. To reach the end of a long box, make separate scroll calls with deltas no larger than the box's height.

## Set an action deadline

An action has 10 seconds by default, while `goto`, `reload`, `goBack` and `goForward` have 30 seconds. You can shorten one call's timeout when waiting longer would not help.

```ts
await page.getByTestId('save-task').click({ timeout: 2000 })
```

A call timeout cannot increase the configured budget or the time left in the test. To allow longer actions, change `timeouts.action` in [Configuration](https://rehearsal.dev/retest/docs/essentials/configuration.md). A deadline limits waiting; it does not make Retest repeat input.

## When the outcome is unknown

Retest sends an action once. If the browser disappears after input went out, Retest reports `outcome_unknown` because it cannot tell what the app did. A `timeout` with `inputSent: true` is uncertain too; the action may or may not have taken effect. Read the evidence and check the app's state before repeating a flow that may have created data.

This page covers web controls. Native apps use different actions, explained in [iOS simulator apps](https://rehearsal.dev/retest/docs/platforms/ios.md) and [macOS apps](https://rehearsal.dev/retest/docs/platforms/macos.md). Files, dialogs and drag operations have their own guides because they need additional handling.

## Next steps

- [Checks](https://rehearsal.dev/retest/docs/essentials/checks.md): Verify the result after an action.
- [Uploads and downloads](https://rehearsal.dev/retest/docs/guides/files.md): Upload files and keep downloads as evidence.
- [Dialogs and popups](https://rehearsal.dev/retest/docs/guides/dialogs-and-popups.md): Answer a dialog or follow a new page.
- [Drag and drop](https://rehearsal.dev/retest/docs/guides/drag-and-drop.md): Move an element onto another element.
