skip to content

How does cypress tap pin let you read a past DOM snapshot from the terminal?

level: middleimportance: nice to knowfreq 18%

answer

  1. The app already knows; the terminal does not
  2. Attach to a session, do not start one
  3. Same time travel, different input device
  4. Pin first, then read the frame
  5. Clear the pin when you finish

basics

~20 s

cypress tap pin attaches to a running Cypress open-mode session and pins a command's DOM snapshot into the app-under-test frame, exactly as clicking that Command Log row would. cypress tap dom then prints that snapshot's HTML.

solid answer

~40 s

`cypress tap` is a CLI extension that attaches to an existing `cypress open` session and exposes what the app knows as terminal output. `cypress tap pin --test-id r4 --command-id 5 --at before` performs the same time travel as clicking that row: the application-under-test frame switches to that snapshot and stays there. While the pin is held, `cypress tap dom` returns the pinned page's HTML — the whole page, or the `outerHTML` of a `--selector` match, capped at 30,000 characters — and `aria` and `inspect` read the same state. `cypress tap pin --clear` releases it. It works only against an open-mode session on a Chromium-based browser, only once the run has finished, and only for tests still held in memory.

code

bash · 5 lines
bash
npx cypress tap reporter --test-id r4
npx cypress tap command --test-id r4 --command-id 5
npx cypress tap pin --test-id r4 --command-id 5 --at before
npx cypress tap dom --selector '[data-testid="ticket-row"]' --at 0
npx cypress tap pin --clear

go deeper

for a junior

Recall that the same time travel you get by clicking a Command Log row is reachable from a terminal, and that reading the page requires pinning a command first.

for a middle

Explain the order of operations and the addressing: find the row, list its snapshots, pin one by name or index, read it, then clear the pin so later reads see the live page.

for a senior

Show that you know where it stops working — open mode only, Chromium only, a finished run, a bounded memory window — and why a shared frame makes concurrent manual clicking dangerous.

for a principal

Weigh what it changes for a team: an agent or script can now read the same evidence an engineer reads, and you have to decide how much of a debugging loop should depend on one developer's live session.

## What `cypress tap` is `cypress tap` is an extension of the Cypress command-line interface that **attaches to a running `cypress open` session** and lets you read and drive it from a terminal. It talks to the app already on screen — it does not start a headless run of its own — so everything the Cypress app knows about the current run becomes available as text: which specs exist, where the run is in its lifecycle, a test's Command Log, one entry's console properties, and the DOM of the application under test. Its subcommands split into three groups: - **Finding and driving a session** — `sessions`, `status`, `specs`, `run`. - **Reading the Command Log** — `reporter` for a spec or test report, `command` for one entry. - **Reading the page** — `pin` to time travel, then `dom`, `aria` and `inspect` to read what is showing. Every subcommand exits `0` on success and `1` on failure, accepts `--json` for a machine-readable form of the same output, and takes `--session` to target one process id when more than one Cypress app is open. ## Pinning a snapshot from the shell `cypress tap pin` does to the Cypress window exactly what clicking that row in the Command Log does: the application-under-test frame switches to the historical snapshot and stays there until the pin is released. You address a row the way `cypress tap reporter` printed it: | Option | What it names | Default | |---|---|---| | `--test-id` | the test, by the id the reporter printed, such as `r5` | required | | `--command-id` | the row — a number, an `e`-prefixed event id, or hook-qualified like `h1:3` | required | | `--at` | which snapshot on that row, by name (`before`, `after`) or 1-based index | the last one | | `--attempt` | which attempt, when the test was retried | the latest | | `--clear` | release the pin and restore the app to its pre-pin state | off | `cypress tap command --test-id r5 --command-id 3` is how you find out what there is to pin: it prints the row, a `SNAPSHOTS` table with a `#`, `NAME` and `TIME` column, and the entry's console properties expanded three levels deep by default. ## Reading the pinned page While a pin is held, three commands read the pinned snapshot rather than the app's live final state: - **`dom`** returns HTML — the whole page, or the `outerHTML` of the element matched by `--selector`, capped at 30,000 characters by default and adjustable with `--max-chars`. If the selector matches more than one element the command refuses, lists the first matches with indexes, and exits `1`; pass `--at <index>` to pick one. - **`aria`** returns the compact role/name/state accessibility tree, which is usually the cheapest way to understand a page. - **`inspect`** returns one element's tag, attributes, computed styles and box model. Release the pin with `cypress tap pin --clear` when you are done, so the next read sees the live page again. ## What it cannot do The constraints matter more here than the flags, because most of them bite silently: 1. **Open mode only.** It attaches to a `cypress open` session; there is nothing to attach to in a headless `cypress run`. 2. **Chromium-based browsers only** — Chrome, Chromium, Edge and Electron. 3. **The run must be finished.** Pins cannot be created while a spec is running, and `dom`, `aria` and `inspect` need a completed run, exactly as clicking a row in the app does. 4. **The window is bounded.** Only the most recent tests still hold snapshots, per `numTestsKeptInMemory`, so a row far back may have nothing to pin. 5. **A rerun releases the pin**, because the snapshot it named belonged to the previous run. 6. **One frame, two drivers.** The CLI and the person at the keyboard share a single application-under-test frame. Unpinning in the app releases the pin the CLI set, and pinning by hand changes what `dom` reads while `status` still reports no pin — so leave the window alone while the CLI is driving it, and check `status` before trusting a read. ## A worked loop on the ticket queue Chasing a support-ticket spec that fails intermittently, without ever leaving the terminal: ```bash npx cypress tap reporter --test-id r4 npx cypress tap command --test-id r4 --command-id 5 npx cypress tap pin --test-id r4 --command-id 5 --at before npx cypress tap dom --selector '[data-testid="ticket-row"]' --at 0 npx cypress tap pin --clear ``` The `reporter` call gives you the failing test's rows and its error. The `command` call tells you the escalate click carried two snapshots and prints what the entry logged. The `pin` puts the page back to the instant before the click, `dom` prints that ticket row's markup, and `--clear` hands the window back. If the row's markup shows the escalate button already disabled, the click never had a target and the failure is a waiting problem rather than an application bug — the same conclusion you would reach by clicking through the app, reached instead in five commands a script or an agent can run.

  • Why should you leave the Cypress window alone while cypress tap is driving it?
    Both share one application-under-test frame. Unpinning in the app releases the pin the CLI set, and pinning a row by hand changes what `dom`, `aria` and `inspect` read while `status` still reports no pin — so a read can silently describe a different moment than the one you asked for. Check `status` before trusting a read.
  • cypress tap dom reports that your selector matched 25 elements. What does it do, and what do you pass?
    It refuses to guess: it prints the first matches with their indexes and a suggested selector for each, then exits `1`. Pass `--at` with the index of the match you want, or narrow the selector until it is unique. The same disambiguation applies whether you are reading the live page or a pinned snapshot.

saying these in an interview costs you the question

  • Thinks cypress tap starts its own headless run
  • Expects it to attach to a cypress run session
  • Reads the DOM without pinning a command first
  • Leaves a pin held and misreads the next command
  • Assumes any command in the spec is still pinnable