skip to content

Stepping Through a Run

Halting a run to look around: pausing the queue and stepping command by command, dropping into a debugger with the subject in hand, and turning on Node-side logs when the browser is not the problem.

on this pageshow

explore

questions

6

In Cypress open mode, what does cy.pause() let you do to a running spec?

level: juniorimportance: must knowfreq 66%

answer

  1. Stop the run and look around
  2. Two controls appear in the Command Log
  3. One command at a time, then stop
  4. Resume finishes, Next advances one
  5. Only option is log

basics

~20 s

cy.pause() stops the spec before the next queued command and hands control to the Command Log, where Resume finishes the run and Next advances exactly one command. While stopped you can click through the application, inspect the DOM, and read network traffic and storage.

solid answer

~50 s

`cy.pause()` halts the spec at the point it was queued and leaves the session stopped. The Command Log grows two controls: **Resume**, which runs everything still queued, and **Next: '<command>'**, which runs exactly one more command and stops again — that stepping loop is the reason the command exists. Between steps the application under test is live and untouched, so you can interact with it, read the DOM in your browser's element inspector, watch requests, and check `localStorage`. `cy.pause()` chains off `cy` or off another command, yields whatever subject it was given, runs no assertions, and cannot time out — dropping it into a chain does not change what that chain means. Its only option is `log`. Do not chain a command that needs a DOM subject after it, since the page can move on while you sit there.

code

javascript · 10 lines
javascript
it('assigns the oldest open ticket', () => {
  cy.visit('/queue')
  cy.get('[data-cy=ticket-row]').should('have.length', 12)

  // stop here and step the rest one command at a time
  cy.pause()

  cy.get('[data-cy=assign-oldest]').click()
  cy.get('[data-cy=ticket-row]').first().should('contain', 'Assigned')
})

go deeper

for a junior

Be ready to say what the two Command Log controls do and to name one thing you would look at between steps, such as the rendered DOM or a request the app fired.

for a middle

Explain that the pause lands on a command boundary because commands are queued, that it yields its subject unchanged, and that Cypress sets the command timeout aside while stopped.

for a senior

An interviewer will want the operating detail: where you place the pause on a flaky spec, why you skip setup you already trust, and why a leftover pause is harmless in CI but still worth removing.

for a principal

Own the team standard: pausing is a local, interactive tool, so a suite that can only be diagnosed by pausing is telling you its failures carry too little evidence on their own.

## What `cy.pause()` actually stops Cypress does not run `cy.get()`, `.click()` and friends the moment your test body reaches them — it enqueues them and drains the queue after the body returns. `cy.pause()` takes a slot in that queue like any other command, and when its turn arrives it stops the drain and gives the session back to you. Nothing is torn down: the browser still holds the application under test, the Command Log still holds every entry recorded so far, and the commands you wrote after the pause are still waiting, untouched. Two consequences follow from that placement: - **The stop lands on a command boundary**, never inside one. If a `cy.get()` was still retrying when the pause was queued, that `cy.get()` resolves first and the pause happens after it. - **The clock stops too.** `cy.pause()` cannot itself time out, and Cypress puts the current command timeout aside while you are stopped, restoring it when you resume. Nothing behind the pause quietly ages out while you read the screen. ## Resume and Next — the stepping loop While a run is paused the Command Log shows two controls: 1. **Resume** — drain the rest of the queue normally. The pause is spent; it will not stop the same test again. 2. **Next: '<command name>'** — run exactly one more command, then stop again. The label names the command about to run, so you always know what you are about to fire. Clicking **Next** repeatedly is what "stepping through a run" means in Cypress. Each click advances one command and re-renders the app preview, so you watch the page change one instruction at a time instead of guessing which of eight chained commands broke the state. ## What you can do while it is stopped The pause is not a frozen screenshot — it is a live browser with your test halfway through: - Click, scroll and type in the application under test by hand, then step the next command and see whether it still finds what it expected. - Open your browser's element inspector and read the real DOM, including attributes the selector was matching on. - Read the network panel for requests the app has already fired, and inspect `localStorage`, `sessionStorage` and cookies. - Click earlier entries in the Command Log to read what each one yielded. ## Where it sits next to the other halting tools | Tool | What it stops | Needs DevTools open | Gives you a stepping control | |---|---|---|---| | `cy.pause()` | the Cypress run, at a command boundary | no | yes — Resume and Next | | `.debug()` | the JavaScript VM, via a `debugger` statement | yes | no | | `debugger` inside `.then()` | the JavaScript VM, when the callback runs | yes | no | | `cy.log('...')` | nothing — it only writes an entry | no | no | The distinction that catches people out is the first column. `cy.pause()` sets no JavaScript breakpoint, so it works with DevTools closed and it does not drop you into a call stack; `.debug()` sets a breakpoint and does nothing at all unless DevTools are open. ## Practical cautions - **`cy.pause()` is transparent to the chain.** It yields the same subject it was given and runs no assertions — an assertion chained after it passes through as if the pause were not there. That is what makes it safe to sprinkle into an existing chain while diagnosing. - **Do not chain a DOM-dependent command straight after it.** You have had the page open in your hands for a minute; the element the previous command yielded may be detached by the time you resume. Put the pause at the end of a chain, or before a fresh `cy.get()`. - **It takes exactly one option**, `log`, which defaults to `true` and controls whether the pause itself appears in the Command Log. - **Remove it before you commit.** In a headless `cypress run` a leftover `cy.pause()` is silently skipped rather than hanging the pipeline, so it will not fail your CI job — but it is dead weight in the spec and a note to the next reader that someone was mid-debug. A useful habit on an intermittently failing suite is to pause immediately *before* the step you suspect, not at the top of the test: `cy.visit('/queue')`, assert the queue rendered, then `cy.pause()` and step into the assignment flow. You skip the setup you already trust and arrive at the interesting moment with the app in exactly the state the test built.

  • Can Cypress's cy.pause() sit partway through a chain rather than at the start of a test?
    Yes. `cy.pause()` chains off another command as well as off `cy`, so `cy.get('[data-cy=ticket-row]').first().pause()` stops after the row is found and yields that same row. It runs no assertions and changes no subject, so inserting it does not alter the chain's meaning. Just avoid chaining a DOM-dependent command after it, because the element can detach while you sit there.
  • How does Cypress's cy.pause() differ from .debug()?
    `cy.pause()` stops the Cypress run at a command boundary and gives you Resume and Next controls in the Command Log; it sets no JavaScript breakpoint and works with DevTools closed. `.debug()` hits a `debugger` statement, which stops nothing unless DevTools are open, and prints the previous command's name, arguments and current subject to the console. Pause to step; debug to inspect a subject.

It is the difference between watching a film and holding the remote: the run keeps its whole state, and you decide when the next frame plays.

saying these in an interview costs you the question

  • Thinks cy.pause() sets a JavaScript breakpoint
  • Says cy.pause() behaves the same in a headless cypress run
  • Believes queued commands keep timing out while paused
  • Confuses cy.pause() with a fixed cy.wait() delay
open as a page

In a Cypress test, why does .debug() pause where a bare debugger line does not?

level: middleimportance: must knowfreq 60%

basics

~20 s

A bare debugger in the test body runs immediately, before any queued cy command has executed, so it stops on an untouched page. .debug() is itself queued, so it fires in order, with the previous command's subject in hand — the same effect as a debugger inside .then().

open as a page

In Cypress, what does Cypress.log() give a custom command that cy.log() does not?

level: middleimportance: should knowfreq 48%

basics

~20 s

cy.log() only appends a plain message entry. Cypress.log() creates a real Command Log entry you control: its name, display name and message, a consoleProps function whose object prints in DevTools when the entry is clicked, and a Log object with end, error, set and snapshot methods.

open as a page

In Cypress, what does DEBUG=cypress:* print, and how do you narrow it down?

level: seniorimportance: should knowfreq 42%

basics

~20 s

It streams the internal logs of Cypress's own Node-side packages to the terminal — argument parsing, project opening, browser launch, spec bundling, network interception — not your application. Narrow it by naming a package or module, combining namespaces with commas, and excluding one with a leading minus.

open as a page

In Cypress, how far should a suite go to hand-shape its Command Log with Cypress.log() and { log: false }?

level: principalimportance: should knowfreq 36%

basics

~20 s

Far enough that a failing run reads as domain steps to someone without the spec open, and no further. Add a Cypress.log() entry where a helper would otherwise be one opaque line; silence plumbing with { log: false }. Hand-written entries rot, so each must earn its upkeep.

open as a page

In a headless Cypress `cypress run`, what happens when a spec calls cy.pause()?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

Nothing. Cypress skips the pause entirely: it yields its subject unchanged and does not even add a Command Log entry, so a forgotten cy.pause() cannot hang a pipeline. To make it stop during a run you need a visible browser that will not exit: cypress run --headed --no-exit.

open as a page