skip to content

Test Runner and Commands

The command chain end to end: what a spec enqueues, what each step yields, which steps retry, and what resets between tests. Most Cypress mistakes start here.

on this pageshow

explore

questions

page 1 of 2

In Cypress, what does `.click()` check about an element before it fires the click?

level: juniorimportance: must knowfreq 76%

answer

  1. The action waits for the element
  2. A fixed list, re-run until it passes
  3. Scroll, hidden, disabled, detached, readonly
  4. Bounded by defaultCommandTimeout, not a sleep
  5. The error names the check that failed

basics

~20 s

Cypress re-runs a fixed readiness list before every action: it scrolls the element into view, then requires it to be attached, visible, enabled, not readonly, not animating and not covered at the point the event will land.

solid answer

~40 s

`.click()` does not fire until the element passes Cypress's actionability checks, and Cypress re-runs that whole list — re-querying the subject each time — until it passes or `defaultCommandTimeout` (`4000` ms) runs out. Cypress scrolls the element into view using `scrollBehavior`, then checks it is not hidden, not `disabled`, not detached from the document, not readonly (only `.type()` and `.clear()` ask for that), not animating, and not covered at the point the event will land — nudging the page when a `position: fixed` or `sticky` element is in the way. Only then does it compute the coordinates, the element's centre unless you pass `position`, and fire the events. A failure names the check that never passed, so the error itself tells you what to fix.

code

javascript · 7 lines
javascript
cy.visit('/catalogue?q=dune')

cy.contains('[data-cy=book-row]', 'Dune')
  .find('[data-cy=borrow]')
  .click({ timeout: 10000, scrollBehavior: 'center' })

cy.get('[data-cy=loan-count]').should('have.text', '1')

go deeper

for a junior

Be ready to name the list without prompting: scroll into view, then not hidden, disabled, detached, readonly, animating or covered, then fire at the coordinates.

for a middle

Explain that the whole list is re-run on every retry against a freshly queried subject, and that the budget is defaultCommandTimeout rather than a fixed pause.

for a senior

Show that you read the specific failure text rather than adding force, and that you know which check produced which message.

for a principal

An interviewer at this level wants the policy: which of these checks a suite is allowed to switch off, where that decision is recorded, and how it is reviewed.

## Why an action command waits at all Cypress runs your spec inside the browser, in the same event loop as the application, and an **action command** simulates the events a real user's interaction would produce. Firing those events at an element a user could not reach would prove nothing, so before `.click()`, `.dblclick()`, `.rightclick()`, `.type()`, `.clear()`, `.check()`, `.uncheck()`, `.select()`, `.trigger()` or `.selectFile()` does anything, Cypress asks whether the element is in a state that would accept the interaction. That question is answered by a **fixed list of checks and actions** that Cypress calls **actionability** — always the same list, in the same order, for every action command. ## The list Cypress runs before the event | Check or action | What it means | Typical cause of a failure | |---|---|---| | Scroll the element into view | Uses `scrollBehavior`, `'top'` by default | not a gate — Cypress always scrolls | | Not hidden | The element is visible by the configured `visibilityStrategy` | a results panel collapsed with `display: none` | | Not disabled | The `disabled` property is not `true` on a form control | the Borrow button still `disabled` while availability loads | | Not detached | The element is still inside the application's `document` | the book row re-rendered under the command | | Not readonly | Only `.type()` and `.clear()` ask for this | the search box locked while a request is in flight | | Not animating | Movement between position samples stays under `animationDistanceThreshold` | a drawer still sliding open | | Not covered | Nothing else sits at the point the event will land on | a cookie banner over the button's centre | | Scroll again if still covered | Nudges the page past a `fixed` or `sticky` coverer | a sticky catalogue header pinned to the top | | Fire at the coordinates | The centre of the element unless you pass `position` | — | Two entries on that list are worth calling out. **Cypress scrolls every time**, even when the element was already on screen, so that a command behaves identically on every run. And **coverage is judged at a single point**, not across the whole box, so a button that is half under an overlay still fails when its centre is the covered part. ## It re-runs the list; it does not sleep The list is not evaluated once. Cypress evaluates it, and if any check fails it evaluates the whole thing again from the top: 1. The subject is re-queried from the chain that produced it, so a row the app has just re-rendered can be picked up on a later attempt. 2. Every check runs again in order — a check that passed a moment ago is not trusted. 3. The moment all of them pass, the events fire **synchronously**, so nothing can change between the last check and the event. The budget for that loop is `defaultCommandTimeout`, `4000` ms by default, or the `{ timeout }` you pass to the individual command. When it runs out, the error you see is the last check that failed, which is why the message is usually specific rather than a bare timeout. ## Reading the failure Each check has its own wording, and the wording is the diagnosis: - `failed because this element is not visible` — with a reason line explaining which rule hid it. - `failed because this element is disabled` — the `disabled` property, not a class or `aria` attribute. - `failed because this element is readonly` — only ever from `.type()` or `.clear()`. - `could not be issued because this element is currently animating` — with the three options that relax it. - `failed because this element ... is being covered by another element` — and it prints the coverer. - `failed because this element ... has CSS pointer-events: none` — a different rule from coverage. ## What the list deliberately leaves out - **Queries never scroll.** `cy.get()`, `.find()`, `.first()` and friends locate elements without moving the page; only action commands scroll. - **`opacity: 0` is actionable.** An element at zero opacity fails a `should('be.visible')` assertion, but an action command will still interact with it. - **`disabled` only counts on form controls.** A `disabled` attribute on a `<div>` does not stop a real user, so it does not stop Cypress either. - **Readonly is not checked for clicks.** A readonly input is perfectly clickable; only typing commands request that check. ## What changed in Cypress 16 As of Cypress 16 the visibility check delegates to the browser's own `Element.checkVisibility()` API under `visibilityStrategy: 'modern'`, which is the default, and `scrollBehavior` additionally accepts a per-axis `{ block, inline }` object as well as the whole-alignment values `'start'`, `'end'`, `'center'` and `'nearest'`. The list itself is unchanged: the same checks run, in the same place, before every action.

  • Which Cypress commands run this readiness list?
    The action commands: `.click()`, `.dblclick()`, `.rightclick()`, `.type()`, `.clear()`, `.check()`, `.uncheck()`, `.select()`, `.trigger()` and `.selectFile()`. Queries such as `cy.get()` and `.find()` do not — they locate elements without touching the page, which is why they never scroll anything into view.
  • Does the readiness list check that an element is `opacity: 0`?
    No, not for actions. An element at zero opacity is treated as actionable and receives the events, even though a `should('be.visible')` assertion on it fails. The two rules deliberately differ: an assertion is asking what the user can see, an action is asking what the user could reach.

saying these in an interview costs you the question

  • Cypress just sleeps before each click
  • The checks run once, not on every retry
  • cy.get() also scrolls the element into view
  • A disabled attribute on a div blocks the click
  • An opacity: 0 element can never be clicked
open as a page

In Cypress, what does `.type()` do with text wrapped in curly braces?

level: juniorimportance: must knowfreq 72%

basics

~20 s

Cypress reads curly braces in .type() as special key sequences rather than literal characters, so {enter} presses Enter and {selectall} selects the field. An unrecognised sequence throws. Type a literal brace with {{}, or pass the option parseSpecialCharSequences: false.

open as a page

In Cypress, what does `const rows = cy.get('[data-cy=book-row]')` put into `rows`?

level: juniorimportance: must knowfreq 88%

basics

~20 s

A Cypress chainer object: a handle you use to append more commands to that chain. It holds no element and no jQuery data, because at the moment the assignment runs cy.get() has only been queued, not executed.

open as a page

In a Cypress test, when do the cy commands in the test body actually run?

level: juniorimportance: must knowfreq 88%

basics

~20 s

Cypress commands do not run when you call them. Each cy call only appends a step to an internal command queue and returns immediately; Cypress drains that queue, one step at a time, after the test body function has returned.

open as a page

In Cypress, what does cy.wrap() do, and when do you need it?

level: juniorimportance: must knowfreq 70%

basics

~20 s

cy.wrap() starts a Cypress chain whose subject is the value you hand it - a plain object, a number, a jQuery element or a promise - so queries, .should() assertions, .its() and .invoke() can run against it.

open as a page

Why does a Cypress alias created in a before() hook work only in the first test?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Cypress clears every alias before each test. Mocha's before() hook runs once per suite, so its alias survives into the first test only, and later tests fail with could not find a registered alias. Register it in beforeEach() instead.

open as a page

In a Cypress spec, in what order do before, beforeEach, afterEach and after run?

level: juniorimportance: must knowfreq 76%

basics

~20 s

Cypress inherits Mocha's hooks. Each before runs once when its describe block starts; then for every it, beforeEach runs, the test runs, afterEach runs; after runs once when the block ends. Outer blocks wrap inner ones.

open as a page

In Cypress, what is the difference between a query command and an action command?

level: juniorimportance: must knowfreq 78%

basics

~20 s

A Cypress query such as cy.get() or .find() is a repeatable read: it re-runs and relinks the chain until the next assertion passes. An action such as .click() changes the app, so it fires once and never relinks.

open as a page

In Cypress, what does `{ force: true }` on `.click()` actually turn off?

level: middleimportance: must knowfreq 68%

basics

~10 s

It skips eight things at once: scrolling into view and the not-hidden, not-disabled, not-detached, not-readonly, not-animating and not-covered checks, plus firing at a descendant. The event goes straight to the element you targeted.

open as a page

In Cypress, what happens when you make a spec's `it()` body an `async` function?

level: middleimportance: must knowfreq 74%

basics

~20 s

Cypress warns in the browser console, then hands Mocha your async function's promise instead of its own command-queue promise. Commands still run in written order once the body yields, so await buys nothing and loosens the test's completion signal.

open as a page

In Cypress, what does a .then() callback's return value do to the next command's subject?

level: middleimportance: must knowfreq 60%

basics

~20 s

A returned value becomes the subject of the next Cypress command. Returning nothing leaves the previous subject in place - unless the callback issued Cypress commands, in which case the last of those commands' yield carries on instead.

open as a page

With Cypress testIsolation on, what is reset between tests and what survives?

level: middleimportance: must knowfreq 64%

basics

~20 s

Cypress clears the page by visiting about:blank and clears cookies, localStorage and sessionStorage in all domains. Everything else, IndexedDB above all, survives untouched, so a test that needs an empty IndexedDB must clear it itself.

open as a page

In Cypress, how many times does a .then() callback run while a later assertion retries?

level: middleimportance: must knowfreq 62%

basics

~10 s

Exactly once. .then() is not a query, so Cypress never re-runs it or anything before it; an assertion after it keeps retrying against the single value the callback already yielded, then times out.

open as a page

In Cypress, when a .should() at the end of cy.get().find() fails, what re-runs?

level: middleimportance: must knowfreq 66%

basics

~10 s

The whole linked run of queries re-runs from the top. Cypress re-executes cy.get() against the current DOM, feeds the result into .find(), and re-checks the assertion, looping until it passes or defaultCommandTimeout expires.

open as a page

When a Cypress command fails, what happens to the commands still queued behind it?

level: seniorimportance: must knowfreq 56%

basics

~20 s

They never run. A failed Cypress command ends the test — everything still queued behind it is abandoned and the test is reported failed. Cypress offers no catch handler on a command, so a step cannot recover and carry on.

open as a page

A Cypress test that borrows the last copy of a book passes, then fails on every retry. Why?

level: seniorimportance: must knowfreq 42%

basics

~20 s

A Cypress retry repeats only the test and its beforeEach and afterEach hooks. Suite-level seeding in a before hook is not repeated, and the backend is not rewound, so the second attempt runs against a catalogue whose last copy is already on loan.

open as a page

In Cypress, what do retries.runMode and retries.openMode control, and what are their defaults?

level: juniorimportance: should knowfreq 55%

basics

~20 s

They set how many extra attempts Cypress gives a failing test: runMode applies to cypress run, openMode to cypress open. Both default to 0, so an unconfigured suite never retries a failing test at all.

open as a page

In Cypress, why does `.click()` error on 14 matched rows while `.dblclick()` does not?

level: middleimportance: should knowfreq 55%

basics

~20 s

Cypress's .click() and .rightclick() default to multiple: false and refuse a subject holding more than one element, telling you to pass { multiple: true }. .dblclick() ships with multiple: true, so it double-clicks each matched element in turn without being asked.

open as a page

Why does a Cypress `.then()` that queues a command and returns a value throw?

level: middleimportance: should knowfreq 46%

basics

~20 s

Because Cypress reads it as mixing async and sync code: the callback queued commands that have not run yet, then handed back a finished value. Cypress throws with the message that cy.then() failed because you are mixing up async and sync code.

open as a page

Why does Cypress drain its command queue serially instead of running commands in parallel?

level: middleimportance: should knowfreq 52%

basics

~20 s

Most Cypress commands mutate browser state — they navigate, set cookies, fire clicks. Running them at once would make a result depend on timing, so Cypress drains its queue one step at a time, each finishing before the next begins.

open as a page

In Cypress, when do you reach for .its() and when for .invoke()?

level: middleimportance: should knowfreq 52%

basics

~20 s

Cypress's .its(path) reads a property off the yielded subject and yields its value; .invoke(name, args) calls a method of that name and yields what it returns. A property holding a function comes back from .its() uncalled.

open as a page

In Cypress, how do cy.get('@books') and this.books differ when reading an alias?

level: middleimportance: should knowfreq 60%

basics

~20 s

cy.get('@books') re-runs the query chain stored with the alias, so it yields a fresh result on every read. this.books is a snapshot written onto Mocha's test context when .as() ran, and it needs a function() test, not an arrow.

open as a page

A Cypress `.click()` fails saying the Borrow button is covered by another element. How do you diagnose it?

level: seniorimportance: should knowfreq 54%

basics

~20 s

Read the second element in the message: that is what sat at the point the click would have landed. Cypress tests only that one point, and it has already tried nudge-scrolling if the coverer was fixed or sticky.

open as a page

Why can a Cypress spec that types into a search box break on the upgrade to 16?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Cypress 16 changed .type()'s default keystroke delay from 10 milliseconds to 0, so the whole string now goes in with no gap. A debounced typeahead that used to fire a request per pause now fires once, and timing-dependent assertions change with it.

open as a page

In Cypress, what happens if a .then() callback returns a Cypress.Promise?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Cypress bundles Bluebird and exposes it as Cypress.Promise. When a queued step's callback returns one, Cypress holds the drain and does not start the next queued command until that promise settles, so real async work fits inside the queue's order.

open as a page

Why can a Cypress .invoke() call the method more than once, and how do you stop it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

As of Cypress 16, .invoke() is a query: it re-runs whenever a command chained after it retries, so the method fires again on every attempt. End the chain at .invoke(), or call the method yourself inside a .then() callback.

open as a page

In a Cypress spec, how do you give every test a value that a slow before() hook read once?

level: seniorimportance: should knowfreq 46%

basics

~10 s

Keep the slow work in before() and capture its result into a variable in the describe scope from inside a .then() callback, then re-register a cheap alias for every test with cy.wrap(value).as('name') in beforeEach().

open as a page

A Cypress beforeEach hook throws: what happens to the rest of that suite?

level: seniorimportance: should knowfreq 48%

basics

~20 s

The test the hook was running for is reported as failed, and every remaining test in that suite is marked skipped rather than run, because the same hook would fail for each of them. A root-level hook skips all remaining tests.

open as a page

In Cypress, why can a .should() in the middle of a chain leave the next query with a stale element?

level: seniorimportance: should knowfreq 46%

basics

~20 s

A mid-chain assertion is a retry boundary. Once it passes, Cypress locks in the subject at that point, so later queries retry from that element instead of re-querying from the top - and a re-render leaves it detached.

open as a page

showing 1–30 of 44