Core Usage

Waiting & Asserting

When an agent goes off the rails, it's usually because it waited wrong, not because it picked the wrong element. After a click or a fill, the page is still moving — what you wait for and how you confirm the action really landed decides whether your script is solid or brittle. This page spells out how wait waits, how expect asserts, how --observe shows you what changed, and how --if-present keeps optional steps idempotent.

Pick the right wait condition — wait

After every action that changes the page, wait for a specific condition instead of blindly sleeping a fixed number of milliseconds. wait supports these forms:

bash
$ chrome-use wait @e1                     # until an element appears
$ chrome-use wait 2000                    # dumb sleep, milliseconds (last resort)
$ chrome-use wait --text "Success"        # until this text shows up on the page
$ chrome-use wait --url "**/dashboard"    # until the URL matches the pattern (glob)
$ chrome-use wait --load networkidle       # until the network goes idle (post-navigation settle)
$ chrome-use wait --load domcontentloaded  # until DOMContentLoaded
$ chrome-use wait --fn "window.myApp.ready === true"  # until a JS condition holds

After any action that changes the page, pick one of these three:

⚠️ Avoid bare wait 2000
Outside of debugging, don't use a fixed-millisecond wait 2000 — it makes scripts slow and brittle. The timeout defaults to 25 seconds, so waiting on a real condition never costs much, and it continues the instant the condition is met.

Confirm the action landed — expect

Once an action is done, confirm the result with an assertion instead of pulling a snapshot and eyeballing it. expect is a pass/fail verb with an exit code (0 pass / 1 false / 2 can't tell), so it composes with && and chrome-use batch, and it costs ~1 line — no reading a whole snapshot.

bash — expect assertions
$ chrome-use click @e8 && chrome-use expect "#toast" visible      # did the toast pop up?
$ chrome-use expect count ".result" ">=" 1                        # did results load?
$ chrome-use expect text @e3 contains "Saved"                    # success message?
$ chrome-use expect url contains /dashboard                       # did navigation land?
$ chrome-use expect "#spinner" gone                               # finished loading?
$ chrome-use requests --clear && chrome-use click @save \
    && chrome-use expect request /api/save --status 2xx           # POST fired and 2xx?
$ chrome-use expect no-errors                                     # no console errors?

expect waits for you

expect polls until the condition holds within the timeout window, so you often don't need a separate wait. The supported conditions are:

--not inverts the check, and --no-wait checks once without polling.

ℹ️ Prerequisites for requests and console
expect request can only see requests captured after tracking started — run requests --clear first; no-errors needs console capture — run console first. See Troubleshooting for more.

See what an action changed — --observe

Add --observe to any action that changes the page (click / fill / type / select / check / press / eval) and you skip the manual "action → wait → snapshot → diff" dance: the result carries an observed delta — interactive rows added/removed (new toasts and validation hints come through the alert channel too), URL changes, and requests fired; if nothing moved, it returns {changed:false}.

bash
$ chrome-use click @e8 --observe   # one ~20-80 token reply shows the dialog/toast/new rows that popped up
✅ expect or --observe?
For a hard pass/fail gate (with exit codes, &&, batches) use expect; to see what actually happened and get a structured diff, use --observe.

Fill a whole form at once — form fill --map

Instead of writing N separate fill / select / check steps, pass a {label or selector: value} map: it resolves each field by <label>, aria-label, placeholder, name, then CSS, and auto-dispatches to the right control type (string → text/dropdown/radio, true/false → checkbox). It can also submit for you (--submit "<text|selector>") and returns {filled, submitted, errors} — where errors are the inline validation messages, so a rejected submit tells you why in the same call.

bash — form fill --map
$ chrome-use form fill --map \
    '{"Email":"a@b.com","Country":"US","Subscribe":true}' \
    --submit "Sign up"
ℹ️ Rich-text editors are the exception
For rich-text editors like DraftJS / Monaco / CodeMirror, fill them individually with fill — it handles them; form fill covers standard controls. More input detail in Interacting.

Optional steps — --if-present

Add --if-present (aliased --optional) to any selector-based action, and when the target doesn't exist it becomes a no-op that still succeeds (↷ skipped, exit code 0) instead of erroring — no need to read the page first to decide, and the flow can be re-run over and over.

bash
$ chrome-use click ".cookie-accept" --if-present   # dismiss a cookie banner that may not be there
$ chrome-use check "#opt-in" --if-present
⚠️ Only skips "element not present"
--if-present only lets a missing target through; a real failure (say, the element is there but won't click) still errors and is not silently swallowed.

Next