Skip to content

Capture a page after interaction

Most screenshots worth taking are not of a page as it first loads. This guide drives the page into the state you want before anything is measured or captured.

Add setup steps

Put the interaction under setup. Steps run in order, before clip and marks are resolved:

screenshots/recipes/order-detail.yaml
name: order-detail

setup:
  - click: { role: link, name: Orders }
  - fill: { label: Search }
    value: Acme
  - wait: { css: '.order-row' }

clip: { css: '.order-row', contains: Acme Corp, pad: 20 }

Each step is a mapping led by one verb. Some verbs take a second key alongside: fill takes value, select takes option, and press and type take on. The full list is in the steps reference.

Wait for the right thing

wait accepts either a number of milliseconds or a query. Prefer the query: it finishes as soon as the element exists, and it fails loudly if the element never arrives, where a fixed wait silently captures a half-rendered page.

If the page needs a moment to settle after the element appears, set site.settle in your configuration rather than adding fixed waits to every recipe.

Handle something that may not be there

A consent banner that appears only on a fresh browser profile will break a recipe that always clicks it. Wrap those steps in optional, which ignores failures:

screenshots/recipes/order-detail.yaml
setup:
  - optional:
      - click: { role: button, name: Accept cookies }
  - click: { role: link, name: Orders }

Get past a confirm the browser draws

alert, confirm and prompt are drawn by the browser rather than by the page, so no query reaches one and no click closes one. Left alone they are dismissed, which means a click on a control guarded by confirm() quietly takes the cancel branch and the shot is of the page that never changed. Say what to do with them before the step that raises one:

screenshots/recipes/order-detail.yaml
setup:
  - dialog: accept
  - click: { role: button, name: Delete }
  - wait: { text: Order deleted }
  - dialog: dismiss

The setting holds until another dialog step replaces it, so a recipe that accepts one dialog and dismisses the next writes both. dialog: accept takes a value, which is what a prompt() is answered with.

This is the failure that looks like a working recipe: nothing errors, and the screenshot is of the wrong state. A shot that comes back as though the click did nothing is the first place to check.

Repeat a step

Use repeat when the same interaction has to happen several times:

screenshots/recipes/order-list.yaml
setup:
  - click: { role: link, name: Orders }
  - repeat: 3
    steps:
      - click: { role: button, name: Load more }
      - wait: 250

repeat is capped at 1000 iterations. To run steps once per item in a list instead, use each — see Share setup between recipes.

Carry a value between steps

readValue puts the contents of an input into a variable that later steps can read:

screenshots/recipes/refund.yaml
setup:
  - readValue: { label: Order total }
    as: total
  - fill: { label: Refund amount }
    value: $total

Variables are substituted in steps only. A $total written in callouts or marks is treated as literal text.

Capture a page that opens in a new tab

Open the second page explicitly, give it a name, and switch to it:

screenshots/recipes/invoice.yaml
setup:
  - openPage: /orders/1042/invoice
    as: invoice
    viewport: { width: 800, height: 1200 }
  - usePage: invoice

Every step after usePage drives that page, and so does the capture.

Leave a note for the next reader

Any step accepts comment, which is ignored at run time. It rides alongside a verb rather than being one, so a comment cannot be a step on its own:

screenshots/recipes/order-detail.yaml
setup:
  - wait: { css: '.order-row' }
    comment: the list has settled, so the row can be measured