Score 100% on the quiz to continue
Cypress Tap, by hand
Drive Cypress Tap yourself: run a spec, wait for a fresh verdict, read the app, and time travel.
Before you hand Cypress Tap to an agent, you will drive it yourself, once, by hand. Everything your agent does in this course, it does with the commands in this lesson. Knowing them is how you check its claims instead of believing them.
What Cypress Tap is
Cypress Tap is a set of commands, npx cypress tap <command>, that drive an already running cypress open session from the terminal. It ships inside the Cypress App (Cypress 15.21.0 and later), so there is nothing extra to install. It is free, needs no Cypress Cloud account, and runs only on your machine.
Three things to know before the first command:
- It attaches to
cypress open, never tocypress run. Tap reads the same Command Log and DOM snapshots you see in the Cypress window, so the window has to be open. Headless runs, like the ones in CI, are not reachable. - It only works in Chromium-based browsers: Chrome, Chromium, Edge, and Electron. That is why the
cy:openscript you wrote in the setup lesson passes--browser chrome. - The terminal and the Cypress window share one browser frame, with no lock between them. If you click around the app in the Cypress window while Tap (or your agent) is reading it, each of you sees the other's changes. Watch the window; do not touch it.
👨🏫 Cypress Tap is in beta. Its commands and output can still change in any release, which is one more reason the setup lesson pinned Cypress to an exact version.
The workflow
Every test in this course is written the same way, whether by you or by your agent. These six steps come from the project's Tap guide, docs/cypress/07-cypress-tap.md:
- Arrange the page state with a minimal spec. Tap can only read the app after a spec has taken it somewhere.
- Read the real app with
aria,dom, andinspectto pick selectors and expected texts. - Write the test: act and assert.
- Wait for a fresh verdict.
- Read the results and the Command Log.
- Refine the assertions using what the app actually shows.
Step 4 is the one people get wrong, so it gets its own section right below.
Waiting for a fresh verdict
run returns before the spec has started. And until the new run starts, status keeps showing the previous run's verdict. So a passed right after a run may describe the run before it.
The only safe way to wait is to compare startedAt:
- Before you run (or save) the spec, note the current
startedAtfromnpx cypress tap status --json. Not afterwards: by then the new run may have started, and you would wait forever for a run that already happened. - Poll
npx cypress tap status --jsonabout every two seconds, one call at a time. - Accept only
passedorfailed, for the spec you expect, with astartedAtdifferent from the one you noted. - Give up after a bounded number of attempts, and stop at once if
statusexits with1.
Here is that loop as a Bash script, which uses Node.js to read the JSON. Run it in a terminal at the root of your project: on macOS and Linux, the default terminal works; on Windows, use Git Bash, as recommended in the prerequisites. Replace the value of base with the startedAt you noted (or leave it as null before the very first run):
base="null"
for i in $(seq 1 60); do
out=$(npx cypress tap status --json 2>/dev/null) || { echo "status failed"; break; }
s=$(echo "$out" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{try{const j=JSON.parse(d);console.log(j.status+"|"+j.startedAt)}catch{console.log("?|?")}})')
st=${s%%|*}; sa=${s#*|}
if { [ "$st" = passed ] || [ "$st" = failed ]; } && [ "$sa" != null ] && [ "$sa" != undefined ] && [ "$sa" != "?" ] && [ "$sa" != "$base" ]; then
echo "$st $sa"; break
fi
sleep 2
doneYou will rarely type it yourself: it is what your agent runs, and the project's Tap guide carries it. But you should be able to read it, because when an agent says "the test passed", this is how it should know.
Saving a spec runs it
Once a spec is selected (the one status shows), Cypress watches it: saving it starts a new run on its own, exactly as when you work in the Cypress window. So after editing the selected spec, do not also call run, or you put two runs in flight. Note startedAt, save, and wait.
👨🏫 The watcher only reruns the selected spec. Saving a different spec does not run it; use npx cypress tap run <spec> for that.Commands overview 📖
Every command accepts these shared flags:
--jsonprints machine-readable output instead of a table.--session <pid>(or-s) picks a session when more than onecypress openis running.--timeout <ms>sets how long Tap waits for the session to answer (default30000).--help(or-h) prints the command's usage.
Every command exits with 0 on success and 1 on failure, so an agent (or a script) can tell the difference without reading the output.
sessions
Lists the running cypress open sessions: process id (PID), project folder, testing type, and browser.
Syntax:
npx cypress tap sessionsExample:
npx cypress tap sessions --jsonstatus
Reports where the session is in its lifecycle, which spec is selected, and the counts of the last run. The stages are not connected, browser not selected, spec not selected, loading, running, passed, and failed. Only passed and failed are verdicts; everything else means "not finished".
Syntax:
npx cypress tap status [--json]Example:
npx cypress tap status --jsonWith --json, look for status, spec, startedAt (when the current run started, null until a run begins), totalTests, and results (passed, failed, pending, and skipped counts).
specs
Lists the specs the session can run, most recently modified first.
Syntax:
npx cypress tap specsExample:
npx cypress tap specsrun
Starts a spec and returns immediately, before a single test has run. It does not wait for, or print, the verdict.
Syntax:
npx cypress tap run <spec-path>Example:
npx cypress tap run cypress/e2e/explore.cy.jsreporter
Without --test-id, it prints the spec overview, like the Cypress window's reporter panel: each test with its result and a test id (such as r2). With --test-id, it prints that test's full Command Log: hooks, every command, network requests, and the error, if any. --attempt picks a retry attempt (1-based).
Syntax:
npx cypress tap reporter [--test-id <id>] [--attempt <n>]Example:
npx cypress tap reporter --test-id r2👨🏫 Copy test ids from reporter's output. Never infer them from the order of the tests in the spec.command
Prints one row of a test's Command Log in detail: its snapshots and console properties. --command-id is the row number from reporter --test-id.
Syntax:
npx cypress tap command --test-id <id> --command-id <row> [--depth <n|all>]Example:
npx cypress tap command --test-id r2 --command-id 3pin
Time travel. It loads a command's DOM snapshot into the app frame, so the next aria, dom, or inspect reads the page as it was at that moment. --at picks the snapshot: before, after, or a 1-based index. --clear releases the pin and returns to the live app.
Syntax:
npx cypress tap pin --test-id <id> --command-id <row> [--at <before|after|n>]
npx cypress tap pin --clearExample:
npx cypress tap pin --test-id r2 --command-id 1👨🏫 Always run pin --clear when you are done reading a pinned snapshot. Until you do, every read shows the past.aria
Prints the page's accessibility tree: each element's role, accessible name, and state, as a screen reader sees them. It is the best first look at a page: it shows what is there and how it is labeled, in a few lines. It reads up to 200 nodes by default (--max-nodes, or -m).
Syntax:
npx cypress tap aria [--selector <css>] [--at <index>]Example:
npx cypress tap ariadom
Prints the markup of the whole page, or of the element a CSS selector matches. Use it for exact texts and attributes, such as data-testid, id, type, and placeholder. It prints up to 30000 characters by default (--max-chars, or -m).
Syntax:
npx cypress tap dom [--selector <css>] [--at <index>]Example:
npx cypress tap dom --selector '[data-testid="emoji-card"]' --at 1inspect
Prints one element's tag, attributes, accessibility node, box model, and its most relevant computed styles. --selector is required.
Syntax:
npx cypress tap inspect --selector <css> [--at <index>]Example:
npx cypress tap inspect --selector '[data-testid="emoji-card"]' --at 1👨🏫 When a selector matches more than one element,dom,aria, andinspectrefuse to guess. They exit with1and list the candidates with an index, and you pick one with--at <index>, which is 0-based here.pin --atis different: there, a number is 1-based, because it counts a command's snapshots, not elements. A deliberately broad selector is a good way to discover what is on the page.
Telemetry
Cypress Tap records which command you ran, with which flags, and whether it failed. The Cypress reference documents the opt-out as the environment variable CYPRESS_DISABLE_GUEST_TELEMETRY=0. Yes, a 0 that disables something: check the reference before relying on it, since Tap is in beta.
Suggested content 📚 and documentation links 🔗
- Cypress Tap reference
- Introducing Cypress Tap (the announcement)
- The Command Log
- Cypress Basics, if
cy.containsand.shouldneed a refresher
Exercise 🎯
You will drive Cypress Tap by hand from a first cy.visit to a test that opens a product, reading the app at each step.
- 1. In one terminal, at the root of your project, run
npm run cy:open. When the browser opens on the spec list, leave it there: select nothing. Keep this terminal running for the whole course; Tap needs it. - 2. In a second terminal, run
npx cypress tap sessions(one row, your project), thennpx cypress tap statusandnpx cypress tap status --json. The stage isspec not selected, andstartedAtis missing ornull. Runnpx cypress tap specs: there are none yet. - 3. Create
cypress/e2e/explore.cy.js. Its only job, for now, is step 1 of the workflow, arranging the state:
describe('Exploring Emoji Mart', () => {
it('opens the store', () => {
cy.visit('/')
})
})- 4. Run
npx cypress tap specsagain and find your spec. NotestartedAtfromnpx cypress tap status --json. Then runnpx cypress tap run cypress/e2e/explore.cy.js, and pollstatus --json(or run the Bash script from the section "Waiting for a fresh verdict") until you get a fresh verdict. Watch the Cypress window as it runs, without touching it. - 5. Run
npx cypress tap reporterand copy the test's id. Then runnpx cypress tap reporter --test-id <id>and read its Command Log. Look for network rows (requests the app made while the test ran): there are none. Emoji Mart loads everything it needs with the page and makes no requests after that, so there will be nothing to intercept in this course. - 6. Now read the app. Run
npx cypress tap ariaand find the store's buttons. Some of them have a name you can read; some have no name at all. Write down which ones. Then runnpx cypress tap dom --selector '[data-testid="emoji-card"]': it refuses, because six cards match, and lists them. Run it again with--at 1to read the second card, andnpx cypress tap inspect --selector '[data-testid="emoji-card"]' --at 1to see its attributes and box. - 7. Look in that
ariaoutput for a control that takes you back from a product's page. There is none, because no product page is open: the test stopped on the store. That is step 1 again: to read a state, a test has to reach it. Note the currentstartedAt, then change the test so it opens the Rocket and asserts that its page is showing, and save:
describe('Exploring Emoji Mart', () => {
it('opens the Rocket', () => {
cy.visit('/')
cy.contains('[data-testid="emoji-card"]', 'Rocket').click()
cy.contains('button', '← Back to all emojis').should('be.visible')
})
})👨🏫 Saving the selected spec runs it, so do not callrunthis time: wait for the fresh verdict. Then runnpx cypress tap ariaand confirm the role and the exact text of the back control. I wrotebuttonbecause that is the roleariashowed me. If yours shows something else, the assertion follows the app, not my example.
- 8. Time travel: run
npx cypress tap reporter --test-id <id>(the id may have changed, so copy it again), find the row number of thevisit, and runnpx cypress tap pin --test-id <id> --command-id <row>. The Cypress window shows the store again, before the click. Runnpx cypress tap ariaand see the six products, even though the live page shows the Rocket. Then runnpx cypress tap pin --clear. - 9. Commit
cypress/e2e/explore.cy.jsand push.
🙊 The instructor solution will be linked here soon.
Show the world what you learned 🌎
To show your professional network what you learned in this lesson, post the following on LinkedIn.
I am taking the "Agentic Testing with Cypress Tap" course by @Walmyr Lima e Silva Filho at the @Talking About Testing School, and I just drove Cypress Tap by hand: I ran a spec from the terminal, waited for a fresh verdict instead of trusting a stale one, read the Command Log, the accessibility tree, and the DOM, and time traveled to an earlier snapshot. #TalkingAboutTesting #TATSchool #AgenticTestingWithCypressTap #CypressTap #AgenticTesting
👨🏫 Remember to tag me in your post. Here is my LinkedIn profile.
Quiz
You run a spec with npx cypress tap run and immediately read status --json. It says passed. Before trusting it, what must you check, and why?
Score 100% on the quiz to continue