Advanced / Workflow
Testing & Dogfood
Even though both drive a browser, testing comes in two very different flavors: one
freezes repetitive manual checks into a rerunnable regression suite,
like frontend unit tests; the other
systematically walks the whole app like a real user,
digging out bugs and UX problems and bringing along complete reproduction evidence.
chrome-use does both — the former with chrome-use test, the latter with a dogfood exploratory flow.
Two flavors of testing
First figure out which one you're doing, then pick the section below. Their goals, outputs, and how they run are all different.
Freeze "open it, click around, check it's right" into *.yaml suites whose exit code drops straight into CI.
Systematically walk the app like a user, recording as you explore, leaving a screenshot, recording, and repro steps for every issue.
Write a rerunnable test suite
Turn the repetitive manual chore of "open it, click around, confirm it's right" into a rerunnable suite, just like frontend unit tests. Every time you find a regression, add a case — the more you use it, the more the suite is worth.
$ chrome-use test <suite.yaml> [--launch | --session <name>] [--json]
- Exit code is 0 when every case passes, 1 if any fails — drop it straight into CI.
- By default it launches a brand-new isolated browser in a
cu-testsession (deterministic, reproducible) and closes it when done. - Pass
--session <name>to run against an already-connected session (for example a real Chrome attached viachrome-use extension connect). - Failing cases automatically save a screenshot to
cu-test-artifacts/<case>.png.
Suite format (YAML)
A suite is made of an optional setup (run once before all cases) and a list of cases. Each case has
steps (the actions to perform) and assert (the checks that must hold).
suite: chatgpt smoke # label (optional)
setup: # run once before all cases (optional)
- account: chatgpt/huayue # inject a cookie-use saved login (optional)
- open: https://chatgpt.com/ # …or any normal step
cases:
- name: home loads logged in
steps: # steps reuse chrome-use's own commands
- open: https://chatgpt.com/
- wait: { load: networkidle }
assert: # every assertion must hold for the case to pass
- url: { contains: chatgpt.com }
- visible: "#prompt-textarea"
- name: composer takes text
steps:
- fill: { sel: "#prompt-textarea", text: "hi" }
assert:
- text: { sel: "#prompt-textarea", contains: hi }
- eval: "!!window.__NEXT_DATA__"
Steps — actions
Each step is a "single-key map" whose key is a chrome-use command.
| Step | Meaning |
|---|---|
open: <url> | navigate |
click: <selector|@ref> | click |
fill: { sel: <s>, text: <t> } | clear then type |
type: { sel: <s>, text: <t> } | type (without clearing) |
press: <key> | key press (e.g. Enter) |
wait: <ms> / wait: { load: networkidle } / wait: <selector> | wait |
scroll: <up|down|...> or { dir: down, px: 500 } | scroll |
eval: "<js>" | run JS |
Assertions — checks
Every assertion ultimately compiles down to a truthy eval.
| Assert | Passes when |
|---|---|
url: { contains|equals|matches: <v> } | page URL matches |
visible: <selector> | element exists and is laid out |
hidden: <selector> | element is absent / not laid out |
text: { sel: <s>, contains|equals|matches: <v> } | element text matches |
count: { sel: <s>, eq: <n> } | exactly N elements match |
eval: "<js>" | the JS expression is truthy |
steps all run in order first, then assert is evaluated one by one, independently. For more ways to write wait and assertions, see
Waiting & Asserting; every command used in steps is covered in the Command Reference.
expect verbchrome-use expect <condition> is the command-line sibling of a suite's assert: —
same contract, exit 0 when it holds, 1 when it doesn't, so it chains straight into a shell:
chrome-use click @save && chrome-use expect "#toast" visible.
It polls until the condition holds or it times out. To write a whole "click this, fill that, assert" flow as a single pass, see
Single-pass scripting.
Login and multiple accounts
account: <id> injects a
cookie-use
saved login into the test session. It can be used in two places:
setup:— injected once before all cases, so the whole suite runs as that account.- Inside a case's
steps:— switch accounts partway through the suite. The new login takes effect at that case's nextopen/navigation, so putaccount:beforeopen. This is how you test "admin can see X, a regular member can't" in one suite, or hop back and forth between accounts.
setup:
- account: myapp/admin # start as admin
cases:
- name: admin sees the settings tab
steps:
- open: https://app.example.com/
assert:
- visible: "#settings-tab"
- name: member does NOT see it
steps:
- account: myapp/member # switch account; takes effect on next open
- open: https://app.example.com/
assert:
- hidden: "#settings-tab"
- name: back to admin
steps:
- account: myapp/admin
- open: https://app.example.com/
assert:
- visible: "#settings-tab"
account: relies on an installed cookie-use; if you don't use it, just leave the line out.
A failed account: switch (wrong id, or cookie-use not installed) fails that case and gives a clear reason.
For more account details see Login & Credentials.
Workflow
- First do it by hand with
open/snapshot/evalto nail down the selectors. - Write it up as a case and put it in a
*.yamlsuite. chrome-use test suite.yaml— green means all good; red shows the failing assertion + a screenshot.- Find another regression later? Add a case. Run the whole suite in CI.
eval assertion against known content instead). Steps run in order, and a failing step fails the case immediately.
Exploratory testing & bug hunting (dogfood)
The other kind of testing has no predefined script; instead it systematically walks a web app end to end, finding bugs, UX issues, and other glitches, and producing complete reproduction evidence for each finding — step-by-step screenshots, a reproduction recording, detailed repro steps — so the results can be handed straight to the relevant team.
vercel.com → vercel-com), put output under ./dogfood-output/, and let the scope be the whole app. Pick a target URL and you're ready to go; only sites that need a login call for account credentials. The examples below all use the session dogfood, the directory dogfood-output/, and the target https://vercel.com — swap in your own.
chrome-use directly — the binary uses the fast Rust client; npx detours through Node.js and is noticeably slower.
The six-step flow
| Step | What it does |
|---|---|
| 1. Initialize | create the session, output directory, report file |
| 2. Authenticate | log in if needed, save the state |
| 3. Orient | navigate to the starting point, take the first snapshot |
| 4. Explore | systematically visit pages, test features |
| 5. Document | screenshot + record every issue as you find it |
| 6. Wrap up | update the summary counts, close the session |
Initialize and log in
$ mkdir -p dogfood-output/screenshots dogfood-output/videos
# start a named session, navigate to the target
$ chrome-use --session dogfood open https://vercel.com
$ chrome-use --session dogfood wait --load networkidle
When login is required, use snapshot -i to find the form's @ref, fill it in and submit, then save the state for reuse. For OTP / email verification codes, ask the user and only enter them after they reply.
$ chrome-use --session dogfood snapshot -i
# find the login form's refs, fill in the credentials
$ chrome-use --session dogfood fill @e1 "you@example.com"
$ chrome-use --session dogfood fill @e2 "your-password"
$ chrome-use --session dogfood click @e3
$ chrome-use --session dogfood wait --load networkidle
# after a successful login, save the state for reuse
$ chrome-use --session dogfood state save dogfood-output/auth-state.json
Explore and gather evidence
Start from the main navigation and visit the top-level sections one by one; within each section test the interactive elements (click buttons, fill forms, open dropdowns/dialogs); check empty states, error handling, boundary inputs; and run real end-to-end flows (create, update, delete).
console/errors, start the session with
AGENT_BROWSER_CAPTURE_CONSOLE=1, for example
AGENT_BROWSER_CAPTURE_CONSOLE=1 chrome-use --session dogfood open <url>, otherwise they return empty.
$ chrome-use --session dogfood snapshot -i
$ chrome-use --session dogfood screenshot --annotate dogfood-output/screenshots/dashboard.png
$ chrome-use --session dogfood errors
$ chrome-use --session dogfood console
snapshot -i — find clickable/fillable elements (buttons, inputs, links);
snapshot (no flag) — read page content (text, headings, data lists).
Repro first: interactive vs static
Exploring and documenting happen in the same pass: when you find an issue, stop and record it immediately, then continue. Match the evidence granularity to the issue type.
Interactive / behavioral issues
These need user interaction to reproduce (features, UX, action-triggered console errors) — use full repro with a recording and step-by-step screenshots: start recording first, then walk the steps at a human pace, screenshot each step, and finally capture the broken state.
# 1. start recording before the repro
$ chrome-use --session dogfood record start dogfood-output/videos/issue-001-repro.webm
# 2. walk the steps at a human pace, pause 1-2s between actions, screenshot each step
$ chrome-use --session dogfood screenshot dogfood-output/screenshots/issue-001-step-1.png
$ sleep 1
# perform the action (click / fill etc.), sleep 1, then step-2 screenshot…
# 3. capture the broken state (pause first so the viewer can see it)
$ sleep 2
$ chrome-use --session dogfood screenshot --annotate dogfood-output/screenshots/issue-001-result.png
# 4. stop recording
$ chrome-use --session dogfood record stop
Static / visible-on-load issues
These are visible without interaction (typos, placeholder text, truncated text, misalignment, console errors thrown on load) — a single annotated screenshot is enough, no recording, no multi-step repro; set "reproduction recording" to N/A.
$ chrome-use --session dogfood screenshot --annotate dogfood-output/screenshots/issue-002.png
What the report looks like
Every finding collects into one Markdown report (say dogfood-output/report.md): a severity-bucketed summary count at the top, then one ### ISSUE- block per issue carrying its severity, page, repro steps, and evidence file paths.
# Dogfood report · vercel.com
## Summary
- Critical: 0
- Major: 1
- Minor: 1
### ISSUE-001 · [Major] Toast never dismisses after save
- Page: /dashboard/settings
- Repro:
1. Open Settings, rename, click Save
2. Wait 5s
- Expected: toast dismisses after ~3s
- Actual: toast stays forever
- Evidence: screenshots/issue-001-result.png, videos/issue-001-repro.webm
### ISSUE-002 · [Minor] Footer copyright year still says 2019
- Page: /
- Evidence: screenshots/issue-002.png (repro recording: N/A)
Wrap up
The goal is to find 5–10 well-documented issues and stop — depth of evidence matters more than count; 5 with full repro beat 20 vague ones. Then reread the report, update the severity counts at the top (every ### ISSUE- block must be reflected in the summary totals), and finally close the session.
$ chrome-use --session dogfood close
A few hard rules
- Reproduction is everything: every issue needs evidence, but match the granularity — interactive issues need a recording and step-by-step screenshots, static ones just one annotated screenshot.
- Verify reproducibility before collecting evidence: before recording/screenshotting, retry at least once to confirm it reproduces reliably; if it's flaky it doesn't count as a valid issue.
- Write the report incrementally: append to the report the moment you find something, don't save it all for the end. A finding survives even if the session is interrupted.
- Never delete output: don't
rmscreenshots, recordings, or the report partway through, and don't close and restart the session. Only move forward. - Type like a human: when recording, fill forms with
type(character by character); only usefillwhen you're not recording and want speed. - Pace the recording for humans: add
sleep 1between actions andsleep 2before the final result screenshot so the recording is watchable at 1x. - Check the console: many issues aren't visible in the UI but show up as JS errors or failed requests.