Skip to main content

Step Types

Prowl supports 28 step types. Most have both a shorthand form (concise, readable) and an explicit form (full control over selectors).

Navigate to a URL (relative to your target.url or absolute).

- navigate: "/"
- navigate: "/login"
- navigate: "https://example.com/page"

click

Click an element. Shorthand finds buttons by text, then falls back to any matching text.

# Finds by button role, then text
- click: "Sign In"
note

Shorthand click uses substring matching on button names. click: "Save" will match a button labeled "Save & New". For exact matching, use click: { selector: 'button:text-is("Save")' }.

fill

Fill an input field. Shorthand finds inputs by label or placeholder text.

# Finds by label, then placeholder
- fill:
"Email": "user@example.com"

type

Type into the currently focused element. Useful after clicking into a field.

- click: "Message"
- type: "Hello, I have a question."

copyText

Extract the text content of an element and store it as a runtime variable for use in later steps. Explicit-only.

  • selector (required) — element to read text from
  • as (required) — variable name to capture the text into
- copyText:
selector: "[data-testid='order-id']"
as: "ORDER_ID"
- navigate: "/orders/{{ORDER_ID}}"

press

Press a keyboard key on a specific element.

- press:
selector: "input[name='search']"
key: "Enter"

select / selectOption

Select a dropdown value. Shorthand finds by label, explicit uses a selector.

# Finds <select> by label, aria-label, or placeholder
- select:
"State": "FL"

assert

Mid-flow assertions. Fails the hunt immediately if the assertion fails.

- assert:
visible: "Welcome back"

- assert:
notVisible: "Error"

- assert:
urlIncludes: "/dashboard"

- assert:
urlEquals: "https://example.com/dashboard"

See the Assertions page for the full reference.

wait

Wait for text to appear on the page.

# Wait for text with default timeout
- wait: "Loading complete"
note

wait uses exact text matching (text="X"). It won't match substrings. For substring matching, use waitForSelector with text=X (without quotes).

waitForSelector

Wait for any Playwright selector to appear.

- waitForSelector:
selector: "[data-testid='results-table']"
timeout: 5000

waitForUrl

Wait for the URL to contain a substring.

- waitForUrl:
value: "/dashboard"
timeout: 10000

waitForNetworkIdle

Wait for all network requests to complete.

- waitForNetworkIdle:
timeout: 5000

waitForDownload

Wait for a file download to start and save it into the run's artifact directory. Explicit-only; pass null to accept any download. Author it immediately after the step that triggers the download.

  • filename (optional) — assert the downloaded file's name; the step fails if it doesn't match
  • timeout (optional, default 30000) — max time to wait, in ms
# Assert the filename
- click: "Export CSV"
- waitForDownload:
filename: "report.csv"

# Accept any download
- click: "Download"
- waitForDownload: null

The downloaded file is saved into the run directory (.prowl/runs/<timestamp>/).

onDialog

Handle browser-native dialogs (alert, confirm, prompt). Register the handler before the action that triggers the dialog.

- onDialog:
action: accept # or "dismiss"
- click: "Delete" # this triggers the confirm dialog

setInputFiles

Set files on <input type="file"> elements. Paths are relative to .prowl/.

# Single file
- setInputFiles:
selector: "[data-testid='avatar-upload']"
files: "fixtures/avatar.png"

# Multiple files
- setInputFiles:
selector: "[data-testid='attachments']"
files:
- "fixtures/doc1.pdf"
- "fixtures/doc2.pdf"

runHunt

Execute another hunt file inline. Enables reusable sub-flows like login.

# Run the hunt as-is
- runHunt: "login-flow"

Circular dependencies are detected automatically (A → B → A will error).

if

Conditionally run steps based on whether a selector is visible. Explicit-only.

  • visible or notVisible (exactly one) — the selector to test
  • then (required) — steps to run when the condition holds
  • else (optional) — steps to run otherwise
- if:
visible: ".cookie-banner"
then:
- click: ".accept"
else:
- wait: "Welcome back"

repeat

Repeat a block of steps a fixed number of times or while a selector condition holds. Explicit-only. The maxSteps guardrail is enforced across all iterations.

  • times (fixed count) or while (condition) — exactly one
  • while.visible / while.notVisible — the condition, when using while
  • maxIterations (required with while) — safety cap on loop count
  • steps (required) — steps to run each iteration
# Fixed count
- repeat:
times: 3
steps:
- click: ".load-more"

# Condition-based loop
- repeat:
while:
visible: ".load-more"
maxIterations: 10
steps:
- click: ".load-more"

mockRoute

Intercept a URL pattern and return a custom response — useful for testing error, loading, or empty states. Explicit-only.

  • url (required) — Playwright route pattern (e.g. **/api/users)
  • response.status (required)
  • exactly one of response.body or response.file
  • response.contentType (optional, default application/json)
- mockRoute:
url: "**/api/users"
response:
status: 200
body: '{"users":[{"id":1}]}'

unmockRoute

Remove a previously registered route mock. Explicit-only.

  • url (required) — must match the mocked route's pattern
- unmockRoute:
url: "**/api/users"

evalScript

Evaluate a JavaScript expression in the browser page context, optionally capturing the result as a runtime variable.

# Evaluate without capturing
- evalScript: "window.scrollTo(0, 0)"

The captured result is stringified and available as {{ROW_COUNT}} in later steps.

runScript

Execute an external JavaScript file in the browser page context. Explicit-only.

  • file (required) — path to the script; absolute, or relative to the config directory (.prowl/)
- runScript:
file: "scripts/seed-data.js"

assertScreenshot

Visual regression check: compare the current screenshot against a stored baseline. Explicit-only.

  • name (required) — baseline identifier; stored at .prowl/baselines/<name>.png
  • threshold (optional, default 0.1) — allowed pixel-difference fraction (0–1)
- assertScreenshot:
name: "homepage"
- assertScreenshot:
name: "checkout-form"
threshold: 0.05

On the first run the baseline is created and the step passes. On later runs the current screenshot is compared against the baseline; the step fails if the difference exceeds threshold, writing a diff image into the run directory. Accept new baselines with prowl update-baselines.

screenshot

Capture a screenshot at any point.

- screenshot:
name: "after-login"

hover

Hover over an element. Useful for triggering tooltips, dropdown menus, or hover states. Explicit-only — requires a selector.

- hover:
selector: "[data-testid='user-menu']"
- hover:
selector: "text=Profile"

scroll

Scroll the page by direction and optional pixel amount.

# Scroll down (default amount)
- scroll:
direction: "down"

# Scroll up by 300px
- scroll:
direction: "up"
amount: 300

scrollTo

Scroll to a specific element on the page. Explicit-only — requires a selector.

- scrollTo:
selector: "[data-testid='pricing-section']"
- scrollTo:
selector: "text=Footer"

Shorthand vs Explicit Quick Reference

ShorthandExplicit Equivalent
click: "Sign In"click: { selector: 'button:has-text("Sign In")' }
fill: { "Email": "val" }fill: { selector: 'input[placeholder="Email"]', value: "val" }
type: "text"fill: { selector: ':focus', value: "text" }
select: { "State": "FL" }selectOption: { selector: 'select[name="state"]', value: "FL" }
wait: "Welcome"waitForSelector: { selector: 'text="Welcome"' }
runHunt: "login"runHunt: { name: "login" }