Skip to content

Undo what a shot changed

A recipe that clicks New order to photograph one has created an order. It is still there on the next run, where the query now matches two rows and clips the wrong one. This guide gives you three ways to stop that.

Closing the browser is not one of them. Every shot already gets its own browser, so cookies and local storage never carry from one recipe to the next — and none of that was the problem. What the application was asked to do outlives the browser that asked.

Option 1: undo it in the recipe

teardown is steps run after the shot has been taken. Put the reverse of your setup in it:

screenshots/recipes/new-order.yaml
name: new-order
install: guide

setup:
  - click: { role: button, name: New order }
  - fill: { label: Customer }
    value: Acme Corp
  - click: { role: button, name: Save }

clip: { css: '.order-row' }

teardown:
  - dialog: accept
  - click: { role: button, name: Delete }

The steps are the same vocabulary as setup, against the same page. A delete guarded by a browser confirm() needs dialog: accept before the click, because an unanswered dialog is dismissed and the delete never happens.

It also runs after a shot that failed

Which is when there is most left behind: the order was created, and then the clip matched nothing. A teardown that only ran on success would leave exactly the runs that need it.

When the teardown itself fails, the error names it — but only if the shot succeeded. When both failed, the error names the shot, because the reason there is no screenshot is worth more than the trouble tidying up after it had.

Option 2: share the undo between recipes

Three recipes that each create an order write the same teardown three times. Move it into a macro, and both halves become one line:

screenshots/macros/delete-order.yaml
steps:
  - dialog: accept
  - click: { role: button, name: Delete }
  - wait: { text: No orders yet }
screenshots/recipes/new-order.yaml
name: new-order
install: guide

setup:
  - use: create-order
clip: { css: '.order-row' }
teardown:
  - use: delete-order

Option 3: do not create anything

The most reliable teardown is the one with nothing to undo. Where your application can be started with the record already present — a seeded fixture, a demo account, a query parameter — the recipe navigates to it and writes nothing:

screenshots/recipes/new-order.yaml
name: new-order
install: guide

url: http://localhost:3000/?seed=order-row
clip: { css: '.order-row' }

This is worth the setup for a shot list that runs in CI, where a failed teardown leaves the next run to start from a state nobody planned.

What teardown does not do

  • It does not run for source: file, which opens no page. A recipe with both is refused rather than ignored.
  • A recipe with retries tears down after every attempt, since every attempt ran the setup again.
  • It cannot undo what the application sent elsewhere. An email or a webhook fired during setup has already gone.